Snapshot 1319ed83a94fd506e54a43d6cef8b6bb2295c525

Change-Id: Ifeff1cc0867b7a1459e6d83cb354883f134fda74
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..adde173
--- /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 \
+		dexgen \
+		dexlist \
+		dexopt \
+		dexdump \
+		dx \
+		tools \
+		unit-tests \
+	))
+
+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..3fbdc64
--- /dev/null
+++ b/CleanSpec.mk
@@ -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.
+#
+
+# 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*)
+$(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..4760004
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,223 @@
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Android-specific code.                        ==
+   =========================================================================
+
+   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
+
+   =========================================================================
+   ==  NOTICE file for the x86 JIT libenc subdirectory.                   ==
+   =========================================================================
+
+Apache Harmony
+Copyright 2006, 2010 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Portions of Harmony were originally developed by
+Intel Corporation and are licensed to the Apache Software
+Foundation under the "Software Grant and Corporate Contribution
+License Agreement" and for which the following copyright notices
+apply
+         (C) Copyright 2005 Intel Corporation
+         (C) Copyright 2005-2006 Intel Corporation
+         (C) Copyright 2006 Intel Corporation
+
+Portions of the Apache Portable Runtime used by DRLVM were
+developed at the National Center for Supercomputing Applications
+(NCSA) at the University of Illinois at Urbana-Champaign.
+
+This software contains code derived from the RSA Data Security
+Inc. MD5 Message-Digest Algorithm.
+
+This software contains code derived from UNIX V7, Copyright(C)
+Caldera International Inc.
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..734f6f5
--- /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.cpp
+
+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
+    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.cpp b/dalvikvm/Main.cpp
new file mode 100644
index 0000000..4aa6e20
--- /dev/null
+++ b/dalvikvm/Main.cpp
@@ -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("java/lang/String");
+    if (env->ExceptionCheck()) {
+        fprintf(stderr, "Got exception while finding class String\n");
+        goto bail;
+    }
+    assert(stringClass != NULL);
+    strArray = env->NewObjectArray(argc, stringClass, NULL);
+    if (env->ExceptionCheck()) {
+        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(argv[i]);
+        if (env->ExceptionCheck()) {
+            fprintf(stderr, "Got exception while allocating Strings\n");
+            goto bail;
+        }
+        assert(argStr != NULL);
+        env->SetObjectArrayElement(strArray, i, argStr);
+        env->DeleteLocalRef(argStr);
+    }
+
+    /* return the array, and ensure we don't delete the local ref to it */
+    result = strArray;
+    strArray = NULL;
+
+bail:
+    env->DeleteLocalRef(stringClass);
+    env->DeleteLocalRef(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(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("java/lang/reflect/Method");
+    if (methodClass == NULL) {
+        fprintf(stderr, "Dalvik VM unable to find class Method\n");
+        goto bail;
+    }
+    getModifiersId = env->GetMethodID(methodClass,
+                        "getModifiers", "()I");
+    if (getModifiersId == NULL) {
+        fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n");
+        goto bail;
+    }
+
+    modifiers = env->CallIntMethod(refMethod, getModifiersId);
+    if ((modifiers & PUBLIC) == 0) {
+        fprintf(stderr, "Dalvik VM: main() is not public\n");
+        goto bail;
+    }
+
+    result = JNI_TRUE;
+
+bail:
+    env->DeleteLocalRef(refMethod);
+    env->DeleteLocalRef(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(slashClass);
+    if (startClass == NULL) {
+        fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass);
+        goto bail;
+    }
+
+    startMeth = env->GetStaticMethodID(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(startClass, startMeth, strArray);
+
+    if (!env->ExceptionCheck())
+        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() != JNI_OK) {
+            fprintf(stderr, "Warning: unable to detach main thread\n");
+            result = 1;
+        }
+
+        if (vm->DestroyJavaVM() != 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..7c22b60
--- /dev/null
+++ b/dexdump/Android.mk
@@ -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.
+
+#
+# dexdump, similar in purpose to objdump.
+#
+LOCAL_PATH:= $(call my-dir)
+
+dexdump_src_files := \
+		DexDump.cpp
+
+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)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdexdump_static
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host command line tool dexdump
+##
+##
+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)
diff --git a/dexdump/DexDump.cpp b/dexdump/DexDump.cpp
new file mode 100644
index 0000000..0d7b425
--- /dev/null
+++ b/dexdump/DexDump.cpp
@@ -0,0 +1,1915 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/CmdUtils.h"
+#include "libdex/DexCatch.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexDebugInfo.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexProto.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/SysUtil.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";
+
+enum OutputFormat {
+    OUTPUT_PLAIN = 0,               /* default */
+    OUTPUT_XML,                     /* fancy */
+};
+
+/* command-line options */
+struct Options {
+    bool checksumOnly;
+    bool disassemble;
+    bool showFileHeaders;
+    bool showSectionHeaders;
+    bool ignoreBadChecksum;
+    bool dumpRegisterMaps;
+    OutputFormat outputFormat;
+    const char* tempFileName;
+    bool exportsOnly;
+    bool verbose;
+};
+
+struct Options gOptions;
+
+/* basic info about a field or method */
+struct FieldMethodInfo {
+    const char* classDescriptor;
+    const char* name;
+    const char* signature;
+};
+
+/*
+ * 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 = (char*)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().
+ */
+enum AccessFor {
+    kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
+    kAccessForMAX
+};
+
+/*
+ * 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);
+}
+
+/*
+ * Helper for dumpInstruction(), which builds the string
+ * representation for the index in the given instruction. This will
+ * first try to use the given buffer, but if the result won't fit,
+ * then this will allocate a new buffer to hold the result. A pointer
+ * to the buffer which holds the full result is always returned, and
+ * this can be compared with the one passed in, to see if the result
+ * needs to be free()d.
+ */
+static char* indexString(DexFile* pDexFile,
+    const DecodedInstruction* pDecInsn, char* buf, size_t bufSize)
+{
+    int outSize;
+    u4 index;
+    u4 width;
+
+    /* TODO: Make the index *always* be in field B, to simplify this code. */
+    switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
+    case kFmt20bc:
+    case kFmt21c:
+    case kFmt35c:
+    case kFmt35ms:
+    case kFmt3rc:
+    case kFmt3rms:
+    case kFmt35mi:
+    case kFmt3rmi:
+        index = pDecInsn->vB;
+        width = 4;
+        break;
+    case kFmt31c:
+        index = pDecInsn->vB;
+        width = 8;
+        break;
+    case kFmt22c:
+    case kFmt22cs:
+        index = pDecInsn->vC;
+        width = 4;
+        break;
+    default:
+        index = 0;
+        width = 4;
+        break;
+    }
+
+    switch (pDecInsn->indexType) {
+    case kIndexUnknown:
+        /*
+         * This function shouldn't ever get called for this type, but do
+         * something sensible here, just to help with debugging.
+         */
+        outSize = snprintf(buf, bufSize, "<unknown-index>");
+        break;
+    case kIndexNone:
+        /*
+         * This function shouldn't ever get called for this type, but do
+         * something sensible here, just to help with debugging.
+         */
+        outSize = snprintf(buf, bufSize, "<no-index>");
+        break;
+    case kIndexVaries:
+        /*
+         * This one should never show up in a dexdump, so no need to try
+         * to get fancy here.
+         */
+        outSize = snprintf(buf, bufSize, "<index-varies> // thing@%0*x",
+                width, index);
+        break;
+    case kIndexTypeRef:
+        if (index < pDexFile->pHeader->typeIdsSize) {
+            outSize = snprintf(buf, bufSize, "%s // type@%0*x",
+                               getClassDescriptor(pDexFile, index), width, index);
+        } else {
+            outSize = snprintf(buf, bufSize, "<type?> // type@%0*x", width, index);
+        }
+        break;
+    case kIndexStringRef:
+        if (index < pDexFile->pHeader->stringIdsSize) {
+            outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x",
+                               dexStringById(pDexFile, index), width, index);
+        } else {
+            outSize = snprintf(buf, bufSize, "<string?> // string@%0*x",
+                               width, index);
+        }
+        break;
+    case kIndexMethodRef:
+        {
+            FieldMethodInfo methInfo;
+            if (getMethodInfo(pDexFile, index, &methInfo)) {
+                outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x",
+                        methInfo.classDescriptor, methInfo.name,
+                        methInfo.signature, width, index);
+                free((void *) methInfo.signature);
+            } else {
+                outSize = snprintf(buf, bufSize, "<method?> // method@%0*x",
+                        width, index);
+            }
+        }
+        break;
+    case kIndexFieldRef:
+        {
+            FieldMethodInfo fieldInfo;
+            if (getFieldInfo(pDexFile, index, &fieldInfo)) {
+                outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x",
+                        fieldInfo.classDescriptor, fieldInfo.name,
+                        fieldInfo.signature, width, index);
+            } else {
+                outSize = snprintf(buf, bufSize, "<field?> // field@%0*x",
+                        width, index);
+            }
+        }
+        break;
+    case kIndexInlineMethod:
+        outSize = snprintf(buf, bufSize, "[%0*x] // inline #%0*x",
+                width, index, width, index);
+        break;
+    case kIndexVtableOffset:
+        outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x",
+                width, index, width, index);
+        break;
+    case kIndexFieldOffset:
+        outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index);
+        break;
+    default:
+        outSize = snprintf(buf, bufSize, "<?>");
+        break;
+    }
+
+    if (outSize >= (int) bufSize) {
+        /*
+         * The buffer wasn't big enough; allocate and retry. Note:
+         * snprintf() doesn't count the '\0' as part of its returned
+         * size, so we add explicit space for it here.
+         */
+        outSize++;
+        buf = (char*)malloc(outSize);
+        if (buf == NULL) {
+            return NULL;
+        }
+        return indexString(pDexFile, pDecInsn, buf, outSize);
+    } else {
+        return buf;
+    }
+}
+
+/*
+ * Dump a single instruction.
+ */
+void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
+    int insnWidth, const DecodedInstruction* pDecInsn)
+{
+    char indexBufChars[200];
+    char *indexBuf = indexBufChars;
+    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));
+    }
+
+    if (pDecInsn->indexType != kIndexNone) {
+        indexBuf = indexString(pDexFile, pDecInsn,
+                indexBufChars, sizeof(indexBufChars));
+    }
+
+    switch (dexGetFormatFromOpcode(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
+    case kFmt31c:        // op vAA, thing@BBBBBBBB
+        printf(" v%d, %s", pDecInsn->vA, indexBuf);
+        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
+    case kFmt22cs:       // [opt] op vA, vB, field offset CCCC
+        printf(" v%d, v%d, %s", pDecInsn->vA, pDecInsn->vB, indexBuf);
+        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 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 {vC, vD, vE, vF, vG}, thing@BBBB
+    case kFmt35ms:       // [opt] invoke-virtual+super
+    case kFmt35mi:       // [opt] inline invoke
+        {
+            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("}, %s", indexBuf);
+        }
+        break;
+    case kFmt3rc:        // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
+    case kFmt3rms:       // [opt] invoke-virtual+super/range
+    case kFmt3rmi:       // [opt] execute-inline/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("}, %s", indexBuf);
+        }
+        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 kFmt00x:        // unknown op or breakpoint
+        break;
+    default:
+        printf(" ???");
+        break;
+    }
+
+    putchar('\n');
+
+    if (indexBuf != indexBufChars) {
+        free(indexBuf);
+    }
+}
+
+/*
+ * 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);
+    free((void *) methInfo.signature);
+
+    insnIdx = 0;
+    while (insnIdx < (int) pCode->insnsSize) {
+        int insnWidth;
+        DecodedInstruction decInsn;
+        u2 instr;
+
+        /*
+         * Note: This code parallels the function
+         * dexGetWidthFromInstruction() in InstrUtils.c, but this version
+         * can deal with data in either endianness.
+         *
+         * TODO: Figure out if this really matters, and possibly change
+         * this to just use dexGetWidthFromInstruction().
+         */
+        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 opcode = dexOpcodeFromCodeUnit(instr);
+            insnWidth = dexGetWidthFromOpcode(opcode);
+            if (insnWidth == 0) {
+                fprintf(stderr,
+                    "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
+                break;
+            }
+        }
+
+        dexDecodeInstruction(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*) (((uintptr_t) 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 = (const u1*)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) {
+        return result;
+    }
+    mapped = true;
+
+    int flags = kDexParseVerifyChecksum;
+    if (gOptions.ignoreBadChecksum)
+        flags |= kDexParseContinueOnError;
+
+    pDexFile = dexFileParse((u1*)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;
+    }
+
+    if (wantUsage) {
+        usage();
+        return 2;
+    }
+
+    int result = 0;
+    while (optind < argc) {
+        result |= process(argv[optind++]);
+    }
+
+    return (result != 0);
+}
diff --git a/dexdump/NOTICE b/dexdump/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/dexdump/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/dexgen/Android.mk b/dexgen/Android.mk
new file mode 100644
index 0000000..59d623a
--- /dev/null
+++ b/dexgen/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 4
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := dexgen
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/dexgen/README.txt b/dexgen/README.txt
new file mode 100644
index 0000000..a542f04
--- /dev/null
+++ b/dexgen/README.txt
@@ -0,0 +1,3 @@
+Home of dexgen, the dex code generator project. It provides API for
+creating dex classes in runtime which is needed e.g. for class mocking.
+This solution is based on the dx tool and uses its classes extensively.
diff --git a/dexgen/src/com/android/dexgen/dex/code/ArrayData.java b/dexgen/src/com/android/dexgen/dex/code/ArrayData.java
new file mode 100644
index 0000000..d89a93f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/ArrayData.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.cst.*;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+
+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/dexgen/src/com/android/dexgen/dex/code/BlockAddresses.java b/dexgen/src/com/android/dexgen/dex/code/BlockAddresses.java
new file mode 100644
index 0000000..fa8096e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.BasicBlock;
+import com.android.dexgen.rop.code.BasicBlockList;
+import com.android.dexgen.rop.code.Insn;
+import com.android.dexgen.rop.code.RopMethod;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/CatchBuilder.java b/dexgen/src/com/android/dexgen/dex/code/CatchBuilder.java
new file mode 100644
index 0000000..adb119a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/CatchHandlerList.java b/dexgen/src/com/android/dexgen/dex/code/CatchHandlerList.java
new file mode 100644
index 0000000..54200ed
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.util.FixedSizeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/CatchTable.java b/dexgen/src/com/android/dexgen/dex/code/CatchTable.java
new file mode 100644
index 0000000..4de0da0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/CodeAddress.java b/dexgen/src/com/android/dexgen/dex/code/CodeAddress.java
new file mode 100644
index 0000000..b9600ee
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/CstInsn.java b/dexgen/src/com/android/dexgen/dex/code/CstInsn.java
new file mode 100644
index 0000000..901266b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/DalvCode.java b/dexgen/src/com/android/dexgen/dex/code/DalvCode.java
new file mode 100644
index 0000000..2df49ed
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/DalvInsn.java b/dexgen/src/com/android/dexgen/dex/code/DalvInsn.java
new file mode 100644
index 0000000..95b5feb
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/DalvInsnList.java b/dexgen/src/com/android/dexgen/dex/code/DalvInsnList.java
new file mode 100644
index 0000000..15f82c1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstBaseMethodRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ExceptionWithContext;
+import com.android.dexgen.util.FixedSizeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/DalvOps.java b/dexgen/src/com/android/dexgen/dex/code/DalvOps.java
new file mode 100644
index 0000000..1d051ea
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/dex/code/Dop.java b/dexgen/src/com/android/dexgen/dex/code/Dop.java
new file mode 100644
index 0000000..dc788f1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/dex/code/Dops.java b/dexgen/src/com/android/dexgen/dex/code/Dops.java
new file mode 100644
index 0000000..afd21e3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.dex.code.form.Form10t;
+import com.android.dexgen.dex.code.form.Form10x;
+import com.android.dexgen.dex.code.form.Form11n;
+import com.android.dexgen.dex.code.form.Form11x;
+import com.android.dexgen.dex.code.form.Form12x;
+import com.android.dexgen.dex.code.form.Form20t;
+import com.android.dexgen.dex.code.form.Form21c;
+import com.android.dexgen.dex.code.form.Form21h;
+import com.android.dexgen.dex.code.form.Form21s;
+import com.android.dexgen.dex.code.form.Form21t;
+import com.android.dexgen.dex.code.form.Form22b;
+import com.android.dexgen.dex.code.form.Form22c;
+import com.android.dexgen.dex.code.form.Form22s;
+import com.android.dexgen.dex.code.form.Form22t;
+import com.android.dexgen.dex.code.form.Form22x;
+import com.android.dexgen.dex.code.form.Form23x;
+import com.android.dexgen.dex.code.form.Form30t;
+import com.android.dexgen.dex.code.form.Form31c;
+import com.android.dexgen.dex.code.form.Form31i;
+import com.android.dexgen.dex.code.form.Form31t;
+import com.android.dexgen.dex.code.form.Form32x;
+import com.android.dexgen.dex.code.form.Form35c;
+import com.android.dexgen.dex.code.form.Form3rc;
+import com.android.dexgen.dex.code.form.Form51l;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/FixedSizeInsn.java b/dexgen/src/com/android/dexgen/dex/code/FixedSizeInsn.java
new file mode 100644
index 0000000..28d8986
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/FixedSizeInsn.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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/HighRegisterPrefix.java b/dexgen/src/com/android/dexgen/dex/code/HighRegisterPrefix.java
new file mode 100644
index 0000000..08794ff
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/InsnFormat.java b/dexgen/src/com/android/dexgen/dex/code/InsnFormat.java
new file mode 100644
index 0000000..bd5b60d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.rop.cst.CstKnownNull;
+import com.android.dexgen.rop.cst.CstLiteral64;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/LocalEnd.java b/dexgen/src/com/android/dexgen/dex/code/LocalEnd.java
new file mode 100644
index 0000000..130b08b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/LocalList.java b/dexgen/src/com/android/dexgen/dex/code/LocalList.java
new file mode 100644
index 0000000..c1c3921
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/LocalSnapshot.java b/dexgen/src/com/android/dexgen/dex/code/LocalSnapshot.java
new file mode 100644
index 0000000..81b78c9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/LocalStart.java b/dexgen/src/com/android/dexgen/dex/code/LocalStart.java
new file mode 100644
index 0000000..ba426e8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/OddSpacer.java b/dexgen/src/com/android/dexgen/dex/code/OddSpacer.java
new file mode 100644
index 0000000..8e2bab8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/OutputCollector.java b/dexgen/src/com/android/dexgen/dex/code/OutputCollector.java
new file mode 100644
index 0000000..7f970eb
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/dex/code/OutputFinisher.java b/dexgen/src/com/android/dexgen/dex/code/OutputFinisher.java
new file mode 100644
index 0000000..98c7e4e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.LocalItem;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMemberRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/PositionList.java b/dexgen/src/com/android/dexgen/dex/code/PositionList.java
new file mode 100644
index 0000000..8b52f26
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/RopToDop.java b/dexgen/src/com/android/dexgen/dex/code/RopToDop.java
new file mode 100644
index 0000000..03d1de8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.Insn;
+import com.android.dexgen.rop.code.RegOps;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.Rop;
+import com.android.dexgen.rop.code.Rops;
+import com.android.dexgen.rop.code.ThrowingCstInsn;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/RopTranslator.java b/dexgen/src/com/android/dexgen/dex/code/RopTranslator.java
new file mode 100644
index 0000000..ad05f2b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.BasicBlock;
+import com.android.dexgen.rop.code.BasicBlockList;
+import com.android.dexgen.rop.code.FillArrayDataInsn;
+import com.android.dexgen.rop.code.Insn;
+import com.android.dexgen.rop.code.LocalVariableInfo;
+import com.android.dexgen.rop.code.PlainCstInsn;
+import com.android.dexgen.rop.code.PlainInsn;
+import com.android.dexgen.rop.code.RegOps;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.rop.code.Rop;
+import com.android.dexgen.rop.code.RopMethod;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.code.SwitchInsn;
+import com.android.dexgen.rop.code.ThrowingCstInsn;
+import com.android.dexgen.rop.code.ThrowingInsn;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.Bits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/SimpleInsn.java b/dexgen/src/com/android/dexgen/dex/code/SimpleInsn.java
new file mode 100644
index 0000000..abef242
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/StdCatchBuilder.java b/dexgen/src/com/android/dexgen/dex/code/StdCatchBuilder.java
new file mode 100644
index 0000000..ba149e7
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.BasicBlock;
+import com.android.dexgen.rop.code.BasicBlockList;
+import com.android.dexgen.rop.code.RopMethod;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/SwitchData.java b/dexgen/src/com/android/dexgen/dex/code/SwitchData.java
new file mode 100644
index 0000000..a7d8465
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/TargetInsn.java b/dexgen/src/com/android/dexgen/dex/code/TargetInsn.java
new file mode 100644
index 0000000..8e02255
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/VariableSizeInsn.java b/dexgen/src/com/android/dexgen/dex/code/VariableSizeInsn.java
new file mode 100644
index 0000000..baa62a3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/ZeroSizeInsn.java b/dexgen/src/com/android/dexgen/dex/code/ZeroSizeInsn.java
new file mode 100644
index 0000000..3c8c94a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form10t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form10t.java
new file mode 100644
index 0000000..4784fc3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form10x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form10x.java
new file mode 100644
index 0000000..63c861c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form11n.java b/dexgen/src/com/android/dexgen/dex/code/form/Form11n.java
new file mode 100644
index 0000000..511d7d1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form11x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form11x.java
new file mode 100644
index 0000000..8bf9bba
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form12x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form12x.java
new file mode 100644
index 0000000..d55a66a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.HighRegisterPrefix;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form20t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form20t.java
new file mode 100644
index 0000000..2760606
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form21c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21c.java
new file mode 100644
index 0000000..33df3d6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form21h.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21h.java
new file mode 100644
index 0000000..ee6ed3e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form21s.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21s.java
new file mode 100644
index 0000000..4b853d0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form21t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21t.java
new file mode 100644
index 0000000..61599f6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form22b.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22b.java
new file mode 100644
index 0000000..6c37d57
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form22c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22c.java
new file mode 100644
index 0000000..b089ec4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form22s.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22s.java
new file mode 100644
index 0000000..0eca280
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form22t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22t.java
new file mode 100644
index 0000000..707bb97
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form22x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22x.java
new file mode 100644
index 0000000..bd6a8df
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form23x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form23x.java
new file mode 100644
index 0000000..eaf1cee
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form30t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form30t.java
new file mode 100644
index 0000000..0909ec8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form31c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form31c.java
new file mode 100644
index 0000000..c87a451
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form31i.java b/dexgen/src/com/android/dexgen/dex/code/form/Form31i.java
new file mode 100644
index 0000000..e74ea86
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form31t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form31t.java
new file mode 100644
index 0000000..212f93b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form32x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form32x.java
new file mode 100644
index 0000000..097fb65
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form35c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form35c.java
new file mode 100644
index 0000000..147aac1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form3rc.java b/dexgen/src/com/android/dexgen/dex/code/form/Form3rc.java
new file mode 100644
index 0000000..a061c6f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/Form51l.java b/dexgen/src/com/android/dexgen/dex/code/form/Form51l.java
new file mode 100644
index 0000000..537eaa9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteral64;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/code/form/SpecialFormat.java b/dexgen/src/com/android/dexgen/dex/code/form/SpecialFormat.java
new file mode 100644
index 0000000..c75f18f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.DalvOps;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/AnnotationItem.java b/dexgen/src/com/android/dexgen/dex/file/AnnotationItem.java
new file mode 100644
index 0000000..a078bc0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotation;
+import com.android.dexgen.rop.annotation.AnnotationVisibility;
+import com.android.dexgen.rop.annotation.NameValuePair;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstAnnotation;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+
+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/dexgen/src/com/android/dexgen/dex/file/AnnotationSetItem.java b/dexgen/src/com/android/dexgen/dex/file/AnnotationSetItem.java
new file mode 100644
index 0000000..46ea2f0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotation;
+import com.android.dexgen.rop.annotation.Annotations;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/AnnotationSetRefItem.java b/dexgen/src/com/android/dexgen/dex/file/AnnotationSetRefItem.java
new file mode 100644
index 0000000..b876ce0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/AnnotationUtils.java b/dexgen/src/com/android/dexgen/dex/file/AnnotationUtils.java
new file mode 100644
index 0000000..111ba8a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotation;
+import com.android.dexgen.rop.annotation.NameValuePair;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstAnnotation;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.rop.cst.CstKnownNull;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+
+import java.util.ArrayList;
+
+import static com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/AnnotationsDirectoryItem.java b/dexgen/src/com/android/dexgen/dex/file/AnnotationsDirectoryItem.java
new file mode 100644
index 0000000..860c16d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotations;
+import com.android.dexgen.rop.annotation.AnnotationsList;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/CatchStructs.java b/dexgen/src/com/android/dexgen/dex/file/CatchStructs.java
new file mode 100644
index 0000000..df9a847
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.dex.code.CatchHandlerList;
+import com.android.dexgen.dex.code.CatchTable;
+import com.android.dexgen.dex.code.DalvCode;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ClassDataItem.java b/dexgen/src/com/android/dexgen/dex/file/ClassDataItem.java
new file mode 100644
index 0000000..c46a4a5
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.Zeroes;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ClassDefItem.java b/dexgen/src/com/android/dexgen/dex/file/ClassDefItem.java
new file mode 100644
index 0000000..6177145
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotations;
+import com.android.dexgen.rop.annotation.AnnotationsList;
+import com.android.dexgen.rop.code.AccessFlags;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ClassDefsSection.java b/dexgen/src/com/android/dexgen/dex/file/ClassDefsSection.java
new file mode 100644
index 0000000..a6392d4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/CodeItem.java b/dexgen/src/com/android/dexgen/dex/file/CodeItem.java
new file mode 100644
index 0000000..1b305c7
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.dex.code.CatchTable;
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvCode;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.DalvInsnList;
+import com.android.dexgen.dex.code.LocalList;
+import com.android.dexgen.dex.code.PositionList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMemberRef;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ExceptionWithContext;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/DebugInfoConstants.java b/dexgen/src/com/android/dexgen/dex/file/DebugInfoConstants.java
new file mode 100644
index 0000000..780f350
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/dex/file/DebugInfoDecoder.java b/dexgen/src/com/android/dexgen/dex/file/DebugInfoDecoder.java
new file mode 100644
index 0000000..da614d2
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.dex.code.DalvCode;
+import com.android.dexgen.dex.code.DalvInsnList;
+import com.android.dexgen.dex.code.LocalList;
+import com.android.dexgen.dex.code.PositionList;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Prototype;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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.dexgen.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/dexgen/src/com/android/dexgen/dex/file/DebugInfoEncoder.java b/dexgen/src/com/android/dexgen/dex/file/DebugInfoEncoder.java
new file mode 100644
index 0000000..663de7e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.dex.code.LocalList;
+import com.android.dexgen.dex.code.PositionList;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Prototype;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+import com.android.dexgen.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.dexgen.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/dexgen/src/com/android/dexgen/dex/file/DebugInfoItem.java b/dexgen/src/com/android/dexgen/dex/file/DebugInfoItem.java
new file mode 100644
index 0000000..82ad444
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.dex.code.DalvCode;
+import com.android.dexgen.dex.code.DalvInsnList;
+import com.android.dexgen.dex.code.LocalList;
+import com.android.dexgen.dex.code.PositionList;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/DexFile.java b/dexgen/src/com/android/dexgen/dex/file/DexFile.java
new file mode 100644
index 0000000..e92aa10
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/file/DexFile.java
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.dex.file;
+
+import com.android.dexgen.dex.file.MixedItemSection.SortType;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstBaseMethodRef;
+import com.android.dexgen.rop.cst.CstEnumRef;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+import com.android.dexgen.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;
+
+/**
+ * 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/dexgen/src/com/android/dexgen/dex/file/EncodedArrayItem.java b/dexgen/src/com/android/dexgen/dex/file/EncodedArrayItem.java
new file mode 100644
index 0000000..cef2375
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotation;
+import com.android.dexgen.rop.annotation.AnnotationVisibility;
+import com.android.dexgen.rop.annotation.NameValuePair;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstAnnotation;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+
+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/dexgen/src/com/android/dexgen/dex/file/EncodedField.java b/dexgen/src/com/android/dexgen/dex/file/EncodedField.java
new file mode 100644
index 0000000..5af2b1f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.code.AccessFlags;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/EncodedMember.java b/dexgen/src/com/android/dexgen/dex/file/EncodedMember.java
new file mode 100644
index 0000000..6c31704
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/EncodedMethod.java b/dexgen/src/com/android/dexgen/dex/file/EncodedMethod.java
new file mode 100644
index 0000000..a35ca2c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.dex.code.DalvCode;
+import com.android.dexgen.rop.code.AccessFlags;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/FieldAnnotationStruct.java b/dexgen/src/com/android/dexgen/dex/file/FieldAnnotationStruct.java
new file mode 100644
index 0000000..95e4dbc
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotations;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/FieldIdItem.java b/dexgen/src/com/android/dexgen/dex/file/FieldIdItem.java
new file mode 100644
index 0000000..4d3721e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/FieldIdsSection.java b/dexgen/src/com/android/dexgen/dex/file/FieldIdsSection.java
new file mode 100644
index 0000000..65177e4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/HeaderItem.java b/dexgen/src/com/android/dexgen/dex/file/HeaderItem.java
new file mode 100644
index 0000000..ed04e25
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/HeaderSection.java b/dexgen/src/com/android/dexgen/dex/file/HeaderSection.java
new file mode 100644
index 0000000..967a90a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/IdItem.java b/dexgen/src/com/android/dexgen/dex/file/IdItem.java
new file mode 100644
index 0000000..0f8301e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/IndexedItem.java b/dexgen/src/com/android/dexgen/dex/file/IndexedItem.java
new file mode 100644
index 0000000..cdc73cb
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/dex/file/Item.java b/dexgen/src/com/android/dexgen/dex/file/Item.java
new file mode 100644
index 0000000..45cdc94
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ItemType.java b/dexgen/src/com/android/dexgen/dex/file/ItemType.java
new file mode 100644
index 0000000..b3e32d0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/MapItem.java b/dexgen/src/com/android/dexgen/dex/file/MapItem.java
new file mode 100644
index 0000000..02472d4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/MemberIdItem.java b/dexgen/src/com/android/dexgen/dex/file/MemberIdItem.java
new file mode 100644
index 0000000..d638f07
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstMemberRef;
+import com.android.dexgen.rop.cst.CstNat;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/MemberIdsSection.java b/dexgen/src/com/android/dexgen/dex/file/MemberIdsSection.java
new file mode 100644
index 0000000..dcfca30
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/dex/file/MethodAnnotationStruct.java b/dexgen/src/com/android/dexgen/dex/file/MethodAnnotationStruct.java
new file mode 100644
index 0000000..e511f10
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotations;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/MethodIdItem.java b/dexgen/src/com/android/dexgen/dex/file/MethodIdItem.java
new file mode 100644
index 0000000..da14e19
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/MethodIdsSection.java b/dexgen/src/com/android/dexgen/dex/file/MethodIdsSection.java
new file mode 100644
index 0000000..3a06af7
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstBaseMethodRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/MixedItemSection.java b/dexgen/src/com/android/dexgen/dex/file/MixedItemSection.java
new file mode 100644
index 0000000..2fda33b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ExceptionWithContext;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/OffsettedItem.java b/dexgen/src/com/android/dexgen/dex/file/OffsettedItem.java
new file mode 100644
index 0000000..246f903
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ParameterAnnotationStruct.java b/dexgen/src/com/android/dexgen/dex/file/ParameterAnnotationStruct.java
new file mode 100644
index 0000000..440da1c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotations;
+import com.android.dexgen.rop.annotation.AnnotationsList;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ProtoIdItem.java b/dexgen/src/com/android/dexgen/dex/file/ProtoIdItem.java
new file mode 100644
index 0000000..ef48cd4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Prototype;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ProtoIdsSection.java b/dexgen/src/com/android/dexgen/dex/file/ProtoIdsSection.java
new file mode 100644
index 0000000..b2af84e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.type.Prototype;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/Section.java b/dexgen/src/com/android/dexgen/dex/file/Section.java
new file mode 100644
index 0000000..7efaf6b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/Statistics.java b/dexgen/src/com/android/dexgen/dex/file/Statistics.java
new file mode 100644
index 0000000..1ec2f93
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/StringDataItem.java b/dexgen/src/com/android/dexgen/dex/file/StringDataItem.java
new file mode 100644
index 0000000..7e28323
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArray;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/StringIdItem.java b/dexgen/src/com/android/dexgen/dex/file/StringIdItem.java
new file mode 100644
index 0000000..30f31d4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/StringIdsSection.java b/dexgen/src/com/android/dexgen/dex/file/StringIdsSection.java
new file mode 100644
index 0000000..9047fb9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstNat;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/TypeIdItem.java b/dexgen/src/com/android/dexgen/dex/file/TypeIdItem.java
new file mode 100644
index 0000000..2c029b0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/TypeIdsSection.java b/dexgen/src/com/android/dexgen/dex/file/TypeIdsSection.java
new file mode 100644
index 0000000..b02b592
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/TypeListItem.java b/dexgen/src/com/android/dexgen/dex/file/TypeListItem.java
new file mode 100644
index 0000000..a78c63d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/UniformItemSection.java b/dexgen/src/com/android/dexgen/dex/file/UniformItemSection.java
new file mode 100644
index 0000000..63ba36b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/UniformListItem.java b/dexgen/src/com/android/dexgen/dex/file/UniformListItem.java
new file mode 100644
index 0000000..88a120d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/dex/file/ValueEncoder.java b/dexgen/src/com/android/dexgen/dex/file/ValueEncoder.java
new file mode 100644
index 0000000..7f30779
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotation;
+import com.android.dexgen.rop.annotation.NameValuePair;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstAnnotation;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstBoolean;
+import com.android.dexgen.rop.cst.CstByte;
+import com.android.dexgen.rop.cst.CstChar;
+import com.android.dexgen.rop.cst.CstDouble;
+import com.android.dexgen.rop.cst.CstEnumRef;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstFloat;
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.rop.cst.CstKnownNull;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.rop.cst.CstLong;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstShort;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/AttConstantValue.java b/dexgen/src/com/android/dexgen/rop/AttConstantValue.java
new file mode 100644
index 0000000..189dd75
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.rop.cst.CstDouble;
+import com.android.dexgen.rop.cst.CstFloat;
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.rop.cst.CstLong;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/Attribute.java b/dexgen/src/com/android/dexgen/rop/Attribute.java
new file mode 100644
index 0000000..02f1e14
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+/**
+ * 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/dexgen/src/com/android/dexgen/rop/AttributeList.java b/dexgen/src/com/android/dexgen/rop/AttributeList.java
new file mode 100644
index 0000000..205b9b7
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+/**
+ * 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/dexgen/src/com/android/dexgen/rop/BaseAttribute.java b/dexgen/src/com/android/dexgen/rop/BaseAttribute.java
new file mode 100644
index 0000000..7ce88c0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/BaseAttribute.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.
+ */
+
+package com.android.dexgen.rop;
+
+
+/**
+ * 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/dexgen/src/com/android/dexgen/rop/ByteBlock.java b/dexgen/src/com/android/dexgen/rop/ByteBlock.java
new file mode 100644
index 0000000..bdeeb0b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.util.IntList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/ByteCatchList.java b/dexgen/src/com/android/dexgen/rop/ByteCatchList.java
new file mode 100644
index 0000000..d2a4857
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.FixedSizeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/Field.java b/dexgen/src/com/android/dexgen/rop/Field.java
new file mode 100644
index 0000000..3e0364b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/FieldList.java b/dexgen/src/com/android/dexgen/rop/FieldList.java
new file mode 100644
index 0000000..ab4f28f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+/**
+ * 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/dexgen/src/com/android/dexgen/rop/LineNumberList.java b/dexgen/src/com/android/dexgen/rop/LineNumberList.java
new file mode 100644
index 0000000..f780066
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/Member.java b/dexgen/src/com/android/dexgen/rop/Member.java
new file mode 100644
index 0000000..dfc17be
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.rop.cst.CstNat;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/StdAttributeList.java b/dexgen/src/com/android/dexgen/rop/StdAttributeList.java
new file mode 100644
index 0000000..bebee21
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/StdField.java b/dexgen/src/com/android/dexgen/rop/StdField.java
new file mode 100644
index 0000000..9adcc30
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/StdField.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.dexgen.rop;
+
+import com.android.dexgen.rop.cst.CstNat;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.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);
+    }
+
+    /**
+     * Constructs an instance having Java field as its pattern.
+     *
+     * @param field {@code non-null;} pattern for dex field
+     */
+    public StdField(java.lang.reflect.Field field) {
+        this(CstType.intern(field.getDeclaringClass()),
+                field.getModifiers(),
+                new CstNat(new CstUtf8(field.getName()),
+                        CstType.intern(field.getType()).getDescriptor()),
+                new StdAttributeList(0));
+    }
+
+    /**
+     * Constructs an instance taking field description as user-friendly arguments.
+     *
+     * @param declaringClass {@code non-null;} the class field belongs to
+     * @param type {@code non-null;} type of the field
+     * @param name {@code non-null;} name of the field
+     * @param modifiers access flags of the field
+     */
+    public StdField(Class definingClass, Class type, String name, int modifiers) {
+        this(CstType.intern(definingClass),
+                modifiers,
+                new CstNat(new CstUtf8(name), CstType.intern(type).getDescriptor()),
+                new StdAttributeList(0));
+    }
+
+    /** {@inheritDoc} */
+    public TypedConstant getConstantValue() {
+        AttributeList attribs = getAttributes();
+        AttConstantValue cval = (AttConstantValue)
+            attribs.findFirst(AttConstantValue.ATTRIBUTE_NAME);
+
+        if (cval == null) {
+            return null;
+        }
+
+        return cval.getConstantValue();
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/rop/StdFieldList.java b/dexgen/src/com/android/dexgen/rop/StdFieldList.java
new file mode 100644
index 0000000..ccb7465
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/StdMember.java b/dexgen/src/com/android/dexgen/rop/StdMember.java
new file mode 100644
index 0000000..6c46051
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/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.dexgen.rop;
+
+import com.android.dexgen.rop.cst.CstNat;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/annotation/Annotation.java b/dexgen/src/com/android/dexgen/rop/annotation/Annotation.java
new file mode 100644
index 0000000..918d2bc
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.annotation;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstAnnotation;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstNat;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.cst.TypedConstant;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.util.MutabilityControl;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/annotation/AnnotationVisibility.java b/dexgen/src/com/android/dexgen/rop/annotation/AnnotationVisibility.java
new file mode 100644
index 0000000..5239164
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.annotation;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/annotation/Annotations.java b/dexgen/src/com/android/dexgen/rop/annotation/Annotations.java
new file mode 100644
index 0000000..a7eca04
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.annotation;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/annotation/AnnotationsList.java b/dexgen/src/com/android/dexgen/rop/annotation/AnnotationsList.java
new file mode 100644
index 0000000..1159932
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.annotation;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/annotation/NameValuePair.java b/dexgen/src/com/android/dexgen/rop/annotation/NameValuePair.java
new file mode 100644
index 0000000..9f96f14
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.annotation;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/AccessFlags.java b/dexgen/src/com/android/dexgen/rop/code/AccessFlags.java
new file mode 100644
index 0000000..4a8b435
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/BasicBlock.java b/dexgen/src/com/android/dexgen/rop/code/BasicBlock.java
new file mode 100644
index 0000000..0f7a59e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.util.IntList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/BasicBlockList.java b/dexgen/src/com/android/dexgen/rop/code/BasicBlockList.java
new file mode 100644
index 0000000..f01c588
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.util.IntList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/ConservativeTranslationAdvice.java b/dexgen/src/com/android/dexgen/rop/code/ConservativeTranslationAdvice.java
new file mode 100644
index 0000000..080432b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/code/CstInsn.java b/dexgen/src/com/android/dexgen/rop/code/CstInsn.java
new file mode 100644
index 0000000..dc5ed39
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/DexTranslationAdvice.java b/dexgen/src/com/android/dexgen/rop/code/DexTranslationAdvice.java
new file mode 100644
index 0000000..b46182d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/Exceptions.java b/dexgen/src/com/android/dexgen/rop/code/Exceptions.java
new file mode 100644
index 0000000..d6584d0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/FillArrayDataInsn.java b/dexgen/src/com/android/dexgen/rop/code/FillArrayDataInsn.java
new file mode 100644
index 0000000..3289b58
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+
+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/dexgen/src/com/android/dexgen/rop/code/Insn.java b/dexgen/src/com/android/dexgen/rop/code/Insn.java
new file mode 100644
index 0000000..4bb10d2
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/InsnList.java b/dexgen/src/com/android/dexgen/rop/code/InsnList.java
new file mode 100644
index 0000000..0046972
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/LocalItem.java b/dexgen/src/com/android/dexgen/rop/code/LocalItem.java
new file mode 100644
index 0000000..78386f1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/LocalVariableExtractor.java b/dexgen/src/com/android/dexgen/rop/code/LocalVariableExtractor.java
new file mode 100644
index 0000000..14f5f15
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.util.Bits;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/LocalVariableInfo.java b/dexgen/src/com/android/dexgen/rop/code/LocalVariableInfo.java
new file mode 100644
index 0000000..b126a4c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.TypeBearer;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/PlainCstInsn.java b/dexgen/src/com/android/dexgen/rop/code/PlainCstInsn.java
new file mode 100644
index 0000000..5f8f753
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/PlainInsn.java b/dexgen/src/com/android/dexgen/rop/code/PlainInsn.java
new file mode 100644
index 0000000..c79e7c1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeBearer;
+import com.android.dexgen.rop.type.TypeList;
+
+/**
+ * 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/dexgen/src/com/android/dexgen/rop/code/RegOps.java b/dexgen/src/com/android/dexgen/rop/code/RegOps.java
new file mode 100644
index 0000000..3af8b7d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/RegisterSpec.java b/dexgen/src/com/android/dexgen/rop/code/RegisterSpec.java
new file mode 100644
index 0000000..30deeca
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeBearer;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/RegisterSpecList.java b/dexgen/src/com/android/dexgen/rop/code/RegisterSpecList.java
new file mode 100644
index 0000000..a0f7a24
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/RegisterSpecSet.java b/dexgen/src/com/android/dexgen/rop/code/RegisterSpecSet.java
new file mode 100644
index 0000000..69e67e9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.MutabilityControl;
+
+/**
+ * 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/dexgen/src/com/android/dexgen/rop/code/Rop.java b/dexgen/src/com/android/dexgen/rop/code/Rop.java
new file mode 100644
index 0000000..db9a6c2
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/RopMethod.java b/dexgen/src/com/android/dexgen/rop/code/RopMethod.java
new file mode 100644
index 0000000..ba65b3f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.util.Bits;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/Rops.java b/dexgen/src/com/android/dexgen/rop/code/Rops.java
new file mode 100644
index 0000000..ad9327e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstBaseMethodRef;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Prototype;
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeBearer;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/SourcePosition.java b/dexgen/src/com/android/dexgen/rop/code/SourcePosition.java
new file mode 100644
index 0000000..cd0ea25
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/SwitchInsn.java b/dexgen/src/com/android/dexgen/rop/code/SwitchInsn.java
new file mode 100644
index 0000000..ee4f4b6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.StdTypeList;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/ThrowingCstInsn.java b/dexgen/src/com/android/dexgen/rop/code/ThrowingCstInsn.java
new file mode 100644
index 0000000..7262254
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/ThrowingInsn.java b/dexgen/src/com/android/dexgen/rop/code/ThrowingInsn.java
new file mode 100644
index 0000000..24611ad
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.code;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/code/TranslationAdvice.java b/dexgen/src/com/android/dexgen/rop/code/TranslationAdvice.java
new file mode 100644
index 0000000..9edd248
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/Constant.java b/dexgen/src/com/android/dexgen/rop/cst/Constant.java
new file mode 100644
index 0000000..deaa5f4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/ConstantPool.java b/dexgen/src/com/android/dexgen/rop/cst/ConstantPool.java
new file mode 100644
index 0000000..1ea188a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstAnnotation.java b/dexgen/src/com/android/dexgen/rop/cst/CstAnnotation.java
new file mode 100644
index 0000000..89b4fd8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstArray.java b/dexgen/src/com/android/dexgen/rop/cst/CstArray.java
new file mode 100644
index 0000000..2fad35e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstBaseMethodRef.java b/dexgen/src/com/android/dexgen/rop/cst/CstBaseMethodRef.java
new file mode 100644
index 0000000..3914272
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Prototype;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstBoolean.java b/dexgen/src/com/android/dexgen/rop/cst/CstBoolean.java
new file mode 100644
index 0000000..a7501e3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstByte.java b/dexgen/src/com/android/dexgen/rop/cst/CstByte.java
new file mode 100644
index 0000000..f9b97cb
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstChar.java b/dexgen/src/com/android/dexgen/rop/cst/CstChar.java
new file mode 100644
index 0000000..d006525
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstDouble.java b/dexgen/src/com/android/dexgen/rop/cst/CstDouble.java
new file mode 100644
index 0000000..84a53e6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstEnumRef.java b/dexgen/src/com/android/dexgen/rop/cst/CstEnumRef.java
new file mode 100644
index 0000000..d566946
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstFieldRef.java b/dexgen/src/com/android/dexgen/rop/cst/CstFieldRef.java
new file mode 100644
index 0000000..6a6218c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstFloat.java b/dexgen/src/com/android/dexgen/rop/cst/CstFloat.java
new file mode 100644
index 0000000..6490f8f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstInteger.java b/dexgen/src/com/android/dexgen/rop/cst/CstInteger.java
new file mode 100644
index 0000000..41ef0a6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstInterfaceMethodRef.java b/dexgen/src/com/android/dexgen/rop/cst/CstInterfaceMethodRef.java
new file mode 100644
index 0000000..c514b84
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstKnownNull.java b/dexgen/src/com/android/dexgen/rop/cst/CstKnownNull.java
new file mode 100644
index 0000000..58d6933
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstLiteral32.java b/dexgen/src/com/android/dexgen/rop/cst/CstLiteral32.java
new file mode 100644
index 0000000..f7f9199
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstLiteral64.java b/dexgen/src/com/android/dexgen/rop/cst/CstLiteral64.java
new file mode 100644
index 0000000..0bf3152
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstLiteralBits.java b/dexgen/src/com/android/dexgen/rop/cst/CstLiteralBits.java
new file mode 100644
index 0000000..97e8bd1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstLong.java b/dexgen/src/com/android/dexgen/rop/cst/CstLong.java
new file mode 100644
index 0000000..f737094
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstMemberRef.java b/dexgen/src/com/android/dexgen/rop/cst/CstMemberRef.java
new file mode 100644
index 0000000..5abca4b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstMethodRef.java b/dexgen/src/com/android/dexgen/rop/cst/CstMethodRef.java
new file mode 100644
index 0000000..0bf3851
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstNat.java b/dexgen/src/com/android/dexgen/rop/cst/CstNat.java
new file mode 100644
index 0000000..34d2bfc
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstShort.java b/dexgen/src/com/android/dexgen/rop/cst/CstShort.java
new file mode 100644
index 0000000..c81a589
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstString.java b/dexgen/src/com/android/dexgen/rop/cst/CstString.java
new file mode 100644
index 0000000..a2babf4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/CstType.java b/dexgen/src/com/android/dexgen/rop/cst/CstType.java
new file mode 100644
index 0000000..8ad418b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/cst/CstType.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.rop.cst;
+
+import com.android.dexgen.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;
+    }
+
+    /**
+     * Returns an interned instance of this class for the given
+     * {@code Class} instance.
+     *
+     * @param clazz {@code non-null;} the underlying {@code Class} object
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static CstType intern(Class clazz) {
+        return intern(Type.intern(clazz));
+    }
+
+    /**
+     * 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;
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/rop/cst/CstUtf8.java b/dexgen/src/com/android/dexgen/rop/cst/CstUtf8.java
new file mode 100644
index 0000000..161a57b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.util.ByteArray;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/StdConstantPool.java b/dexgen/src/com/android/dexgen/rop/cst/StdConstantPool.java
new file mode 100644
index 0000000..5f1728a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.util.ExceptionWithContext;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/TypedConstant.java b/dexgen/src/com/android/dexgen/rop/cst/TypedConstant.java
new file mode 100644
index 0000000..251f057
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/cst/Zeroes.java b/dexgen/src/com/android/dexgen/rop/cst/Zeroes.java
new file mode 100644
index 0000000..28da7db
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.cst;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/type/Prototype.java b/dexgen/src/com/android/dexgen/rop/type/Prototype.java
new file mode 100644
index 0000000..33fa918
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/type/Prototype.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/type/StdTypeList.java b/dexgen/src/com/android/dexgen/rop/type/StdTypeList.java
new file mode 100644
index 0000000..a3e81ff
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.type;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/type/Type.java b/dexgen/src/com/android/dexgen/rop/type/Type.java
new file mode 100644
index 0000000..365bd78
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/rop/type/Type.java
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.rop.type;
+
+import com.android.dexgen.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);
+
+    /** {@code non-null;} table mapping types as {@code Class} objects to internal form */
+    private static final HashMap<Class, Type> CLASS_TYPE_MAP =
+        new HashMap<Class, Type>();
+
+    /** 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().
+         */
+
+        /*
+         * Create a mapping between types as Java Class objects
+         * and types in dx internal format.
+         */
+        CLASS_TYPE_MAP.put(boolean.class, BOOLEAN);
+        CLASS_TYPE_MAP.put(short.class, SHORT);
+        CLASS_TYPE_MAP.put(int.class, INT);
+        CLASS_TYPE_MAP.put(long.class, LONG);
+        CLASS_TYPE_MAP.put(char.class, CHAR);
+        CLASS_TYPE_MAP.put(byte.class, BYTE);
+        CLASS_TYPE_MAP.put(float.class, FLOAT);
+        CLASS_TYPE_MAP.put(double.class, DOUBLE);
+        CLASS_TYPE_MAP.put(void.class, VOID);
+    }
+
+    /**
+     * {@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 represented by
+     * given {@code Class} object. 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 clazz {@code non-null;} class whose descriptor
+     * will be internalized
+     * @return {@code non-null;} the corresponding instance
+     */
+    public static Type intern(Class clazz) {
+        return intern(getInternalTypeName(clazz));
+    }
+
+    /**
+     * 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" + 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" + 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 represented by
+     * given {@code Class} object, allowing {@code "V"} to return the type
+     * for {@code void}. Other than that one caveat, this method
+     * is identical to {@link #intern}.
+     *
+     * @param clazz {@code non-null;} class which descriptor
+     * will be internalized
+     * @return {@code non-null;} the corresponding instance
+     */
+    public static Type internReturnType(Class clazz) {
+        return internReturnType(getInternalTypeName(clazz));
+    }
+
+    /**
+     * 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 + ';');
+    }
+
+    /**
+     * Converts type name in the format as returned by reflection
+     * into dex internal form.
+     *
+     * @param clazz {@code non-null;} class whose name will be internalized
+     * @return string with the type name in dex internal format
+     */
+    public static String getInternalTypeName(Class clazz) {
+        if (clazz == null) {
+            throw new NullPointerException("clazz == null");
+        }
+
+        if (clazz.isPrimitive()) {
+            return CLASS_TYPE_MAP.get(clazz).getDescriptor();
+        }
+
+        String slashed = clazz.getName().replace('.', '/');
+
+        if (clazz.isArray()) {
+            return slashed;
+        }
+
+        return 'L' + slashed + ';';
+    }
+
+    /**
+     * 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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/rop/type/TypeBearer.java b/dexgen/src/com/android/dexgen/rop/type/TypeBearer.java
new file mode 100644
index 0000000..da7a7ef
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.rop.type;
+
+import com.android.dexgen.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/dexgen/src/com/android/dexgen/rop/type/TypeList.java b/dexgen/src/com/android/dexgen/rop/type/TypeList.java
new file mode 100644
index 0000000..dedcbc9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/AnnotatedOutput.java b/dexgen/src/com/android/dexgen/util/AnnotatedOutput.java
new file mode 100644
index 0000000..3ff4cf5
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/BitIntSet.java b/dexgen/src/com/android/dexgen/util/BitIntSet.java
new file mode 100644
index 0000000..0cf9fc6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/Bits.java b/dexgen/src/com/android/dexgen/util/Bits.java
new file mode 100644
index 0000000..5c97cc9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/ByteArray.java b/dexgen/src/com/android/dexgen/util/ByteArray.java
new file mode 100644
index 0000000..93144b3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/ByteArrayAnnotatedOutput.java b/dexgen/src/com/android/dexgen/util/ByteArrayAnnotatedOutput.java
new file mode 100644
index 0000000..5fad9a9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/DexClassLoaderHelper.java b/dexgen/src/com/android/dexgen/util/DexClassLoaderHelper.java
new file mode 100644
index 0000000..97118ea
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/DexClassLoaderHelper.java
@@ -0,0 +1,59 @@
+/*
+ * 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.dexgen.util;
+
+import dalvik.system.DexClassLoader;
+
+/**
+ * Class used indirectly for loading generated dex classes. It allows the caller
+ * to obtain appropriate {@code DexClassLoader} instance, which can be then used for
+ * loading classes.
+ */
+public class DexClassLoaderHelper {
+
+    private static class DexClassLoaderHelperHolder {
+        private static final DexClassLoaderHelper INSTANCE = new DexClassLoaderHelper();
+    }
+
+    private DexClassLoaderHelper() {
+        // intentionally empty to disable direct instantiation
+    }
+
+    /**
+     * Returns the sole instance of {@code DexClassLoaderHelper}.
+     *
+     * @return dex {@code DexClassLoaderHelper} sole instance
+     */
+    public static DexClassLoaderHelper getInstance() {
+        return DexClassLoaderHelperHolder.INSTANCE;
+    }
+
+    /**
+     * Creates and returns DexClassLoader instance with its classpath
+     * set to {@code pathHolder}.
+     *
+     * @param pathHolder {@code non-null;} location of jar archive containing dex
+     * classes canned into a working PathHolder instance.
+     * @return dex class loader instance with its classpath set to location
+     * indicated by {@code pathHolder}
+     */
+    public ClassLoader getDexClassLoader(PathHolder pathHolder) {
+        ClassLoader myLoader = DexClassLoaderHelper.class.getClassLoader();
+        return new DexClassLoader(pathHolder.getJarFilePath(), pathHolder.getDirLocation(),
+                null, myLoader);
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/util/DexClassLoadingException.java b/dexgen/src/com/android/dexgen/util/DexClassLoadingException.java
new file mode 100644
index 0000000..ba4d350
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/DexClassLoadingException.java
@@ -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.
+ */
+
+package com.android.dexgen.util;
+
+/**
+ * An exception type used to aggregate all the unexpected situations while
+ * trying to save dex file with generated class and load the class afterwards.
+ */
+public class DexClassLoadingException extends Exception {
+
+    /**
+     * Encapsulates any checked exception being thrown in time between saving
+     * generated dex class to a file and loading it via DexClassLoader with
+     * an user-friendly message and passing the original exception as well.
+     *
+     * @param thr {@code non-null;} lower level exception with more detailed
+     * error message
+     */
+    public DexClassLoadingException(Throwable thr) {
+        super("Loading generated dex class has failed", thr);
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/util/DexJarMaker.java b/dexgen/src/com/android/dexgen/util/DexJarMaker.java
new file mode 100644
index 0000000..4fe5a56
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/DexJarMaker.java
@@ -0,0 +1,95 @@
+/*
+ * 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.dexgen.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+/**
+ * Helper class used to encapsulate generated .dex file into .jar
+ * so that it fits {@code DexClassLoader} constructor.
+ */
+public class DexJarMaker {
+
+    /** indicates name of the dex file added to jar */
+    public static final String DEX_FILE_NAME_IN_JAR = "classes" + PathHolder.DEX_FILE_EXTENSION;
+
+    /** {@code non-null;} storage for all the paths related to current dex file */
+    private final PathHolder pathHolder;
+
+    public DexJarMaker(PathHolder pathHolder) {
+        this.pathHolder = pathHolder;
+    }
+
+    /** Packs previously added files into a single jar archive. */
+    public void create() throws DexClassLoadingException {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        JarOutputStream target = null;
+        try {
+            target = new JarOutputStream(
+                    new BufferedOutputStream(new FileOutputStream(pathHolder.getJarFilePath())),
+                    manifest);
+            add(new File(pathHolder.getDexFilePath()), target);
+
+        } catch (IOException e) {
+            throw new DexClassLoadingException(e);
+        }
+        finally {
+            try {
+                if (target != null) {
+                    target.close();
+                }
+            } catch(IOException e) {
+                // Ignoring deliberately in order to keep the original exception clear.
+            }
+        }
+    }
+
+    /**
+     * Adds indicated file to the requested archive.
+     *
+     * @param source {@code non-null;} dex file to add
+     * @param target {@code non-null;} target jar archive
+     * @throws IOException
+     */
+    private void add(File source, JarOutputStream target) throws IOException {
+
+        if (!source.isFile()) {
+            throw new IllegalArgumentException("Wrong source dex file provided");
+        }
+
+        BufferedInputStream in = new BufferedInputStream(new FileInputStream(source));
+        JarEntry entry = new JarEntry(DEX_FILE_NAME_IN_JAR);
+        entry.setTime(source.lastModified());
+        target.putNextEntry(entry);
+
+        int curr = -1;
+        while ((curr = in.read()) != -1) {
+            target.write(curr);
+        }
+        target.closeEntry();
+    }
+}
\ No newline at end of file
diff --git a/dexgen/src/com/android/dexgen/util/ExceptionWithContext.java b/dexgen/src/com/android/dexgen/util/ExceptionWithContext.java
new file mode 100644
index 0000000..67e7f72
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/FileUtils.java b/dexgen/src/com/android/dexgen/util/FileUtils.java
new file mode 100644
index 0000000..a5bbff4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/FixedSizeList.java b/dexgen/src/com/android/dexgen/util/FixedSizeList.java
new file mode 100644
index 0000000..039b5b0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/Hex.java b/dexgen/src/com/android/dexgen/util/Hex.java
new file mode 100644
index 0000000..4dafb77
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/HexParser.java b/dexgen/src/com/android/dexgen/util/HexParser.java
new file mode 100644
index 0000000..cc4f909
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/IndentingWriter.java b/dexgen/src/com/android/dexgen/util/IndentingWriter.java
new file mode 100644
index 0000000..05d4b0c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/IntIterator.java b/dexgen/src/com/android/dexgen/util/IntIterator.java
new file mode 100644
index 0000000..42d92fa
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/IntList.java b/dexgen/src/com/android/dexgen/util/IntList.java
new file mode 100644
index 0000000..ad29c0b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/IntSet.java b/dexgen/src/com/android/dexgen/util/IntSet.java
new file mode 100644
index 0000000..4de7525
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/LabeledItem.java b/dexgen/src/com/android/dexgen/util/LabeledItem.java
new file mode 100644
index 0000000..63cd067
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/LabeledList.java b/dexgen/src/com/android/dexgen/util/LabeledList.java
new file mode 100644
index 0000000..a59e87d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.util;
+
+import com.android.dexgen.rop.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/dexgen/src/com/android/dexgen/util/Leb128Utils.java b/dexgen/src/com/android/dexgen/util/Leb128Utils.java
new file mode 100644
index 0000000..05b38e2
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/ListIntSet.java b/dexgen/src/com/android/dexgen/util/ListIntSet.java
new file mode 100644
index 0000000..b262ebb
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/MutabilityControl.java b/dexgen/src/com/android/dexgen/util/MutabilityControl.java
new file mode 100644
index 0000000..b3ee691
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/MutabilityException.java b/dexgen/src/com/android/dexgen/util/MutabilityException.java
new file mode 100644
index 0000000..2188fe5
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/Output.java b/dexgen/src/com/android/dexgen/util/Output.java
new file mode 100644
index 0000000..469c66a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/PathHolder.java b/dexgen/src/com/android/dexgen/util/PathHolder.java
new file mode 100644
index 0000000..b23ce66
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/util/PathHolder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.dexgen.util;
+
+import java.io.File;
+
+/**
+ *  Helper class used primarily for holding path on the device of different
+ *  files arising in the dex class generation process.
+ */
+public class PathHolder {
+
+    public static final String DEX_FILE_EXTENSION = ".dex";
+
+    public static final String JAR_FILE_EXTENSION = ".jar";
+
+    /** {@code non-null;} directory location of the dex-related files */
+    private final String dirLocation;
+
+    /** {@code non-null;} common file name prefix of the created files */
+    private final String fileNamePrefix;
+
+    /**
+     * Creates an instance of {@code PathHolder} initialized with the directory
+     * location for storage of temporary files and common file name prefix for these
+     * files.
+     *
+     * @param dirLocation {@code non-null;} path to directory used for storage of temporary files
+     * @param fileNamePrefix {@code non-null;} common file name prefix across all the temporary
+     * files involved in the dex class generation and loading process
+     */
+    public PathHolder(String dirLocation, String fileNamePrefix) {
+        if (dirLocation == null) {
+            throw new NullPointerException("dirLocation == null");
+        }
+        if (fileNamePrefix == null) {
+            throw new NullPointerException("fileNamePrefix == null");
+        }
+
+        this.dirLocation = dirLocation;
+        this.fileNamePrefix = fileNamePrefix;
+    }
+
+    public String getFileName() {
+        return fileNamePrefix;
+    }
+
+    public String getDexFilePath() {
+        return dirLocation + File.separator + fileNamePrefix + DEX_FILE_EXTENSION;
+    }
+
+    public String getDexFileName() {
+        return fileNamePrefix + DEX_FILE_EXTENSION;
+    }
+
+    public String getJarFilePath() {
+        return dirLocation + File.separator + fileNamePrefix + JAR_FILE_EXTENSION;
+    }
+
+    public String getJarFileName() {
+        return fileNamePrefix + JAR_FILE_EXTENSION;
+    }
+
+    public String getDirLocation() {
+        return dirLocation;
+    }
+}
diff --git a/dexgen/src/com/android/dexgen/util/ToHuman.java b/dexgen/src/com/android/dexgen/util/ToHuman.java
new file mode 100644
index 0000000..bbf044c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/TwoColumnOutput.java b/dexgen/src/com/android/dexgen/util/TwoColumnOutput.java
new file mode 100644
index 0000000..17a4c42
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/Warning.java b/dexgen/src/com/android/dexgen/util/Warning.java
new file mode 100644
index 0000000..204d877
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/util/Writers.java b/dexgen/src/com/android/dexgen/util/Writers.java
new file mode 100644
index 0000000..046967e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexlist/Android.mk b/dexlist/Android.mk
new file mode 100644
index 0000000..55602dd
--- /dev/null
+++ b/dexlist/Android.mk
@@ -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.
+
+#
+# dexlist -- list all concrete methods found in a DEX file
+#
+LOCAL_PATH:= $(call my-dir)
+
+dexdump_src_files := \
+		DexList.cpp
+
+dexdump_c_includes := \
+		dalvik \
+		$(JNI_H_INCLUDE)
+
+dexdump_shared_libraries :=
+
+dexdump_static_libraries := \
+		libdex
+
+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)
diff --git a/dexlist/DexList.cpp b/dexlist/DexList.cpp
new file mode 100644
index 0000000..03f0230
--- /dev/null
+++ b/dexlist/DexList.cpp
@@ -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.
+ */
+
+/*
+ * List all methods in all concrete classes in one or more DEX files.
+ */
+
+#include "libdex/DexFile.h"
+
+#include "libdex/CmdUtils.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexDebugInfo.h"
+#include "libdex/DexProto.h"
+#include "libdex/SysUtil.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 = (char*)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((u1*)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..81fa1c8
--- /dev/null
+++ b/dexopt/Android.mk
@@ -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.
+
+#
+# dexopt, the DEX file optimizer.  This is fully integrated with the VM,
+# so it must be linked against the full VM shared library.
+#
+LOCAL_PATH:= $(call my-dir)
+
+local_src_files := \
+		OptMain.cpp
+
+local_c_includes := \
+		dalvik \
+		dalvik/libdex \
+		dalvik/vm \
+		$(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 libz
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dexopt
+
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_SHARED_LIBRARIES += libstlport
+
+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 libz
+    LOCAL_LDLIBS += -ldl -lpthread
+    LOCAL_CFLAGS += -DANDROID_SMP=1
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := dexopt
+    include $(BUILD_HOST_EXECUTABLE)
+endif
diff --git a/dexopt/OptMain.cpp b/dexopt/OptMain.cpp
new file mode 100644
index 0000000..3cdf5be
--- /dev/null
+++ b/dexopt/OptMain.cpp
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Command-line DEX optimization and verification entry point.
+ *
+ * There are three ways to launch this:
+ * (1) From the VM.  This takes a dozen args, one of which is a file
+ *     descriptor that acts as both input and output.  This allows us to
+ *     remain ignorant of where the DEX data originally came from.
+ * (2) From installd or another native application.  Pass in a file
+ *     descriptor for a zip file, a file descriptor for the output, and
+ *     a filename for debug messages.  Many assumptions are made about
+ *     what's going on (verification + optimization are enabled, boot
+ *     class path is in BOOTCLASSPATH, etc).
+ * (3) On the host during a build for preoptimization. This behaves
+ *     almost the same as (2), except it takes file names instead of
+ *     file descriptors.
+ *
+ * There are some fragile aspects around bootclasspath entries, owing
+ * largely to the VM's history of working on whenever it thought it needed
+ * instead of strictly doing what it was told.  If optimizing bootclasspath
+ * entries, always do them in the order in which they appear in the path.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+
+#include "cutils/log.h"
+#include "cutils/process_name.h"
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static const char* kClassesDex = "classes.dex";
+
+
+/*
+ * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
+ * up front for the DEX optimization header.
+ */
+static int extractAndProcessZip(int zipFd, int cacheFd,
+    const char* debugFileName, bool isBootstrap, const char* bootClassPath,
+    const char* dexoptFlagStr)
+{
+    ZipArchive zippy;
+    ZipEntry zipEntry;
+    size_t uncompLen;
+    long modWhen, crc32;
+    off_t dexOffset;
+    int err;
+    int result = -1;
+    int dexoptFlags = 0;        /* bit flags, from enum DexoptFlags */
+    DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
+    DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
+
+    memset(&zippy, 0, sizeof(zippy));
+
+    /* make sure we're still at the start of an empty file */
+    if (lseek(cacheFd, 0, SEEK_END) != 0) {
+        ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName);
+        goto bail;
+    }
+
+    /*
+     * Write a skeletal DEX optimization header.  We want the classes.dex
+     * to come just after it.
+     */
+    err = dexOptCreateEmptyHeader(cacheFd);
+    if (err != 0)
+        goto bail;
+
+    /* record the file position so we can get back here later */
+    dexOffset = lseek(cacheFd, 0, SEEK_CUR);
+    if (dexOffset < 0)
+        goto bail;
+
+    /*
+     * Open the zip archive, find the DEX entry.
+     */
+    if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
+        ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);
+        goto bail;
+    }
+
+    zipEntry = dexZipFindEntry(&zippy, kClassesDex);
+    if (zipEntry == NULL) {
+        ALOGW("DexOptZ: zip archive '%s' does not include %s",
+            debugFileName, kClassesDex);
+        goto bail;
+    }
+
+    /*
+     * Extract some info about the zip entry.
+     */
+    if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
+            &modWhen, &crc32) != 0)
+    {
+        ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", 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) {
+        ALOGW("DexOptZ: extraction of %s from %s failed",
+            kClassesDex, debugFileName);
+        goto bail;
+    }
+
+    /* Parse the options. */
+    if (dexoptFlagStr[0] != '\0') {
+        const char* opc;
+        const char* val;
+
+        opc = strstr(dexoptFlagStr, "v=");      /* verification */
+        if (opc != NULL) {
+            switch (*(opc+2)) {
+            case 'n':   verifyMode = VERIFY_MODE_NONE;          break;
+            case 'r':   verifyMode = VERIFY_MODE_REMOTE;        break;
+            case 'a':   verifyMode = VERIFY_MODE_ALL;           break;
+            default:                                            break;
+            }
+        }
+
+        opc = strstr(dexoptFlagStr, "o=");      /* optimization */
+        if (opc != NULL) {
+            switch (*(opc+2)) {
+            case 'n':   dexOptMode = OPTIMIZE_MODE_NONE;        break;
+            case 'v':   dexOptMode = OPTIMIZE_MODE_VERIFIED;    break;
+            case 'a':   dexOptMode = OPTIMIZE_MODE_ALL;         break;
+            case 'f':   dexOptMode = OPTIMIZE_MODE_FULL;        break;
+            default:                                            break;
+            }
+        }
+
+        opc = strstr(dexoptFlagStr, "m=y");     /* register map */
+        if (opc != NULL) {
+            dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
+        }
+
+        opc = strstr(dexoptFlagStr, "u=");      /* uniprocessor target */
+        if (opc != NULL) {
+            switch (*(opc+2)) {
+            case 'y':   dexoptFlags |= DEXOPT_UNIPROCESSOR;     break;
+            case 'n':   dexoptFlags |= DEXOPT_SMP;              break;
+            default:                                            break;
+            }
+        }
+    }
+
+    /*
+     * Prep the VM and perform the optimization.
+     */
+
+    if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
+            dexoptFlags) != 0)
+    {
+        ALOGE("DexOptZ: VM init failed");
+        goto bail;
+    }
+
+    //vmStarted = 1;
+
+    /* do the optimization */
+    if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
+            modWhen, crc32, isBootstrap))
+    {
+        ALOGE("Optimization failed");
+        goto bail;
+    }
+
+    /* we don't shut the VM down -- process is about to exit */
+
+    result = 0;
+
+bail:
+    dexZipCloseArchive(&zippy);
+    return result;
+}
+
+/*
+ * Common functionality for normal device-side processing as well as
+ * preoptimization.
+ */
+static int processZipFile(int zipFd, int cacheFd, const char* zipName,
+        const char *dexoptFlags)
+{
+    char* bcpCopy = NULL;
+
+    /*
+     * Check to see if this is a bootstrap class entry. If so, truncate
+     * the path.
+     */
+    const char* bcp = getenv("BOOTCLASSPATH");
+    if (bcp == NULL) {
+        ALOGE("DexOptZ: BOOTCLASSPATH not set");
+        return -1;
+    }
+
+    bool isBootstrap = false;
+    const char* match = strstr(bcp, zipName);
+    if (match != NULL) {
+        /*
+         * TODO: we have a partial string match, but that doesn't mean
+         * we've matched an entire path component. We should make sure
+         * that we're matching on the full zipName, and if not we
+         * should re-do the strstr starting at (match+1).
+         *
+         * The scenario would be a bootclasspath with something like
+         * "/system/framework/core.jar" while we're trying to optimize
+         * "/framework/core.jar". Not very likely since all paths are
+         * absolute and end with ".jar", but not impossible.
+         */
+        int matchOffset = match - bcp;
+        if (matchOffset > 0 && bcp[matchOffset-1] == ':')
+            matchOffset--;
+        ALOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d",
+            zipName, matchOffset);
+        bcpCopy = strdup(bcp);
+        bcpCopy[matchOffset] = '\0';
+
+        bcp = bcpCopy;
+        ALOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'", bcp);
+        isBootstrap = true;
+    }
+
+    int result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
+            bcp, dexoptFlags);
+
+    free(bcpCopy);
+    return result;
+}
+
+/* advance to the next arg and extract it */
+#define GET_ARG(_var, _func, _msg)                                          \
+    {                                                                       \
+        char* endp;                                                         \
+        (_var) = _func(*++argv, &endp, 0);                                  \
+        if (*endp != '\0') {                                                \
+            ALOGE("%s '%s'", _msg, *argv);                                   \
+            goto bail;                                                      \
+        }                                                                   \
+        --argc;                                                             \
+    }
+
+/*
+ * Parse arguments.  We want:
+ *   0. (name of dexopt command -- ignored)
+ *   1. "--zip"
+ *   2. zip fd (input, read-only)
+ *   3. cache fd (output, read-write, locked with flock)
+ *   4. filename of zipfile being optimized (used for debug messages and
+ *      for comparing against BOOTCLASSPATH; does not need to be
+ *      accessible or even exist)
+ *   5. dexopt flags
+ *
+ * The BOOTCLASSPATH environment variable is assumed to hold the correct
+ * boot class path.  If the filename provided appears in the boot class
+ * path, the path will be truncated just before that entry (so that, if
+ * you were to dexopt "core.jar", your bootclasspath would be empty).
+ *
+ * This does not try to normalize the boot class path name, so the
+ * filename test won't catch you if you get creative.
+ */
+static int fromZip(int argc, char* const argv[])
+{
+    int result = -1;
+    int zipFd, cacheFd;
+    const char* zipName;
+    char* bcpCopy = NULL;
+    const char* dexoptFlags;
+
+    if (argc != 6) {
+        ALOGE("Wrong number of args for --zip (found %d)", argc);
+        goto bail;
+    }
+
+    /* skip "--zip" */
+    argc--;
+    argv++;
+
+    GET_ARG(zipFd, strtol, "bad zip fd");
+    GET_ARG(cacheFd, strtol, "bad cache fd");
+    zipName = *++argv;
+    --argc;
+    dexoptFlags = *++argv;
+    --argc;
+
+    result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);
+
+bail:
+    return result;
+}
+
+/*
+ * Parse arguments for a preoptimization run. This is when dalvikvm is run
+ * on a host to optimize dex files for eventual running on a (different)
+ * device. We want:
+ *   0. (name of dexopt command -- ignored)
+ *   1. "--preopt"
+ *   2. zipfile name
+ *   3. output file name
+ *   4. dexopt flags
+ *
+ * The BOOTCLASSPATH environment variable is assumed to hold the correct
+ * boot class path.  If the filename provided appears in the boot class
+ * path, the path will be truncated just before that entry (so that, if
+ * you were to dexopt "core.jar", your bootclasspath would be empty).
+ *
+ * This does not try to normalize the boot class path name, so the
+ * filename test won't catch you if you get creative.
+ */
+static int preopt(int argc, char* const argv[])
+{
+    int zipFd = -1;
+    int outFd = -1;
+    int result = -1;
+
+    if (argc != 5) {
+        /*
+         * Use stderr here, since this variant is meant to be called on
+         * the host side.
+         */
+        fprintf(stderr, "Wrong number of args for --preopt (found %d)\n",
+                argc);
+        return -1;
+    }
+
+    const char* zipName = argv[2];
+    const char* outName = argv[3];
+    const char* dexoptFlags = argv[4];
+
+    if (strstr(dexoptFlags, "u=y") == NULL &&
+        strstr(dexoptFlags, "u=n") == NULL)
+    {
+        fprintf(stderr, "Either 'u=y' or 'u=n' must be specified\n");
+        return -1;
+    }
+
+    zipFd = open(zipName, O_RDONLY);
+    if (zipFd < 0) {
+        perror(argv[0]);
+        return -1;
+    }
+
+    outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);
+    if (outFd < 0) {
+        perror(argv[0]);
+        goto bail;
+    }
+
+    result = processZipFile(zipFd, outFd, zipName, dexoptFlags);
+
+bail:
+    if (zipFd >= 0) {
+        close(zipFd);
+    }
+
+    if (outFd >= 0) {
+        close(outFd);
+    }
+
+    return result;
+}
+
+/*
+ * Parse arguments for an "old-style" invocation directly from the VM.
+ *
+ * Here's what we want:
+ *   0. (name of dexopt command -- ignored)
+ *   1. "--dex"
+ *   2. DALVIK_VM_BUILD value, as a sanity check
+ *   3. file descriptor, locked with flock, for DEX file being optimized
+ *   4. DEX offset within file
+ *   5. DEX length
+ *   6. filename of file being optimized (for debug messages only)
+ *   7. modification date of source (goes into dependency section)
+ *   8. CRC of source (goes into dependency section)
+ *   9. flags (optimization level, isBootstrap)
+ *  10. bootclasspath entry #1
+ *  11. bootclasspath entry #2
+ *   ...
+ *
+ * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the
+ * argument list and calls this executable.
+ *
+ * The bootclasspath entries become the dependencies for this DEX file.
+ *
+ * The open file descriptor MUST NOT be for one of the bootclasspath files.
+ * The parent has the descriptor locked, and we'll try to lock it again as
+ * part of processing the bootclasspath.  (We can catch this and return
+ * an error by comparing filenames or by opening the bootclasspath files
+ * and stat()ing them for inode numbers).
+ */
+static int fromDex(int argc, char* const argv[])
+{
+    int result = -1;
+    bool vmStarted = false;
+    char* bootClassPath = NULL;
+    int fd, flags, vmBuildVersion;
+    long offset, length;
+    const char* debugFileName;
+    u4 crc, modWhen;
+    char* endp;
+    bool onlyOptVerifiedDex = false;
+    DexClassVerifyMode verifyMode;
+    DexOptimizerMode dexOptMode;
+
+    if (argc < 10) {
+        /* don't have all mandatory args */
+        ALOGE("Not enough arguments for --dex (found %d)", argc);
+        goto bail;
+    }
+
+    /* skip "--dex" */
+    argc--;
+    argv++;
+
+    /*
+     * Extract the args.
+     */
+    GET_ARG(vmBuildVersion, strtol, "bad vm build");
+    if (vmBuildVersion != DALVIK_VM_BUILD) {
+        ALOGE("DexOpt: build rev does not match VM: %d vs %d",
+            vmBuildVersion, DALVIK_VM_BUILD);
+        goto bail;
+    }
+    GET_ARG(fd, strtol, "bad fd");
+    GET_ARG(offset, strtol, "bad offset");
+    GET_ARG(length, strtol, "bad length");
+    debugFileName = *++argv;
+    --argc;
+    GET_ARG(modWhen, strtoul, "bad modWhen");
+    GET_ARG(crc, strtoul, "bad crc");
+    GET_ARG(flags, strtol, "bad flags");
+
+    ALOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=%#x crc=%#x flg=%d (argc=%d)",
+        fd, offset, length, debugFileName, modWhen, crc, flags, argc);
+    assert(argc > 0);
+
+    if (--argc == 0) {
+        bootClassPath = strdup("");
+    } else {
+        int i, bcpLen;
+        char* const* argp;
+        char* cp;
+
+        bcpLen = 0;
+        for (i = 0, argp = argv; i < argc; i++) {
+            ++argp;
+            ALOGV("DEP: '%s'", *argp);
+            bcpLen += strlen(*argp) + 1;
+        }
+
+        cp = bootClassPath = (char*) malloc(bcpLen +1);
+        for (i = 0, argp = argv; i < argc; i++) {
+            int strLen;
+
+            ++argp;
+            strLen = strlen(*argp);
+            if (i != 0)
+                *cp++ = ':';
+            memcpy(cp, *argp, strLen);
+            cp += strLen;
+        }
+        *cp = '\0';
+
+        assert((int) strlen(bootClassPath) == bcpLen-1);
+    }
+    ALOGV("  bootclasspath is '%s'", bootClassPath);
+
+    /* start the VM partway */
+
+    /* ugh -- upgrade these to a bit field if they get any more complex */
+    if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
+        if ((flags & DEXOPT_VERIFY_ALL) != 0)
+            verifyMode = VERIFY_MODE_ALL;
+        else
+            verifyMode = VERIFY_MODE_REMOTE;
+    } else {
+        verifyMode = VERIFY_MODE_NONE;
+    }
+    if ((flags & DEXOPT_OPT_ENABLED) != 0) {
+        if ((flags & DEXOPT_OPT_ALL) != 0)
+            dexOptMode = OPTIMIZE_MODE_ALL;
+        else
+            dexOptMode = OPTIMIZE_MODE_VERIFIED;
+    } else {
+        dexOptMode = OPTIMIZE_MODE_NONE;
+    }
+
+    if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
+        ALOGE("VM init failed");
+        goto bail;
+    }
+
+    vmStarted = true;
+
+    /* do the optimization */
+    if (!dvmContinueOptimization(fd, offset, length, debugFileName,
+            modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
+    {
+        ALOGE("Optimization failed");
+        goto bail;
+    }
+
+    result = 0;
+
+bail:
+    /*
+     * In theory we should gracefully shut the VM down at this point.  In
+     * practice that only matters if we're checking for memory leaks with
+     * valgrind -- simply exiting is much faster.
+     *
+     * As it turns out, the DEX optimizer plays a little fast and loose
+     * with class loading.  We load all of the classes from a partially-
+     * formed DEX file, which is unmapped when we're done.  If we want to
+     * do clean shutdown here, perhaps for testing with valgrind, we need
+     * to skip the munmap call there.
+     */
+#if 0
+    if (vmStarted) {
+        ALOGI("DexOpt shutting down, result=%d", result);
+        dvmShutdown();
+    }
+#endif
+
+    free(bootClassPath);
+    ALOGV("DexOpt command complete (result=%d)", result);
+    return result;
+}
+
+/*
+ * Main entry point.  Decide where to go.
+ */
+int main(int argc, char* const argv[])
+{
+    set_process_name("dexopt");
+
+    setvbuf(stdout, NULL, _IONBF, 0);
+
+    if (argc > 1) {
+        if (strcmp(argv[1], "--zip") == 0)
+            return fromZip(argc, argv);
+        else if (strcmp(argv[1], "--dex") == 0)
+            return fromDex(argc, argv);
+        else if (strcmp(argv[1], "--preopt") == 0)
+            return preopt(argc, argv);
+    }
+
+    fprintf(stderr,
+        "Usage:\n\n"
+        "Short version: Don't use this.\n\n"
+        "Slightly longer version: This system-internal tool is used to\n"
+        "produce optimized dex files. See the source code for details.\n");
+
+    return 1;
+}
diff --git a/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..78ba945
--- /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>
+          instruction must not be used if the same
+          <code>new-instance</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>invoke-&lt;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..8021145
--- /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/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..5c444de
--- /dev/null
+++ b/docs/embedded-vm-control.html
@@ -0,0 +1,271 @@
+<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="#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>
+
+
+<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="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 "Gingerbread" 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..3707377
--- /dev/null
+++ b/docs/heap-profiling.html
@@ -0,0 +1,215 @@
+<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>
+
+<h3>Android 3.0 ("Honeycomb")</h3>
+<p>
+A new command-line tool has been added:
+</p>
+<blockquote><pre>am dumpheap &lt;pid&gt; &lt;output-file-name&gt;</pre></blockquote>
+<p>
+Unlike the <code>SIGUSR1</code> approach, this does not require a rooted
+phone.  It's only necessary for the application to be debuggable (by setting
+<code>android:debuggable="true"</code> in the <code>&lt;application&gt;</code>
+element of the app manifest).  The output file is opened by "am", which
+means you can write the data to a file on <code>/sdcard</code> without
+needing the <code>WRITE_EXTERNAL_STORAGE</code> permission in your app.
+<p>
+The <code>runhat</code> shell function has been updated to use this.
+</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/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/porting-guide.html b/docs/porting-guide.html
new file mode 100644
index 0000000..d1a1ea3
--- /dev/null
+++ b/docs/porting-guide.html
@@ -0,0 +1,374 @@
+<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.
+</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.
+<li>Disable the Dalvik JIT.  You can do this in the general device
+configuration, or by editing the initialization of WITH_JIT in
+<code>dalvik/vm/Dvm.mk</code> to always be <code>false</code>.
+</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>mmm dalvik/vm</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>vm/mterp/config-*</code> files
+for examples.
+</p>
+
+
+<h3>Replacing Stubs</h3>
+
+<p>
+There are roughly 250 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>
+
+
+<h2>Other Performance Issues</h2>
+
+<p>
+The <code>System.arraycopy()</code> function is heavily used.  The
+implementation relies on the bionic C library to provide a fast,
+platform-optimized data copy function for arrays with elements wider
+than one byte.  If you're not using bionic, or your platform does not
+have an implementation of this method, Dalvik will use correct but
+sub-optimal algorithms instead.  For best performance you will want
+to provide your own version.
+</p><p>
+See the comments in <code>dalvik/vm/native/java_lang_System.c</code>
+for details.
+</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..276967f
--- /dev/null
+++ b/docs/verifier.html
@@ -0,0 +1,216 @@
+<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>Monitor Verification</h2>
+
+<p>
+If a method locks an object with a <code>synchronized</code> statement, the
+object must be unlocked before the method returns.  At the bytecode level,
+this means the method must execute a matching <code>monitor-exit</code>
+for every <code>monitor-enter</code> instruction, whether the function
+completes normally or abnormally.  The bytecode verifier optionally
+enforces this.
+
+<p>
+The verifier uses a fairly simple-minded model.  If you enter a monitor
+held in register N, you can exit the monitor using register N or any
+subsequently-made copies of register N.  The verifier does not attempt
+to identify previously-made copies, track loads and stores through
+fields, or recognize identical constant values (for example, the result
+values from two <code>const-class</code> instructions on the same class
+will be the same reference, but the verifier doesn't recognize this).
+
+<p>
+Further, you may only exit the monitor most recently entered.  "Hand
+over hand" locking techniques, e.g. "lock A; lock B; unlock A; unlock B",
+are not allowed.
+
+<p>
+This means that there are a number of situations in which the verifier
+will throw an exception on code that would execute correctly at run time.
+This is not expected to be an issue for compiler-generated bytecode.
+
+<p>
+For implementation convenience, the maximum nesting depth of
+<code>synchronized</code> statements has been set to 32.  This is not
+a limitation on the recursion count.  The only way to trip this would be
+to have a single method with more than 32 nested <code>synchronized</code>
+statements, something that is unlikely to occur.
+
+
+<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/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..b31b23b
--- /dev/null
+++ b/dx/Android.mk
@@ -0,0 +1,74 @@
+# 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, \
+		junit-tests \
+		src \
+	))
+
+include $(subdirs)
diff --git a/dx/NOTICE b/dx/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/dx/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/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/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..ac14fe6
--- /dev/null
+++ b/dx/etc/dx.bat
@@ -0,0 +1,89 @@
+@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

+

+rem Check we have a valid Java.exe in the path.

+set java_exe=

+call ..\tools\lib\find_java.bat

+if not defined java_exe goto :EOF

+

+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 and a stack size of 1meg.

+rem This can be overridden by using "-JXmx..." and "-JXss..." options below.

+set defaultXmx=-Xmx1024M

+set defaultXss=-Xss1m

+

+REM Capture all arguments that are not -J options.

+REM Note that when reading the input arguments with %1, the cmd.exe

+REM automagically converts --name=value arguments into 2 arguments "--name"

+REM followed by "value". Dx has been changed to know how to deal with that.

+set params=

+

+:firstArg

+if [%1]==[] goto endArgs

+set a=%~1

+

+    if [%defaultXmx%]==[] goto notXmx

+    if %a:~0,5% NEQ -JXmx goto notXmx

+        set defaultXmx=

+    :notXmx

+

+    if [%defaultXss%]==[] goto notXss

+    if %a:~0,5% NEQ -JXss goto notXss

+        set defaultXss=

+    :notXss

+

+    if %a:~0,2% NEQ -J goto notJ

+        set javaOpts=%javaOpts% -%a:~2%

+        shift /1

+        goto firstArg

+

+    :notJ

+    set params=%params% %1

+    shift /1

+    goto firstArg

+

+:endArgs

+

+set javaOpts=%javaOpts% %defaultXmx% %defaultXss%

+

+call %java_exe% %javaOpts% -Djava.ext.dirs=%frameworkdir% -jar %jarpath% %params%

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/junit-tests/Android.mk b/dx/junit-tests/Android.mk
new file mode 100644
index 0000000..3f2c611
--- /dev/null
+++ b/dx/junit-tests/Android.mk
@@ -0,0 +1,9 @@
+# Copyright 2011 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAVA_LIBRARIES := dx junit
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= dx-tests
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/dx/junit-tests/com/android/dx/util/BitIntSetTest.java b/dx/junit-tests/com/android/dx/util/BitIntSetTest.java
new file mode 100644
index 0000000..d46d14f
--- /dev/null
+++ b/dx/junit-tests/com/android/dx/util/BitIntSetTest.java
@@ -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.
+ */
+
+package com.android.dx.util;
+
+import java.util.NoSuchElementException;
+import junit.framework.TestCase;
+
+public final class BitIntSetTest 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/junit-tests/com/android/dx/util/BitsTest.java b/dx/junit-tests/com/android/dx/util/BitsTest.java
new file mode 100644
index 0000000..e427513
--- /dev/null
+++ b/dx/junit-tests/com/android/dx/util/BitsTest.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase;
+
+public final class BitsTest 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/junit-tests/com/android/dx/util/IntListTest.java b/dx/junit-tests/com/android/dx/util/IntListTest.java
new file mode 100644
index 0000000..7a53a67
--- /dev/null
+++ b/dx/junit-tests/com/android/dx/util/IntListTest.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.util;
+
+import junit.framework.TestCase;
+
+public final class IntListTest extends TestCase {
+    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/junit-tests/com/android/dx/util/ListIntSetTest.java b/dx/junit-tests/com/android/dx/util/ListIntSetTest.java
new file mode 100644
index 0000000..868e630
--- /dev/null
+++ b/dx/junit-tests/com/android/dx/util/ListIntSetTest.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.util;
+
+import java.util.NoSuchElementException;
+import junit.framework.TestCase;
+
+public final class ListIntSetTest 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/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..130025a
--- /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.7";
+}
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..52def9c
--- /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.CstString;
+
+/**
+ * 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 CstString signature;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param signature {@code non-null;} the signature string
+     */
+    public AttSignature(CstString 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 CstString 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..cc19d27
--- /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.CstString;
+
+/**
+ * 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 CstString sourceFile;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param sourceFile {@code non-null;} the name of the source file
+     */
+    public AttSourceFile(CstString 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 CstString 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..830118c
--- /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.CstString;
+import com.android.dx.rop.cst.CstType;
+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,
+                    CstString 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 CstString 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,
+                    CstString 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 CstString 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..c56dd3e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BaseMachine.java
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+    /** specifies if local has info in the local variable table */
+    private boolean localInfo;
+
+    /** {@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;
+        localInfo = false;
+        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 " + type3.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 localInfo(boolean local) {
+        localInfo = local;
+    }
+
+    /** {@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 whether the loaded local has info in the local variable table.
+     *
+     * @return {@code true} if local arg has info in the local variable table
+     */
+    protected final boolean getLocalInfo() {
+        return localInfo;
+    }
+
+    /**
+     * 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}.
+     *
+     * @param isMove {@code true} if the operation being performed on the
+     * local is a move. This will cause constant values to be propagated
+     * to the returned local
+     * @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(boolean isMove) {
+        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) {
+            /*
+             * If this is to be a move operation and the result is a
+             * known value, make the returned localTarget embody that
+             * value.
+             */
+            if (isMove) {
+                return localTarget.withType(result);
+            } else {
+                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(false));
+        } else {
+            ExecutionStack stack = frame.getStack();
+            for (int i = 0; i < resultCount; i++) {
+                if (localInfo) {
+                    stack.setLocal();
+                }
+                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..56d3991
--- /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.CstType;
+import com.android.dx.rop.cst.CstString;
+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..f4ea007
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BytecodeArray.java
@@ -0,0 +1,1422 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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: {
+                    /*
+                     * Note: This is a load from either a byte[] or a
+                     * boolean[].
+                     */
+                    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: {
+                    /*
+                     * Note: This is a load from either a byte[] or a
+                     * boolean[].
+                     */
+                    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;
+        }
+    }
+
+    /**
+     * Implementation of {@link Visitor}, which just pays attention
+     * to constant values.
+     */
+    class ConstantParserVisitor extends BaseVisitor {
+        Constant cst;
+        int length;
+        int value;
+
+        /** Empty constructor */
+        ConstantParserVisitor() {
+        }
+
+        private void clear() {
+            length = 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitInvalid(int opcode, int offset, int length) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value) {
+            this.cst = cst;
+            this.length = length;
+            this.value = value;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitBranch(int opcode, int offset, int length,
+                int target) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initVals) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void setPreviousOffset(int offset) {
+            // Intentionally left empty
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        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..39c2399
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ConcreteMethod.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.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.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.CstString;
+import com.android.dx.rop.cst.CstType;
+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 CstString 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, cf.getAccessFlags(), cf.getSourceFile(), keepLines, keepLocals);
+    }
+
+    public ConcreteMethod(Method method, int accessFlags, CstString sourceFile,
+            boolean keepLines, boolean keepLocals) {
+        this.method = method;
+        this.accSuper = (accessFlags & AccessFlags.ACC_SUPER) != 0;
+        this.sourceFile = sourceFile;
+
+        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 CstString getName() {
+        return method.getName();
+    }
+
+    /** {@inheritDoc} */
+    public CstString 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..51f6334
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ExecutionStack.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 non-null;} array specifying whether stack contents have entries
+     * in the local variable table
+     */
+    private final boolean[] local;
+    /**
+     * {@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];
+        local = new boolean[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);
+        System.arraycopy(local, 0, result.local, 0, local.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;
+            local[i] = false;
+        }
+
+        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++;
+    }
+
+    /**
+     * Flags the next value pushed onto the stack as having local info.
+     */
+    public void setLocal() {
+        throwIfImmutable();
+
+        local[stackPtr] = true;
+    }
+
+    /**
+     * 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 whether or not it has local info.
+     *
+     * @param n {@code >= 0;} which element to peek at
+     * @return {@code true} if the value has local info, {@code false} otherwise
+     * @throws SimException thrown if {@code n >= size()}
+     */
+    public boolean peekLocal(int n) {
+        if (n < 0) {
+            throw new IllegalArgumentException("n < 0");
+        }
+
+        if (n >= stackPtr) {
+            throw new SimException("stack: underflow");
+        }
+
+        return local[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;
+        local[stackPtr - 1] = false;
+        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..2962698
--- /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.CstString;
+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) {
+                CstString 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, CstString name,
+            CstString descriptor, CstString 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 CstString name;
+
+        /** {@code null-ok;} the variable's type descriptor */
+        private final CstString descriptor;
+
+        /** {@code null-ok;} the variable's type signature */
+        private final CstString 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, CstString name,
+                CstString descriptor, CstString 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 CstString 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 CstString 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(CstString 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..a81feaf
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Machine.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.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);
+
+    /**
+     * Used to specify if a loaded local variable has info in the local
+     * variable table.
+     *
+     * @param local {@code true} if local arg has info in local variable table
+     */
+    public void localInfo(boolean local);
+
+    /**
+     * 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..715cfd8
--- /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 != null && 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..f45bc1f
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/RopperMachine.java
@@ -0,0 +1,963 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstInteger;
+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.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 CstString("newInstance"),
+                                    new CstString("(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(opcode == ByteOps.ISTORE);
+        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 firstType = sources.get(0).getTypeBearer();
+            TypeBearer lastType = sources.get(1).getTypeBearer();
+
+            if ((lastType.isConstant() || firstType.isConstant()) &&
+                 advice.hasConstantOperation(rop, sources.get(0),
+                                             sources.get(1))) {
+
+                if (lastType.isConstant()) {
+                    /*
+                     * 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();
+
+                    // For subtraction, change to addition and invert constant
+                    if (rop.getOpcode() == RegOps.SUB) {
+                        ropOpcode = RegOps.ADD;
+                        CstInteger cstInt = (CstInteger) lastType;
+                        cst = CstInteger.make(-cstInt.getValue());
+                    }
+                } else {
+                    /*
+                     * The target architecture has an instruction that can
+                     * build in the constant found in the first argument,
+                     * so pull it out of the sources and just use it as a
+                     * constant here.
+                     */
+                    cst = (Constant) firstType;
+                    sources = sources.withoutFirst();
+                }
+
+                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..c097831
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Simulator.java
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.util.Hex;
+
+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");
+    }
+
+    /**
+     * Returns the required array type for an array load or store
+     * instruction, based on a given implied type and an observed
+     * actual array type.
+     *
+     * <p>The interesting cases here have to do with object arrays,
+     * <code>byte[]</code>s, <code>boolean[]</code>s, and
+     * known-nulls.</p>
+     *
+     * <p>In the case of arrays of objects, we want to narrow the type
+     * to the actual array present on the stack, as long as what is
+     * present is an object type. Similarly, due to a quirk of the
+     * original bytecode representation, the instructions for dealing
+     * with <code>byte[]</code> and <code>boolean[]</code> are
+     * undifferentiated, and we aim here to return whichever one was
+     * actually present on the stack.</p>
+     *
+     * <p>In the case where there is a known-null on the stack where
+     * an array is expected, we just fall back to the implied type of
+     * the instruction. Due to the quirk described above, this means
+     * that source code that uses <code>boolean[]</code> might get
+     * translated surprisingly -- but correctly -- into an instruction
+     * that specifies a <code>byte[]</code>. It will be correct,
+     * because should the code actually execute, it will necessarily
+     * throw a <code>NullPointerException</code>, and it won't matter
+     * what opcode variant is used to achieve that result.</p>
+     *
+     * @param impliedType {@code non-null;} type implied by the
+     * instruction; is <i>not</i> an array type
+     * @param foundArrayType {@code non-null;} type found on the
+     * stack; is either an array type or a known-null
+     * @return {@code non-null;} the array type that should be
+     * required in this context
+     */
+    private static Type requiredArrayTypeFor(Type impliedType,
+            Type foundArrayType) {
+        if (foundArrayType == Type.KNOWN_NULL) {
+            return impliedType.getArrayType();
+        }
+
+        if ((impliedType == Type.OBJECT)
+                && foundArrayType.isArray()
+                && foundArrayType.getComponentType().isReference()) {
+            return foundArrayType;
+        }
+
+        if ((impliedType == Type.BYTE)
+                && (foundArrayType == Type.BOOLEAN_ARRAY)) {
+            /*
+             * Per above, an instruction with implied byte[] is also
+             * allowed to be used on boolean[].
+             */
+            return Type.BOOLEAN_ARRAY;
+        }
+
+        return impliedType.getArrayType();
+    }
+
+    /**
+     * 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: {
+                    /*
+                     * See comment on requiredArrayTypeFor() for explanation
+                     * about what's going on here.
+                     */
+                    Type foundArrayType = frame.getStack().peekType(1);
+                    Type requiredArrayType =
+                        requiredArrayTypeFor(type, foundArrayType);
+
+                    // Make type agree with the discovered requiredArrayType.
+                    type = requiredArrayType.getComponentType();
+
+                    machine.popArgs(frame, requiredArrayType, 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: {
+                    /*
+                     * See comment on requiredArrayTypeFor() for
+                     * explanation about what's going on here. In
+                     * addition to that, the category 1 vs. 2 thing
+                     * below is to deal with the fact that, if the
+                     * element type is category 2, we have to skip
+                     * over one extra stack slot to find the array.
+                     */
+                    ExecutionStack stack = frame.getStack();
+                    int peekDepth = type.isCategory1() ? 2 : 3;
+                    Type foundArrayType = stack.peekType(peekDepth);
+                    boolean foundArrayLocal = stack.peekLocal(peekDepth);
+
+                    Type requiredArrayType =
+                        requiredArrayTypeFor(type, foundArrayType);
+
+                    /*
+                     * Make type agree with the discovered requiredArrayType
+                     * if it has local info.
+                     */
+                    if (foundArrayLocal) {
+                        type = requiredArrayType.getComponentType();
+                    }
+
+                    machine.popArgs(frame, requiredArrayType, 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.localInfo(local != null);
+                    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..7ec2fba
--- /dev/null
+++ b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 static com.android.dx.cf.cst.ConstantTags.CONSTANT_Class;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Double;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Fieldref;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Float;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Integer;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_InterfaceMethodref;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Long;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Methodref;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_NameAndType;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_String;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Utf8;
+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.StdConstantPool;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import java.util.BitSet;
+
+/**
+ * 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);
+        }
+
+        /*
+         * Track the constant value's original string type. True if constants[i] was
+         * a CONSTANT_Utf8, false for any other type including CONSTANT_string.
+         */
+        BitSet wasUtf8 = new BitSet(offsets.length);
+
+        for (int i = 1; i < offsets.length; i++) {
+            int offset = offsets[i];
+            if ((offset != 0) && (pool.getOrNull(i) == null)) {
+                parse0(i, wasUtf8);
+            }
+        }
+
+        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;
+                    }
+                }
+                String human = wasUtf8.get(i)
+                        ? Hex.u2(i) + ": utf8{\"" + cst.toHuman() + "\"}"
+                        : Hex.u2(i) + ": " + cst.toString();
+                observer.parsed(bytes, offset, nextOffset - offset, human);
+            }
+
+            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, BitSet wasUtf8) {
+        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);
+                    wasUtf8.set(idx);
+                    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);
+                    CstString name = (CstString) parse0(nameIndex, wasUtf8);
+                    cst = new CstType(Type.internClassName(name.getString()));
+                    break;
+                }
+                case CONSTANT_String: {
+                    int stringIndex = bytes.getUnsignedShort(at + 1);
+                    cst = parse0(stringIndex, wasUtf8);
+                    break;
+                }
+                case CONSTANT_Fieldref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex, wasUtf8);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
+                    cst = new CstFieldRef(type, nat);
+                    break;
+                }
+                case CONSTANT_Methodref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex, wasUtf8);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
+                    cst = new CstMethodRef(type, nat);
+                    break;
+                }
+                case CONSTANT_InterfaceMethodref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex, wasUtf8);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
+                    cst = new CstInterfaceMethodRef(type, nat);
+                    break;
+                }
+                case CONSTANT_NameAndType: {
+                    int nameIndex = bytes.getUnsignedShort(at + 1);
+                    CstString name = (CstString) parse0(nameIndex, wasUtf8);
+                    int descriptorIndex = bytes.getUnsignedShort(at + 3);
+                    CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
+                    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 CstString 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 CstString(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..fdbc990
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AnnotationParser.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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();
+        CstString typeString = (CstString) pool.get(typeIndex);
+        CstType type = new CstType(Type.intern(typeString.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();
+        CstString elementName = (CstString) 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) {
+            CstString humanTag = new CstString(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();
+                CstString value = (CstString) pool.get(classInfoIndex);
+                Type type = Type.internReturnType(value.getString());
+
+                if (observer != null) {
+                    parsed(2, "class_info: " + type.toHuman());
+                }
+
+                return new CstType(type);
+            }
+            case 's': {
+                return parseConstant();
+            }
+            case 'e': {
+                requireLength(4);
+
+                int typeNameIndex = input.readUnsignedShort();
+                int constNameIndex = input.readUnsignedShort();
+                CstString typeName = (CstString) pool.get(typeNameIndex);
+                CstString constName = (CstString) 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 CstString)
+                ? ((CstString) 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..f7486eb
--- /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.CstString;
+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");
+        }
+
+        CstString name = null;
+
+        try {
+            ByteArray bytes = cf.getBytes();
+            ConstantPool pool = cf.getConstantPool();
+            int nameIdx = bytes.getUnsignedShort(offset);
+            int length = bytes.getInt(offset + 2);
+
+            name = (CstString) 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..7621bf7
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/ClassPathOpener.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.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 lastModified milliseconds since 1970-Jan-1 00:00:00 GMT
+         * @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, long lastModified, 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, file.lastModified(), 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, one.getTime(), 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..4f1c85d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstString;
+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
+     *
+     * See http://en.wikipedia.org/wiki/Java_class_file for an up-to-date
+     * list of version numbers. Currently known (taken from that table) are:
+     *
+     *     J2SE 6.0 = 50 (0x32 hex),
+     *     J2SE 5.0 = 49 (0x31 hex),
+     *     JDK 1.4 = 48 (0x30 hex),
+     *     JDK 1.3 = 47 (0x2F hex),
+     *     JDK 1.2 = 46 (0x2E hex),
+     *     JDK 1.1 = 45 (0x2D hex).
+     *
+     * 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
+     *
+     * Note: if you change this, please change "java.class.version" in System.java.
+     */
+    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 CstString 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..605bab8
--- /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.CstString;
+import com.android.dx.rop.cst.CstType;
+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);
+                CstString name = (CstString) pool.get(nameIdx);
+                CstString desc = (CstString) 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..ae04a13
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.CstString;
+import com.android.dx.rop.cst.CstType;
+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);
+            CstString name = (CstString) 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();
+                CstString name = (CstString) pool.get(nameIdx);
+                CstString type = (CstString) pool.get(typeIdx);
+                CstString descriptor = null;
+                CstString 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);
+        CstString cst = (CstString) 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);
+        CstString cst = (CstString) 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..cb5237a
--- /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.CstString;
+import com.android.dx.rop.cst.CstType;
+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 CstString 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..b346de4
--- /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.CstString;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * 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 CstString 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 CstString 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..e67b216
--- /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.CstString;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * 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 CstString getName() {
+        return nat.getName();
+    }
+
+    /** {@inheritDoc} */
+    public final CstString 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..6540e35
--- /dev/null
+++ b/dx/src/com/android/dx/command/Main.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * 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" +
+        "  [--num-threads=<n>] [--incremental] [--force-jumbo]\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 --find-usages <file.dex> <declaring type> <member>\n" +
+        "    Find references and declarations to a field or method.\n" +
+        "    declaring type: a class name in internal form, like " +
+        "Ljava/lang/Object;\n" +
+        "    member: a field or method name, like hashCode\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("--find-usages")) {
+                    com.android.dx.command.findusages.Main.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..6584b60
--- /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, long lastModified, 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..87f152a
--- /dev/null
+++ b/dx/src/com/android/dx/command/dexer/Main.java
@@ -0,0 +1,1223 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.code.SimException;
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.command.DxConsole;
+import com.android.dx.command.UsageException;
+import com.android.dx.dex.DexFormat;
+import com.android.dx.dex.DexOptions;
+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.io.DexBuffer;
+import com.android.dx.merge.CollisionPolicy;
+import com.android.dx.merge.DexMerger;
+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.CstString;
+import com.android.dx.util.FileUtils;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+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.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+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 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", "sip", "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;
+
+    /** Library .dex files to merge into the output .dex. */
+    private static final List<byte[]> libraryDexBuffers = new ArrayList<byte[]>();
+
+    /** thread pool object used for multi-threaded file processing */
+    private static ExecutorService threadPool;
+
+    /** true if any files are successfully processed */
+    private static boolean anyFilesProcessed;
+
+    /** class files older than this must be defined in the target dex file. */
+    private static long minimumFileAge = 0;
+
+    /**
+     * 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) throws IOException {
+        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) throws IOException {
+        // Reset the error/warning count to start fresh.
+        warnings = 0;
+        errors = 0;
+        // empty the list, so that  tools that load dx and keep it around
+        // for multiple runs don't reuse older buffers.
+        libraryDexBuffers.clear();
+
+        args = arguments;
+        args.makeOptionsObjects();
+
+        File incrementalOutFile = null;
+        if (args.incremental) {
+            if (args.outName == null) {
+                System.err.println(
+                        "error: no incremental output name specified");
+                return -1;
+            }
+            incrementalOutFile = new File(args.outName);
+            if (incrementalOutFile.exists()) {
+                minimumFileAge = incrementalOutFile.lastModified();
+            }
+        }
+
+        if (!processAllFiles()) {
+            return 1;
+        }
+
+        if (args.incremental && !anyFilesProcessed) {
+            return 0; // this was a no-op incremental build
+        }
+
+        // this array is null if no classes were defined
+        byte[] outArray = null;
+
+        if (!outputDex.isEmpty()) {
+            outArray = writeDex();
+
+            if (outArray == null) {
+                return 2;
+            }
+        }
+
+        if (args.incremental) {
+            outArray = mergeIncremental(outArray, incrementalOutFile);
+        }
+
+        outArray = mergeLibraryDexBuffers(outArray);
+
+        if (args.jarOutput) {
+            // Effectively free up the (often massive) DexFile memory.
+            outputDex = null;
+
+            if (!createJar(args.outName, outArray)) {
+                return 3;
+            }
+        } else if (outArray != null && args.outName != null) {
+            OutputStream out = openOutput(args.outName);
+            out.write(outArray);
+            closeOutput(out);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Merges the dex files {@code update} and {@code base}, preferring
+     * {@code update}'s definition for types defined in both dex files.
+     *
+     * @param base a file to find the previous dex file. May be a .dex file, a
+     *     jar file possibly containing a .dex file, or null.
+     * @return the bytes of the merged dex file, or null if both the update
+     *     and the base dex do not exist.
+     */
+    private static byte[] mergeIncremental(byte[] update, File base) throws IOException {
+        DexBuffer dexA = null;
+        DexBuffer dexB = null;
+
+        if (update != null) {
+            dexA = new DexBuffer(update);
+        }
+
+        if (base.exists()) {
+            dexB = new DexBuffer(base);
+        }
+
+        DexBuffer result;
+        if (dexA == null && dexB == null) {
+            return null;
+        } else if (dexA == null) {
+            result = dexB;
+        } else if (dexB == null) {
+            result = dexA;
+        } else {
+            result = new DexMerger(dexA, dexB, CollisionPolicy.KEEP_FIRST).merge();
+        }
+
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        result.writeTo(bytesOut);
+        return bytesOut.toByteArray();
+    }
+
+    /**
+     * Merges the dex files in library jars. If multiple dex files define the
+     * same type, this fails with an exception.
+     */
+    private static byte[] mergeLibraryDexBuffers(byte[] outArray) throws IOException {
+        for (byte[] libraryDexBuffer : libraryDexBuffers) {
+            if (outArray == null) {
+                outArray = libraryDexBuffer;
+                continue;
+            }
+
+            DexBuffer a = new DexBuffer(outArray);
+            DexBuffer b = new DexBuffer(libraryDexBuffer);
+            DexBuffer ab = new DexMerger(a, b, CollisionPolicy.FAIL).merge();
+            outArray = ab.getBytes();
+        }
+
+        return outArray;
+    }
+
+    /**
+     * 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(args.dexOptions);
+
+        if (args.jarOutput) {
+            outputResources = new TreeMap<String, byte[]>();
+        }
+
+        if (args.dumpWidth != 0) {
+            outputDex.setDumpWidth(args.dumpWidth);
+        }
+
+        anyFilesProcessed = false;
+        String[] fileNames = args.fileNames;
+
+        if (args.numThreads > 1) {
+            threadPool = Executors.newFixedThreadPool(args.numThreads);
+        }
+
+        try {
+            for (int i = 0; i < fileNames.length; i++) {
+                if (processOne(fileNames[i])) {
+                    anyFilesProcessed = true;
+                }
+            }
+        } catch (StopProcessing ex) {
+            /*
+             * Ignore it and just let the warning/error reporting do
+             * their things.
+             */
+        }
+
+        if (args.numThreads > 1) {
+            try {
+                threadPool.shutdown();
+                threadPool.awaitTermination(600L, TimeUnit.SECONDS);
+            } catch (InterruptedException ex) {
+                throw new RuntimeException("Timed out waiting for threads.");
+            }
+        }
+
+        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 (args.incremental && !anyFilesProcessed) {
+            return true;
+        }
+
+        if (!(anyFilesProcessed || 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, long lastModified, byte[] bytes) {
+                if (args.numThreads > 1) {
+                    threadPool.execute(new ParallelProcessor(name, lastModified, bytes));
+                    return false;
+                } else {
+                    return Main.processFileBytes(name, lastModified, bytes);
+                }
+            }
+            public void onException(Exception ex) {
+                if (ex instanceof StopProcessing) {
+                    throw (StopProcessing) ex;
+                } else if (ex instanceof SimException) {
+                    DxConsole.err.println("\nEXCEPTION FROM SIMULATION:");
+                    DxConsole.err.println(ex.getMessage() + "\n");
+                    DxConsole.err.println(((SimException) ex).getContext());
+                } else {
+                    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, long lastModified, byte[] bytes) {
+        boolean isClass = name.endsWith(".class");
+        boolean isClassesDex = name.equals(DexFormat.DEX_IN_JAR_NAME);
+        boolean keepResources = (outputResources != null);
+
+        if (!isClass && !isClassesDex && !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) {
+                synchronized (outputResources) {
+                    outputResources.put(fixedName, bytes);
+                }
+            }
+            if (lastModified < minimumFileAge) {
+                return true;
+            }
+            return processClass(fixedName, bytes);
+        } else if (isClassesDex) {
+            synchronized (libraryDexBuffers) {
+                libraryDexBuffers.add(bytes);
+            }
+            return true;
+        } else {
+            synchronized (outputResources) {
+                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, args.dexOptions);
+            synchronized (outputDex) {
+                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[]} and 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 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.statistics) {
+                    DxConsole.out.println(outputDex.getStatistics().toHuman());
+                }
+            } finally {
+                if (humanOut != null) {
+                    humanOut.flush();
+                }
+                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 array containing the dex file to include, or null if the
+     *     output contains no class defs.
+     * @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);
+
+            if (dexArray != null) {
+                outputResources.put(DexFormat.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", DexFormat.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.
+             */
+            CstString 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;
+
+        /** what API level to target */
+        public int targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES;
+
+        /** how much source position info to preserve */
+        public int positionInfo = PositionList.LINES;
+
+        /** whether to keep local variable information */
+        public boolean localInfo = true;
+
+        /** whether to merge with the output dex file if it exists. */
+        public boolean incremental = false;
+
+        /** whether to force generation of const-string/jumbo for all indexes,
+         *  to allow merges between dex files with many strings. */
+        public boolean forceJumbo = false;
+
+        /** {@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 class file transformation */
+        public CfOptions cfOptions;
+
+        /** Options for dex file output */
+        public DexOptions dexOptions;
+
+        /** number of threads to run with */
+        public int numThreads = 1;
+
+        private static class ArgumentsParser {
+
+            /** The arguments to process. */
+            private final String[] arguments;
+            /** The index of the next argument to process. */
+            private int index;
+            /** The current argument being processed after a {@link #getNext()} call. */
+            private String current;
+            /** The last value of an argument processed by {@link #isArg(String)}. */
+            private String lastValue;
+
+            public ArgumentsParser(String[] arguments) {
+                this.arguments = arguments;
+                index = 0;
+            }
+
+            public String getCurrent() {
+                return current;
+            }
+
+            public String getLastValue() {
+                return lastValue;
+            }
+
+            /**
+             * Moves on to the next argument.
+             * Returns false when we ran out of arguments that start with --.
+             */
+            public boolean getNext() {
+                if (index >= arguments.length) {
+                    return false;
+                }
+                current = arguments[index];
+                if (current.equals("--") || !current.startsWith("--")) {
+                    return false;
+                }
+                index++;
+                return true;
+            }
+
+            /**
+             * Similar to {@link #getNext()}, this moves on the to next argument.
+             * It does not check however whether the argument starts with --
+             * and thus can be used to retrieve values.
+             */
+            private boolean getNextValue() {
+                if (index >= arguments.length) {
+                    return false;
+                }
+                current = arguments[index];
+                index++;
+                return true;
+            }
+
+            /**
+             * Returns all the arguments that have not been processed yet.
+             */
+            public String[] getRemaining() {
+                int n = arguments.length - index;
+                String[] remaining = new String[n];
+                if (n > 0) {
+                    System.arraycopy(arguments, index, remaining, 0, n);
+                }
+                return remaining;
+            }
+
+            /**
+             * Checks the current argument against the given prefix.
+             * If prefix is in the form '--name=', an extra value is expected.
+             * The argument can then be in the form '--name=value' or as a 2-argument
+             * form '--name value'.
+             */
+            public boolean isArg(String prefix) {
+                int n = prefix.length();
+                if (n > 0 && prefix.charAt(n-1) == '=') {
+                    // Argument accepts a value. Capture it.
+                    if (current.startsWith(prefix)) {
+                        // Argument is in the form --name=value, split the value out
+                        lastValue = current.substring(n);
+                        return true;
+                    } else {
+                        // Check whether we have "--name value" as 2 arguments
+                        prefix = prefix.substring(0, n-1);
+                        if (current.equals(prefix)) {
+                            if (getNextValue()) {
+                                lastValue = current;
+                                return true;
+                            } else {
+                                System.err.println("Missing value after parameter " + prefix);
+                                throw new UsageException();
+                            }
+                        }
+                        return false;
+                    }
+                } else {
+                    // Argument does not accept a value.
+                    return current.equals(prefix);
+                }
+            }
+        }
+
+        /**
+         * Parses the given command-line arguments.
+         *
+         * @param args {@code non-null;} the arguments
+         */
+        public void parse(String[] args) {
+            ArgumentsParser parser = new ArgumentsParser(args);
+
+            while(parser.getNext()) {
+                if (parser.isArg("--debug")) {
+                    debug = true;
+                } else if (parser.isArg("--verbose")) {
+                    verbose = true;
+                } else if (parser.isArg("--verbose-dump")) {
+                    verboseDump = true;
+                } else if (parser.isArg("--no-files")) {
+                    emptyOk = true;
+                } else if (parser.isArg("--no-optimize")) {
+                    optimize = false;
+                } else if (parser.isArg("--no-strict")) {
+                    strictNameCheck = false;
+                } else if (parser.isArg("--core-library")) {
+                    coreLibrary = true;
+                } else if (parser.isArg("--statistics")) {
+                    statistics = true;
+                } else if (parser.isArg("--optimize-list=")) {
+                    if (dontOptimizeListFile != null) {
+                        System.err.println("--optimize-list and "
+                                + "--no-optimize-list are incompatible.");
+                        throw new UsageException();
+                    }
+                    optimize = true;
+                    optimizeListFile = parser.getLastValue();
+                } else if (parser.isArg("--no-optimize-list=")) {
+                    if (dontOptimizeListFile != null) {
+                        System.err.println("--optimize-list and "
+                                + "--no-optimize-list are incompatible.");
+                        throw new UsageException();
+                    }
+                    optimize = true;
+                    dontOptimizeListFile = parser.getLastValue();
+                } else if (parser.isArg("--keep-classes")) {
+                    keepClassesInJar = true;
+                } else if (parser.isArg("--output=")) {
+                    outName = parser.getLastValue();
+                    if (FileUtils.hasArchiveSuffix(outName)) {
+                        jarOutput = true;
+                    } else if (outName.endsWith(".dex") ||
+                               outName.equals("-")) {
+                        jarOutput = false;
+                    } else {
+                        System.err.println("unknown output extension: " +
+                                           outName);
+                        throw new UsageException();
+                    }
+                } else if (parser.isArg("--dump-to=")) {
+                    humanOutName = parser.getLastValue();
+                } else if (parser.isArg("--dump-width=")) {
+                    dumpWidth = Integer.parseInt(parser.getLastValue());
+                } else if (parser.isArg("--dump-method=")) {
+                    methodToDump = parser.getLastValue();
+                    jarOutput = false;
+                } else if (parser.isArg("--positions=")) {
+                    String pstr = parser.getLastValue().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 (parser.isArg("--no-locals")) {
+                    localInfo = false;
+                } else if (parser.isArg("--num-threads=")) {
+                    numThreads = Integer.parseInt(parser.getLastValue());
+                } else if (parser.isArg("--incremental")) {
+                    incremental = true;
+                } else if (parser.isArg("--force-jumbo")) {
+                    forceJumbo = true;
+                } else {
+                    System.err.println("unknown option: " + parser.getCurrent());
+                    throw new UsageException();
+                }
+            }
+
+            fileNames = parser.getRemaining();
+            if (fileNames.length == 0) {
+                if (!emptyOk) {
+                    System.err.println("no input files specified");
+                    throw new UsageException();
+                }
+            } else if (emptyOk) {
+                System.out.println("ignoring input files");
+            }
+
+            if ((humanOutName == null) && (methodToDump != null)) {
+                humanOutName = "-";
+            }
+
+            makeOptionsObjects();
+        }
+
+        /**
+         * Copies relevent arguments over into CfOptions and
+         * DexOptions instances.
+         */
+        private void makeOptionsObjects() {
+            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;
+
+            dexOptions = new DexOptions();
+            dexOptions.targetApiLevel = targetApiLevel;
+            dexOptions.forceJumbo = forceJumbo;
+        }
+    }
+
+    /** Runnable helper class to process files in multiple threads */
+    private static class ParallelProcessor implements Runnable {
+
+        String path;
+        long lastModified;
+        byte[] bytes;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param path {@code non-null;} filename of element. May not be a valid
+         * filesystem path.
+         * @param bytes {@code non-null;} file data
+         */
+        private ParallelProcessor(String path, long lastModified, byte bytes[]) {
+            this.path = path;
+            this.lastModified = lastModified;
+            this.bytes = bytes;
+        }
+
+        /**
+         * Task run by each thread in the thread pool. Runs processFileBytes
+         * with the given path and bytes.
+         */
+        public void run() {
+            if (Main.processFileBytes(path, lastModified, bytes)) {
+                anyFilesProcessed = true;
+            }
+        }
+    }
+}
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..6919b09
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BlockDumper.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+        }
+
+        if ((member.getAccessFlags() & (AccessFlags.ACC_ABSTRACT |
+                AccessFlags.ACC_NATIVE)) != 0) {
+            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) {
+        TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+        BytecodeArray code = meth.getCode();
+        ByteArray bytes = code.getBytes();
+        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();
+        int[] order = blocks.getLabelsInOrder();
+
+        sb.append("first " + Hex.u2(rmeth.getFirstLabel()) + "\n");
+
+        for (int label : order) {
+            BasicBlock bb = blocks.get(blocks.indexOfLabel(label));
+            sb.append("block ");
+            sb.append(Hex.u2(label));
+            sb.append("\n");
+
+            IntList preds = rmeth.labelToPredecessors(label);
+            int psz = preds.size();
+            for (int i = 0; i < psz; i++) {
+                sb.append("  pred ");
+                sb.append(Hex.u2(preds.get(i)));
+                sb.append("\n");
+            }
+
+            InsnList il = bb.getInsns();
+            int ilsz = il.size();
+            for (int i = 0; i < ilsz; i++) {
+                Insn one = il.get(i);
+                sb.append("  ");
+                sb.append(il.get(i).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 i = 0; i < ssz; i++) {
+                    int succ = successors.get(i);
+                    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..0572a30
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/SsaDumper.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.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;
+        }
+
+        if ((member.getAccessFlags() & (AccessFlags.ACC_ABSTRACT |
+                AccessFlags.ACC_NATIVE)) != 0) {
+            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/command/findusages/FindUsages.java b/dx/src/com/android/dx/command/findusages/FindUsages.java
new file mode 100644
index 0000000..6651f53
--- /dev/null
+++ b/dx/src/com/android/dx/command/findusages/FindUsages.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.findusages;
+
+import com.android.dx.io.ClassData;
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.CodeReader;
+import com.android.dx.io.DexBuffer;
+import com.android.dx.io.FieldId;
+import com.android.dx.io.MethodId;
+import com.android.dx.io.OpcodeInfo;
+import com.android.dx.io.instructions.DecodedInstruction;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public final class FindUsages {
+    private final DexBuffer dex;
+    private final Set<Integer> methodIds;
+    private final Set<Integer> fieldIds;
+    private final CodeReader codeReader = new CodeReader();
+    private final PrintWriter out;
+
+    private ClassDef currentClass;
+    private ClassData.Method currentMethod;
+
+    public FindUsages(final DexBuffer dex, String declaredBy, String memberName, final PrintWriter out) {
+        this.dex = dex;
+        this.out = out;
+
+        Set<Integer> typeStringIndexes = new HashSet<Integer>();
+        Set<Integer> memberNameIndexes = new HashSet<Integer>();
+        Pattern declaredByPattern = Pattern.compile(declaredBy);
+        Pattern memberNamePattern = Pattern.compile(memberName);
+        List<String> strings = dex.strings();
+        for (int i = 0; i < strings.size(); ++i) {
+            String string = strings.get(i);
+            if (declaredByPattern.matcher(string).matches()) {
+                typeStringIndexes.add(i);
+            }
+            if (memberNamePattern.matcher(string).matches()) {
+                memberNameIndexes.add(i);
+            }
+        }
+        if (typeStringIndexes.isEmpty() || memberNameIndexes.isEmpty()) {
+            methodIds = fieldIds = null;
+            return; // these symbols are not mentioned in this dex
+        }
+
+        methodIds = new HashSet<Integer>();
+        fieldIds = new HashSet<Integer>();
+        for (int typeStringIndex : typeStringIndexes) {
+            int typeIndex = Collections.binarySearch(dex.typeIds(), typeStringIndex);
+            if (typeIndex < 0) {
+                continue; // this type name isn't used as a type in this dex
+            }
+            methodIds.addAll(getMethodIds(dex, memberNameIndexes, typeIndex));
+            fieldIds.addAll(getFieldIds(dex, memberNameIndexes, typeIndex));
+        }
+
+        codeReader.setFieldVisitor(new CodeReader.Visitor() {
+            public void visit(DecodedInstruction[] all,
+                    DecodedInstruction one) {
+                int fieldId = one.getIndex();
+                if (fieldIds.contains(fieldId)) {
+                    out.println(location() + ": field reference " + dex.fieldIds().get(fieldId)
+                            + " (" + OpcodeInfo.getName(one.getOpcode()) + ")");
+                }
+            }
+        });
+
+        codeReader.setMethodVisitor(new CodeReader.Visitor() {
+            public void visit(DecodedInstruction[] all, DecodedInstruction one) {
+                int methodId = one.getIndex();
+                if (methodIds.contains(methodId)) {
+                    out.println(location() + ": method reference " + dex.methodIds().get(methodId)
+                            + " (" + OpcodeInfo.getName(one.getOpcode()) + ")");
+                }
+            }
+        });
+    }
+
+    private String location() {
+        String className = dex.typeNames().get(currentClass.getTypeIndex());
+        if (currentMethod != null) {
+            MethodId methodId = dex.methodIds().get(currentMethod.getMethodIndex());
+            return className + "." + dex.strings().get(methodId.getNameIndex());
+        } else {
+            return className;
+        }
+    }
+
+    /**
+     * Prints usages to out.
+     */
+    public void findUsages() {
+        if (fieldIds == null || methodIds == null) {
+            return;
+        }
+
+        for (ClassDef classDef : dex.classDefs()) {
+            currentClass = classDef;
+            currentMethod = null;
+
+            if (classDef.getClassDataOffset() == 0) {
+                continue;
+            }
+
+            ClassData classData = dex.readClassData(classDef);
+            for (ClassData.Field field : classData.allFields()) {
+                int fieldIndex = field.getFieldIndex();
+                if (fieldIds.contains(fieldIndex)) {
+                    out.println(location() + " field declared " + dex.fieldIds().get(fieldIndex));
+                }
+            }
+
+            for (ClassData.Method method : classData.allMethods()) {
+                currentMethod = method;
+                int methodIndex = method.getMethodIndex();
+                if (methodIds.contains(methodIndex)) {
+                    out.println(location() + " method declared " + dex.methodIds().get(methodIndex));
+                }
+                if (method.getCodeOffset() != 0) {
+                    codeReader.visitAll(dex.readCode(method).getInstructions());
+                }
+            }
+        }
+
+        currentClass = null;
+        currentMethod = null;
+    }
+
+    /**
+     * Returns the fields with {@code memberNameIndex} declared by {@code
+     * declaringType}.
+     */
+    private Set<Integer> getFieldIds(DexBuffer dex, Set<Integer> memberNameIndexes, int declaringType) {
+        Set<Integer> fields = new HashSet<Integer>();
+        int fieldIndex = 0;
+        for (FieldId fieldId : dex.fieldIds()) {
+            if (memberNameIndexes.contains(fieldId.getNameIndex())
+                    && declaringType == fieldId.getDeclaringClassIndex()) {
+                fields.add(fieldIndex);
+            }
+            fieldIndex++;
+        }
+        return fields;
+    }
+
+    /**
+     * Returns the methods with {@code memberNameIndex} declared by {@code
+     * declaringType} and its subtypes.
+     */
+    private Set<Integer> getMethodIds(DexBuffer dex, Set<Integer> memberNameIndexes, int declaringType) {
+        Set<Integer> subtypes = findAssignableTypes(dex, declaringType);
+
+        Set<Integer> methods = new HashSet<Integer>();
+        int methodIndex = 0;
+        for (MethodId method : dex.methodIds()) {
+            if (memberNameIndexes.contains(method.getNameIndex())
+                    && subtypes.contains(method.getDeclaringClassIndex())) {
+                methods.add(methodIndex);
+            }
+            methodIndex++;
+        }
+        return methods;
+    }
+
+    /**
+     * Returns the set of types that can be assigned to {@code typeIndex}.
+     */
+    private Set<Integer> findAssignableTypes(DexBuffer dex, int typeIndex) {
+        Set<Integer> assignableTypes = new HashSet<Integer>();
+        assignableTypes.add(typeIndex);
+
+        for (ClassDef classDef : dex.classDefs()) {
+            if (assignableTypes.contains(classDef.getSupertypeIndex())) {
+                assignableTypes.add(classDef.getTypeIndex());
+                continue;
+            }
+
+            for (int implemented : classDef.getInterfaces()) {
+                if (assignableTypes.contains(implemented)) {
+                    assignableTypes.add(classDef.getTypeIndex());
+                    break;
+                }
+            }
+        }
+
+        return assignableTypes;
+    }
+}
diff --git a/dx/src/com/android/dx/command/findusages/Main.java b/dx/src/com/android/dx/command/findusages/Main.java
new file mode 100644
index 0000000..ba29787
--- /dev/null
+++ b/dx/src/com/android/dx/command/findusages/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.findusages;
+
+import com.android.dx.io.DexBuffer;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public final class Main {
+    public static void main(String[] args) throws IOException {
+        String dexFile = args[0];
+        String declaredBy = args[1];
+        String memberName = args[2];
+
+        DexBuffer dex = new DexBuffer(new File(dexFile));
+        PrintWriter out = new PrintWriter(System.out);
+        new FindUsages(dex, declaredBy, memberName, out).findUsages();
+        out.flush();
+    }
+}
diff --git a/dx/src/com/android/dx/command/grep/Grep.java b/dx/src/com/android/dx/command/grep/Grep.java
new file mode 100644
index 0000000..741de97
--- /dev/null
+++ b/dx/src/com/android/dx/command/grep/Grep.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.grep;
+
+import com.android.dx.io.ClassData;
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.CodeReader;
+import com.android.dx.io.DexBuffer;
+import com.android.dx.io.EncodedValueReader;
+import com.android.dx.io.MethodId;
+import com.android.dx.io.instructions.DecodedInstruction;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public final class Grep {
+    private final DexBuffer dex;
+    private final CodeReader codeReader = new CodeReader();
+    private final Set<Integer> stringIds;
+
+    private final PrintWriter out;
+    private int count = 0;
+
+    private ClassDef currentClass;
+    private ClassData.Method currentMethod;
+
+    public Grep(final DexBuffer dex, Pattern pattern, final PrintWriter out) {
+        this.dex = dex;
+        this.out = out;
+
+        stringIds = getStringIds(dex, pattern);
+
+        codeReader.setStringVisitor(new CodeReader.Visitor() {
+            public void visit(DecodedInstruction[] all, DecodedInstruction one) {
+                encounterString(one.getIndex());
+            }
+        });
+    }
+
+    private EncodedValueReader newEncodedValueReader(DexBuffer.Section section) {
+        return new EncodedValueReader(section) {
+            @Override protected void visitString(int type, int index) {
+                encounterString(index);
+            }
+        };
+    }
+
+    private void encounterString(int index) {
+        if (stringIds.contains(index)) {
+            out.println(location() + " " + dex.strings().get(index));
+            count++;
+        }
+    }
+
+    private String location() {
+        String className = dex.typeNames().get(currentClass.getTypeIndex());
+        if (currentMethod != null) {
+            MethodId methodId = dex.methodIds().get(currentMethod.getMethodIndex());
+            return className + "." + dex.strings().get(methodId.getNameIndex());
+        } else {
+            return className;
+        }
+    }
+
+    /**
+     * Prints usages to out. Returns the number of matches found.
+     */
+    public int grep() {
+        for (ClassDef classDef : dex.classDefs()) {
+            currentClass = classDef;
+            currentMethod = null;
+
+            if (classDef.getClassDataOffset() == 0) {
+                continue;
+            }
+
+            ClassData classData = dex.readClassData(classDef);
+
+            // find the strings in encoded constants
+            int staticValuesOffset = classDef.getStaticValuesOffset();
+            if (staticValuesOffset != 0) {
+                newEncodedValueReader(dex.open(staticValuesOffset)).readArray();
+            }
+
+            // find the strings in method bodies
+            for (ClassData.Method method : classData.allMethods()) {
+                currentMethod = method;
+                if (method.getCodeOffset() != 0) {
+                    codeReader.visitAll(dex.readCode(method).getInstructions());
+                }
+            }
+        }
+
+        currentClass = null;
+        currentMethod = null;
+        return count;
+    }
+
+    private Set<Integer> getStringIds(DexBuffer dex, Pattern pattern) {
+        Set<Integer> stringIds = new HashSet<Integer>();
+        int stringIndex = 0;
+        for (String s : dex.strings()) {
+            if (pattern.matcher(s).find()) {
+                stringIds.add(stringIndex);
+            }
+            stringIndex++;
+        }
+        return stringIds;
+    }
+}
diff --git a/dx/src/com/android/dx/command/grep/Main.java b/dx/src/com/android/dx/command/grep/Main.java
new file mode 100644
index 0000000..e3b119a
--- /dev/null
+++ b/dx/src/com/android/dx/command/grep/Main.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.grep;
+
+import com.android.dx.io.DexBuffer;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.regex.Pattern;
+
+public final class Main {
+    public static void main(String[] args) throws IOException {
+        String dexFile = args[0];
+        String pattern = args[1];
+
+        DexBuffer dex = new DexBuffer(new File(dexFile));
+        int count = new Grep(dex, Pattern.compile(pattern), new PrintWriter(System.out)).grep();
+        System.exit((count > 0) ? 0 : 1);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/DexFormat.java b/dx/src/com/android/dx/dex/DexFormat.java
new file mode 100644
index 0000000..3543b35
--- /dev/null
+++ b/dx/src/com/android/dx/dex/DexFormat.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex;
+
+/**
+ * Constants that show up in and are otherwise related to {@code .dex}
+ * files, and helper methods for same.
+ */
+public final class DexFormat {
+    private DexFormat() {}
+
+    /**
+     * API level to target in order to produce the most modern file
+     * format
+     */
+    public static final int API_CURRENT = 14;
+
+    /** API level to target in order to suppress extended opcode usage */
+    public static final int API_NO_EXTENDED_OPCODES = 13;
+
+    /**
+     * file name of the primary {@code .dex} file inside an
+     * application or library {@code .jar} file
+     */
+    public static final String DEX_IN_JAR_NAME = "classes.dex";
+
+    /** common prefix for all dex file "magic numbers" */
+    public static final String MAGIC_PREFIX = "dex\n";
+
+    /** common suffix for all dex file "magic numbers" */
+    public static final String MAGIC_SUFFIX = "\0";
+
+    /** dex file version number for the current format variant */
+    public static final String VERSION_CURRENT = "036";
+
+    /** dex file version number for API level 13 and earlier */
+    public static final String VERSION_FOR_API_13 = "035";
+
+    /**
+     * value used to indicate endianness of file contents
+     */
+    public static final int ENDIAN_TAG = 0x12345678;
+
+    /**
+     * Returns the API level corresponding to the given magic number,
+     * or {@code -1} if the given array is not a well-formed dex file
+     * magic number.
+     */
+    public static int magicToApi(byte[] magic) {
+        if (magic.length != 8) {
+            return -1;
+        }
+
+        if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') ||
+                (magic[7] != '\0')) {
+            return -1;
+        }
+
+        String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]);
+
+        if (version.equals(VERSION_CURRENT)) {
+            return API_CURRENT;
+        } else if (version.equals(VERSION_FOR_API_13)) {
+            return 13;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the magic number corresponding to the given target API level.
+     */
+    public static String apiToMagic(int targetApiLevel) {
+        String version;
+
+        if (targetApiLevel >= API_CURRENT) {
+            version = VERSION_CURRENT;
+        } else {
+            version = VERSION_FOR_API_13;
+        }
+
+        return MAGIC_PREFIX + version + MAGIC_SUFFIX;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/DexOptions.java b/dx/src/com/android/dx/dex/DexOptions.java
new file mode 100644
index 0000000..0a07451
--- /dev/null
+++ b/dx/src/com/android/dx/dex/DexOptions.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex;
+
+/**
+ * Container for options used to control details of dex file generation.
+ */
+public class DexOptions {
+    /** target API level */
+    public int targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES;
+
+    /** force generation of jumbo opcodes */
+    public boolean forceJumbo = false;
+
+    /**
+     * Gets the dex file magic number corresponding to this instance.
+     */
+    public String getMagic() {
+        return DexFormat.apiToMagic(targetApiLevel);
+    }
+
+    /**
+     * Returns whether extended opcodes are allowed. This became
+     * allowed as of Ice Cream Sandwich.
+     */
+    public boolean canUseExtendedOpcodes() {
+        return targetApiLevel >= DexFormat.API_CURRENT;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/SizeOf.java b/dx/src/com/android/dx/dex/SizeOf.java
new file mode 100644
index 0000000..6ded782
--- /dev/null
+++ b/dx/src/com/android/dx/dex/SizeOf.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex;
+
+public final class SizeOf {
+    private SizeOf() {}
+
+    public static final int UBYTE = 1;
+    public static final int USHORT = 2;
+    public static final int UINT = 4;
+
+    public static final int SIGNATURE = UBYTE * 20;
+
+    /**
+     * magic ubyte[8]
+     * checksum uint
+     * signature ubyte[20]
+     * file_size uint
+     * header_size uint
+     * endian_tag uint
+     * link_size uint
+     * link_off uint
+     * map_off uint
+     * string_ids_size uint
+     * string_ids_off uint
+     * type_ids_size uint
+     * type_ids_off uint
+     * proto_ids_size uint
+     * proto_ids_off uint
+     * field_ids_size uint
+     * field_ids_off uint
+     * method_ids_size uint
+     * method_ids_off uint
+     * class_defs_size uint
+     * class_defs_off uint
+     * data_size uint
+     * data_off uint
+     */
+    public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70
+
+    /**
+     * string_data_off uint
+     */
+    public static final int STRING_ID_ITEM = UINT;
+
+    /**
+     * descriptor_idx uint
+     */
+    public static final int TYPE_ID_ITEM = UINT;
+
+    /**
+     * type_idx ushort
+     */
+    public static final int TYPE_ITEM = USHORT;
+
+    /**
+     * shorty_idx uint
+     * return_type_idx uint
+     * return_type_idx uint
+     */
+    public static final int PROTO_ID_ITEM = UINT + UINT + UINT;
+
+    /**
+     * class_idx ushort
+     * type_idx/proto_idx ushort
+     * name_idx uint
+     */
+    public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT;
+
+    /**
+     * class_idx uint
+     * access_flags uint
+     * superclass_idx uint
+     * interfaces_off uint
+     * source_file_idx uint
+     * annotations_off uint
+     * class_data_off uint
+     * static_values_off uint
+     */
+    public static final int CLASS_DEF_ITEM = 8 * UINT;
+
+    /**
+     * type ushort
+     * unused ushort
+     * size uint
+     * offset uint
+     */
+    public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT;
+
+    /**
+     * start_addr uint
+     * insn_count ushort
+     * handler_off ushort
+     */
+    public static final int TRY_ITEM = UINT + USHORT + USHORT;
+}
diff --git a/dx/src/com/android/dx/dex/TableOfContents.java b/dx/src/com/android/dx/dex/TableOfContents.java
new file mode 100644
index 0000000..fbe4626
--- /dev/null
+++ b/dx/src/com/android/dx/dex/TableOfContents.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex;
+
+import com.android.dx.io.DexBuffer;
+import com.android.dx.util.DexException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+/**
+ * The file header and map.
+ */
+public final class TableOfContents {
+
+    /*
+     * TODO: factor out ID constants.
+     */
+
+    public final Section header = new Section(0x0000);
+    public final Section stringIds = new Section(0x0001);
+    public final Section typeIds = new Section(0x0002);
+    public final Section protoIds = new Section(0x0003);
+    public final Section fieldIds = new Section(0x0004);
+    public final Section methodIds = new Section(0x0005);
+    public final Section classDefs = new Section(0x0006);
+    public final Section mapList = new Section(0x1000);
+    public final Section typeLists = new Section(0x1001);
+    public final Section annotationSetRefLists = new Section(0x1002);
+    public final Section annotationSets = new Section(0x1003);
+    public final Section classDatas = new Section(0x2000);
+    public final Section codes = new Section(0x2001);
+    public final Section stringDatas = new Section(0x2002);
+    public final Section debugInfos = new Section(0x2003);
+    public final Section annotations = new Section(0x2004);
+    public final Section encodedArrays = new Section(0x2005);
+    public final Section annotationsDirectories = new Section(0x2006);
+    public final Section[] sections = {
+            header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList,
+            typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas,
+            debugInfos, annotations, encodedArrays, annotationsDirectories
+    };
+
+    public int checksum;
+    public byte[] signature;
+    public int fileSize;
+    public int linkSize;
+    public int linkOff;
+    public int dataSize;
+    public int dataOff;
+
+    public TableOfContents() {
+        signature = new byte[20];
+    }
+
+    public void readFrom(DexBuffer buffer) throws IOException {
+        readHeader(buffer.open(0));
+        readMap(buffer.open(mapList.off));
+        computeSizesFromOffsets();
+    }
+
+    private void readHeader(DexBuffer.Section headerIn) throws UnsupportedEncodingException {
+        byte[] magic = headerIn.readByteArray(8);
+        int apiTarget = DexFormat.magicToApi(magic);
+
+        if (apiTarget != DexFormat.API_NO_EXTENDED_OPCODES) {
+            throw new DexException("Unexpected magic: " + Arrays.toString(magic));
+        }
+
+        checksum = headerIn.readInt();
+        signature = headerIn.readByteArray(20);
+        fileSize = headerIn.readInt();
+        int headerSize = headerIn.readInt();
+        if (headerSize != SizeOf.HEADER_ITEM) {
+            throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize));
+        }
+        int endianTag = headerIn.readInt();
+        if (endianTag != DexFormat.ENDIAN_TAG) {
+            throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
+        }
+        linkSize = headerIn.readInt();
+        linkOff = headerIn.readInt();
+        mapList.off = headerIn.readInt();
+        if (mapList.off == 0) {
+            throw new DexException("Cannot merge dex files that do not contain a map");
+        }
+        stringIds.size = headerIn.readInt();
+        stringIds.off = headerIn.readInt();
+        typeIds.size = headerIn.readInt();
+        typeIds.off = headerIn.readInt();
+        protoIds.size = headerIn.readInt();
+        protoIds.off = headerIn.readInt();
+        fieldIds.size = headerIn.readInt();
+        fieldIds.off = headerIn.readInt();
+        methodIds.size = headerIn.readInt();
+        methodIds.off = headerIn.readInt();
+        classDefs.size = headerIn.readInt();
+        classDefs.off = headerIn.readInt();
+        dataSize = headerIn.readInt();
+        dataOff = headerIn.readInt();
+    }
+
+    private void readMap(DexBuffer.Section in) throws IOException {
+        int mapSize = in.readInt();
+        Section previous = null;
+        for (int i = 0; i < mapSize; i++) {
+            short type = in.readShort();
+            in.readShort(); // unused
+            Section section = getSection(type);
+            int size = in.readInt();
+            int offset = in.readInt();
+
+            if ((section.size != 0 && section.size != size)
+                    || (section.off != -1 && section.off != offset)) {
+                throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type));
+            }
+
+            section.size = size;
+            section.off = offset;
+
+            if (previous != null && previous.off > section.off) {
+                throw new DexException("Map is unsorted at " + previous + ", " + section);
+            }
+
+            previous = section;
+        }
+        Arrays.sort(sections);
+    }
+
+    public void computeSizesFromOffsets() {
+        int end = dataOff + dataSize;
+        for (int i = sections.length - 1; i >= 0; i--) {
+            Section section = sections[i];
+            if (section.off == -1) {
+                continue;
+            }
+            if (section.off > end) {
+                throw new DexException("Map is unsorted at " + section);
+            }
+            section.byteCount = end - section.off;
+            end = section.off;
+        }
+    }
+
+    private Section getSection(short type) {
+        for (Section section : sections) {
+            if (section.type == type) {
+                return section;
+            }
+        }
+        throw new IllegalArgumentException("No such map item: " + type);
+    }
+
+    public void writeHeader(DexBuffer.Section out) throws IOException {
+        out.write(DexFormat.apiToMagic(DexFormat.API_NO_EXTENDED_OPCODES).getBytes("UTF-8"));
+        out.writeInt(checksum);
+        out.write(signature);
+        out.writeInt(fileSize);
+        out.writeInt(SizeOf.HEADER_ITEM);
+        out.writeInt(DexFormat.ENDIAN_TAG);
+        out.writeInt(linkSize);
+        out.writeInt(linkOff);
+        out.writeInt(mapList.off);
+        out.writeInt(stringIds.size);
+        out.writeInt(stringIds.off);
+        out.writeInt(typeIds.size);
+        out.writeInt(typeIds.off);
+        out.writeInt(protoIds.size);
+        out.writeInt(protoIds.off);
+        out.writeInt(fieldIds.size);
+        out.writeInt(fieldIds.off);
+        out.writeInt(methodIds.size);
+        out.writeInt(methodIds.off);
+        out.writeInt(classDefs.size);
+        out.writeInt(classDefs.off);
+        out.writeInt(dataSize);
+        out.writeInt(dataOff);
+    }
+
+    public void writeMap(DexBuffer.Section out) throws IOException {
+        int count = 0;
+        for (Section section : sections) {
+            if (section.exists()) {
+                count++;
+            }
+        }
+
+        out.writeInt(count);
+        for (Section section : sections) {
+            if (section.exists()) {
+                out.writeShort(section.type);
+                out.writeShort((short) 0);
+                out.writeInt(section.size);
+                out.writeInt(section.off);
+            }
+        }
+    }
+
+    public static class Section implements Comparable<Section> {
+        public final short type;
+        public int size = 0;
+        public int off = -1;
+        public int byteCount = 0;
+
+        public Section(int type) {
+            this.type = (short) type;
+        }
+
+        public boolean exists() {
+            return size > 0;
+        }
+
+        public int compareTo(Section section) {
+            if (off != section.off) {
+                return off < section.off ? -1 : 1;
+            }
+            return 0;
+        }
+
+        @Override public String toString() {
+            return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size);
+        }
+    }
+}
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..8dc8b92
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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..8b94f91
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CfTranslator.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DexOptions;
+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.CstString;
+import com.android.dx.rop.cst.CstType;
+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 cfOptions options for class translation
+     * @param dexOptions options for dex output
+     * @return {@code non-null;} the translated class
+     */
+    public static ClassDefItem translate(String filePath, byte[] bytes,
+            CfOptions cfOptions, DexOptions dexOptions) {
+        try {
+            return translate0(filePath, bytes, cfOptions, dexOptions);
+        } 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 cfOptions options for class translation
+     * @param dexOptions options for dex output
+     * @return {@code non-null;} the translated class
+     */
+    private static ClassDefItem translate0(String filePath, byte[] bytes,
+            CfOptions cfOptions, DexOptions dexOptions) {
+        DirectClassFile cf =
+            new DirectClassFile(bytes, filePath, cfOptions.strictNameCheck);
+
+        cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        cf.getMagic();
+
+        OptimizerOptions.loadOptimizeLists(cfOptions.optimizeListFile,
+                cfOptions.dontOptimizeListFile);
+
+        // Build up a class to output.
+
+        CstType thisClass = cf.getThisClass();
+        int classAccessFlags = cf.getAccessFlags() & ~AccessFlags.ACC_SUPER;
+        CstString sourceFile = (cfOptions.positionInfo == PositionList.NONE) ? null :
+            cf.getSourceFile();
+        ClassDefItem out =
+            new ClassDefItem(thisClass, classAccessFlags,
+                    cf.getSuperclass(), cf.getInterfaces(), sourceFile);
+
+        Annotations classAnnotations =
+            AttributeTranslator.getClassAnnotations(cf, cfOptions);
+        if (classAnnotations.size() != 0) {
+            out.setClassAnnotations(classAnnotations);
+        }
+
+        processFields(cf, out);
+        processMethods(cf, cfOptions, dexOptions, 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 cfOptions {@code non-null;} options for class translation
+     * @param dexOptions {@code non-null;} options for dex output
+     * @param out {@code non-null;} output class
+     */
+    private static void processMethods(DirectClassFile cf, CfOptions cfOptions,
+            DexOptions dexOptions, 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,
+                                (cfOptions.positionInfo != PositionList.NONE),
+                                cfOptions.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 (cfOptions.optimize &&
+                            OptimizerOptions.shouldOptimize(canonicalName)) {
+                        if (DEBUG) {
+                            System.err.println("Optimizing " + canonicalName);
+                        }
+
+                        nonOptRmeth = rmeth;
+                        rmeth = Optimizer.optimize(rmeth,
+                                paramSize, isStatic, cfOptions.localInfo, advice);
+
+                        if (DEBUG) {
+                            OptimizerOptions.compareOptimizerStep(nonOptRmeth,
+                                    paramSize, isStatic, cfOptions, advice, rmeth);
+                        }
+
+                        if (cfOptions.statistics) {
+                            CodeStatistics.updateRopStatistics(
+                                    nonOptRmeth, rmeth);
+                        }
+                    }
+
+                    LocalVariableInfo locals = null;
+
+                    if (cfOptions.localInfo) {
+                        locals = LocalVariableExtractor.extract(rmeth);
+                    }
+
+                    code = RopTranslator.translate(rmeth, cfOptions.positionInfo,
+                            locals, paramSize, dexOptions);
+
+                    if (cfOptions.statistics && nonOptRmeth != null) {
+                        updateDexStatistics(cfOptions, dexOptions, 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 cfOptions, DexOptions dexOptions,
+            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,
+                cfOptions.positionInfo, locals, paramSize, dexOptions);
+        DalvCode nonOptCode = RopTranslator.translate(nonOptRmeth,
+                cfOptions.positionInfo, locals, paramSize, dexOptions);
+
+        /*
+         * 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..6674b75
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/ArrayData.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.io.Opcodes;
+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(Opcodes.FILL_ARRAY_DATA_PAYLOAD);
+        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("fill-array-data-payload // 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..b31e31c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CodeAddress.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.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 {
+    /** If this address should bind closely to the following real instruction */
+    private final boolean bindsClosely;
+
+    /**
+     * 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) {
+        this(position, false);
+    }
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param bindsClosely if the address should bind closely to the following
+     *                     real instruction.
+     */
+    public CodeAddress(SourcePosition position, boolean bindsClosely) {
+        super(position);
+        this.bindsClosely = bindsClosely;
+    }
+
+    /** {@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";
+    }
+
+    /**
+     * Gets whether this address binds closely to the following "real"
+     * (non-zero-length) instruction.
+     *
+     * When a prefix is added to an instruction (for example, to move a value
+     * from a high register to a low register), this determines whether this
+     * {@code CodeAddress} will point to the prefix, or to the instruction
+     * itself.
+     *
+     * If bindsClosely is true, the address will point to the instruction
+     * itself, otherwise it will point to the prefix (if any)
+     *
+     * @return true if this address binds closely to the next real instruction
+     */
+    public boolean getBindsClosely() {
+        return bindsClosely;
+    }
+}
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..d0cf395
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsn.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import java.util.BitSet;
+
+/**
+ * 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.
+     * Uses the given BitSet to determine which registers require
+     * replacement, and ignores registers that are already compatible.
+     * 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.
+     *
+     * @param compatRegs {@code non-null;} set of compatible registers
+     * @return {@code >= 0;} the minimum distinct register requirement
+     */
+    public final int getMinimumRegisterRequirement(BitSet compatRegs) {
+        boolean hasResult = hasResult();
+        int regSz = registers.size();
+        int resultRequirement = 0;
+        int sourceRequirement = 0;
+
+        if (hasResult && !compatRegs.get(0)) {
+            resultRequirement = registers.get(0).getCategory();
+        }
+
+        for (int i = hasResult ? 1 : 0; i < regSz; i++) {
+            if (!compatRegs.get(i)) {
+                sourceRequirement += registers.get(i).getCategory();
+            }
+        }
+
+        return Math.max(sourceRequirement, resultRequirement);
+    }
+
+    /**
+     * Gets the instruction that is equivalent to this one, except that
+     * it uses sequential registers starting at {@code 0} (storing
+     * the result, if any, in register {@code 0} as well).
+     *
+     * @return {@code non-null;} the replacement
+     */
+    public DalvInsn getLowRegVersion() {
+        RegisterSpecList regs =
+            registers.withExpandedRegisters(0, hasResult(), null);
+        return withRegisters(regs);
+    }
+
+    /**
+     * Gets the instruction prefix required, if any, to use in an expanded
+     * version of this instance. Will not generate moves for registers
+     * marked compatible to the format by the given BitSet.
+     *
+     * @see #expandedVersion
+     *
+     * @param compatRegs {@code non-null;} set of compatible registers
+     * @return {@code null-ok;} the prefix, if any
+     */
+    public DalvInsn expandedPrefix(BitSet compatRegs) {
+        RegisterSpecList regs = registers;
+        boolean firstBit = compatRegs.get(0);
+
+        if (hasResult()) compatRegs.set(0);
+
+        regs = regs.subset(compatRegs);
+
+        if (hasResult()) compatRegs.set(0, firstBit);
+
+        if (regs.size() == 0) return null;
+
+        return new HighRegisterPrefix(position, regs);
+    }
+
+    /**
+     * Gets the instruction suffix required, if any, to use in an expanded
+     * version of this instance. Will not generate a move for a register
+     * marked compatible to the format by the given BitSet.
+     *
+     * @see #expandedVersion
+     *
+     * @param compatRegs {@code non-null;} set of compatible registers
+     * @return {@code null-ok;} the suffix, if any
+     */
+    public DalvInsn expandedSuffix(BitSet compatRegs) {
+        if (hasResult() && !compatRegs.get(0)) {
+            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
+     * it replaces incompatible registers with sequential registers
+     * starting at {@code 0} (storing the result, if any, in register
+     * {@code 0} as well). The sequence of instructions from
+     * {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null)
+     * surrounding the result of a call to this method are the expanded
+     * transformation of this instance, and it is guaranteed that the
+     * number of low registers used will be the number returned by
+     * {@link #getMinimumRegisterRequirement}.
+     *
+     * @param compatRegs {@code non-null;} set of compatible registers
+     * @return {@code non-null;} the replacement
+     */
+    public DalvInsn expandedVersion(BitSet compatRegs) {
+        RegisterSpecList regs =
+            registers.withExpandedRegisters(0, hasResult(), compatRegs);
+        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..e856cb4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsnList.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.Opcodes;
+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() == Opcodes.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/Dop.java b/dx/src/com/android/dx/dex/code/Dop.java
new file mode 100644
index 0000000..51d1b51
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dop.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.OpcodeInfo;
+import com.android.dx.io.Opcodes;
+
+/**
+ * Representation of an opcode.
+ */
+public final class Dop {
+    /** {@code Opcodes.isValid();} the opcode value itself */
+    private final int opcode;
+
+    /** {@code Opcodes.isValid();} the opcode family */
+    private final int family;
+
+    /**
+     * {@code Opcodes.isValid();} what opcode (by number) to try next
+     * when attempting to match an opcode to particular arguments;
+     * {@code Opcodes.NO_NEXT} to indicate that this is the last
+     * opcode to try in a particular chain
+     */
+    private final int nextOpcode;
+
+    /** {@code non-null;} the instruction format */
+    private final InsnFormat format;
+
+    /** whether this opcode uses a result register */
+    private final boolean hasResult;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code Opcodes.isValid();} the opcode value
+     * itself
+     * @param family {@code Opcodes.isValid();} the opcode family
+     * @param nextOpcode {@code Opcodes.isValid();} what opcode (by
+     * number) to try next when attempting to match an opcode to
+     * particular arguments; {@code Opcodes.NO_NEXT} to indicate that
+     * this is the last opcode to try in a particular chain
+     * @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
+     */
+    public Dop(int opcode, int family, int nextOpcode, InsnFormat format,
+            boolean hasResult) {
+        if (!Opcodes.isValidShape(opcode)) {
+            throw new IllegalArgumentException("bogus opcode");
+        }
+
+        if (!Opcodes.isValidShape(family)) {
+            throw new IllegalArgumentException("bogus family");
+        }
+
+        if (!Opcodes.isValidShape(nextOpcode)) {
+            throw new IllegalArgumentException("bogus nextOpcode");
+        }
+
+        if (format == null) {
+            throw new NullPointerException("format == null");
+        }
+
+        this.opcode = opcode;
+        this.family = family;
+        this.nextOpcode = nextOpcode;
+        this.format = format;
+        this.hasResult = hasResult;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return getName();
+    }
+
+    /**
+     * Gets the opcode value.
+     *
+     * @return {@code Opcodes.MIN_VALUE..Opcodes.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 Opcodes.MIN_VALUE..Opcodes.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 OpcodeInfo.getName(opcode);
+    }
+
+    /**
+     * Gets the opcode value to try next when attempting to match an
+     * opcode to particular arguments. This returns {@code
+     * Opcodes.NO_NEXT} to indicate that this is the last opcode to
+     * try in a particular chain.
+     *
+     * @return {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the opcode value
+     */
+    public int getNextOpcode() {
+        return nextOpcode;
+    }
+
+    /**
+     * 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 Opcodes.IF_EQ:  return Dops.IF_NE;
+            case Opcodes.IF_NE:  return Dops.IF_EQ;
+            case Opcodes.IF_LT:  return Dops.IF_GE;
+            case Opcodes.IF_GE:  return Dops.IF_LT;
+            case Opcodes.IF_GT:  return Dops.IF_LE;
+            case Opcodes.IF_LE:  return Dops.IF_GT;
+            case Opcodes.IF_EQZ: return Dops.IF_NEZ;
+            case Opcodes.IF_NEZ: return Dops.IF_EQZ;
+            case Opcodes.IF_LTZ: return Dops.IF_GEZ;
+            case Opcodes.IF_GEZ: return Dops.IF_LTZ;
+            case Opcodes.IF_GTZ: return Dops.IF_LEZ;
+            case Opcodes.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..cc5c173
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dops.java
@@ -0,0 +1,1243 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DexOptions;
+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;
+import com.android.dx.io.Opcodes;
+
+/**
+ * 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). TODO: Retire the usage of this
+     * constant.
+     */
+    public static final Dop SPECIAL_FORMAT =
+        new Dop(Opcodes.SPECIAL_FORMAT, Opcodes.SPECIAL_FORMAT,
+                Opcodes.NO_NEXT, SpecialFormat.THE_ONE, false);
+
+    // BEGIN(dops); GENERATED AUTOMATICALLY BY opcode-gen
+    public static final Dop NOP =
+        new Dop(Opcodes.NOP, Opcodes.NOP,
+            Opcodes.NO_NEXT, Form10x.THE_ONE, false);
+
+    public static final Dop MOVE =
+        new Dop(Opcodes.MOVE, Opcodes.MOVE,
+            Opcodes.MOVE_FROM16, Form12x.THE_ONE, true);
+
+    public static final Dop MOVE_FROM16 =
+        new Dop(Opcodes.MOVE_FROM16, Opcodes.MOVE,
+            Opcodes.MOVE_16, Form22x.THE_ONE, true);
+
+    public static final Dop MOVE_16 =
+        new Dop(Opcodes.MOVE_16, Opcodes.MOVE,
+            Opcodes.NO_NEXT, Form32x.THE_ONE, true);
+
+    public static final Dop MOVE_WIDE =
+        new Dop(Opcodes.MOVE_WIDE, Opcodes.MOVE_WIDE,
+            Opcodes.MOVE_WIDE_FROM16, Form12x.THE_ONE, true);
+
+    public static final Dop MOVE_WIDE_FROM16 =
+        new Dop(Opcodes.MOVE_WIDE_FROM16, Opcodes.MOVE_WIDE,
+            Opcodes.MOVE_WIDE_16, Form22x.THE_ONE, true);
+
+    public static final Dop MOVE_WIDE_16 =
+        new Dop(Opcodes.MOVE_WIDE_16, Opcodes.MOVE_WIDE,
+            Opcodes.NO_NEXT, Form32x.THE_ONE, true);
+
+    public static final Dop MOVE_OBJECT =
+        new Dop(Opcodes.MOVE_OBJECT, Opcodes.MOVE_OBJECT,
+            Opcodes.MOVE_OBJECT_FROM16, Form12x.THE_ONE, true);
+
+    public static final Dop MOVE_OBJECT_FROM16 =
+        new Dop(Opcodes.MOVE_OBJECT_FROM16, Opcodes.MOVE_OBJECT,
+            Opcodes.MOVE_OBJECT_16, Form22x.THE_ONE, true);
+
+    public static final Dop MOVE_OBJECT_16 =
+        new Dop(Opcodes.MOVE_OBJECT_16, Opcodes.MOVE_OBJECT,
+            Opcodes.NO_NEXT, Form32x.THE_ONE, true);
+
+    public static final Dop MOVE_RESULT =
+        new Dop(Opcodes.MOVE_RESULT, Opcodes.MOVE_RESULT,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, true);
+
+    public static final Dop MOVE_RESULT_WIDE =
+        new Dop(Opcodes.MOVE_RESULT_WIDE, Opcodes.MOVE_RESULT_WIDE,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, true);
+
+    public static final Dop MOVE_RESULT_OBJECT =
+        new Dop(Opcodes.MOVE_RESULT_OBJECT, Opcodes.MOVE_RESULT_OBJECT,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, true);
+
+    public static final Dop MOVE_EXCEPTION =
+        new Dop(Opcodes.MOVE_EXCEPTION, Opcodes.MOVE_EXCEPTION,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, true);
+
+    public static final Dop RETURN_VOID =
+        new Dop(Opcodes.RETURN_VOID, Opcodes.RETURN_VOID,
+            Opcodes.NO_NEXT, Form10x.THE_ONE, false);
+
+    public static final Dop RETURN =
+        new Dop(Opcodes.RETURN, Opcodes.RETURN,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, false);
+
+    public static final Dop RETURN_WIDE =
+        new Dop(Opcodes.RETURN_WIDE, Opcodes.RETURN_WIDE,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, false);
+
+    public static final Dop RETURN_OBJECT =
+        new Dop(Opcodes.RETURN_OBJECT, Opcodes.RETURN_OBJECT,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, false);
+
+    public static final Dop CONST_4 =
+        new Dop(Opcodes.CONST_4, Opcodes.CONST,
+            Opcodes.CONST_16, Form11n.THE_ONE, true);
+
+    public static final Dop CONST_16 =
+        new Dop(Opcodes.CONST_16, Opcodes.CONST,
+            Opcodes.CONST_HIGH16, Form21s.THE_ONE, true);
+
+    public static final Dop CONST =
+        new Dop(Opcodes.CONST, Opcodes.CONST,
+            Opcodes.NO_NEXT, Form31i.THE_ONE, true);
+
+    public static final Dop CONST_HIGH16 =
+        new Dop(Opcodes.CONST_HIGH16, Opcodes.CONST,
+            Opcodes.CONST, Form21h.THE_ONE, true);
+
+    public static final Dop CONST_WIDE_16 =
+        new Dop(Opcodes.CONST_WIDE_16, Opcodes.CONST_WIDE,
+            Opcodes.CONST_WIDE_HIGH16, Form21s.THE_ONE, true);
+
+    public static final Dop CONST_WIDE_32 =
+        new Dop(Opcodes.CONST_WIDE_32, Opcodes.CONST_WIDE,
+            Opcodes.CONST_WIDE, Form31i.THE_ONE, true);
+
+    public static final Dop CONST_WIDE =
+        new Dop(Opcodes.CONST_WIDE, Opcodes.CONST_WIDE,
+            Opcodes.NO_NEXT, Form51l.THE_ONE, true);
+
+    public static final Dop CONST_WIDE_HIGH16 =
+        new Dop(Opcodes.CONST_WIDE_HIGH16, Opcodes.CONST_WIDE,
+            Opcodes.CONST_WIDE_32, Form21h.THE_ONE, true);
+
+    public static final Dop CONST_STRING =
+        new Dop(Opcodes.CONST_STRING, Opcodes.CONST_STRING,
+            Opcodes.CONST_STRING_JUMBO, Form21c.THE_ONE, true);
+
+    public static final Dop CONST_STRING_JUMBO =
+        new Dop(Opcodes.CONST_STRING_JUMBO, Opcodes.CONST_STRING,
+            Opcodes.NO_NEXT, Form31c.THE_ONE, true);
+
+    public static final Dop CONST_CLASS =
+        new Dop(Opcodes.CONST_CLASS, Opcodes.CONST_CLASS,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop MONITOR_ENTER =
+        new Dop(Opcodes.MONITOR_ENTER, Opcodes.MONITOR_ENTER,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, false);
+
+    public static final Dop MONITOR_EXIT =
+        new Dop(Opcodes.MONITOR_EXIT, Opcodes.MONITOR_EXIT,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, false);
+
+    public static final Dop CHECK_CAST =
+        new Dop(Opcodes.CHECK_CAST, Opcodes.CHECK_CAST,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop INSTANCE_OF =
+        new Dop(Opcodes.INSTANCE_OF, Opcodes.INSTANCE_OF,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop ARRAY_LENGTH =
+        new Dop(Opcodes.ARRAY_LENGTH, Opcodes.ARRAY_LENGTH,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop NEW_INSTANCE =
+        new Dop(Opcodes.NEW_INSTANCE, Opcodes.NEW_INSTANCE,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop NEW_ARRAY =
+        new Dop(Opcodes.NEW_ARRAY, Opcodes.NEW_ARRAY,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop FILLED_NEW_ARRAY =
+        new Dop(Opcodes.FILLED_NEW_ARRAY, Opcodes.FILLED_NEW_ARRAY,
+            Opcodes.FILLED_NEW_ARRAY_RANGE, Form35c.THE_ONE, false);
+
+    public static final Dop FILLED_NEW_ARRAY_RANGE =
+        new Dop(Opcodes.FILLED_NEW_ARRAY_RANGE, Opcodes.FILLED_NEW_ARRAY,
+            Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
+
+    public static final Dop FILL_ARRAY_DATA =
+        new Dop(Opcodes.FILL_ARRAY_DATA, Opcodes.FILL_ARRAY_DATA,
+            Opcodes.NO_NEXT, Form31t.THE_ONE, false);
+
+    public static final Dop THROW =
+        new Dop(Opcodes.THROW, Opcodes.THROW,
+            Opcodes.NO_NEXT, Form11x.THE_ONE, false);
+
+    public static final Dop GOTO =
+        new Dop(Opcodes.GOTO, Opcodes.GOTO,
+            Opcodes.GOTO_16, Form10t.THE_ONE, false);
+
+    public static final Dop GOTO_16 =
+        new Dop(Opcodes.GOTO_16, Opcodes.GOTO,
+            Opcodes.GOTO_32, Form20t.THE_ONE, false);
+
+    public static final Dop GOTO_32 =
+        new Dop(Opcodes.GOTO_32, Opcodes.GOTO,
+            Opcodes.NO_NEXT, Form30t.THE_ONE, false);
+
+    public static final Dop PACKED_SWITCH =
+        new Dop(Opcodes.PACKED_SWITCH, Opcodes.PACKED_SWITCH,
+            Opcodes.NO_NEXT, Form31t.THE_ONE, false);
+
+    public static final Dop SPARSE_SWITCH =
+        new Dop(Opcodes.SPARSE_SWITCH, Opcodes.SPARSE_SWITCH,
+            Opcodes.NO_NEXT, Form31t.THE_ONE, false);
+
+    public static final Dop CMPL_FLOAT =
+        new Dop(Opcodes.CMPL_FLOAT, Opcodes.CMPL_FLOAT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop CMPG_FLOAT =
+        new Dop(Opcodes.CMPG_FLOAT, Opcodes.CMPG_FLOAT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop CMPL_DOUBLE =
+        new Dop(Opcodes.CMPL_DOUBLE, Opcodes.CMPL_DOUBLE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop CMPG_DOUBLE =
+        new Dop(Opcodes.CMPG_DOUBLE, Opcodes.CMPG_DOUBLE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop CMP_LONG =
+        new Dop(Opcodes.CMP_LONG, Opcodes.CMP_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop IF_EQ =
+        new Dop(Opcodes.IF_EQ, Opcodes.IF_EQ,
+            Opcodes.NO_NEXT, Form22t.THE_ONE, false);
+
+    public static final Dop IF_NE =
+        new Dop(Opcodes.IF_NE, Opcodes.IF_NE,
+            Opcodes.NO_NEXT, Form22t.THE_ONE, false);
+
+    public static final Dop IF_LT =
+        new Dop(Opcodes.IF_LT, Opcodes.IF_LT,
+            Opcodes.NO_NEXT, Form22t.THE_ONE, false);
+
+    public static final Dop IF_GE =
+        new Dop(Opcodes.IF_GE, Opcodes.IF_GE,
+            Opcodes.NO_NEXT, Form22t.THE_ONE, false);
+
+    public static final Dop IF_GT =
+        new Dop(Opcodes.IF_GT, Opcodes.IF_GT,
+            Opcodes.NO_NEXT, Form22t.THE_ONE, false);
+
+    public static final Dop IF_LE =
+        new Dop(Opcodes.IF_LE, Opcodes.IF_LE,
+            Opcodes.NO_NEXT, Form22t.THE_ONE, false);
+
+    public static final Dop IF_EQZ =
+        new Dop(Opcodes.IF_EQZ, Opcodes.IF_EQZ,
+            Opcodes.NO_NEXT, Form21t.THE_ONE, false);
+
+    public static final Dop IF_NEZ =
+        new Dop(Opcodes.IF_NEZ, Opcodes.IF_NEZ,
+            Opcodes.NO_NEXT, Form21t.THE_ONE, false);
+
+    public static final Dop IF_LTZ =
+        new Dop(Opcodes.IF_LTZ, Opcodes.IF_LTZ,
+            Opcodes.NO_NEXT, Form21t.THE_ONE, false);
+
+    public static final Dop IF_GEZ =
+        new Dop(Opcodes.IF_GEZ, Opcodes.IF_GEZ,
+            Opcodes.NO_NEXT, Form21t.THE_ONE, false);
+
+    public static final Dop IF_GTZ =
+        new Dop(Opcodes.IF_GTZ, Opcodes.IF_GTZ,
+            Opcodes.NO_NEXT, Form21t.THE_ONE, false);
+
+    public static final Dop IF_LEZ =
+        new Dop(Opcodes.IF_LEZ, Opcodes.IF_LEZ,
+            Opcodes.NO_NEXT, Form21t.THE_ONE, false);
+
+    public static final Dop AGET =
+        new Dop(Opcodes.AGET, Opcodes.AGET,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AGET_WIDE =
+        new Dop(Opcodes.AGET_WIDE, Opcodes.AGET_WIDE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AGET_OBJECT =
+        new Dop(Opcodes.AGET_OBJECT, Opcodes.AGET_OBJECT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AGET_BOOLEAN =
+        new Dop(Opcodes.AGET_BOOLEAN, Opcodes.AGET_BOOLEAN,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AGET_BYTE =
+        new Dop(Opcodes.AGET_BYTE, Opcodes.AGET_BYTE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AGET_CHAR =
+        new Dop(Opcodes.AGET_CHAR, Opcodes.AGET_CHAR,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AGET_SHORT =
+        new Dop(Opcodes.AGET_SHORT, Opcodes.AGET_SHORT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop APUT =
+        new Dop(Opcodes.APUT, Opcodes.APUT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, false);
+
+    public static final Dop APUT_WIDE =
+        new Dop(Opcodes.APUT_WIDE, Opcodes.APUT_WIDE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, false);
+
+    public static final Dop APUT_OBJECT =
+        new Dop(Opcodes.APUT_OBJECT, Opcodes.APUT_OBJECT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, false);
+
+    public static final Dop APUT_BOOLEAN =
+        new Dop(Opcodes.APUT_BOOLEAN, Opcodes.APUT_BOOLEAN,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, false);
+
+    public static final Dop APUT_BYTE =
+        new Dop(Opcodes.APUT_BYTE, Opcodes.APUT_BYTE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, false);
+
+    public static final Dop APUT_CHAR =
+        new Dop(Opcodes.APUT_CHAR, Opcodes.APUT_CHAR,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, false);
+
+    public static final Dop APUT_SHORT =
+        new Dop(Opcodes.APUT_SHORT, Opcodes.APUT_SHORT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, false);
+
+    public static final Dop IGET =
+        new Dop(Opcodes.IGET, Opcodes.IGET,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop IGET_WIDE =
+        new Dop(Opcodes.IGET_WIDE, Opcodes.IGET_WIDE,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop IGET_OBJECT =
+        new Dop(Opcodes.IGET_OBJECT, Opcodes.IGET_OBJECT,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop IGET_BOOLEAN =
+        new Dop(Opcodes.IGET_BOOLEAN, Opcodes.IGET_BOOLEAN,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop IGET_BYTE =
+        new Dop(Opcodes.IGET_BYTE, Opcodes.IGET_BYTE,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop IGET_CHAR =
+        new Dop(Opcodes.IGET_CHAR, Opcodes.IGET_CHAR,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop IGET_SHORT =
+        new Dop(Opcodes.IGET_SHORT, Opcodes.IGET_SHORT,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, true);
+
+    public static final Dop IPUT =
+        new Dop(Opcodes.IPUT, Opcodes.IPUT,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, false);
+
+    public static final Dop IPUT_WIDE =
+        new Dop(Opcodes.IPUT_WIDE, Opcodes.IPUT_WIDE,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, false);
+
+    public static final Dop IPUT_OBJECT =
+        new Dop(Opcodes.IPUT_OBJECT, Opcodes.IPUT_OBJECT,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, false);
+
+    public static final Dop IPUT_BOOLEAN =
+        new Dop(Opcodes.IPUT_BOOLEAN, Opcodes.IPUT_BOOLEAN,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, false);
+
+    public static final Dop IPUT_BYTE =
+        new Dop(Opcodes.IPUT_BYTE, Opcodes.IPUT_BYTE,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, false);
+
+    public static final Dop IPUT_CHAR =
+        new Dop(Opcodes.IPUT_CHAR, Opcodes.IPUT_CHAR,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, false);
+
+    public static final Dop IPUT_SHORT =
+        new Dop(Opcodes.IPUT_SHORT, Opcodes.IPUT_SHORT,
+            Opcodes.NO_NEXT, Form22c.THE_ONE, false);
+
+    public static final Dop SGET =
+        new Dop(Opcodes.SGET, Opcodes.SGET,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop SGET_WIDE =
+        new Dop(Opcodes.SGET_WIDE, Opcodes.SGET_WIDE,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop SGET_OBJECT =
+        new Dop(Opcodes.SGET_OBJECT, Opcodes.SGET_OBJECT,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop SGET_BOOLEAN =
+        new Dop(Opcodes.SGET_BOOLEAN, Opcodes.SGET_BOOLEAN,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop SGET_BYTE =
+        new Dop(Opcodes.SGET_BYTE, Opcodes.SGET_BYTE,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop SGET_CHAR =
+        new Dop(Opcodes.SGET_CHAR, Opcodes.SGET_CHAR,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop SGET_SHORT =
+        new Dop(Opcodes.SGET_SHORT, Opcodes.SGET_SHORT,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop SPUT =
+        new Dop(Opcodes.SPUT, Opcodes.SPUT,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, false);
+
+    public static final Dop SPUT_WIDE =
+        new Dop(Opcodes.SPUT_WIDE, Opcodes.SPUT_WIDE,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, false);
+
+    public static final Dop SPUT_OBJECT =
+        new Dop(Opcodes.SPUT_OBJECT, Opcodes.SPUT_OBJECT,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, false);
+
+    public static final Dop SPUT_BOOLEAN =
+        new Dop(Opcodes.SPUT_BOOLEAN, Opcodes.SPUT_BOOLEAN,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, false);
+
+    public static final Dop SPUT_BYTE =
+        new Dop(Opcodes.SPUT_BYTE, Opcodes.SPUT_BYTE,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, false);
+
+    public static final Dop SPUT_CHAR =
+        new Dop(Opcodes.SPUT_CHAR, Opcodes.SPUT_CHAR,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, false);
+
+    public static final Dop SPUT_SHORT =
+        new Dop(Opcodes.SPUT_SHORT, Opcodes.SPUT_SHORT,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, false);
+
+    public static final Dop INVOKE_VIRTUAL =
+        new Dop(Opcodes.INVOKE_VIRTUAL, Opcodes.INVOKE_VIRTUAL,
+            Opcodes.INVOKE_VIRTUAL_RANGE, Form35c.THE_ONE, false);
+
+    public static final Dop INVOKE_SUPER =
+        new Dop(Opcodes.INVOKE_SUPER, Opcodes.INVOKE_SUPER,
+            Opcodes.INVOKE_SUPER_RANGE, Form35c.THE_ONE, false);
+
+    public static final Dop INVOKE_DIRECT =
+        new Dop(Opcodes.INVOKE_DIRECT, Opcodes.INVOKE_DIRECT,
+            Opcodes.INVOKE_DIRECT_RANGE, Form35c.THE_ONE, false);
+
+    public static final Dop INVOKE_STATIC =
+        new Dop(Opcodes.INVOKE_STATIC, Opcodes.INVOKE_STATIC,
+            Opcodes.INVOKE_STATIC_RANGE, Form35c.THE_ONE, false);
+
+    public static final Dop INVOKE_INTERFACE =
+        new Dop(Opcodes.INVOKE_INTERFACE, Opcodes.INVOKE_INTERFACE,
+            Opcodes.INVOKE_INTERFACE_RANGE, Form35c.THE_ONE, false);
+
+    public static final Dop INVOKE_VIRTUAL_RANGE =
+        new Dop(Opcodes.INVOKE_VIRTUAL_RANGE, Opcodes.INVOKE_VIRTUAL,
+            Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
+
+    public static final Dop INVOKE_SUPER_RANGE =
+        new Dop(Opcodes.INVOKE_SUPER_RANGE, Opcodes.INVOKE_SUPER,
+            Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
+
+    public static final Dop INVOKE_DIRECT_RANGE =
+        new Dop(Opcodes.INVOKE_DIRECT_RANGE, Opcodes.INVOKE_DIRECT,
+            Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
+
+    public static final Dop INVOKE_STATIC_RANGE =
+        new Dop(Opcodes.INVOKE_STATIC_RANGE, Opcodes.INVOKE_STATIC,
+            Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
+
+    public static final Dop INVOKE_INTERFACE_RANGE =
+        new Dop(Opcodes.INVOKE_INTERFACE_RANGE, Opcodes.INVOKE_INTERFACE,
+            Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
+
+    public static final Dop NEG_INT =
+        new Dop(Opcodes.NEG_INT, Opcodes.NEG_INT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop NOT_INT =
+        new Dop(Opcodes.NOT_INT, Opcodes.NOT_INT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop NEG_LONG =
+        new Dop(Opcodes.NEG_LONG, Opcodes.NEG_LONG,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop NOT_LONG =
+        new Dop(Opcodes.NOT_LONG, Opcodes.NOT_LONG,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop NEG_FLOAT =
+        new Dop(Opcodes.NEG_FLOAT, Opcodes.NEG_FLOAT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop NEG_DOUBLE =
+        new Dop(Opcodes.NEG_DOUBLE, Opcodes.NEG_DOUBLE,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop INT_TO_LONG =
+        new Dop(Opcodes.INT_TO_LONG, Opcodes.INT_TO_LONG,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop INT_TO_FLOAT =
+        new Dop(Opcodes.INT_TO_FLOAT, Opcodes.INT_TO_FLOAT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop INT_TO_DOUBLE =
+        new Dop(Opcodes.INT_TO_DOUBLE, Opcodes.INT_TO_DOUBLE,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop LONG_TO_INT =
+        new Dop(Opcodes.LONG_TO_INT, Opcodes.LONG_TO_INT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop LONG_TO_FLOAT =
+        new Dop(Opcodes.LONG_TO_FLOAT, Opcodes.LONG_TO_FLOAT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop LONG_TO_DOUBLE =
+        new Dop(Opcodes.LONG_TO_DOUBLE, Opcodes.LONG_TO_DOUBLE,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop FLOAT_TO_INT =
+        new Dop(Opcodes.FLOAT_TO_INT, Opcodes.FLOAT_TO_INT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop FLOAT_TO_LONG =
+        new Dop(Opcodes.FLOAT_TO_LONG, Opcodes.FLOAT_TO_LONG,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop FLOAT_TO_DOUBLE =
+        new Dop(Opcodes.FLOAT_TO_DOUBLE, Opcodes.FLOAT_TO_DOUBLE,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop DOUBLE_TO_INT =
+        new Dop(Opcodes.DOUBLE_TO_INT, Opcodes.DOUBLE_TO_INT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop DOUBLE_TO_LONG =
+        new Dop(Opcodes.DOUBLE_TO_LONG, Opcodes.DOUBLE_TO_LONG,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop DOUBLE_TO_FLOAT =
+        new Dop(Opcodes.DOUBLE_TO_FLOAT, Opcodes.DOUBLE_TO_FLOAT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop INT_TO_BYTE =
+        new Dop(Opcodes.INT_TO_BYTE, Opcodes.INT_TO_BYTE,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop INT_TO_CHAR =
+        new Dop(Opcodes.INT_TO_CHAR, Opcodes.INT_TO_CHAR,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop INT_TO_SHORT =
+        new Dop(Opcodes.INT_TO_SHORT, Opcodes.INT_TO_SHORT,
+            Opcodes.NO_NEXT, Form12x.THE_ONE, true);
+
+    public static final Dop ADD_INT =
+        new Dop(Opcodes.ADD_INT, Opcodes.ADD_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SUB_INT =
+        new Dop(Opcodes.SUB_INT, Opcodes.SUB_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop MUL_INT =
+        new Dop(Opcodes.MUL_INT, Opcodes.MUL_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop DIV_INT =
+        new Dop(Opcodes.DIV_INT, Opcodes.DIV_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop REM_INT =
+        new Dop(Opcodes.REM_INT, Opcodes.REM_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AND_INT =
+        new Dop(Opcodes.AND_INT, Opcodes.AND_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop OR_INT =
+        new Dop(Opcodes.OR_INT, Opcodes.OR_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop XOR_INT =
+        new Dop(Opcodes.XOR_INT, Opcodes.XOR_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SHL_INT =
+        new Dop(Opcodes.SHL_INT, Opcodes.SHL_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SHR_INT =
+        new Dop(Opcodes.SHR_INT, Opcodes.SHR_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop USHR_INT =
+        new Dop(Opcodes.USHR_INT, Opcodes.USHR_INT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop ADD_LONG =
+        new Dop(Opcodes.ADD_LONG, Opcodes.ADD_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SUB_LONG =
+        new Dop(Opcodes.SUB_LONG, Opcodes.SUB_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop MUL_LONG =
+        new Dop(Opcodes.MUL_LONG, Opcodes.MUL_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop DIV_LONG =
+        new Dop(Opcodes.DIV_LONG, Opcodes.DIV_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop REM_LONG =
+        new Dop(Opcodes.REM_LONG, Opcodes.REM_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop AND_LONG =
+        new Dop(Opcodes.AND_LONG, Opcodes.AND_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop OR_LONG =
+        new Dop(Opcodes.OR_LONG, Opcodes.OR_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop XOR_LONG =
+        new Dop(Opcodes.XOR_LONG, Opcodes.XOR_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SHL_LONG =
+        new Dop(Opcodes.SHL_LONG, Opcodes.SHL_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SHR_LONG =
+        new Dop(Opcodes.SHR_LONG, Opcodes.SHR_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop USHR_LONG =
+        new Dop(Opcodes.USHR_LONG, Opcodes.USHR_LONG,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop ADD_FLOAT =
+        new Dop(Opcodes.ADD_FLOAT, Opcodes.ADD_FLOAT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SUB_FLOAT =
+        new Dop(Opcodes.SUB_FLOAT, Opcodes.SUB_FLOAT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop MUL_FLOAT =
+        new Dop(Opcodes.MUL_FLOAT, Opcodes.MUL_FLOAT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop DIV_FLOAT =
+        new Dop(Opcodes.DIV_FLOAT, Opcodes.DIV_FLOAT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop REM_FLOAT =
+        new Dop(Opcodes.REM_FLOAT, Opcodes.REM_FLOAT,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop ADD_DOUBLE =
+        new Dop(Opcodes.ADD_DOUBLE, Opcodes.ADD_DOUBLE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop SUB_DOUBLE =
+        new Dop(Opcodes.SUB_DOUBLE, Opcodes.SUB_DOUBLE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop MUL_DOUBLE =
+        new Dop(Opcodes.MUL_DOUBLE, Opcodes.MUL_DOUBLE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop DIV_DOUBLE =
+        new Dop(Opcodes.DIV_DOUBLE, Opcodes.DIV_DOUBLE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop REM_DOUBLE =
+        new Dop(Opcodes.REM_DOUBLE, Opcodes.REM_DOUBLE,
+            Opcodes.NO_NEXT, Form23x.THE_ONE, true);
+
+    public static final Dop ADD_INT_2ADDR =
+        new Dop(Opcodes.ADD_INT_2ADDR, Opcodes.ADD_INT,
+            Opcodes.ADD_INT, Form12x.THE_ONE, true);
+
+    public static final Dop SUB_INT_2ADDR =
+        new Dop(Opcodes.SUB_INT_2ADDR, Opcodes.SUB_INT,
+            Opcodes.SUB_INT, Form12x.THE_ONE, true);
+
+    public static final Dop MUL_INT_2ADDR =
+        new Dop(Opcodes.MUL_INT_2ADDR, Opcodes.MUL_INT,
+            Opcodes.MUL_INT, Form12x.THE_ONE, true);
+
+    public static final Dop DIV_INT_2ADDR =
+        new Dop(Opcodes.DIV_INT_2ADDR, Opcodes.DIV_INT,
+            Opcodes.DIV_INT, Form12x.THE_ONE, true);
+
+    public static final Dop REM_INT_2ADDR =
+        new Dop(Opcodes.REM_INT_2ADDR, Opcodes.REM_INT,
+            Opcodes.REM_INT, Form12x.THE_ONE, true);
+
+    public static final Dop AND_INT_2ADDR =
+        new Dop(Opcodes.AND_INT_2ADDR, Opcodes.AND_INT,
+            Opcodes.AND_INT, Form12x.THE_ONE, true);
+
+    public static final Dop OR_INT_2ADDR =
+        new Dop(Opcodes.OR_INT_2ADDR, Opcodes.OR_INT,
+            Opcodes.OR_INT, Form12x.THE_ONE, true);
+
+    public static final Dop XOR_INT_2ADDR =
+        new Dop(Opcodes.XOR_INT_2ADDR, Opcodes.XOR_INT,
+            Opcodes.XOR_INT, Form12x.THE_ONE, true);
+
+    public static final Dop SHL_INT_2ADDR =
+        new Dop(Opcodes.SHL_INT_2ADDR, Opcodes.SHL_INT,
+            Opcodes.SHL_INT, Form12x.THE_ONE, true);
+
+    public static final Dop SHR_INT_2ADDR =
+        new Dop(Opcodes.SHR_INT_2ADDR, Opcodes.SHR_INT,
+            Opcodes.SHR_INT, Form12x.THE_ONE, true);
+
+    public static final Dop USHR_INT_2ADDR =
+        new Dop(Opcodes.USHR_INT_2ADDR, Opcodes.USHR_INT,
+            Opcodes.USHR_INT, Form12x.THE_ONE, true);
+
+    public static final Dop ADD_LONG_2ADDR =
+        new Dop(Opcodes.ADD_LONG_2ADDR, Opcodes.ADD_LONG,
+            Opcodes.ADD_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop SUB_LONG_2ADDR =
+        new Dop(Opcodes.SUB_LONG_2ADDR, Opcodes.SUB_LONG,
+            Opcodes.SUB_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop MUL_LONG_2ADDR =
+        new Dop(Opcodes.MUL_LONG_2ADDR, Opcodes.MUL_LONG,
+            Opcodes.MUL_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop DIV_LONG_2ADDR =
+        new Dop(Opcodes.DIV_LONG_2ADDR, Opcodes.DIV_LONG,
+            Opcodes.DIV_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop REM_LONG_2ADDR =
+        new Dop(Opcodes.REM_LONG_2ADDR, Opcodes.REM_LONG,
+            Opcodes.REM_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop AND_LONG_2ADDR =
+        new Dop(Opcodes.AND_LONG_2ADDR, Opcodes.AND_LONG,
+            Opcodes.AND_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop OR_LONG_2ADDR =
+        new Dop(Opcodes.OR_LONG_2ADDR, Opcodes.OR_LONG,
+            Opcodes.OR_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop XOR_LONG_2ADDR =
+        new Dop(Opcodes.XOR_LONG_2ADDR, Opcodes.XOR_LONG,
+            Opcodes.XOR_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop SHL_LONG_2ADDR =
+        new Dop(Opcodes.SHL_LONG_2ADDR, Opcodes.SHL_LONG,
+            Opcodes.SHL_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop SHR_LONG_2ADDR =
+        new Dop(Opcodes.SHR_LONG_2ADDR, Opcodes.SHR_LONG,
+            Opcodes.SHR_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop USHR_LONG_2ADDR =
+        new Dop(Opcodes.USHR_LONG_2ADDR, Opcodes.USHR_LONG,
+            Opcodes.USHR_LONG, Form12x.THE_ONE, true);
+
+    public static final Dop ADD_FLOAT_2ADDR =
+        new Dop(Opcodes.ADD_FLOAT_2ADDR, Opcodes.ADD_FLOAT,
+            Opcodes.ADD_FLOAT, Form12x.THE_ONE, true);
+
+    public static final Dop SUB_FLOAT_2ADDR =
+        new Dop(Opcodes.SUB_FLOAT_2ADDR, Opcodes.SUB_FLOAT,
+            Opcodes.SUB_FLOAT, Form12x.THE_ONE, true);
+
+    public static final Dop MUL_FLOAT_2ADDR =
+        new Dop(Opcodes.MUL_FLOAT_2ADDR, Opcodes.MUL_FLOAT,
+            Opcodes.MUL_FLOAT, Form12x.THE_ONE, true);
+
+    public static final Dop DIV_FLOAT_2ADDR =
+        new Dop(Opcodes.DIV_FLOAT_2ADDR, Opcodes.DIV_FLOAT,
+            Opcodes.DIV_FLOAT, Form12x.THE_ONE, true);
+
+    public static final Dop REM_FLOAT_2ADDR =
+        new Dop(Opcodes.REM_FLOAT_2ADDR, Opcodes.REM_FLOAT,
+            Opcodes.REM_FLOAT, Form12x.THE_ONE, true);
+
+    public static final Dop ADD_DOUBLE_2ADDR =
+        new Dop(Opcodes.ADD_DOUBLE_2ADDR, Opcodes.ADD_DOUBLE,
+            Opcodes.ADD_DOUBLE, Form12x.THE_ONE, true);
+
+    public static final Dop SUB_DOUBLE_2ADDR =
+        new Dop(Opcodes.SUB_DOUBLE_2ADDR, Opcodes.SUB_DOUBLE,
+            Opcodes.SUB_DOUBLE, Form12x.THE_ONE, true);
+
+    public static final Dop MUL_DOUBLE_2ADDR =
+        new Dop(Opcodes.MUL_DOUBLE_2ADDR, Opcodes.MUL_DOUBLE,
+            Opcodes.MUL_DOUBLE, Form12x.THE_ONE, true);
+
+    public static final Dop DIV_DOUBLE_2ADDR =
+        new Dop(Opcodes.DIV_DOUBLE_2ADDR, Opcodes.DIV_DOUBLE,
+            Opcodes.DIV_DOUBLE, Form12x.THE_ONE, true);
+
+    public static final Dop REM_DOUBLE_2ADDR =
+        new Dop(Opcodes.REM_DOUBLE_2ADDR, Opcodes.REM_DOUBLE,
+            Opcodes.REM_DOUBLE, Form12x.THE_ONE, true);
+
+    public static final Dop ADD_INT_LIT16 =
+        new Dop(Opcodes.ADD_INT_LIT16, Opcodes.ADD_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop RSUB_INT =
+        new Dop(Opcodes.RSUB_INT, Opcodes.RSUB_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop MUL_INT_LIT16 =
+        new Dop(Opcodes.MUL_INT_LIT16, Opcodes.MUL_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop DIV_INT_LIT16 =
+        new Dop(Opcodes.DIV_INT_LIT16, Opcodes.DIV_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop REM_INT_LIT16 =
+        new Dop(Opcodes.REM_INT_LIT16, Opcodes.REM_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop AND_INT_LIT16 =
+        new Dop(Opcodes.AND_INT_LIT16, Opcodes.AND_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop OR_INT_LIT16 =
+        new Dop(Opcodes.OR_INT_LIT16, Opcodes.OR_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop XOR_INT_LIT16 =
+        new Dop(Opcodes.XOR_INT_LIT16, Opcodes.XOR_INT,
+            Opcodes.NO_NEXT, Form22s.THE_ONE, true);
+
+    public static final Dop ADD_INT_LIT8 =
+        new Dop(Opcodes.ADD_INT_LIT8, Opcodes.ADD_INT,
+            Opcodes.ADD_INT_LIT16, Form22b.THE_ONE, true);
+
+    public static final Dop RSUB_INT_LIT8 =
+        new Dop(Opcodes.RSUB_INT_LIT8, Opcodes.RSUB_INT,
+            Opcodes.RSUB_INT, Form22b.THE_ONE, true);
+
+    public static final Dop MUL_INT_LIT8 =
+        new Dop(Opcodes.MUL_INT_LIT8, Opcodes.MUL_INT,
+            Opcodes.MUL_INT_LIT16, Form22b.THE_ONE, true);
+
+    public static final Dop DIV_INT_LIT8 =
+        new Dop(Opcodes.DIV_INT_LIT8, Opcodes.DIV_INT,
+            Opcodes.DIV_INT_LIT16, Form22b.THE_ONE, true);
+
+    public static final Dop REM_INT_LIT8 =
+        new Dop(Opcodes.REM_INT_LIT8, Opcodes.REM_INT,
+            Opcodes.REM_INT_LIT16, Form22b.THE_ONE, true);
+
+    public static final Dop AND_INT_LIT8 =
+        new Dop(Opcodes.AND_INT_LIT8, Opcodes.AND_INT,
+            Opcodes.AND_INT_LIT16, Form22b.THE_ONE, true);
+
+    public static final Dop OR_INT_LIT8 =
+        new Dop(Opcodes.OR_INT_LIT8, Opcodes.OR_INT,
+            Opcodes.OR_INT_LIT16, Form22b.THE_ONE, true);
+
+    public static final Dop XOR_INT_LIT8 =
+        new Dop(Opcodes.XOR_INT_LIT8, Opcodes.XOR_INT,
+            Opcodes.XOR_INT_LIT16, Form22b.THE_ONE, true);
+
+    public static final Dop SHL_INT_LIT8 =
+        new Dop(Opcodes.SHL_INT_LIT8, Opcodes.SHL_INT,
+            Opcodes.NO_NEXT, Form22b.THE_ONE, true);
+
+    public static final Dop SHR_INT_LIT8 =
+        new Dop(Opcodes.SHR_INT_LIT8, Opcodes.SHR_INT,
+            Opcodes.NO_NEXT, Form22b.THE_ONE, true);
+
+    public static final Dop USHR_INT_LIT8 =
+        new Dop(Opcodes.USHR_INT_LIT8, Opcodes.USHR_INT,
+            Opcodes.NO_NEXT, Form22b.THE_ONE, true);
+
+    // END(dops)
+
+    // Static initialization.
+    static {
+        DOPS = new Dop[Opcodes.MAX_VALUE - Opcodes.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 Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the
+     * opcode value
+     * @return {@code non-null;} the associated opcode instance
+     */
+    public static Dop get(int opcode) {
+        int idx = opcode - Opcodes.MIN_VALUE;
+
+        try {
+            Dop result = DOPS[idx];
+            if (result != null) {
+                return result;
+            }
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Fall through.
+        }
+
+        throw new IllegalArgumentException("bogus opcode");
+    }
+
+    /**
+     * Gets the next {@link Dop} in the instruction fitting chain after the
+     * given instance, if any.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param options {@code non-null;} options, used to determine
+     * which opcodes are potentially off-limits
+     * @return {@code null-ok;} the next opcode in the same family, in the
+     * chain of opcodes to try, or {@code null} if the given opcode is
+     * the last in its chain
+     */
+    public static Dop getNextOrNull(Dop opcode, DexOptions options) {
+        boolean suppressExtendedOpcodes = !options.canUseExtendedOpcodes();
+
+        for (;;) {
+            int nextOpcode = opcode.getNextOpcode();
+
+            if (nextOpcode == Opcodes.NO_NEXT) {
+                return null;
+            }
+
+            opcode = get(nextOpcode);
+
+            if (suppressExtendedOpcodes && Opcodes.isExtended(nextOpcode)) {
+                /*
+                 * Continuing rather than just returning null here
+                 * protects against the possibility that an
+                 * instruction fitting chain might list non-extended
+                 * opcodes after extended ones.
+                 */
+                continue;
+            }
+
+            return opcode;
+        }
+    }
+
+    /**
+     * 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() - Opcodes.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..c866fec
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/InsnFormat.java
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.rop.cst.CstString;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.BitSet;
+
+/**
+ * Base class for all instruction format handlers. Instruction format
+ * handlers know how to translate {@link DalvInsn} instances into
+ * streams of code units, as well as human-oriented listing strings
+ * representing such translations.
+ */
+public abstract class InsnFormat {
+    /**
+     * flag to enable/disable the new extended opcode formats; meant as a
+     * temporary measure until VM support for the salient opcodes is
+     * added. TODO: Remove this declaration when the VM can deal.
+     */
+    public static boolean ALLOW_EXTENDED_OPCODES = true;
+
+    /**
+     * 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 which of a given instruction's registers will fit in
+     * this instance's format.
+     *
+     * <p>The default implementation of this method always returns
+     * an empty BitSet. Subclasses must override this method if they
+     * have registers.</p>
+     *
+     * @param insn {@code non-null;} the instruction to check
+     * @return {@code non-null;} a BitSet flagging registers in the
+     * register list that are compatible to this format
+     */
+    public BitSet compatibleRegs(DalvInsn insn) {
+        return new BitSet();
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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 register range string.
+     *
+     * @param list {@code non-null;} the list of registers (which must be
+     * sequential)
+     * @return {@code non-null;} the string form
+     */
+    protected static String regRangeString(RegisterSpecList list) {
+        int size = list.size();
+        StringBuilder sb = new StringBuilder(30);
+
+        sb.append("{");
+
+        switch (size) {
+            case 0: {
+                // Nothing to do.
+                break;
+            }
+            case 1: {
+                sb.append(list.get(0).regString());
+                break;
+            }
+            default: {
+                RegisterSpec lastReg = list.get(size - 1);
+                if (lastReg.getCategory() == 2) {
+                    /*
+                     * Add one to properly represent a list-final
+                     * category-2 register.
+                     */
+                    lastReg = lastReg.withOffset(1);
+                }
+
+                sb.append(list.get(0).regString());
+                sb.append("..");
+                sb.append(lastReg.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 the constant string for a {@link CstInsn}
+     * in human form.
+     *
+     * @param insn {@code non-null;} a constant-bearing instruction
+     * @return {@code non-null;} the human string form of the contained
+     * constant
+     */
+    protected static String cstString(DalvInsn insn) {
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        return cst instanceof CstString ? ((CstString) cst).toQuoted() : 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 list of registers are sequential,
+     * including degenerate cases for empty or single-element lists.
+     *
+     * @param list {@code non-null;} the list of registers
+     * @return {@code true} iff the list is sequentially ordered
+     */
+    protected static boolean isRegListSequential(RegisterSpecList list) {
+        int sz = list.size();
+
+        if (sz < 2) {
+            return true;
+        }
+
+        int first = list.get(0).getReg();
+        int next = first;
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = list.get(i);
+            if (one.getReg() != next) {
+                return false;
+            }
+            next += one.getCategory();
+        }
+
+        return true;
+    }
+
+    /**
+     * 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 get an extended (16-bit) opcode out of an
+     * instruction, returning it as a code unit. The opcode
+     * <i>must</i> be an extended opcode.
+     *
+     * @param insn {@code non-null;} the instruction containing the
+     * extended opcode
+     * @return the opcode as a code unit
+     */
+    protected static short opcodeUnit(DalvInsn insn) {
+        int opcode = insn.getOpcode().getOpcode();
+
+        if ((opcode < 0x100) || (opcode > 0xffff)) {
+            throw new IllegalArgumentException("opcode out of range 0..65535");
+        }
+
+        return (short) opcode;
+    }
+
+    /**
+     * 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 three code units to the given output destination, where the
+     * second and third are represented as single <code>int</code> and emitted
+     * in little-endian order.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1c2 code unit pair to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, int c1c2) {
+        write(out, c0, (short) c1c2, (short) (c1c2 >> 16));
+    }
+
+    /**
+     * Writes four code units to the given output destination, where the
+     * second and third are represented as single <code>int</code> and emitted
+     * in little-endian order.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1c2 code unit pair to write
+     * @param c3 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, int c1c2,
+            short c3) {
+        write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3);
+    }
+
+    /**
+     * Writes five code units to the given output destination, where the
+     * second and third are represented as single <code>int</code> and emitted
+     * in little-endian order.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1c2 code unit pair to write
+     * @param c3 code unit to write
+     * @param c4 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, int c1c2,
+            short c3, short c4) {
+        write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4);
+    }
+
+    /**
+     * Writes five code units to the given output destination, where the
+     * second through fifth are represented as single <code>long</code>
+     * and emitted in little-endian order.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1c2c3c4 code unit quad to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) {
+        write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16),
+                (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48));
+    }
+}
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..ab8343a
--- /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.CstString;
+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 CstString getName() {
+            return spec.getLocalItem().getName();
+        }
+
+        /**
+         * Gets the variable signature.
+         *
+         * @return {@code null-ok;} the variable signature
+         */
+        public CstString 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..f44f9cc
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OddSpacer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.Opcodes;
+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(Opcodes.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..a5e54a8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputCollector.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DexOptions;
+
+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 dexOptions {@code non-null;} options for dex output
+     * @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(DexOptions dexOptions, int initialCapacity, int suffixInitialCapacity,
+            int regCount) {
+        this.finisher = new OutputFinisher(dexOptions, 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..c9387fa
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputFinisher.java
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DexOptions;
+import com.android.dx.io.Opcodes;
+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.CstString;
+import com.android.dx.rop.type.Type;
+
+import com.android.dx.util.DexException;
+import java.util.ArrayList;
+import java.util.BitSet;
+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 non-null;} options for dex output */
+    private final DexOptions dexOptions;
+
+    /**
+     * {@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 dexOptions {@code non-null;} options for dex output
+     * @param regCount {@code >= 0;} register count for the method
+     * @param initialCapacity {@code >= 0;} initial capacity of the
+     * instructions list
+     */
+    public OutputFinisher(DexOptions dexOptions, int initialCapacity, int regCount) {
+        this.dexOptions = dexOptions;
+        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();
+        CstString name = local.getName();
+        CstString 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");
+        }
+
+        Dop[] opcodes = makeOpcodesArray();
+        reserveRegisters(opcodes);
+        massageInstructions(opcodes);
+        assignAddressesAndFixBranches();
+
+        return DalvInsnList.makeImmutable(insns,
+                reservedCount + unreservedRegCount);
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which extracts
+     * the opcode out of each instruction into a separate array, to be
+     * further manipulated as things progress.
+     *
+     * @return {@code non-null;} the array of opcodes
+     */
+    private Dop[] makeOpcodesArray() {
+        int size = insns.size();
+        Dop[] result = new Dop[size];
+
+        for (int i = 0; i < size; i++) {
+            result[i] = insns.get(i).getOpcode();
+        }
+
+        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 opcodes} array so
+     * as to avoid extra work when constructing the massaged
+     * instruction list.
+     *
+     * @param opcodes {@code non-null;} array of per-instruction
+     * opcode selections
+     */
+    private void reserveRegisters(Dop[] opcodes) {
+        int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
+
+        /*
+         * Call calculateReservedCount() and then perform register
+         * reservation, repeatedly until no new reservations happen.
+         */
+        for (;;) {
+            int newReservedCount = calculateReservedCount(opcodes);
+            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 opcodes} list to help avoid extra work in future
+     * register reservation passes.
+     *
+     * @param opcodes {@code non-null;} array of per-instruction
+     * opcode selections
+     * @return {@code >= 0;} the count of reserved registers
+     */
+    private int calculateReservedCount(Dop[] opcodes) {
+        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);
+            Dop originalOpcode = opcodes[i];
+            Dop newOpcode = findOpcodeForInsn(insn, originalOpcode);
+
+            if (newOpcode == null) {
+                /*
+                 * The instruction will need to be expanded, so find the
+                 * expanded opcode and reserve registers for it.
+                 */
+                Dop expandedOp = findExpandedOpcodeForInsn(insn);
+                BitSet compatRegs = expandedOp.getFormat().compatibleRegs(insn);
+                int reserve = insn.getMinimumRegisterRequirement(compatRegs);
+                if (reserve > newReservedCount) {
+                    newReservedCount = reserve;
+                }
+            } else if (originalOpcode == newOpcode) {
+                continue;
+            }
+
+            opcodes[i] = newOpcode;
+        }
+
+        return newReservedCount;
+    }
+
+    /**
+     * Attempts to fit the given instruction into a specific opcode,
+     * returning the opcode whose 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
+     * opcode as a first "best guess" and then pessimizes from there
+     * if necessary.
+     *
+     * @param insn {@code non-null;} the instruction in question
+     * @param guess {@code null-ok;} the current guess as to the best
+     * opcode; {@code null} means that no simple opcode fits
+     * @return {@code null-ok;} a possibly-different opcode; either a
+     * {@code non-null} good fit or {@code null} to indicate that no
+     * simple opcode fits
+     */
+    private Dop findOpcodeForInsn(DalvInsn insn, Dop guess) {
+        /*
+         * Note: The initial guess might be null, meaning that an
+         * earlier call to this method already determined that there
+         * was no possible simple opcode fit.
+         */
+
+        while (guess != null) {
+            if (guess.getFormat().isCompatible(insn)) {
+                /*
+                 * Don't break out for const_string to generate jumbo version
+                 * when option is enabled.
+                 */
+                if (!dexOptions.forceJumbo ||
+                    guess.getOpcode() != Opcodes.CONST_STRING) {
+                    break;
+                }
+            }
+
+            guess = Dops.getNextOrNull(guess, dexOptions);
+        }
+
+        return guess;
+    }
+
+    /**
+     * Finds the proper opcode for the given instruction, ignoring
+     * register constraints.
+     *
+     * @param insn {@code non-null;} the instruction in question
+     * @return {@code non-null;} the opcode that fits
+     */
+    private Dop findExpandedOpcodeForInsn(DalvInsn insn) {
+        Dop result = findOpcodeForInsn(insn.getLowRegVersion(), insn.getOpcode());
+        if (result == null) {
+            throw new DexException("No expanded opcode for " + insn);
+        }
+        return result;
+    }
+
+    /**
+     * 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 opcodes {@code non-null;} array of per-instruction
+     * opcode selections
+     */
+    private void massageInstructions(Dop[] opcodes) {
+        if (reservedCount == 0) {
+            /*
+             * The easy common case: No registers were reserved, so we
+             * merely need to replace any instructions whose format
+             * (and hence whose opcode) 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 originalOpcode = insn.getOpcode();
+                Dop currentOpcode = opcodes[i];
+
+                if (originalOpcode != currentOpcode) {
+                    insns.set(i, insn.withOpcode(currentOpcode));
+                }
+            }
+        } else {
+            /*
+             * The difficult uncommon case: Some instructions have to be
+             * expanded to deal with high registers.
+             */
+            insns = performExpansion(opcodes);
+        }
+    }
+
+    /**
+     * 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 opcodes {@code non-null;} array of per-instruction
+     * opcode selections
+     * @return {@code non-null;} the replacement list
+     */
+    private ArrayList<DalvInsn> performExpansion(Dop[] opcodes) {
+        int size = insns.size();
+        ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
+
+        ArrayList<CodeAddress> closelyBoundAddresses = new ArrayList<CodeAddress>();
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            Dop originalOpcode = insn.getOpcode();
+            Dop currentOpcode = opcodes[i];
+            DalvInsn prefix;
+            DalvInsn suffix;
+
+            if (currentOpcode != null) {
+                // No expansion is necessary.
+                prefix = null;
+                suffix = null;
+            } else {
+                // Expansion is required.
+                currentOpcode = findExpandedOpcodeForInsn(insn);
+                BitSet compatRegs =
+                    currentOpcode.getFormat().compatibleRegs(insn);
+                prefix = insn.expandedPrefix(compatRegs);
+                suffix = insn.expandedSuffix(compatRegs);
+
+                // Expand necessary registers to fit the new format
+                insn = insn.expandedVersion(compatRegs);
+            }
+
+            if (insn instanceof CodeAddress) {
+                // If we have a closely bound address, don't add it yet,
+                // because we need to add it after the prefix for the
+                // instruction it is bound to.
+                if (((CodeAddress) insn).getBindsClosely()) {
+                    closelyBoundAddresses.add((CodeAddress)insn);
+                    continue;
+                }
+            }
+
+            if (prefix != null) {
+                result.add(prefix);
+            }
+
+            // Add any pending closely bound addresses
+            if (!(insn instanceof ZeroSizeInsn) && closelyBoundAddresses.size() > 0) {
+                for (CodeAddress codeAddress: closelyBoundAddresses) {
+                    result.add(codeAddress);
+                }
+                closelyBoundAddresses.clear();
+            }
+
+            if (currentOpcode != originalOpcode) {
+                insn = insn.withOpcode(currentOpcode);
+            }
+            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 opcode = insn.getOpcode();
+            TargetInsn target = (TargetInsn) insn;
+
+            if (opcode.getFormat().branchFits(target)) {
+                continue;
+            }
+
+            if (opcode.getFamily() == Opcodes.GOTO) {
+                // It is a goto; widen it if possible.
+                opcode = findOpcodeForInsn(insn, opcode);
+                if (opcode == 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");
+                }
+                insns.set(i, insn.withOpcode(opcode));
+            } 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..0330113
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopToDop.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstType;
+import com.android.dx.rop.cst.CstString;
+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.
+    }
+
+    /*
+     * The following comment lists each opcode that should be considered
+     * the "head" of an opcode chain, in terms of the process of fitting
+     * an instruction's arguments to an actual opcode. This list is
+     * automatically generated and may be of use in double-checking the
+     * manually-generated static initialization code for this class.
+     *
+     * TODO: Make opcode-gen produce useful code in this case instead
+     * of just a comment.
+     */
+
+    // BEGIN(first-opcodes); GENERATED AUTOMATICALLY BY opcode-gen
+    //     Opcodes.NOP
+    //     Opcodes.MOVE
+    //     Opcodes.MOVE_WIDE
+    //     Opcodes.MOVE_OBJECT
+    //     Opcodes.MOVE_RESULT
+    //     Opcodes.MOVE_RESULT_WIDE
+    //     Opcodes.MOVE_RESULT_OBJECT
+    //     Opcodes.MOVE_EXCEPTION
+    //     Opcodes.RETURN_VOID
+    //     Opcodes.RETURN
+    //     Opcodes.RETURN_WIDE
+    //     Opcodes.RETURN_OBJECT
+    //     Opcodes.CONST_4
+    //     Opcodes.CONST_WIDE_16
+    //     Opcodes.CONST_STRING
+    //     Opcodes.CONST_CLASS
+    //     Opcodes.MONITOR_ENTER
+    //     Opcodes.MONITOR_EXIT
+    //     Opcodes.CHECK_CAST
+    //     Opcodes.INSTANCE_OF
+    //     Opcodes.ARRAY_LENGTH
+    //     Opcodes.NEW_INSTANCE
+    //     Opcodes.NEW_ARRAY
+    //     Opcodes.FILLED_NEW_ARRAY
+    //     Opcodes.FILL_ARRAY_DATA
+    //     Opcodes.THROW
+    //     Opcodes.GOTO
+    //     Opcodes.PACKED_SWITCH
+    //     Opcodes.SPARSE_SWITCH
+    //     Opcodes.CMPL_FLOAT
+    //     Opcodes.CMPG_FLOAT
+    //     Opcodes.CMPL_DOUBLE
+    //     Opcodes.CMPG_DOUBLE
+    //     Opcodes.CMP_LONG
+    //     Opcodes.IF_EQ
+    //     Opcodes.IF_NE
+    //     Opcodes.IF_LT
+    //     Opcodes.IF_GE
+    //     Opcodes.IF_GT
+    //     Opcodes.IF_LE
+    //     Opcodes.IF_EQZ
+    //     Opcodes.IF_NEZ
+    //     Opcodes.IF_LTZ
+    //     Opcodes.IF_GEZ
+    //     Opcodes.IF_GTZ
+    //     Opcodes.IF_LEZ
+    //     Opcodes.AGET
+    //     Opcodes.AGET_WIDE
+    //     Opcodes.AGET_OBJECT
+    //     Opcodes.AGET_BOOLEAN
+    //     Opcodes.AGET_BYTE
+    //     Opcodes.AGET_CHAR
+    //     Opcodes.AGET_SHORT
+    //     Opcodes.APUT
+    //     Opcodes.APUT_WIDE
+    //     Opcodes.APUT_OBJECT
+    //     Opcodes.APUT_BOOLEAN
+    //     Opcodes.APUT_BYTE
+    //     Opcodes.APUT_CHAR
+    //     Opcodes.APUT_SHORT
+    //     Opcodes.IGET
+    //     Opcodes.IGET_WIDE
+    //     Opcodes.IGET_OBJECT
+    //     Opcodes.IGET_BOOLEAN
+    //     Opcodes.IGET_BYTE
+    //     Opcodes.IGET_CHAR
+    //     Opcodes.IGET_SHORT
+    //     Opcodes.IPUT
+    //     Opcodes.IPUT_WIDE
+    //     Opcodes.IPUT_OBJECT
+    //     Opcodes.IPUT_BOOLEAN
+    //     Opcodes.IPUT_BYTE
+    //     Opcodes.IPUT_CHAR
+    //     Opcodes.IPUT_SHORT
+    //     Opcodes.SGET
+    //     Opcodes.SGET_WIDE
+    //     Opcodes.SGET_OBJECT
+    //     Opcodes.SGET_BOOLEAN
+    //     Opcodes.SGET_BYTE
+    //     Opcodes.SGET_CHAR
+    //     Opcodes.SGET_SHORT
+    //     Opcodes.SPUT
+    //     Opcodes.SPUT_WIDE
+    //     Opcodes.SPUT_OBJECT
+    //     Opcodes.SPUT_BOOLEAN
+    //     Opcodes.SPUT_BYTE
+    //     Opcodes.SPUT_CHAR
+    //     Opcodes.SPUT_SHORT
+    //     Opcodes.INVOKE_VIRTUAL
+    //     Opcodes.INVOKE_SUPER
+    //     Opcodes.INVOKE_DIRECT
+    //     Opcodes.INVOKE_STATIC
+    //     Opcodes.INVOKE_INTERFACE
+    //     Opcodes.NEG_INT
+    //     Opcodes.NOT_INT
+    //     Opcodes.NEG_LONG
+    //     Opcodes.NOT_LONG
+    //     Opcodes.NEG_FLOAT
+    //     Opcodes.NEG_DOUBLE
+    //     Opcodes.INT_TO_LONG
+    //     Opcodes.INT_TO_FLOAT
+    //     Opcodes.INT_TO_DOUBLE
+    //     Opcodes.LONG_TO_INT
+    //     Opcodes.LONG_TO_FLOAT
+    //     Opcodes.LONG_TO_DOUBLE
+    //     Opcodes.FLOAT_TO_INT
+    //     Opcodes.FLOAT_TO_LONG
+    //     Opcodes.FLOAT_TO_DOUBLE
+    //     Opcodes.DOUBLE_TO_INT
+    //     Opcodes.DOUBLE_TO_LONG
+    //     Opcodes.DOUBLE_TO_FLOAT
+    //     Opcodes.INT_TO_BYTE
+    //     Opcodes.INT_TO_CHAR
+    //     Opcodes.INT_TO_SHORT
+    //     Opcodes.ADD_INT_2ADDR
+    //     Opcodes.SUB_INT_2ADDR
+    //     Opcodes.MUL_INT_2ADDR
+    //     Opcodes.DIV_INT_2ADDR
+    //     Opcodes.REM_INT_2ADDR
+    //     Opcodes.AND_INT_2ADDR
+    //     Opcodes.OR_INT_2ADDR
+    //     Opcodes.XOR_INT_2ADDR
+    //     Opcodes.SHL_INT_2ADDR
+    //     Opcodes.SHR_INT_2ADDR
+    //     Opcodes.USHR_INT_2ADDR
+    //     Opcodes.ADD_LONG_2ADDR
+    //     Opcodes.SUB_LONG_2ADDR
+    //     Opcodes.MUL_LONG_2ADDR
+    //     Opcodes.DIV_LONG_2ADDR
+    //     Opcodes.REM_LONG_2ADDR
+    //     Opcodes.AND_LONG_2ADDR
+    //     Opcodes.OR_LONG_2ADDR
+    //     Opcodes.XOR_LONG_2ADDR
+    //     Opcodes.SHL_LONG_2ADDR
+    //     Opcodes.SHR_LONG_2ADDR
+    //     Opcodes.USHR_LONG_2ADDR
+    //     Opcodes.ADD_FLOAT_2ADDR
+    //     Opcodes.SUB_FLOAT_2ADDR
+    //     Opcodes.MUL_FLOAT_2ADDR
+    //     Opcodes.DIV_FLOAT_2ADDR
+    //     Opcodes.REM_FLOAT_2ADDR
+    //     Opcodes.ADD_DOUBLE_2ADDR
+    //     Opcodes.SUB_DOUBLE_2ADDR
+    //     Opcodes.MUL_DOUBLE_2ADDR
+    //     Opcodes.DIV_DOUBLE_2ADDR
+    //     Opcodes.REM_DOUBLE_2ADDR
+    //     Opcodes.ADD_INT_LIT8
+    //     Opcodes.RSUB_INT_LIT8
+    //     Opcodes.MUL_INT_LIT8
+    //     Opcodes.DIV_INT_LIT8
+    //     Opcodes.REM_INT_LIT8
+    //     Opcodes.AND_INT_LIT8
+    //     Opcodes.OR_INT_LIT8
+    //     Opcodes.XOR_INT_LIT8
+    //     Opcodes.SHL_INT_LIT8
+    //     Opcodes.SHR_INT_LIT8
+    //     Opcodes.USHR_INT_LIT8
+    // END(first-opcodes)
+
+    static {
+        /*
+         * Note: The choices made here are to pick the optimistically
+         * smallest Dalvik opcode, and leave it to later processing to
+         * pessimize. See the automatically-generated comment above
+         * for reference.
+         */
+        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.
+
+        MAP.put(Rops.SUB_CONST_INT,        Dops.RSUB_INT_LIT8);
+        /*
+         * Note: No dalvik ops for any type of sub_const; instead
+         * there's a *reverse* sub (constant - reg) for ints only.
+         */
+
+        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..46ed811
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopTranslator.java
@@ -0,0 +1,882 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DexOptions;
+import com.android.dx.io.Opcodes;
+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.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;} options for dex output */
+    private final DexOptions dexOptions;
+
+    /** {@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
+     * @param dexOptions {@code non-null;} options for dex output
+     * @return {@code non-null;} the translated version
+     */
+    public static DalvCode translate(RopMethod method, int positionInfo,
+            LocalVariableInfo locals, int paramSize, DexOptions dexOptions) {
+        RopTranslator translator =
+            new RopTranslator(method, positionInfo, locals, paramSize, dexOptions);
+        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
+     * @param dexOptions {@code non-null;} options for dex output
+     */
+    private RopTranslator(RopMethod method, int positionInfo, LocalVariableInfo locals,
+            int paramSize, DexOptions dexOptions) {
+        this.dexOptions = dexOptions;
+        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(dexOptions, 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() {
+            @Override
+            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);
+            // make a new address that binds closely to the switch instruction
+            CodeAddress switchAddress =
+                new CodeAddress(lastAddress.getPosition(), true);
+            SwitchData dataInsn =
+                new SwitchData(pos, switchAddress, cases, switchTargets);
+            Dop opcode = dataInsn.isPacked() ?
+                Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH;
+            TargetInsn switchInsn =
+                new TargetInsn(opcode, pos, getRegs(insn), dataAddress);
+
+            addOutput(switchAddress);
+            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() != Opcodes.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..8fc80b1
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/SwitchData.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.Opcodes;
+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(Opcodes.PACKED_SWITCH_PAYLOAD);
+            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(Opcodes.SPARSE_SWITCH_PAYLOAD);
+            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-payload // 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..ced4a64
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10t.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.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 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..4be3aa0
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10x.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.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 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..479af6e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11n.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.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInNibble(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@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..82dda65
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11x.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@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..aabab8a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form12x.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(2);
+
+        bits.set(0, unsignedFitsInNibble(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInNibble(regs.get(1).getReg()));
+        return bits;
+    }
+
+    /** {@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..a19ed28
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form20t.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.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 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..0335dc7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21c.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.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;
+
+import java.util.BitSet;
+
+/**
+ * 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();
+        Constant cst = ci.getConstant();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        return (cst instanceof CstType) ||
+            (cst instanceof CstFieldRef) ||
+            (cst instanceof CstString);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+        BitSet bits = new BitSet(sz);
+        boolean compat = unsignedFitsInByte(regs.get(0).getReg());
+
+        if (sz == 1) {
+            bits.set(0, compat);
+        } else {
+            if (regs.get(0).getReg() == regs.get(1).getReg()) {
+                bits.set(0, compat);
+                bits.set(1, compat);
+            }
+        }
+
+        return bits;
+    }
+
+    /** {@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..02cc0fd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21h.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@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..9264ec0
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21s.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.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@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..8adb668
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21t.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.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@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 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..e5a8b5d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22b.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.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(2);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInByte(regs.get(1).getReg()));
+        return bits;
+    }
+
+    /** {@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..5ffdb86
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22c.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(2);
+
+        bits.set(0, unsignedFitsInNibble(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInNibble(regs.get(1).getReg()));
+        return bits;
+    }
+
+    /** {@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..03d180a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22s.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.
+ */
+
+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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(2);
+
+        bits.set(0, unsignedFitsInNibble(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInNibble(regs.get(1).getReg()));
+        return bits;
+    }
+
+    /** {@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..15ce0f8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22t.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.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(2);
+
+        bits.set(0, unsignedFitsInNibble(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInNibble(regs.get(1).getReg()));
+        return bits;
+    }
+
+    /** {@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 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..01eec0b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22x.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(2);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInShort(regs.get(1).getReg()));
+        return bits;
+    }
+
+    /** {@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..9164482
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form23x.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.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(3);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInByte(regs.get(1).getReg()));
+        bits.set(2, unsignedFitsInByte(regs.get(2).getReg()));
+        return bits;
+    }
+
+    /** {@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..86a3e82
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form30t.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.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 void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, 0), offset);
+    }
+}
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..3295fda
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31c.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+        BitSet bits = new BitSet(sz);
+        boolean compat = unsignedFitsInByte(regs.get(0).getReg());
+
+        if (sz == 1) {
+            bits.set(0, compat);
+        } else {
+            if (regs.get(0).getReg() == regs.get(1).getReg()) {
+                bits.set(0, compat);
+                bits.set(1, compat);
+            }
+        }
+
+        return bits;
+    }
+
+    /** {@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()), cpi);
+    }
+}
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..b52341d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31i.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@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()), value);
+    }
+}
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..1999bba
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31t.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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        return true;
+    }
+
+    /** {@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()), offset);
+    }
+}
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..abed0e9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form32x.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(2);
+
+        bits.set(0, unsignedFitsInShort(regs.get(0).getReg()));
+        bits.set(1, unsignedFitsInShort(regs.get(1).getReg()));
+        return bits;
+    }
+
+    /** {@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..b9c12c6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form35c.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+        BitSet bits = new BitSet(sz);
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec reg = regs.get(i);
+            /*
+             * 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.
+             */
+            bits.set(i, unsignedFitsInNibble(reg.getReg() +
+                                             reg.getCategory() - 1));
+        }
+
+        return bits;
+    }
+
+    /** {@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..1727af5
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form3rc.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.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) {
+        return regRangeString(insn.getRegisters()) + ", " +
+            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();
+        Constant cst = ci.getConstant();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        if (!((cst instanceof CstMethodRef) ||
+              (cst instanceof CstType))) {
+            return false;
+        }
+
+        RegisterSpecList regs = ci.getRegisters();
+        int sz = regs.size();
+
+        return (regs.size() == 0) ||
+            (isRegListSequential(regs) &&
+             unsignedFitsInShort(regs.get(0).getReg()) &&
+             unsignedFitsInByte(regs.getWordCount()));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int cpi = ((CstInsn) insn).getIndex();
+        int firstReg = (regs.size() == 0) ? 0 : regs.get(0).getReg();
+        int count = regs.getWordCount();
+
+        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..4dc7bcd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form51l.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.CstLiteral64;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.BitSet;
+
+/**
+ * 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 BitSet compatibleRegs(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        BitSet bits = new BitSet(1);
+
+        bits.set(0, unsignedFitsInByte(regs.get(0).getReg()));
+        return bits;
+    }
+
+    /** {@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()), value);
+    }
+}
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..87091b5
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/SpecialFormat.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.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+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 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..1d92247
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationItem.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstString;
+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()) {
+            CstString 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..350ed9a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 CstString ACCESS_FLAGS_STRING = new CstString("accessFlags");
+
+    /** {@code non-null;} the UTF-8 constant {@code "name"} */
+    private static final CstString NAME_STRING = new CstString("name");
+
+    /** {@code non-null;} the UTF-8 constant {@code "value"} */
+    private static final CstString VALUE_STRING = new CstString("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_STRING, 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_STRING, 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_STRING, 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(CstString name, int accessFlags) {
+        Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM);
+        Constant nameCst = (name != null) ? name : CstKnownNull.THE_ONE;
+
+        result.put(new NameValuePair(NAME_STRING, nameCst));
+        result.put(new NameValuePair(ACCESS_FLAGS_STRING,
+                        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_STRING, 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(CstString 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_STRING, 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_STRING, 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..8b0f1bd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CatchStructs.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.writeUleb128(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.writeSleb128(-(listSize - 1));
+                listSize--;
+            } else {
+                out.writeSleb128(listSize);
+            }
+
+            for (int i = 0; i < listSize; i++) {
+                CatchHandlerList.Entry entry = list.get(i);
+                out.writeUleb128(
+                        typeIds.indexOf(entry.getExceptionType()));
+                out.writeUleb128(entry.getHandler());
+            }
+
+            if (catchesAll) {
+                out.writeUleb128(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..e9ae18b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDataItem.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Writers;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+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.writeUleb128(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..df3945a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDefItem.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.SizeOf;
+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.CstString;
+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.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;
+
+/**
+ * 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 {
+
+    /** {@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 CstString 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, CstString 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 SizeOf.CLASS_DEF_ITEM;
+    }
+
+    /** {@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 CstString 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..9fb4845
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstString;
+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.ByteArrayByteInput;
+import com.android.dx.util.ByteInput;
+import com.android.dx.util.ExceptionWithContext;
+
+import com.android.dx.util.Leb128Utils;
+import java.io.IOException;
+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 CstString("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"
+     *
+     * @return index into file's string ids table, -1 means null
+     * @throws IOException
+     */
+    private int readStringIndex(ByteInput bs) throws IOException {
+        int offsetIndex = Leb128Utils.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 {
+        ByteInput bs = new ByteArrayByteInput(encoded);
+
+        line = Leb128Utils.readUnsignedLeb128(bs);
+        int szParams = Leb128Utils.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.readByte() & 0xff;
+
+            switch (opcode) {
+                case DBG_START_LOCAL: {
+                    int reg = Leb128Utils.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 = Leb128Utils.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 = Leb128Utils.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 = Leb128Utils.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 += Leb128Utils.readUnsignedLeb128(bs);
+                break;
+
+                case DBG_ADVANCE_LINE:
+                    line += Leb128Utils.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");
+        }
+    }
+}
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..ae87fc3
--- /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.CstString;
+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.writeUleb128(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.writeUleb128(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;
+            }
+
+            CstString 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(' ');
+
+        CstString 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());
+        }
+
+        CstString 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(CstString string) throws IOException {
+        if ((string == null) || (file == null)) {
+            output.writeUleb128(0);
+        } else {
+            output.writeUleb128(
+                    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.writeUleb128(0);
+        } else {
+            output.writeUleb128(
+                    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.writeUleb128(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.writeSleb128(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.writeUleb128(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.writeUleb128(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..09b2712
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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..6a9882c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DexFile.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DexFormat;
+import com.android.dx.dex.DexOptions;
+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.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 {
+    /** options controlling the creation of the file */
+    private DexOptions dexOptions;
+
+    /** {@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(DexOptions dexOptions) {
+        this.dexOptions = dexOptions;
+
+        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;
+    }
+
+    /**
+     * Returns true if this dex doesn't contain any class defs.
+     */
+    public boolean isEmpty() {
+        return classDefs.items().isEmpty();
+    }
+
+    /**
+     * Gets the dex-creation options object.
+     */
+    public DexOptions getDexOptions() {
+        return dexOptions;
+    }
+
+    /**
+     * 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 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..3d05ab3
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedArrayItem.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.cst.CstArray;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * 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..fdfa5d2
--- /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.CstString;
+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 CstString 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.writeUleb128(diff);
+        out.writeUleb128(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..6277646
--- /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.CstString;
+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 CstString 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..c3f71b7
--- /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.CstString;
+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 CstString 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.writeUleb128(diff);
+        out.writeUleb128(accessFlags);
+        out.writeUleb128(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..3816eb8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/HeaderItem.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.DexFormat;
+import com.android.dx.dex.SizeOf;
+import com.android.dx.rop.cst.CstString;
+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 {
+    /**
+     * 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 SizeOf.HEADER_ITEM;
+    }
+
+    /** {@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;
+
+        String magic = file.getDexOptions().getMagic();
+
+        if (out.annotates()) {
+            out.annotate(8, "magic: " + new CstString(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(SizeOf.HEADER_ITEM));
+            out.annotate(4, "endian_tag:      " + Hex.u4(DexFormat.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(SizeOf.HEADER_ITEM);
+        out.writeInt(DexFormat.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..08a3123
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdItem.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.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;
+import com.android.dx.dex.SizeOf;
+
+/**
+ * Representation of a member (field or method) reference inside a
+ * Dalvik file.
+ */
+public abstract class MemberIdItem extends IdItem {
+    /** {@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 SizeOf.MEMBER_ID_ITEM;
+    }
+
+    /** {@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..ee844d9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdsSection.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.file;
+
+import com.android.dx.util.DexException;
+import java.util.Formatter;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Member (field or method) refs list section of a {@code .dex} file.
+ */
+public abstract class MemberIdsSection extends UniformItemSection {
+    /** The largest addressable member is 0xffff, in the dex spec as field@CCCC or meth@CCCC. */
+    private static final int MAX_MEMBERS = 0x10000;
+
+    /**
+     * 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;
+
+        if (items().size() > MAX_MEMBERS) {
+            throw new DexException(tooManyMembersMessage());
+        }
+
+        for (Object i : items()) {
+            ((MemberIdItem) i).setIndex(idx);
+            idx++;
+        }
+    }
+
+    private String tooManyMembersMessage() {
+        Map<String, AtomicInteger> membersByPackage = new TreeMap<String, AtomicInteger>();
+        for (Object member : items()) {
+            String packageName = ((MemberIdItem) member).getDefiningClass().getPackageName();
+            AtomicInteger count = membersByPackage.get(packageName);
+            if (count == null) {
+                count = new AtomicInteger();
+                membersByPackage.put(packageName, count);
+            }
+            count.incrementAndGet();
+        }
+
+        Formatter formatter = new Formatter();
+        String memberType = this instanceof MethodIdsSection ? "methods" : "fields";
+        formatter.format("Too many %s: %d; max is %d. By package:",
+                memberType, items().size(), MAX_MEMBERS);
+        for (Map.Entry<String, AtomicInteger> entry : membersByPackage.entrySet()) {
+            formatter.format("%n%6d %s", entry.getValue().get(), entry.getKey());
+        }
+        return formatter.toString();
+    }
+}
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..235a8c8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ProtoIdItem.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.SizeOf;
+import com.android.dx.rop.cst.CstString;
+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 {
+    /** {@code non-null;} the wrapped prototype */
+    private final Prototype prototype;
+
+    /** {@code non-null;} the short-form of the prototype */
+    private final CstString 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 CstString 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 CstString(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 SizeOf.PROTO_ID_ITEM;
+    }
+
+    /** {@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..e85a823
--- /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.CstString;
+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 CstString value;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param value {@code non-null;} the string value
+     */
+    public StringDataItem(CstString 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(CstString 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.writeUleb128(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..533427d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdItem.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.SizeOf;
+import com.android.dx.rop.cst.CstString;
+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 {
+    /** {@code non-null;} the string value */
+    private final CstString 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(CstString 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 SizeOf.STRING_ID_ITEM;
+    }
+
+    /** {@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 CstString 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..2f7c40b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdsSection.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.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.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<CstString, 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<CstString, 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();
+
+        IndexedItem result = strings.get((CstString) 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) {
+        return intern(new StringIdItem(new CstString(string)));
+    }
+
+    /**
+     * 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(CstString 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();
+
+        CstString 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(CstString 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();
+    }
+
+    /** {@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..04be2a1
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeIdItem.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.dex.SizeOf;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstString;
+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 {
+    /**
+     * 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 SizeOf.TYPE_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        file.getStringIds().intern(getDefiningClass().getDescriptor());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        CstType type = getDefiningClass();
+        CstString 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..9c433a3
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ValueEncoder.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.writeUleb128(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.writeUleb128(typeIds.indexOf(annotation.getType()));
+
+        Collection<NameValuePair> pairs = annotation.getNameValuePairs();
+        int size = pairs.size();
+
+        if (annotates) {
+            out.annotate("  size: " + Hex.u4(size));
+        }
+
+        out.writeUleb128(size);
+
+        int at = 0;
+        for (NameValuePair pair : pairs) {
+            CstString 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.writeUleb128(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/io/Annotation.java b/dx/src/com/android/dx/io/Annotation.java
new file mode 100644
index 0000000..d3be592
--- /dev/null
+++ b/dx/src/com/android/dx/io/Annotation.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.util.Unsigned;
+
+/**
+ * An annotation.
+ */
+public final class Annotation implements Comparable<Annotation> {
+    private final DexBuffer buffer;
+    private final byte visibility;
+    private final int typeIndex;
+    private final int[] names;
+    private final EncodedValue[] values;
+
+    public Annotation(DexBuffer buffer, byte visibility, int typeIndex, int[] names,
+            EncodedValue[] values) {
+        this.buffer = buffer;
+        this.visibility = visibility;
+        this.typeIndex = typeIndex;
+        this.names = names;
+        this.values = values;
+    }
+
+    public byte getVisibility() {
+        return visibility;
+    }
+
+    public int getTypeIndex() {
+        return typeIndex;
+    }
+
+    public int[] getNames() {
+        return names;
+    }
+
+    public EncodedValue[] getValues() {
+        return values;
+    }
+
+    public void writeTo(DexBuffer.Section out) {
+        out.writeByte(visibility);
+        out.writeUleb128(typeIndex);
+        out.writeUleb128(names.length);
+        for (int i = 0; i < names.length; i++) {
+            out.writeUleb128(names[i]);
+            values[i].writeTo(out);
+        }
+    }
+
+    @Override public int compareTo(Annotation other) {
+        if (typeIndex != other.typeIndex) {
+            return Unsigned.compare(typeIndex, other.typeIndex);
+        }
+        int size = Math.min(names.length, other.names.length);
+        for (int i = 0; i < size; i++) {
+            if (names[i] != other.names[i]) {
+                return Unsigned.compare(names[i], other.names[i]);
+            }
+            int compare = values[i].compareTo(other.values[i]);
+            if (compare != 0) {
+                return compare;
+            }
+        }
+        return names.length - other.names.length;
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return visibility + " " + typeIndex;
+        }
+
+        StringBuilder result = new StringBuilder();
+        result.append(visibility);
+        result.append(" ");
+        result.append(buffer.typeNames().get(typeIndex));
+        result.append("[");
+        for (int i = 0; i < names.length; i++) {
+            if (i > 0) {
+                result.append(", ");
+            }
+            result.append(buffer.strings().get(names[i]));
+            result.append("=");
+            result.append(values[i]);
+        }
+        result.append("]");
+        return result.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/io/ClassData.java b/dx/src/com/android/dx/io/ClassData.java
new file mode 100644
index 0000000..5da7ddd
--- /dev/null
+++ b/dx/src/com/android/dx/io/ClassData.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+public final class ClassData {
+    private final Field[] staticFields;
+    private final Field[] instanceFields;
+    private final Method[] directMethods;
+    private final Method[] virtualMethods;
+
+    public ClassData(Field[] staticFields, Field[] instanceFields,
+            Method[] directMethods, Method[] virtualMethods) {
+        this.staticFields = staticFields;
+        this.instanceFields = instanceFields;
+        this.directMethods = directMethods;
+        this.virtualMethods = virtualMethods;
+    }
+
+    public Field[] getStaticFields() {
+        return staticFields;
+    }
+
+    public Field[] getInstanceFields() {
+        return instanceFields;
+    }
+
+    public Method[] getDirectMethods() {
+        return directMethods;
+    }
+
+    public Method[] getVirtualMethods() {
+        return virtualMethods;
+    }
+
+    public Field[] allFields() {
+        Field[] result = new Field[staticFields.length + instanceFields.length];
+        System.arraycopy(staticFields, 0, result, 0, staticFields.length);
+        System.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length);
+        return result;
+    }
+
+    public Method[] allMethods() {
+        Method[] result = new Method[directMethods.length + virtualMethods.length];
+        System.arraycopy(directMethods, 0, result, 0, directMethods.length);
+        System.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length);
+        return result;
+    }
+
+    public static class Field {
+        private final int fieldIndex;
+        private final int accessFlags;
+
+        public Field(int fieldIndex, int accessFlags) {
+            this.fieldIndex = fieldIndex;
+            this.accessFlags = accessFlags;
+        }
+
+        public int getFieldIndex() {
+            return fieldIndex;
+        }
+
+        public int getAccessFlags() {
+            return accessFlags;
+        }
+    }
+
+    public static class Method {
+        private final int methodIndex;
+        private final int accessFlags;
+        private final int codeOffset;
+
+        public Method(int methodIndex, int accessFlags, int codeOffset) {
+            this.methodIndex = methodIndex;
+            this.accessFlags = accessFlags;
+            this.codeOffset = codeOffset;
+        }
+
+        public int getMethodIndex() {
+            return methodIndex;
+        }
+
+        public int getAccessFlags() {
+            return accessFlags;
+        }
+
+        public int getCodeOffset() {
+            return codeOffset;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/io/ClassDef.java b/dx/src/com/android/dx/io/ClassDef.java
new file mode 100644
index 0000000..5c8d10b
--- /dev/null
+++ b/dx/src/com/android/dx/io/ClassDef.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+/**
+ * A type definition.
+ */
+public final class ClassDef {
+    public static final int NO_INDEX = -1;
+    private final DexBuffer buffer;
+    private final int offset;
+    private final int typeIndex;
+    private final int accessFlags;
+    private final int supertypeIndex;
+    private final int interfacesOffset;
+    private final int sourceFileIndex;
+    private final int annotationsOffset;
+    private final int classDataOffset;
+    private final int staticValuesOffset;
+
+    public ClassDef(DexBuffer buffer, int offset, int typeIndex, int accessFlags,
+            int supertypeIndex, int interfacesOffset, int sourceFileIndex,
+            int annotationsOffset, int classDataOffset, int staticValuesOffset) {
+        this.buffer = buffer;
+        this.offset = offset;
+        this.typeIndex = typeIndex;
+        this.accessFlags = accessFlags;
+        this.supertypeIndex = supertypeIndex;
+        this.interfacesOffset = interfacesOffset;
+        this.sourceFileIndex = sourceFileIndex;
+        this.annotationsOffset = annotationsOffset;
+        this.classDataOffset = classDataOffset;
+        this.staticValuesOffset = staticValuesOffset;
+    }
+
+    public int getOffset() {
+        return offset;
+    }
+
+    public int getTypeIndex() {
+        return typeIndex;
+    }
+
+    public int getSupertypeIndex() {
+        return supertypeIndex;
+    }
+
+    public int getInterfacesOffset() {
+        return interfacesOffset;
+    }
+
+    public short[] getInterfaces() {
+        return buffer.readTypeList(interfacesOffset).getTypes();
+    }
+
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+
+    public int getSourceFileIndex() {
+        return sourceFileIndex;
+    }
+
+    public int getAnnotationsOffset() {
+        return annotationsOffset;
+    }
+
+    public int getClassDataOffset() {
+        return classDataOffset;
+    }
+
+    public int getStaticValuesOffset() {
+        return staticValuesOffset;
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return typeIndex + " " + supertypeIndex;
+        }
+
+        StringBuilder result = new StringBuilder();
+        result.append(buffer.typeNames().get(typeIndex));
+        if (supertypeIndex != NO_INDEX) {
+            result.append(" extends ").append(buffer.typeNames().get(supertypeIndex));
+        }
+        return result.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/io/Code.java b/dx/src/com/android/dx/io/Code.java
new file mode 100644
index 0000000..82da862
--- /dev/null
+++ b/dx/src/com/android/dx/io/Code.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+public final class Code {
+    private final int registersSize;
+    private final int insSize;
+    private final int outsSize;
+    private final int debugInfoOffset;
+    private final short[] instructions;
+    private final Try[] tries;
+    private final CatchHandler[] catchHandlers;
+
+    public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset,
+            short[] instructions, Try[] tries, CatchHandler[] catchHandlers) {
+        this.registersSize = registersSize;
+        this.insSize = insSize;
+        this.outsSize = outsSize;
+        this.debugInfoOffset = debugInfoOffset;
+        this.instructions = instructions;
+        this.tries = tries;
+        this.catchHandlers = catchHandlers;
+    }
+
+    public int getRegistersSize() {
+        return registersSize;
+    }
+
+    public int getInsSize() {
+        return insSize;
+    }
+
+    public int getOutsSize() {
+        return outsSize;
+    }
+
+    public int getDebugInfoOffset() {
+        return debugInfoOffset;
+    }
+
+    public short[] getInstructions() {
+        return instructions;
+    }
+
+    public Try[] getTries() {
+        return tries;
+    }
+
+    public CatchHandler[] getCatchHandlers() {
+        return catchHandlers;
+    }
+
+    public static class Try {
+        final int startAddress;
+        final int instructionCount;
+        final int catchHandlerIndex;
+
+        Try(int startAddress, int instructionCount, int catchHandlerIndex) {
+            this.startAddress = startAddress;
+            this.instructionCount = instructionCount;
+            this.catchHandlerIndex = catchHandlerIndex;
+        }
+
+        public int getStartAddress() {
+            return startAddress;
+        }
+
+        public int getInstructionCount() {
+            return instructionCount;
+        }
+
+        /**
+         * Returns this try's catch handler <strong>index</strong>. Note that
+         * this is distinct from the its catch handler <strong>offset</strong>.
+         */
+        public int getCatchHandlerIndex() {
+            return catchHandlerIndex;
+        }
+    }
+
+    public static class CatchHandler {
+        final int[] typeIndexes;
+        final int[] addresses;
+        final int catchAllAddress;
+        final int offset;
+
+        public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) {
+            this.typeIndexes = typeIndexes;
+            this.addresses = addresses;
+            this.catchAllAddress = catchAllAddress;
+            this.offset = offset;
+        }
+
+        public int[] getTypeIndexes() {
+            return typeIndexes;
+        }
+
+        public int[] getAddresses() {
+            return addresses;
+        }
+
+        public int getCatchAllAddress() {
+            return catchAllAddress;
+        }
+
+        public int getOffset() {
+            return offset;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/io/CodeReader.java b/dx/src/com/android/dx/io/CodeReader.java
new file mode 100644
index 0000000..cab1063
--- /dev/null
+++ b/dx/src/com/android/dx/io/CodeReader.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.io.instructions.DecodedInstruction;
+import com.android.dx.util.DexException;
+
+/**
+ * Walks through a block of code and calls visitor call backs.
+ */
+public final class CodeReader {
+    private Visitor fallbackVisitor = null;
+    private Visitor stringVisitor = null;
+    private Visitor typeVisitor = null;
+    private Visitor fieldVisitor = null;
+    private Visitor methodVisitor = null;
+
+    /**
+     * Sets {@code visitor} as the visitor for all instructions.
+     */
+    public void setAllVisitors(Visitor visitor) {
+        fallbackVisitor = visitor;
+        stringVisitor = visitor;
+        typeVisitor = visitor;
+        fieldVisitor = visitor;
+        methodVisitor = visitor;
+    }
+
+    /**
+     * Sets {@code visitor} as the visitor for all instructions not
+     * otherwise handled.
+     */
+    public void setFallbackVisitor(Visitor visitor) {
+        fallbackVisitor = visitor;
+    }
+
+    /**
+     * Sets {@code visitor} as the visitor for all string instructions.
+     */
+    public void setStringVisitor(Visitor visitor) {
+        stringVisitor = visitor;
+    }
+
+    /**
+     * Sets {@code visitor} as the visitor for all type instructions.
+     */
+    public void setTypeVisitor(Visitor visitor) {
+        typeVisitor = visitor;
+    }
+
+    /**
+     * Sets {@code visitor} as the visitor for all field instructions.
+     */
+    public void setFieldVisitor(Visitor visitor) {
+        fieldVisitor = visitor;
+    }
+
+    /**
+     * Sets {@code visitor} as the visitor for all method instructions.
+     */
+    public void setMethodVisitor(Visitor visitor) {
+        methodVisitor = visitor;
+    }
+
+    public void visitAll(DecodedInstruction[] decodedInstructions)
+            throws DexException {
+        int size = decodedInstructions.length;
+
+        for (int i = 0; i < size; i++) {
+            DecodedInstruction one = decodedInstructions[i];
+            if (one == null) {
+                continue;
+            }
+
+            callVisit(decodedInstructions, one);
+        }
+    }
+
+    public void visitAll(short[] encodedInstructions) throws DexException {
+        DecodedInstruction[] decodedInstructions =
+            DecodedInstruction.decodeAll(encodedInstructions);
+        visitAll(decodedInstructions);
+    }
+
+    private void callVisit(DecodedInstruction[] all, DecodedInstruction one) {
+        Visitor visitor = null;
+
+        switch (OpcodeInfo.getIndexType(one.getOpcode())) {
+            case STRING_REF: visitor = stringVisitor; break;
+            case TYPE_REF:   visitor = typeVisitor;   break;
+            case FIELD_REF:  visitor = fieldVisitor;  break;
+            case METHOD_REF: visitor = methodVisitor; break;
+        }
+
+        if (visitor == null) {
+            visitor = fallbackVisitor;
+        }
+
+        if (visitor != null) {
+            visitor.visit(all, one);
+        }
+    }
+
+    public interface Visitor {
+        void visit(DecodedInstruction[] all, DecodedInstruction one);
+    }
+}
diff --git a/dx/src/com/android/dx/io/DexBuffer.java b/dx/src/com/android/dx/io/DexBuffer.java
new file mode 100644
index 0000000..9fbc78c
--- /dev/null
+++ b/dx/src/com/android/dx/io/DexBuffer.java
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.dex.DexFormat;
+import com.android.dx.dex.SizeOf;
+import com.android.dx.dex.TableOfContents;
+import com.android.dx.io.Code.CatchHandler;
+import com.android.dx.io.Code.Try;
+import com.android.dx.merge.TypeList;
+import com.android.dx.util.ByteInput;
+import com.android.dx.util.ByteOutput;
+import com.android.dx.util.DexException;
+import com.android.dx.util.FileUtils;
+import com.android.dx.util.Leb128Utils;
+import com.android.dx.util.Mutf8;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UTFDataFormatException;
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * The bytes of a dex file in memory for reading and writing. All int offsets
+ * are unsigned.
+ */
+public final class DexBuffer {
+    private byte[] data;
+    private final TableOfContents tableOfContents = new TableOfContents();
+    private int length = 0;
+
+    private final List<String> strings = new AbstractList<String>() {
+        @Override public String get(int index) {
+            checkBounds(index, tableOfContents.stringIds.size);
+            return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
+                    .readString();
+        }
+        @Override public int size() {
+            return tableOfContents.stringIds.size;
+        }
+    };
+
+    private final List<Integer> typeIds = new AbstractList<Integer>() {
+        @Override public Integer get(int index) {
+            checkBounds(index, tableOfContents.typeIds.size);
+            return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();
+        }
+        @Override public int size() {
+            return tableOfContents.typeIds.size;
+        }
+    };
+
+    private final List<String> typeNames = new AbstractList<String>() {
+        @Override public String get(int index) {
+            checkBounds(index, tableOfContents.typeIds.size);
+            return strings.get(typeIds.get(index));
+        }
+        @Override public int size() {
+            return tableOfContents.typeIds.size;
+        }
+    };
+
+    private final List<ProtoId> protoIds = new AbstractList<ProtoId>() {
+        @Override public ProtoId get(int index) {
+            checkBounds(index, tableOfContents.protoIds.size);
+            return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
+                    .readProtoId();
+        }
+        @Override public int size() {
+            return tableOfContents.protoIds.size;
+        }
+    };
+
+    private final List<FieldId> fieldIds = new AbstractList<FieldId>() {
+        @Override public FieldId get(int index) {
+            checkBounds(index, tableOfContents.fieldIds.size);
+            return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+                    .readFieldId();
+        }
+        @Override public int size() {
+            return tableOfContents.fieldIds.size;
+        }
+    };
+
+    private final List<MethodId> methodIds = new AbstractList<MethodId>() {
+        @Override public MethodId get(int index) {
+            checkBounds(index, tableOfContents.methodIds.size);
+            return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
+                    .readMethodId();
+        }
+        @Override public int size() {
+            return tableOfContents.methodIds.size;
+        }
+    };
+
+    /**
+     * Creates a new dex buffer defining no classes.
+     */
+    public DexBuffer() {
+        this.data = new byte[0];
+    }
+
+    /**
+     * Creates a new dex buffer that reads from {@code data}. It is an error to
+     * modify {@code data} after using it to create a dex buffer.
+     */
+    public DexBuffer(byte[] data) throws IOException {
+        this.data = data;
+        this.length = data.length;
+        this.tableOfContents.readFrom(this);
+    }
+
+    /**
+     * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
+     */
+    public DexBuffer(InputStream in) throws IOException {
+        loadFrom(in);
+    }
+
+    /**
+     * Creates a new dex buffer from the dex file {@code file}.
+     */
+    public DexBuffer(File file) throws IOException {
+        if (FileUtils.hasArchiveSuffix(file.getName())) {
+            ZipFile zipFile = new ZipFile(file);
+            ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
+            if (entry != null) {
+                loadFrom(zipFile.getInputStream(entry));
+                zipFile.close();
+            } else {
+                throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
+            }
+        } else if (file.getName().endsWith(".dex")) {
+            loadFrom(new FileInputStream(file));
+        } else {
+            throw new DexException("unknown output extension: " + file);
+        }
+    }
+
+    private void loadFrom(InputStream in) throws IOException {
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        byte[] buffer = new byte[8192];
+
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            bytesOut.write(buffer, 0, count);
+        }
+        in.close();
+
+        this.data = bytesOut.toByteArray();
+        this.length = data.length;
+        this.tableOfContents.readFrom(this);
+    }
+
+    private static void checkBounds(int index, int length) {
+        if (index < 0 || index >= length) {
+            throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
+        }
+    }
+
+    public void writeTo(OutputStream out) throws IOException {
+        out.write(data);
+    }
+
+    public void writeTo(File dexOut) throws IOException {
+        OutputStream out = new FileOutputStream(dexOut);
+        writeTo(out);
+        out.close();
+    }
+
+    public TableOfContents getTableOfContents() {
+        return tableOfContents;
+    }
+
+    public Section open(int position) {
+        if (position < 0 || position > length) {
+            throw new IllegalArgumentException("position=" + position + " length=" + length);
+        }
+        return new Section(position);
+    }
+
+    public Section appendSection(int maxByteCount, String name) {
+        int limit = fourByteAlign(length + maxByteCount);
+        Section result = new Section(name, length, limit);
+        length = limit;
+        return result;
+    }
+
+    public void noMoreSections() {
+        data = new byte[length];
+    }
+
+    public int getLength() {
+        return length;
+    }
+
+    public static int fourByteAlign(int position) {
+        return (position + 3) & ~3;
+    }
+
+    public byte[] getBytes() {
+        return data;
+    }
+
+    public List<String> strings() {
+        return strings;
+    }
+
+    public List<Integer> typeIds() {
+        return typeIds;
+    }
+
+    public List<String> typeNames() {
+        return typeNames;
+    }
+
+    public List<ProtoId> protoIds() {
+        return protoIds;
+    }
+
+    public List<FieldId> fieldIds() {
+        return fieldIds;
+    }
+
+    public List<MethodId> methodIds() {
+        return methodIds;
+    }
+
+    public Iterable<ClassDef> classDefs() {
+        return new Iterable<ClassDef>() {
+            public Iterator<ClassDef> iterator() {
+                if (!tableOfContents.classDefs.exists()) {
+                    return Collections.<ClassDef>emptySet().iterator();
+                }
+                return new Iterator<ClassDef>() {
+                    private DexBuffer.Section in = open(tableOfContents.classDefs.off);
+                    private int count = 0;
+
+                    public boolean hasNext() {
+                        return count < tableOfContents.classDefs.size;
+                    }
+                    public ClassDef next() {
+                        if (!hasNext()) {
+                            throw new NoSuchElementException();
+                        }
+                        count++;
+                        return in.readClassDef();
+                    }
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    public TypeList readTypeList(int offset) {
+        if (offset == 0) {
+            return TypeList.EMPTY;
+        }
+        return open(offset).readTypeList();
+    }
+
+    public ClassData readClassData(ClassDef classDef) {
+        int offset = classDef.getClassDataOffset();
+        if (offset == 0) {
+            throw new IllegalArgumentException("offset == 0");
+        }
+        return open(offset).readClassData();
+    }
+
+    public Code readCode(ClassData.Method method) {
+        int offset = method.getCodeOffset();
+        if (offset == 0) {
+            throw new IllegalArgumentException("offset == 0");
+        }
+        return open(offset).readCode();
+    }
+
+    public final class Section implements ByteInput, ByteOutput {
+        private final String name;
+        private int position;
+        private final int limit;
+        private final int initialPosition;
+
+        private Section(String name, int position, int limit) {
+            this.name = name;
+            this.position = this.initialPosition = position;
+            this.limit = limit;
+        }
+
+        private Section(int position) {
+            this("section", position, data.length);
+        }
+
+        public int getPosition() {
+            return position;
+        }
+
+        public int readInt() {
+            int result = (data[position] & 0xff)
+                    | (data[position + 1] & 0xff) << 8
+                    | (data[position + 2] & 0xff) << 16
+                    | (data[position + 3] & 0xff) << 24;
+            position += 4;
+            return result;
+        }
+
+        public short readShort() {
+            int result = (data[position] & 0xff)
+                    | (data[position + 1] & 0xff) << 8;
+            position += 2;
+            return (short) result;
+        }
+
+        public int readUnsignedShort() {
+            return readShort() & 0xffff;
+        }
+
+        public byte readByte() {
+            return (byte) (data[position++] & 0xff);
+        }
+
+        public byte[] readByteArray(int length) {
+            byte[] result = Arrays.copyOfRange(data, position, position + length);
+            position += length;
+            return result;
+        }
+
+        public short[] readShortArray(int length) {
+            short[] result = new short[length];
+            for (int i = 0; i < length; i++) {
+                result[i] = readShort();
+            }
+            return result;
+        }
+
+        public int readUleb128() {
+            return Leb128Utils.readUnsignedLeb128(this);
+        }
+
+        public int readUleb128p1() {
+            return Leb128Utils.readUnsignedLeb128(this) - 1;
+        }
+
+        public int readSleb128() {
+            return Leb128Utils.readSignedLeb128(this);
+        }
+
+        public TypeList readTypeList() {
+            int size = readInt();
+            short[] types = new short[size];
+            for (int i = 0; i < size; i++) {
+                types[i] = readShort();
+            }
+            alignToFourBytes();
+            return new TypeList(DexBuffer.this, types);
+        }
+
+        public String readString() {
+            int offset = readInt();
+            int savedPosition = position;
+            position = offset;
+            try {
+                int expectedLength = readUleb128();
+                String result = Mutf8.decode(this, new char[expectedLength]);
+                if (result.length() != expectedLength) {
+                    throw new DexException("Declared length " + expectedLength
+                            + " doesn't match decoded length of " + result.length());
+                }
+                return result;
+            } catch (UTFDataFormatException e) {
+                throw new DexException(e);
+            } finally {
+                position = savedPosition;
+            }
+        }
+
+        public FieldId readFieldId() {
+            int declaringClassIndex = readUnsignedShort();
+            int typeIndex = readUnsignedShort();
+            int nameIndex = readInt();
+            return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex);
+        }
+
+        public MethodId readMethodId() {
+            int declaringClassIndex = readUnsignedShort();
+            int protoIndex = readUnsignedShort();
+            int nameIndex = readInt();
+            return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex);
+        }
+
+        public ProtoId readProtoId() {
+            int shortyIndex = readInt();
+            int returnTypeIndex = readInt();
+            int parametersOffset = readInt();
+            return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset);
+        }
+
+        public ClassDef readClassDef() {
+            int offset = getPosition();
+            int type = readInt();
+            int accessFlags = readInt();
+            int supertype = readInt();
+            int interfacesOffset = readInt();
+            int sourceFileIndex = readInt();
+            int annotationsOffset = readInt();
+            int classDataOffset = readInt();
+            int staticValuesOffset = readInt();
+            return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype,
+                    interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
+                    staticValuesOffset);
+        }
+
+        private Code readCode() {
+            int registersSize = readUnsignedShort();
+            int insSize = readUnsignedShort();
+            int outsSize = readUnsignedShort();
+            int triesSize = readUnsignedShort();
+            int debugInfoOffset = readInt();
+            int instructionsSize = readInt();
+            short[] instructions = readShortArray(instructionsSize);
+            Try[] tries;
+            CatchHandler[] catchHandlers;
+            if (triesSize > 0) {
+                if (instructions.length % 2 == 1) {
+                    readShort(); // padding
+                }
+
+                /*
+                 * We can't read the tries until we've read the catch handlers.
+                 * Unfortunately they're in the opposite order in the dex file
+                 * so we need to read them out-of-order.
+                 */
+                Section triesSection = open(position);
+                skip(triesSize * SizeOf.TRY_ITEM);
+                catchHandlers = readCatchHandlers();
+                tries = triesSection.readTries(triesSize, catchHandlers);
+            } else {
+                tries = new Try[0];
+                catchHandlers = new CatchHandler[0];
+            }
+            return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
+                    tries, catchHandlers);
+        }
+
+        private CatchHandler[] readCatchHandlers() {
+            int baseOffset = position;
+            int catchHandlersSize = readUleb128();
+            CatchHandler[] result = new CatchHandler[catchHandlersSize];
+            for (int i = 0; i < catchHandlersSize; i++) {
+                int offset = position - baseOffset;
+                result[i] = readCatchHandler(offset);
+            }
+            return result;
+        }
+
+        private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
+            Try[] result = new Try[triesSize];
+            for (int i = 0; i < triesSize; i++) {
+                int startAddress = readInt();
+                int instructionCount = readUnsignedShort();
+                int handlerOffset = readUnsignedShort();
+                int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
+                result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
+            }
+            return result;
+        }
+
+        private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
+            for (int i = 0; i < catchHandlers.length; i++) {
+                CatchHandler catchHandler = catchHandlers[i];
+                if (catchHandler.getOffset() == offset) {
+                    return i;
+                }
+            }
+            throw new IllegalArgumentException();
+        }
+
+        private CatchHandler readCatchHandler(int offset) {
+            int size = readSleb128();
+            int handlersCount = Math.abs(size);
+            int[] typeIndexes = new int[handlersCount];
+            int[] addresses = new int[handlersCount];
+            for (int i = 0; i < handlersCount; i++) {
+                typeIndexes[i] = readUleb128();
+                addresses[i] = readUleb128();
+            }
+            int catchAllAddress = size <= 0 ? readUleb128() : -1;
+            return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
+        }
+
+        private ClassData readClassData() {
+            int staticFieldsSize = readUleb128();
+            int instanceFieldsSize = readUleb128();
+            int directMethodsSize = readUleb128();
+            int virtualMethodsSize = readUleb128();
+            ClassData.Field[] staticFields = readFields(staticFieldsSize);
+            ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
+            ClassData.Method[] directMethods = readMethods(directMethodsSize);
+            ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
+            return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
+        }
+
+        private ClassData.Field[] readFields(int count) {
+            ClassData.Field[] result = new ClassData.Field[count];
+            int fieldIndex = 0;
+            for (int i = 0; i < count; i++) {
+                fieldIndex += readUleb128(); // field index diff
+                int accessFlags = readUleb128();
+                result[i] = new ClassData.Field(fieldIndex, accessFlags);
+            }
+            return result;
+        }
+
+        private ClassData.Method[] readMethods(int count) {
+            ClassData.Method[] result = new ClassData.Method[count];
+            int methodIndex = 0;
+            for (int i = 0; i < count; i++) {
+                methodIndex += readUleb128(); // method index diff
+                int accessFlags = readUleb128();
+                int codeOff = readUleb128();
+                result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
+            }
+            return result;
+        }
+
+        public Annotation readAnnotation() {
+            byte visibility = readByte();
+            int typeIndex = readUleb128();
+            int size = readUleb128();
+            int[] names = new int[size];
+            EncodedValue[] values = new EncodedValue[size];
+            for (int i = 0; i < size; i++) {
+                names[i] = readUleb128();
+                values[i] = readEncodedValue();
+            }
+            return new Annotation(DexBuffer.this, visibility, typeIndex, names, values);
+        }
+
+        public EncodedValue readEncodedValue() {
+            int start = position;
+            new EncodedValueReader(this).readValue();
+            int end = position;
+            return new EncodedValue(Arrays.copyOfRange(data, start, end));
+        }
+
+        public EncodedValue readEncodedArray() {
+            int start = position;
+            new EncodedValueReader(this).readArray();
+            int end = position;
+            return new EncodedValue(Arrays.copyOfRange(data, start, end));
+        }
+
+        private void ensureCapacity(int size) {
+            if (position + size > limit) {
+                throw new DexException("Section limit " + limit + " exceeded by " + name);
+            }
+        }
+
+        public void skip(int count) {
+            if (count < 0) {
+                throw new IllegalArgumentException();
+            }
+            ensureCapacity(count);
+            position += count;
+        }
+
+        /**
+         * Writes 0x00 until the position is aligned to a multiple of 4.
+         */
+        public void alignToFourBytes() {
+            int unalignedCount = position;
+            position = DexBuffer.fourByteAlign(position);
+            for (int i = unalignedCount; i < position; i++) {
+                data[i] = 0;
+            }
+        }
+
+        public void assertFourByteAligned() {
+            if ((position & 3) != 0) {
+                throw new IllegalStateException("Not four byte aligned!");
+            }
+        }
+
+        public void write(byte[] bytes) {
+            ensureCapacity(bytes.length);
+            System.arraycopy(bytes, 0, data, position, bytes.length);
+            position += bytes.length;
+        }
+
+        public void writeByte(int b) {
+            ensureCapacity(1);
+            data[position++] = (byte) b;
+        }
+
+        public void writeShort(short i) {
+            ensureCapacity(2);
+            data[position    ] = (byte) i;
+            data[position + 1] = (byte) (i >>> 8);
+            position += 2;
+        }
+
+        public void writeUnsignedShort(int i) {
+            short s = (short) i;
+            if (i != (s & 0xffff)) {
+                throw new IllegalArgumentException("Expected an unsigned short: " + i);
+            }
+            writeShort(s);
+        }
+
+        public void write(short[] shorts) {
+            for (short s : shorts) {
+                writeShort(s);
+            }
+        }
+
+        public void writeInt(int i) {
+            ensureCapacity(4);
+            data[position    ] = (byte) i;
+            data[position + 1] = (byte) (i >>>  8);
+            data[position + 2] = (byte) (i >>> 16);
+            data[position + 3] = (byte) (i >>> 24);
+            position += 4;
+        }
+
+        public void writeUleb128(int i) {
+            try {
+                Leb128Utils.writeUnsignedLeb128(this, i);
+                ensureCapacity(0);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                throw new DexException("Section limit " + limit + " exceeded by " + name);
+            }
+        }
+
+        public void writeUleb128p1(int i) {
+            writeUleb128(i + 1);
+        }
+
+        public void writeSleb128(int i) {
+            try {
+                Leb128Utils.writeSignedLeb128(this, i);
+                ensureCapacity(0);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                throw new DexException("Section limit " + limit + " exceeded by " + name);
+            }
+        }
+
+        public void writeStringData(String value) {
+            try {
+                int length = value.length();
+                writeUleb128(length);
+                write(Mutf8.encode(value));
+                writeByte(0);
+            } catch (UTFDataFormatException e) {
+                throw new AssertionError();
+            }
+        }
+
+        public void writeTypeList(TypeList typeList) {
+            short[] types = typeList.getTypes();
+            writeInt(types.length);
+            for (short type : types) {
+                writeShort(type);
+            }
+            alignToFourBytes();
+        }
+
+        /**
+         * Returns the number of bytes remaining in this section.
+         */
+        public int remaining() {
+            return limit - position;
+        }
+
+        /**
+         * Returns the number of bytes used by this section.
+         */
+        public int used () {
+            return position - initialPosition;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/io/DexHasher.java b/dx/src/com/android/dx/io/DexHasher.java
new file mode 100644
index 0000000..416b3e2
--- /dev/null
+++ b/dx/src/com/android/dx/io/DexHasher.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.zip.Adler32;
+
+/**
+ * Generates and stores the checksum and signature of a dex file.
+ */
+public final class DexHasher {
+    private static final int CHECKSUM_OFFSET = 8;
+    private static final int CHECKSUM_SIZE = 4;
+    private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
+    private static final int SIGNATURE_SIZE = 20;
+
+    /**
+     * Returns the signature of all but the first 32 bytes of {@code dex}. The
+     * first 32 bytes of dex files are not specified to be included in the
+     * signature.
+     */
+    public byte[] computeSignature(DexBuffer dex) throws IOException {
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError();
+        }
+        int offset = SIGNATURE_OFFSET + SIGNATURE_SIZE;
+
+        byte[] bytes = dex.getBytes();
+        digest.update(bytes, offset, bytes.length - offset);
+        return digest.digest();
+    }
+
+    /**
+     * Returns the checksum of all but the first 12 bytes of {@code dex}.
+     */
+    public int computeChecksum(DexBuffer dex) throws IOException {
+        Adler32 adler32 = new Adler32();
+        int offset = CHECKSUM_OFFSET + CHECKSUM_SIZE;
+
+        byte[] bytes = dex.getBytes();
+        adler32.update(bytes, offset, bytes.length - offset);
+        return (int) adler32.getValue();
+    }
+
+    /**
+     * Generates the signature and checksum of the dex file {@code out} and
+     * writes them to the file.
+     */
+    public void writeHashes(DexBuffer dex) throws IOException {
+        byte[] signature = computeSignature(dex);
+        dex.open(SIGNATURE_OFFSET).write(signature);
+
+        int checksum = computeChecksum(dex);
+        dex.open(CHECKSUM_OFFSET).writeInt(checksum);
+    }
+}
diff --git a/dx/src/com/android/dx/io/DexIndexPrinter.java b/dx/src/com/android/dx/io/DexIndexPrinter.java
new file mode 100644
index 0000000..85c2015
--- /dev/null
+++ b/dx/src/com/android/dx/io/DexIndexPrinter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.dex.TableOfContents;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Executable that prints all indices of a dex file.
+ */
+public final class DexIndexPrinter {
+    private final DexBuffer dexBuffer;
+    private final TableOfContents tableOfContents;
+
+    public DexIndexPrinter(File file) throws IOException {
+        this.dexBuffer = new DexBuffer(file);
+        this.tableOfContents = dexBuffer.getTableOfContents();
+    }
+
+    private void printMap() {
+        for (TableOfContents.Section section : tableOfContents.sections) {
+            if (section.off != -1) {
+                System.out.println("section " + Integer.toHexString(section.type)
+                        + " off=" + Integer.toHexString(section.off)
+                        + " size=" + Integer.toHexString(section.size)
+                        + " byteCount=" + Integer.toHexString(section.byteCount));
+            }
+        }
+    }
+
+    private void printStrings() throws IOException {
+        int index = 0;
+        for (String string : dexBuffer.strings()) {
+            System.out.println("string " + index + ": " + string);
+            index++;
+        }
+    }
+
+    private void printTypeIds() throws IOException {
+        int index = 0;
+        for (Integer type : dexBuffer.typeIds()) {
+            System.out.println("type " + index + ": " + dexBuffer.strings().get(type));
+            index++;
+        }
+    }
+
+    private void printProtoIds() throws IOException {
+        int index = 0;
+        for (ProtoId protoId : dexBuffer.protoIds()) {
+            System.out.println("proto " + index + ": " + protoId);
+            index++;
+        }
+    }
+
+    private void printFieldIds() throws IOException {
+        int index = 0;
+        for (FieldId fieldId : dexBuffer.fieldIds()) {
+            System.out.println("field " + index + ": " + fieldId);
+            index++;
+        }
+    }
+
+    private void printMethodIds() throws IOException {
+        int index = 0;
+        for (MethodId methodId : dexBuffer.methodIds()) {
+            System.out.println("methodId " + index + ": " + methodId);
+            index++;
+        }
+    }
+
+    private void printTypeLists() throws IOException {
+        if (tableOfContents.typeLists.off == -1) {
+            System.out.println("No type lists");
+            return;
+        }
+        DexBuffer.Section in = dexBuffer.open(tableOfContents.typeLists.off);
+        for (int i = 0; i < tableOfContents.typeLists.size; i++) {
+            int size = in.readInt();
+            System.out.print("Type list i=" + i + ", size=" + size + ", elements=");
+            for (int t = 0; t < size; t++) {
+                System.out.print(" " + dexBuffer.typeNames().get((int) in.readShort()));
+            }
+            if (size % 2 == 1) {
+                in.readShort(); // retain alignment
+            }
+            System.out.println();
+        }
+    }
+
+    private void printClassDefs() {
+        int index = 0;
+        for (ClassDef classDef : dexBuffer.classDefs()) {
+            System.out.println("class def " + index + ": " + classDef);
+            index++;
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        DexIndexPrinter indexPrinter = new DexIndexPrinter(new File(args[0]));
+        indexPrinter.printMap();
+        indexPrinter.printStrings();
+        indexPrinter.printTypeIds();
+        indexPrinter.printProtoIds();
+        indexPrinter.printFieldIds();
+        indexPrinter.printMethodIds();
+        indexPrinter.printTypeLists();
+        indexPrinter.printClassDefs();
+    }
+}
diff --git a/dx/src/com/android/dx/io/EncodedValue.java b/dx/src/com/android/dx/io/EncodedValue.java
new file mode 100644
index 0000000..c83c364
--- /dev/null
+++ b/dx/src/com/android/dx/io/EncodedValue.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.util.ByteArrayByteInput;
+import com.android.dx.util.ByteInput;
+
+/**
+ * An encoded value or array.
+ */
+public final class EncodedValue implements Comparable<EncodedValue> {
+    private final byte[] data;
+
+    public EncodedValue(byte[] data) {
+        this.data = data;
+    }
+
+    public ByteInput asByteInput() {
+        return new ByteArrayByteInput(data);
+    }
+
+    public byte[] getBytes() {
+        return data;
+    }
+
+    public void writeTo(DexBuffer.Section out) {
+        out.write(data);
+    }
+
+    @Override public int compareTo(EncodedValue other) {
+        int size = Math.min(data.length, other.data.length);
+        for (int i = 0; i < size; i++) {
+            if (data[i] != other.data[i]) {
+                return (data[i] & 0xff) - (other.data[i] & 0xff);
+            }
+        }
+        return data.length - other.data.length;
+    }
+
+    @Override public String toString() {
+        return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")";
+    }
+}
diff --git a/dx/src/com/android/dx/io/EncodedValueReader.java b/dx/src/com/android/dx/io/EncodedValueReader.java
new file mode 100644
index 0000000..ec84898
--- /dev/null
+++ b/dx/src/com/android/dx/io/EncodedValueReader.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.util.ByteInput;
+import com.android.dx.util.Leb128Utils;
+
+/**
+ * SAX-style reader for encoded values.
+ * TODO: convert this to a pull-style reader
+ */
+public class EncodedValueReader {
+    public static final int ENCODED_BYTE = 0x00;
+    public static final int ENCODED_SHORT = 0x02;
+    public static final int ENCODED_CHAR = 0x03;
+    public static final int ENCODED_INT = 0x04;
+    public static final int ENCODED_LONG = 0x06;
+    public static final int ENCODED_FLOAT = 0x10;
+    public static final int ENCODED_DOUBLE = 0x11;
+    public static final int ENCODED_STRING = 0x17;
+    public static final int ENCODED_TYPE = 0x18;
+    public static final int ENCODED_FIELD = 0x19;
+    public static final int ENCODED_ENUM = 0x1b;
+    public static final int ENCODED_METHOD = 0x1a;
+    public static final int ENCODED_ARRAY = 0x1c;
+    public static final int ENCODED_ANNOTATION = 0x1d;
+    public static final int ENCODED_NULL = 0x1e;
+    public static final int ENCODED_BOOLEAN = 0x1f;
+
+    protected final ByteInput in;
+
+    public EncodedValueReader(ByteInput in) {
+        this.in = in;
+    }
+
+    public EncodedValueReader(EncodedValue in) {
+        this(in.asByteInput());
+    }
+
+    public final void readArray() {
+        int size = Leb128Utils.readUnsignedLeb128(in);
+        visitArray(size);
+
+        for (int i = 0; i < size; i++) {
+            readValue();
+        }
+    }
+
+    public final void readAnnotation() {
+        int typeIndex = Leb128Utils.readUnsignedLeb128(in);
+        int size = Leb128Utils.readUnsignedLeb128(in);
+        visitAnnotation(typeIndex, size);
+
+        for (int i = 0; i < size; i++) {
+            visitAnnotationName(Leb128Utils.readUnsignedLeb128(in));
+            readValue();
+        }
+    }
+
+    public final void readValue() {
+        int argAndType = in.readByte() & 0xff;
+        int type = argAndType & 0x1f;
+        int arg = (argAndType & 0xe0) >> 5;
+        int size = arg + 1;
+
+        switch (type) {
+        case ENCODED_BYTE:
+        case ENCODED_SHORT:
+        case ENCODED_CHAR:
+        case ENCODED_INT:
+        case ENCODED_LONG:
+        case ENCODED_FLOAT:
+        case ENCODED_DOUBLE:
+            visitPrimitive(argAndType, type, arg, size);
+            break;
+        case ENCODED_STRING:
+            visitString(type, readIndex(in, size));
+            break;
+        case ENCODED_TYPE:
+            visitType(type, readIndex(in, size));
+            break;
+        case ENCODED_FIELD:
+        case ENCODED_ENUM:
+            visitField(type, readIndex(in, size));
+            break;
+        case ENCODED_METHOD:
+            visitMethod(type, readIndex(in, size));
+            break;
+        case ENCODED_ARRAY:
+            visitArrayValue(argAndType);
+            readArray();
+            break;
+        case ENCODED_ANNOTATION:
+            visitAnnotationValue(argAndType);
+            readAnnotation();
+            break;
+        case ENCODED_NULL:
+            visitEncodedNull(argAndType);
+            break;
+        case ENCODED_BOOLEAN:
+            visitEncodedBoolean(argAndType);
+            break;
+        }
+    }
+
+    protected void visitArray(int size) {}
+    protected void visitAnnotation(int typeIndex, int size) {}
+    protected void visitAnnotationName(int nameIndex) {}
+    protected void visitPrimitive(int argAndType, int type, int arg, int size) {
+        for (int i = 0; i < size; i++) {
+            in.readByte();
+        }
+    }
+    protected void visitString(int type, int index) {}
+    protected void visitType(int type, int index) {}
+    protected void visitField(int type, int index) {}
+    protected void visitMethod(int type, int index) {}
+    protected void visitArrayValue(int argAndType) {}
+    protected void visitAnnotationValue(int argAndType) {}
+    protected void visitEncodedBoolean(int argAndType) {}
+    protected void visitEncodedNull(int argAndType) {}
+
+    private int readIndex(ByteInput in, int byteCount) {
+        int result = 0;
+        int shift = 0;
+        for (int i = 0; i < byteCount; i++) {
+            result += (in.readByte() & 0xff) << shift;
+            shift += 8;
+        }
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/io/FieldId.java b/dx/src/com/android/dx/io/FieldId.java
new file mode 100644
index 0000000..da409fa
--- /dev/null
+++ b/dx/src/com/android/dx/io/FieldId.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.util.Unsigned;
+
+public final class FieldId implements Comparable<FieldId> {
+    private final DexBuffer buffer;
+    private final int declaringClassIndex;
+    private final int typeIndex;
+    private final int nameIndex;
+
+    public FieldId(DexBuffer buffer, int declaringClassIndex, int typeIndex, int nameIndex) {
+        this.buffer = buffer;
+        this.declaringClassIndex = declaringClassIndex;
+        this.typeIndex = typeIndex;
+        this.nameIndex = nameIndex;
+    }
+
+    public int getDeclaringClassIndex() {
+        return declaringClassIndex;
+    }
+
+    public int getTypeIndex() {
+        return typeIndex;
+    }
+
+    public int getNameIndex() {
+        return nameIndex;
+    }
+
+    public int compareTo(FieldId other) {
+        if (declaringClassIndex != other.declaringClassIndex) {
+            return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
+        }
+        if (nameIndex != other.nameIndex) {
+            return Unsigned.compare(nameIndex, other.nameIndex);
+        }
+        return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0
+    }
+
+    public void writeTo(DexBuffer.Section out) {
+        out.writeUnsignedShort(declaringClassIndex);
+        out.writeUnsignedShort(typeIndex);
+        out.writeInt(nameIndex);
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return declaringClassIndex + " " + typeIndex + " " + nameIndex;
+        }
+        return buffer.typeNames().get(typeIndex) + "." + buffer.strings().get(nameIndex);
+    }
+}
diff --git a/dx/src/com/android/dx/io/IndexType.java b/dx/src/com/android/dx/io/IndexType.java
new file mode 100644
index 0000000..bbddfa8
--- /dev/null
+++ b/dx/src/com/android/dx/io/IndexType.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+/**
+ * The various types that an index in a Dalvik instruction might refer to.
+ */
+public enum IndexType {
+    /** "Unknown." Used for undefined opcodes. */
+    UNKNOWN,
+
+    /** no index used */
+    NONE,
+
+    /** "It depends." Used for {@code throw-verification-error}. */
+    VARIES,
+
+    /** type reference index */
+    TYPE_REF,
+
+    /** string reference index */
+    STRING_REF,
+
+    /** method reference index */
+    METHOD_REF,
+
+    /** field reference index */
+    FIELD_REF,
+
+    /** inline method index (for inline linked method invocations) */
+    INLINE_METHOD,
+
+    /** direct vtable offset (for static linked method invocations) */
+    VTABLE_OFFSET,
+
+    /** direct field offset (for static linked field accesses) */
+    FIELD_OFFSET;
+}
diff --git a/dx/src/com/android/dx/io/MethodId.java b/dx/src/com/android/dx/io/MethodId.java
new file mode 100644
index 0000000..e653661
--- /dev/null
+++ b/dx/src/com/android/dx/io/MethodId.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.util.Unsigned;
+
+public final class MethodId implements Comparable<MethodId> {
+    private final DexBuffer buffer;
+    private final int declaringClassIndex;
+    private final int protoIndex;
+    private final int nameIndex;
+
+    public MethodId(DexBuffer buffer, int declaringClassIndex, int protoIndex, int nameIndex) {
+        this.buffer = buffer;
+        this.declaringClassIndex = declaringClassIndex;
+        this.protoIndex = protoIndex;
+        this.nameIndex = nameIndex;
+    }
+
+    public int getDeclaringClassIndex() {
+        return declaringClassIndex;
+    }
+
+    public int getProtoIndex() {
+        return protoIndex;
+    }
+
+    public int getNameIndex() {
+        return nameIndex;
+    }
+
+    public int compareTo(MethodId other) {
+        if (declaringClassIndex != other.declaringClassIndex) {
+            return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
+        }
+        if (nameIndex != other.nameIndex) {
+            return Unsigned.compare(nameIndex, other.nameIndex);
+        }
+        return Unsigned.compare(protoIndex, other.protoIndex);
+    }
+
+    public void writeTo(DexBuffer.Section out) {
+        out.writeUnsignedShort(declaringClassIndex);
+        out.writeUnsignedShort(protoIndex);
+        out.writeInt(nameIndex);
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return declaringClassIndex + " " + protoIndex + " " + nameIndex;
+        }
+        return buffer.typeNames().get(declaringClassIndex)
+                + "." + buffer.strings().get(nameIndex)
+                + buffer.readTypeList(buffer.protoIds().get(protoIndex).getParametersOffset());
+    }
+}
diff --git a/dx/src/com/android/dx/io/OpcodeInfo.java b/dx/src/com/android/dx/io/OpcodeInfo.java
new file mode 100644
index 0000000..2040d35
--- /dev/null
+++ b/dx/src/com/android/dx/io/OpcodeInfo.java
@@ -0,0 +1,1265 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.io.instructions.InstructionCodec;
+import com.android.dx.util.Hex;
+
+/**
+ * Information about each Dalvik opcode.
+ */
+public final class OpcodeInfo {
+    /*
+     * TODO: Merge at least most of the info from the Dops class into
+     * this one.
+     */
+
+    /** non-null; array containing all the information */
+    private static final Info[] INFO;
+
+    /**
+     * pseudo-opcode used for nonstandard formatted "instructions"
+     * (which are mostly not actually instructions, though they do
+     * appear in instruction lists). TODO: Retire the usage of this
+     * constant.
+     */
+    public static final Info SPECIAL_FORMAT =
+        new Info(Opcodes.SPECIAL_FORMAT, "<special>",
+                InstructionCodec.FORMAT_00X, IndexType.NONE);
+
+    // TODO: These payload opcodes should be generated by opcode-gen.
+
+    public static final Info PACKED_SWITCH_PAYLOAD =
+        new Info(Opcodes.PACKED_SWITCH_PAYLOAD, "packed-switch-payload",
+                InstructionCodec.FORMAT_PACKED_SWITCH_PAYLOAD,
+                IndexType.NONE);
+
+    public static final Info SPARSE_SWITCH_PAYLOAD =
+        new Info(Opcodes.SPARSE_SWITCH_PAYLOAD, "sparse-switch-payload",
+                InstructionCodec.FORMAT_SPARSE_SWITCH_PAYLOAD,
+                IndexType.NONE);
+
+    public static final Info FILL_ARRAY_DATA_PAYLOAD =
+        new Info(Opcodes.FILL_ARRAY_DATA_PAYLOAD, "fill-array-data-payload",
+                InstructionCodec.FORMAT_FILL_ARRAY_DATA_PAYLOAD,
+                IndexType.NONE);
+
+    // BEGIN(opcode-info-defs); GENERATED AUTOMATICALLY BY opcode-gen
+    public static final Info NOP =
+        new Info(Opcodes.NOP, "nop",
+            InstructionCodec.FORMAT_10X, IndexType.NONE);
+
+    public static final Info MOVE =
+        new Info(Opcodes.MOVE, "move",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info MOVE_FROM16 =
+        new Info(Opcodes.MOVE_FROM16, "move/from16",
+            InstructionCodec.FORMAT_22X, IndexType.NONE);
+
+    public static final Info MOVE_16 =
+        new Info(Opcodes.MOVE_16, "move/16",
+            InstructionCodec.FORMAT_32X, IndexType.NONE);
+
+    public static final Info MOVE_WIDE =
+        new Info(Opcodes.MOVE_WIDE, "move-wide",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info MOVE_WIDE_FROM16 =
+        new Info(Opcodes.MOVE_WIDE_FROM16, "move-wide/from16",
+            InstructionCodec.FORMAT_22X, IndexType.NONE);
+
+    public static final Info MOVE_WIDE_16 =
+        new Info(Opcodes.MOVE_WIDE_16, "move-wide/16",
+            InstructionCodec.FORMAT_32X, IndexType.NONE);
+
+    public static final Info MOVE_OBJECT =
+        new Info(Opcodes.MOVE_OBJECT, "move-object",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info MOVE_OBJECT_FROM16 =
+        new Info(Opcodes.MOVE_OBJECT_FROM16, "move-object/from16",
+            InstructionCodec.FORMAT_22X, IndexType.NONE);
+
+    public static final Info MOVE_OBJECT_16 =
+        new Info(Opcodes.MOVE_OBJECT_16, "move-object/16",
+            InstructionCodec.FORMAT_32X, IndexType.NONE);
+
+    public static final Info MOVE_RESULT =
+        new Info(Opcodes.MOVE_RESULT, "move-result",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info MOVE_RESULT_WIDE =
+        new Info(Opcodes.MOVE_RESULT_WIDE, "move-result-wide",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info MOVE_RESULT_OBJECT =
+        new Info(Opcodes.MOVE_RESULT_OBJECT, "move-result-object",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info MOVE_EXCEPTION =
+        new Info(Opcodes.MOVE_EXCEPTION, "move-exception",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info RETURN_VOID =
+        new Info(Opcodes.RETURN_VOID, "return-void",
+            InstructionCodec.FORMAT_10X, IndexType.NONE);
+
+    public static final Info RETURN =
+        new Info(Opcodes.RETURN, "return",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info RETURN_WIDE =
+        new Info(Opcodes.RETURN_WIDE, "return-wide",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info RETURN_OBJECT =
+        new Info(Opcodes.RETURN_OBJECT, "return-object",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info CONST_4 =
+        new Info(Opcodes.CONST_4, "const/4",
+            InstructionCodec.FORMAT_11N, IndexType.NONE);
+
+    public static final Info CONST_16 =
+        new Info(Opcodes.CONST_16, "const/16",
+            InstructionCodec.FORMAT_21S, IndexType.NONE);
+
+    public static final Info CONST =
+        new Info(Opcodes.CONST, "const",
+            InstructionCodec.FORMAT_31I, IndexType.NONE);
+
+    public static final Info CONST_HIGH16 =
+        new Info(Opcodes.CONST_HIGH16, "const/high16",
+            InstructionCodec.FORMAT_21H, IndexType.NONE);
+
+    public static final Info CONST_WIDE_16 =
+        new Info(Opcodes.CONST_WIDE_16, "const-wide/16",
+            InstructionCodec.FORMAT_21S, IndexType.NONE);
+
+    public static final Info CONST_WIDE_32 =
+        new Info(Opcodes.CONST_WIDE_32, "const-wide/32",
+            InstructionCodec.FORMAT_31I, IndexType.NONE);
+
+    public static final Info CONST_WIDE =
+        new Info(Opcodes.CONST_WIDE, "const-wide",
+            InstructionCodec.FORMAT_51L, IndexType.NONE);
+
+    public static final Info CONST_WIDE_HIGH16 =
+        new Info(Opcodes.CONST_WIDE_HIGH16, "const-wide/high16",
+            InstructionCodec.FORMAT_21H, IndexType.NONE);
+
+    public static final Info CONST_STRING =
+        new Info(Opcodes.CONST_STRING, "const-string",
+            InstructionCodec.FORMAT_21C, IndexType.STRING_REF);
+
+    public static final Info CONST_STRING_JUMBO =
+        new Info(Opcodes.CONST_STRING_JUMBO, "const-string/jumbo",
+            InstructionCodec.FORMAT_31C, IndexType.STRING_REF);
+
+    public static final Info CONST_CLASS =
+        new Info(Opcodes.CONST_CLASS, "const-class",
+            InstructionCodec.FORMAT_21C, IndexType.TYPE_REF);
+
+    public static final Info MONITOR_ENTER =
+        new Info(Opcodes.MONITOR_ENTER, "monitor-enter",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info MONITOR_EXIT =
+        new Info(Opcodes.MONITOR_EXIT, "monitor-exit",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info CHECK_CAST =
+        new Info(Opcodes.CHECK_CAST, "check-cast",
+            InstructionCodec.FORMAT_21C, IndexType.TYPE_REF);
+
+    public static final Info INSTANCE_OF =
+        new Info(Opcodes.INSTANCE_OF, "instance-of",
+            InstructionCodec.FORMAT_22C, IndexType.TYPE_REF);
+
+    public static final Info ARRAY_LENGTH =
+        new Info(Opcodes.ARRAY_LENGTH, "array-length",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info NEW_INSTANCE =
+        new Info(Opcodes.NEW_INSTANCE, "new-instance",
+            InstructionCodec.FORMAT_21C, IndexType.TYPE_REF);
+
+    public static final Info NEW_ARRAY =
+        new Info(Opcodes.NEW_ARRAY, "new-array",
+            InstructionCodec.FORMAT_22C, IndexType.TYPE_REF);
+
+    public static final Info FILLED_NEW_ARRAY =
+        new Info(Opcodes.FILLED_NEW_ARRAY, "filled-new-array",
+            InstructionCodec.FORMAT_35C, IndexType.TYPE_REF);
+
+    public static final Info FILLED_NEW_ARRAY_RANGE =
+        new Info(Opcodes.FILLED_NEW_ARRAY_RANGE, "filled-new-array/range",
+            InstructionCodec.FORMAT_3RC, IndexType.TYPE_REF);
+
+    public static final Info FILL_ARRAY_DATA =
+        new Info(Opcodes.FILL_ARRAY_DATA, "fill-array-data",
+            InstructionCodec.FORMAT_31T, IndexType.NONE);
+
+    public static final Info THROW =
+        new Info(Opcodes.THROW, "throw",
+            InstructionCodec.FORMAT_11X, IndexType.NONE);
+
+    public static final Info GOTO =
+        new Info(Opcodes.GOTO, "goto",
+            InstructionCodec.FORMAT_10T, IndexType.NONE);
+
+    public static final Info GOTO_16 =
+        new Info(Opcodes.GOTO_16, "goto/16",
+            InstructionCodec.FORMAT_20T, IndexType.NONE);
+
+    public static final Info GOTO_32 =
+        new Info(Opcodes.GOTO_32, "goto/32",
+            InstructionCodec.FORMAT_30T, IndexType.NONE);
+
+    public static final Info PACKED_SWITCH =
+        new Info(Opcodes.PACKED_SWITCH, "packed-switch",
+            InstructionCodec.FORMAT_31T, IndexType.NONE);
+
+    public static final Info SPARSE_SWITCH =
+        new Info(Opcodes.SPARSE_SWITCH, "sparse-switch",
+            InstructionCodec.FORMAT_31T, IndexType.NONE);
+
+    public static final Info CMPL_FLOAT =
+        new Info(Opcodes.CMPL_FLOAT, "cmpl-float",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info CMPG_FLOAT =
+        new Info(Opcodes.CMPG_FLOAT, "cmpg-float",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info CMPL_DOUBLE =
+        new Info(Opcodes.CMPL_DOUBLE, "cmpl-double",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info CMPG_DOUBLE =
+        new Info(Opcodes.CMPG_DOUBLE, "cmpg-double",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info CMP_LONG =
+        new Info(Opcodes.CMP_LONG, "cmp-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info IF_EQ =
+        new Info(Opcodes.IF_EQ, "if-eq",
+            InstructionCodec.FORMAT_22T, IndexType.NONE);
+
+    public static final Info IF_NE =
+        new Info(Opcodes.IF_NE, "if-ne",
+            InstructionCodec.FORMAT_22T, IndexType.NONE);
+
+    public static final Info IF_LT =
+        new Info(Opcodes.IF_LT, "if-lt",
+            InstructionCodec.FORMAT_22T, IndexType.NONE);
+
+    public static final Info IF_GE =
+        new Info(Opcodes.IF_GE, "if-ge",
+            InstructionCodec.FORMAT_22T, IndexType.NONE);
+
+    public static final Info IF_GT =
+        new Info(Opcodes.IF_GT, "if-gt",
+            InstructionCodec.FORMAT_22T, IndexType.NONE);
+
+    public static final Info IF_LE =
+        new Info(Opcodes.IF_LE, "if-le",
+            InstructionCodec.FORMAT_22T, IndexType.NONE);
+
+    public static final Info IF_EQZ =
+        new Info(Opcodes.IF_EQZ, "if-eqz",
+            InstructionCodec.FORMAT_21T, IndexType.NONE);
+
+    public static final Info IF_NEZ =
+        new Info(Opcodes.IF_NEZ, "if-nez",
+            InstructionCodec.FORMAT_21T, IndexType.NONE);
+
+    public static final Info IF_LTZ =
+        new Info(Opcodes.IF_LTZ, "if-ltz",
+            InstructionCodec.FORMAT_21T, IndexType.NONE);
+
+    public static final Info IF_GEZ =
+        new Info(Opcodes.IF_GEZ, "if-gez",
+            InstructionCodec.FORMAT_21T, IndexType.NONE);
+
+    public static final Info IF_GTZ =
+        new Info(Opcodes.IF_GTZ, "if-gtz",
+            InstructionCodec.FORMAT_21T, IndexType.NONE);
+
+    public static final Info IF_LEZ =
+        new Info(Opcodes.IF_LEZ, "if-lez",
+            InstructionCodec.FORMAT_21T, IndexType.NONE);
+
+    public static final Info AGET =
+        new Info(Opcodes.AGET, "aget",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AGET_WIDE =
+        new Info(Opcodes.AGET_WIDE, "aget-wide",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AGET_OBJECT =
+        new Info(Opcodes.AGET_OBJECT, "aget-object",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AGET_BOOLEAN =
+        new Info(Opcodes.AGET_BOOLEAN, "aget-boolean",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AGET_BYTE =
+        new Info(Opcodes.AGET_BYTE, "aget-byte",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AGET_CHAR =
+        new Info(Opcodes.AGET_CHAR, "aget-char",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AGET_SHORT =
+        new Info(Opcodes.AGET_SHORT, "aget-short",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info APUT =
+        new Info(Opcodes.APUT, "aput",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info APUT_WIDE =
+        new Info(Opcodes.APUT_WIDE, "aput-wide",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info APUT_OBJECT =
+        new Info(Opcodes.APUT_OBJECT, "aput-object",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info APUT_BOOLEAN =
+        new Info(Opcodes.APUT_BOOLEAN, "aput-boolean",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info APUT_BYTE =
+        new Info(Opcodes.APUT_BYTE, "aput-byte",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info APUT_CHAR =
+        new Info(Opcodes.APUT_CHAR, "aput-char",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info APUT_SHORT =
+        new Info(Opcodes.APUT_SHORT, "aput-short",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info IGET =
+        new Info(Opcodes.IGET, "iget",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IGET_WIDE =
+        new Info(Opcodes.IGET_WIDE, "iget-wide",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IGET_OBJECT =
+        new Info(Opcodes.IGET_OBJECT, "iget-object",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IGET_BOOLEAN =
+        new Info(Opcodes.IGET_BOOLEAN, "iget-boolean",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IGET_BYTE =
+        new Info(Opcodes.IGET_BYTE, "iget-byte",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IGET_CHAR =
+        new Info(Opcodes.IGET_CHAR, "iget-char",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IGET_SHORT =
+        new Info(Opcodes.IGET_SHORT, "iget-short",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IPUT =
+        new Info(Opcodes.IPUT, "iput",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IPUT_WIDE =
+        new Info(Opcodes.IPUT_WIDE, "iput-wide",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IPUT_OBJECT =
+        new Info(Opcodes.IPUT_OBJECT, "iput-object",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IPUT_BOOLEAN =
+        new Info(Opcodes.IPUT_BOOLEAN, "iput-boolean",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IPUT_BYTE =
+        new Info(Opcodes.IPUT_BYTE, "iput-byte",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IPUT_CHAR =
+        new Info(Opcodes.IPUT_CHAR, "iput-char",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info IPUT_SHORT =
+        new Info(Opcodes.IPUT_SHORT, "iput-short",
+            InstructionCodec.FORMAT_22C, IndexType.FIELD_REF);
+
+    public static final Info SGET =
+        new Info(Opcodes.SGET, "sget",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SGET_WIDE =
+        new Info(Opcodes.SGET_WIDE, "sget-wide",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SGET_OBJECT =
+        new Info(Opcodes.SGET_OBJECT, "sget-object",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SGET_BOOLEAN =
+        new Info(Opcodes.SGET_BOOLEAN, "sget-boolean",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SGET_BYTE =
+        new Info(Opcodes.SGET_BYTE, "sget-byte",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SGET_CHAR =
+        new Info(Opcodes.SGET_CHAR, "sget-char",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SGET_SHORT =
+        new Info(Opcodes.SGET_SHORT, "sget-short",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SPUT =
+        new Info(Opcodes.SPUT, "sput",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SPUT_WIDE =
+        new Info(Opcodes.SPUT_WIDE, "sput-wide",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SPUT_OBJECT =
+        new Info(Opcodes.SPUT_OBJECT, "sput-object",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SPUT_BOOLEAN =
+        new Info(Opcodes.SPUT_BOOLEAN, "sput-boolean",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SPUT_BYTE =
+        new Info(Opcodes.SPUT_BYTE, "sput-byte",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SPUT_CHAR =
+        new Info(Opcodes.SPUT_CHAR, "sput-char",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info SPUT_SHORT =
+        new Info(Opcodes.SPUT_SHORT, "sput-short",
+            InstructionCodec.FORMAT_21C, IndexType.FIELD_REF);
+
+    public static final Info INVOKE_VIRTUAL =
+        new Info(Opcodes.INVOKE_VIRTUAL, "invoke-virtual",
+            InstructionCodec.FORMAT_35C, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_SUPER =
+        new Info(Opcodes.INVOKE_SUPER, "invoke-super",
+            InstructionCodec.FORMAT_35C, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_DIRECT =
+        new Info(Opcodes.INVOKE_DIRECT, "invoke-direct",
+            InstructionCodec.FORMAT_35C, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_STATIC =
+        new Info(Opcodes.INVOKE_STATIC, "invoke-static",
+            InstructionCodec.FORMAT_35C, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_INTERFACE =
+        new Info(Opcodes.INVOKE_INTERFACE, "invoke-interface",
+            InstructionCodec.FORMAT_35C, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_VIRTUAL_RANGE =
+        new Info(Opcodes.INVOKE_VIRTUAL_RANGE, "invoke-virtual/range",
+            InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_SUPER_RANGE =
+        new Info(Opcodes.INVOKE_SUPER_RANGE, "invoke-super/range",
+            InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_DIRECT_RANGE =
+        new Info(Opcodes.INVOKE_DIRECT_RANGE, "invoke-direct/range",
+            InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_STATIC_RANGE =
+        new Info(Opcodes.INVOKE_STATIC_RANGE, "invoke-static/range",
+            InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF);
+
+    public static final Info INVOKE_INTERFACE_RANGE =
+        new Info(Opcodes.INVOKE_INTERFACE_RANGE, "invoke-interface/range",
+            InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF);
+
+    public static final Info NEG_INT =
+        new Info(Opcodes.NEG_INT, "neg-int",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info NOT_INT =
+        new Info(Opcodes.NOT_INT, "not-int",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info NEG_LONG =
+        new Info(Opcodes.NEG_LONG, "neg-long",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info NOT_LONG =
+        new Info(Opcodes.NOT_LONG, "not-long",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info NEG_FLOAT =
+        new Info(Opcodes.NEG_FLOAT, "neg-float",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info NEG_DOUBLE =
+        new Info(Opcodes.NEG_DOUBLE, "neg-double",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info INT_TO_LONG =
+        new Info(Opcodes.INT_TO_LONG, "int-to-long",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info INT_TO_FLOAT =
+        new Info(Opcodes.INT_TO_FLOAT, "int-to-float",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info INT_TO_DOUBLE =
+        new Info(Opcodes.INT_TO_DOUBLE, "int-to-double",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info LONG_TO_INT =
+        new Info(Opcodes.LONG_TO_INT, "long-to-int",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info LONG_TO_FLOAT =
+        new Info(Opcodes.LONG_TO_FLOAT, "long-to-float",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info LONG_TO_DOUBLE =
+        new Info(Opcodes.LONG_TO_DOUBLE, "long-to-double",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info FLOAT_TO_INT =
+        new Info(Opcodes.FLOAT_TO_INT, "float-to-int",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info FLOAT_TO_LONG =
+        new Info(Opcodes.FLOAT_TO_LONG, "float-to-long",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info FLOAT_TO_DOUBLE =
+        new Info(Opcodes.FLOAT_TO_DOUBLE, "float-to-double",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info DOUBLE_TO_INT =
+        new Info(Opcodes.DOUBLE_TO_INT, "double-to-int",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info DOUBLE_TO_LONG =
+        new Info(Opcodes.DOUBLE_TO_LONG, "double-to-long",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info DOUBLE_TO_FLOAT =
+        new Info(Opcodes.DOUBLE_TO_FLOAT, "double-to-float",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info INT_TO_BYTE =
+        new Info(Opcodes.INT_TO_BYTE, "int-to-byte",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info INT_TO_CHAR =
+        new Info(Opcodes.INT_TO_CHAR, "int-to-char",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info INT_TO_SHORT =
+        new Info(Opcodes.INT_TO_SHORT, "int-to-short",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info ADD_INT =
+        new Info(Opcodes.ADD_INT, "add-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SUB_INT =
+        new Info(Opcodes.SUB_INT, "sub-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info MUL_INT =
+        new Info(Opcodes.MUL_INT, "mul-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info DIV_INT =
+        new Info(Opcodes.DIV_INT, "div-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info REM_INT =
+        new Info(Opcodes.REM_INT, "rem-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AND_INT =
+        new Info(Opcodes.AND_INT, "and-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info OR_INT =
+        new Info(Opcodes.OR_INT, "or-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info XOR_INT =
+        new Info(Opcodes.XOR_INT, "xor-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SHL_INT =
+        new Info(Opcodes.SHL_INT, "shl-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SHR_INT =
+        new Info(Opcodes.SHR_INT, "shr-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info USHR_INT =
+        new Info(Opcodes.USHR_INT, "ushr-int",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info ADD_LONG =
+        new Info(Opcodes.ADD_LONG, "add-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SUB_LONG =
+        new Info(Opcodes.SUB_LONG, "sub-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info MUL_LONG =
+        new Info(Opcodes.MUL_LONG, "mul-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info DIV_LONG =
+        new Info(Opcodes.DIV_LONG, "div-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info REM_LONG =
+        new Info(Opcodes.REM_LONG, "rem-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info AND_LONG =
+        new Info(Opcodes.AND_LONG, "and-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info OR_LONG =
+        new Info(Opcodes.OR_LONG, "or-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info XOR_LONG =
+        new Info(Opcodes.XOR_LONG, "xor-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SHL_LONG =
+        new Info(Opcodes.SHL_LONG, "shl-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SHR_LONG =
+        new Info(Opcodes.SHR_LONG, "shr-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info USHR_LONG =
+        new Info(Opcodes.USHR_LONG, "ushr-long",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info ADD_FLOAT =
+        new Info(Opcodes.ADD_FLOAT, "add-float",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SUB_FLOAT =
+        new Info(Opcodes.SUB_FLOAT, "sub-float",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info MUL_FLOAT =
+        new Info(Opcodes.MUL_FLOAT, "mul-float",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info DIV_FLOAT =
+        new Info(Opcodes.DIV_FLOAT, "div-float",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info REM_FLOAT =
+        new Info(Opcodes.REM_FLOAT, "rem-float",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info ADD_DOUBLE =
+        new Info(Opcodes.ADD_DOUBLE, "add-double",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info SUB_DOUBLE =
+        new Info(Opcodes.SUB_DOUBLE, "sub-double",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info MUL_DOUBLE =
+        new Info(Opcodes.MUL_DOUBLE, "mul-double",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info DIV_DOUBLE =
+        new Info(Opcodes.DIV_DOUBLE, "div-double",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info REM_DOUBLE =
+        new Info(Opcodes.REM_DOUBLE, "rem-double",
+            InstructionCodec.FORMAT_23X, IndexType.NONE);
+
+    public static final Info ADD_INT_2ADDR =
+        new Info(Opcodes.ADD_INT_2ADDR, "add-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SUB_INT_2ADDR =
+        new Info(Opcodes.SUB_INT_2ADDR, "sub-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info MUL_INT_2ADDR =
+        new Info(Opcodes.MUL_INT_2ADDR, "mul-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info DIV_INT_2ADDR =
+        new Info(Opcodes.DIV_INT_2ADDR, "div-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info REM_INT_2ADDR =
+        new Info(Opcodes.REM_INT_2ADDR, "rem-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info AND_INT_2ADDR =
+        new Info(Opcodes.AND_INT_2ADDR, "and-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info OR_INT_2ADDR =
+        new Info(Opcodes.OR_INT_2ADDR, "or-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info XOR_INT_2ADDR =
+        new Info(Opcodes.XOR_INT_2ADDR, "xor-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SHL_INT_2ADDR =
+        new Info(Opcodes.SHL_INT_2ADDR, "shl-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SHR_INT_2ADDR =
+        new Info(Opcodes.SHR_INT_2ADDR, "shr-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info USHR_INT_2ADDR =
+        new Info(Opcodes.USHR_INT_2ADDR, "ushr-int/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info ADD_LONG_2ADDR =
+        new Info(Opcodes.ADD_LONG_2ADDR, "add-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SUB_LONG_2ADDR =
+        new Info(Opcodes.SUB_LONG_2ADDR, "sub-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info MUL_LONG_2ADDR =
+        new Info(Opcodes.MUL_LONG_2ADDR, "mul-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info DIV_LONG_2ADDR =
+        new Info(Opcodes.DIV_LONG_2ADDR, "div-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info REM_LONG_2ADDR =
+        new Info(Opcodes.REM_LONG_2ADDR, "rem-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info AND_LONG_2ADDR =
+        new Info(Opcodes.AND_LONG_2ADDR, "and-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info OR_LONG_2ADDR =
+        new Info(Opcodes.OR_LONG_2ADDR, "or-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info XOR_LONG_2ADDR =
+        new Info(Opcodes.XOR_LONG_2ADDR, "xor-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SHL_LONG_2ADDR =
+        new Info(Opcodes.SHL_LONG_2ADDR, "shl-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SHR_LONG_2ADDR =
+        new Info(Opcodes.SHR_LONG_2ADDR, "shr-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info USHR_LONG_2ADDR =
+        new Info(Opcodes.USHR_LONG_2ADDR, "ushr-long/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info ADD_FLOAT_2ADDR =
+        new Info(Opcodes.ADD_FLOAT_2ADDR, "add-float/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SUB_FLOAT_2ADDR =
+        new Info(Opcodes.SUB_FLOAT_2ADDR, "sub-float/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info MUL_FLOAT_2ADDR =
+        new Info(Opcodes.MUL_FLOAT_2ADDR, "mul-float/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info DIV_FLOAT_2ADDR =
+        new Info(Opcodes.DIV_FLOAT_2ADDR, "div-float/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info REM_FLOAT_2ADDR =
+        new Info(Opcodes.REM_FLOAT_2ADDR, "rem-float/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info ADD_DOUBLE_2ADDR =
+        new Info(Opcodes.ADD_DOUBLE_2ADDR, "add-double/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info SUB_DOUBLE_2ADDR =
+        new Info(Opcodes.SUB_DOUBLE_2ADDR, "sub-double/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info MUL_DOUBLE_2ADDR =
+        new Info(Opcodes.MUL_DOUBLE_2ADDR, "mul-double/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info DIV_DOUBLE_2ADDR =
+        new Info(Opcodes.DIV_DOUBLE_2ADDR, "div-double/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info REM_DOUBLE_2ADDR =
+        new Info(Opcodes.REM_DOUBLE_2ADDR, "rem-double/2addr",
+            InstructionCodec.FORMAT_12X, IndexType.NONE);
+
+    public static final Info ADD_INT_LIT16 =
+        new Info(Opcodes.ADD_INT_LIT16, "add-int/lit16",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info RSUB_INT =
+        new Info(Opcodes.RSUB_INT, "rsub-int",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info MUL_INT_LIT16 =
+        new Info(Opcodes.MUL_INT_LIT16, "mul-int/lit16",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info DIV_INT_LIT16 =
+        new Info(Opcodes.DIV_INT_LIT16, "div-int/lit16",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info REM_INT_LIT16 =
+        new Info(Opcodes.REM_INT_LIT16, "rem-int/lit16",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info AND_INT_LIT16 =
+        new Info(Opcodes.AND_INT_LIT16, "and-int/lit16",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info OR_INT_LIT16 =
+        new Info(Opcodes.OR_INT_LIT16, "or-int/lit16",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info XOR_INT_LIT16 =
+        new Info(Opcodes.XOR_INT_LIT16, "xor-int/lit16",
+            InstructionCodec.FORMAT_22S, IndexType.NONE);
+
+    public static final Info ADD_INT_LIT8 =
+        new Info(Opcodes.ADD_INT_LIT8, "add-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info RSUB_INT_LIT8 =
+        new Info(Opcodes.RSUB_INT_LIT8, "rsub-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info MUL_INT_LIT8 =
+        new Info(Opcodes.MUL_INT_LIT8, "mul-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info DIV_INT_LIT8 =
+        new Info(Opcodes.DIV_INT_LIT8, "div-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info REM_INT_LIT8 =
+        new Info(Opcodes.REM_INT_LIT8, "rem-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info AND_INT_LIT8 =
+        new Info(Opcodes.AND_INT_LIT8, "and-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info OR_INT_LIT8 =
+        new Info(Opcodes.OR_INT_LIT8, "or-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info XOR_INT_LIT8 =
+        new Info(Opcodes.XOR_INT_LIT8, "xor-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info SHL_INT_LIT8 =
+        new Info(Opcodes.SHL_INT_LIT8, "shl-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info SHR_INT_LIT8 =
+        new Info(Opcodes.SHR_INT_LIT8, "shr-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    public static final Info USHR_INT_LIT8 =
+        new Info(Opcodes.USHR_INT_LIT8, "ushr-int/lit8",
+            InstructionCodec.FORMAT_22B, IndexType.NONE);
+
+    // END(opcode-info-defs)
+
+    // Static initialization.
+    static {
+        INFO = new Info[Opcodes.MAX_VALUE - Opcodes.MIN_VALUE + 1];
+
+        // TODO: Stop using this constant.
+        set(SPECIAL_FORMAT);
+
+        // TODO: These payload opcodes should be generated by opcode-gen.
+        set(PACKED_SWITCH_PAYLOAD);
+        set(SPARSE_SWITCH_PAYLOAD);
+        set(FILL_ARRAY_DATA_PAYLOAD);
+
+        // BEGIN(opcode-info-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(opcode-info-init)
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private OpcodeInfo() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the {@link @Info} for the given opcode value.
+     *
+     * @param opcode {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the
+     * opcode value
+     * @return non-null; the associated opcode information instance
+     */
+    public static Info get(int opcode) {
+        int idx = opcode - Opcodes.MIN_VALUE;
+
+        try {
+            Info result = INFO[idx];
+            if (result != null) {
+                return result;
+            }
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Fall through.
+        }
+
+        throw new IllegalArgumentException("bogus opcode: "
+                + Hex.u2or4(opcode));
+    }
+
+    /**
+     * Gets the name of the given opcode.
+     */
+    public static String getName(int opcode) {
+        return get(opcode).getName();
+    }
+
+    /**
+     * Gets the format (an {@link InstructionCodec}) for the given opcode
+     * value.
+     */
+    public static InstructionCodec getFormat(int opcode) {
+        return get(opcode).getFormat();
+    }
+
+    /**
+     * Gets the {@link IndexType} for the given opcode value.
+     */
+    public static IndexType getIndexType(int opcode) {
+        return get(opcode).getIndexType();
+    }
+
+    /**
+     * Puts the given opcode into the table of all ops.
+     *
+     * @param opcode non-null; the opcode
+     */
+    private static void set(Info opcode) {
+        int idx = opcode.getOpcode() - Opcodes.MIN_VALUE;
+        INFO[idx] = opcode;
+    }
+
+    /**
+     * Information about an opcode.
+     */
+    public static class Info {
+        private final int opcode;
+        private final String name;
+        private final InstructionCodec format;
+        private final IndexType indexType;
+
+        public Info(int opcode, String name, InstructionCodec format,
+                IndexType indexType) {
+            this.opcode = opcode;
+            this.name = name;
+            this.format = format;
+            this.indexType = indexType;
+        }
+
+        public int getOpcode() {
+            return opcode;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public InstructionCodec getFormat() {
+            return format;
+        }
+
+        public IndexType getIndexType() {
+            return indexType;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/io/Opcodes.java b/dx/src/com/android/dx/io/Opcodes.java
new file mode 100644
index 0000000..6dba49d
--- /dev/null
+++ b/dx/src/com/android/dx/io/Opcodes.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+/**
+ * All the Dalvik opcode value constants. See the related spec
+ * document for the meaning and instruction format of each opcode.
+ */
+public final class Opcodes {
+    /**
+     * pseudo-opcode used for nonstandard format payload "instructions". TODO:
+     * Retire this concept, and start treating the payload instructions
+     * more like the rest.
+     */
+    public static final int SPECIAL_FORMAT = -1;
+
+    /**
+     * pseudo-opcode used to indicate there is no next opcode; used
+     * in opcode chaining lists
+     */
+    public static final int NO_NEXT = -1;
+
+    /** minimum valid opcode value */
+    public static final int MIN_VALUE = -1;
+
+    /** maximum valid opcode value */
+    public static final int MAX_VALUE = 0xffff;
+
+    // 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 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 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 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;
+    // END(opcodes)
+
+    // TODO: Generate these payload opcodes with opcode-gen.
+
+    /**
+     * special pseudo-opcode value for packed-switch data payload
+     * instructions
+     */
+    public static final int PACKED_SWITCH_PAYLOAD = 0x100;
+
+    /** special pseudo-opcode value for packed-switch data payload
+     * instructions
+     */
+    public static final int SPARSE_SWITCH_PAYLOAD = 0x200;
+
+    /** special pseudo-opcode value for fill-array-data data payload
+     * instructions
+     */
+    public static final int FILL_ARRAY_DATA_PAYLOAD = 0x300;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Opcodes() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Determines if the given opcode has the right "shape" to be
+     * valid. This includes the range {@code 0x01..0xfe}, the range
+     * {@code 0x00ff..0xffff} where the low-order byte is either
+     * {@code 0} or {@code 0xff}, and the special opcode values {@code
+     * SPECIAL_FORMAT} and {@code NO_NEXT}. Note that not all of the
+     * opcode values that pass this test are in fact used. This method
+     * is meant to perform a quick check to reject blatantly wrong
+     * values (e.g. when validating arguments).
+     *
+     * @param opcode the opcode value
+     * @return {@code true} iff the value has the right "shape" to be
+     * possibly valid
+     */
+    public static boolean isValidShape(int opcode) {
+        /*
+         * Note: This method bakes in knowledge that all opcodes are
+         * one of the forms:
+         *
+         *   * single byte in range 0x01..0xfe -- normal opcodes
+         *   * (byteValue << 8) -- nop and data payload opcodes
+         *   * ((byteValue << 8) | 0xff) -- 16-bit extended opcodes
+         *   * SPECIAL_FORMAT or NO_NEXT -- pseudo-opcodes
+         */
+
+        // Note: SPECIAL_FORMAT == NO_NEXT.
+        if (opcode < SPECIAL_FORMAT) {
+            return false;
+        } else if (opcode == SPECIAL_FORMAT) {
+            return true;
+        }
+
+        int lowByte = opcode & 0xff;
+        if ((lowByte == 0) || (lowByte == 0xff)) {
+            return true;
+        }
+
+        return (opcode & 0xff00) == 0;
+    }
+
+    /**
+     * Gets whether ({@code true}) or not ({@code false}) the given
+     * opcode value is an "extended" opcode (not counting the nop-like
+     * payload opcodes). Extended opcodes require a full 16-bit code
+     * unit to represent, without leaving space for an argument byte.
+     * 
+     * @param opcode the opcode value
+     * @return {@code true} iff the opcode is an "extended" opcode
+     */
+    public static boolean isExtended(int opcode) {
+        /*
+         * Note: Extended opcodes all have the form ((byteValue << 8)
+         * | 0xff).
+         */
+        return (opcode >= 0x00ff);
+    }
+
+    /**
+     * Gets the opcode out of an opcode unit, the latter of which may also
+     * include one or more argument values.
+     *
+     * @param opcodeUnit the opcode-containing code unit
+     * @return the extracted opcode
+     */
+    public static int extractOpcodeFromUnit(int opcodeUnit) {
+        /*
+         * Note: This method bakes in knowledge that all opcodes are
+         * either single-byte or of the forms (byteValue << 8) or
+         * ((byteValue << 8) | 0xff).
+         */
+
+        int lowByte = opcodeUnit & 0xff;
+        return ((lowByte == 0) || (lowByte == 0xff)) ? opcodeUnit : lowByte;
+    }
+}
diff --git a/dx/src/com/android/dx/io/ProtoId.java b/dx/src/com/android/dx/io/ProtoId.java
new file mode 100644
index 0000000..98c0777
--- /dev/null
+++ b/dx/src/com/android/dx/io/ProtoId.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io;
+
+import com.android.dx.util.Unsigned;
+
+public final class ProtoId implements Comparable<ProtoId> {
+    private final DexBuffer buffer;
+    private final int shortyIndex;
+    private final int returnTypeIndex;
+    private final int parametersOffset;
+
+    public ProtoId(DexBuffer buffer, int shortyIndex, int returnTypeIndex, int parametersOffset) {
+        this.buffer = buffer;
+        this.shortyIndex = shortyIndex;
+        this.returnTypeIndex = returnTypeIndex;
+        this.parametersOffset = parametersOffset;
+    }
+
+    public int compareTo(ProtoId other) {
+        if (returnTypeIndex != other.returnTypeIndex) {
+            return Unsigned.compare(returnTypeIndex, other.returnTypeIndex);
+        }
+        return Unsigned.compare(parametersOffset, other.parametersOffset);
+    }
+
+    public int getShortyIndex() {
+        return shortyIndex;
+    }
+
+    public int getReturnTypeIndex() {
+        return returnTypeIndex;
+    }
+
+    public int getParametersOffset() {
+        return parametersOffset;
+    }
+
+    public void writeTo(DexBuffer.Section out) {
+        out.writeInt(shortyIndex);
+        out.writeInt(returnTypeIndex);
+        out.writeInt(parametersOffset);
+    }
+
+    @Override public String toString() {
+        if (buffer == null) {
+            return shortyIndex + " " + returnTypeIndex + " " + parametersOffset;
+        }
+
+        return buffer.strings().get(shortyIndex)
+                + ": " + buffer.typeNames().get(returnTypeIndex)
+                + " " + buffer.readTypeList(parametersOffset);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/AddressMap.java b/dx/src/com/android/dx/io/instructions/AddressMap.java
new file mode 100644
index 0000000..a8dbe0b
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/AddressMap.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import java.io.EOFException;
+import java.util.HashMap;
+
+/**
+ * Map from addresses to addresses, where addresses are all
+ * {@code int}s.
+ */
+public final class AddressMap {
+    /** underlying map. TODO: This might be too inefficient. */
+    private final HashMap<Integer,Integer> map;
+
+    /**
+     * Constructs an instance.
+     */
+    public AddressMap() {
+        map = new HashMap<Integer,Integer>();
+    }
+
+    /**
+     * Gets the value address corresponding to the given key address. Returns
+     * {@code -1} if there is no mapping.
+     */
+    public int get(int keyAddress) {
+        Integer value = map.get(keyAddress);
+        return (value == null) ? -1 : value;
+    }
+
+    /**
+     * Sets the value address associated with the given key address.
+     */
+    public void put(int keyAddress, int valueAddress) {
+        map.put(keyAddress, valueAddress);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/BaseCodeCursor.java b/dx/src/com/android/dx/io/instructions/BaseCodeCursor.java
new file mode 100644
index 0000000..6915fa8
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/BaseCodeCursor.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import java.io.EOFException;
+
+/**
+ * Base implementation of {@link CodeCursor}.
+ */
+public abstract class BaseCodeCursor implements CodeCursor {
+    /** base address map */
+    private final AddressMap baseAddressMap;
+
+    /** next index within {@link #array} to read from or write to */
+    private int cursor;
+
+    /**
+     * Constructs an instance.
+     */
+    public BaseCodeCursor() {
+        this.baseAddressMap = new AddressMap();
+        this.cursor = 0;
+    }
+
+    /** @inheritDoc */
+    public final int cursor() {
+        return cursor;
+    }
+
+    /** @inheritDoc */
+    public final int baseAddressForCursor() {
+        int mapped = baseAddressMap.get(cursor);
+        return (mapped >= 0) ? mapped : cursor;
+    }
+
+    /** @inheritDoc */
+    public final void setBaseAddress(int targetAddress, int baseAddress) {
+        baseAddressMap.put(targetAddress, baseAddress);
+    }
+
+    /**
+     * Advance the cursor by the indicated amount.
+     */
+    protected final void advance(int amount) {
+        cursor += amount;
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/CodeCursor.java b/dx/src/com/android/dx/io/instructions/CodeCursor.java
new file mode 100644
index 0000000..68eb9c9
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/CodeCursor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+/**
+ * Cursor over code units, for reading or writing out Dalvik bytecode.
+ */
+public interface CodeCursor {
+    /**
+     * Gets the cursor. The cursor is the offset in code units from
+     * the start of the input of the next code unit to be read or
+     * written, where the input generally consists of the code for a
+     * single method.
+     */
+    public int cursor();
+
+    /**
+     * Gets the base address associated with the current cursor. This
+     * differs from the cursor value when explicitly set (by {@link
+     * #setBaseAddress). This is used, in particular, to convey base
+     * addresses to switch data payload instructions, whose relative
+     * addresses are relative to the address of a dependant switch
+     * instruction.
+     */
+    public int baseAddressForCursor();
+
+    /**
+     * Sets the base address for the given target address to be as indicated.
+     *
+     * @see #baseAddressForCursor
+     */
+    public void setBaseAddress(int targetAddress, int baseAddress);
+}
diff --git a/dx/src/com/android/dx/io/instructions/CodeInput.java b/dx/src/com/android/dx/io/instructions/CodeInput.java
new file mode 100644
index 0000000..41a5ef7
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/CodeInput.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import java.io.EOFException;
+
+/**
+ * Input stream of code units, for reading in Dalvik bytecode.
+ */
+public interface CodeInput extends CodeCursor {
+    /**
+     * Returns whether there are any more code units to read. This
+     * is analogous to {@code hasNext()} on an interator.
+     */
+    public boolean hasMore();
+
+    /**
+     * Reads a code unit.
+     */
+    public int read() throws EOFException;
+
+    /**
+     * Reads two code units, treating them as a little-endian {@code int}.
+     */
+    public int readInt() throws EOFException;
+
+    /**
+     * Reads four code units, treating them as a little-endian {@code long}.
+     */
+    public long readLong() throws EOFException;
+}
diff --git a/dx/src/com/android/dx/io/instructions/CodeOutput.java b/dx/src/com/android/dx/io/instructions/CodeOutput.java
new file mode 100644
index 0000000..7d0077e
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/CodeOutput.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+/**
+ * Output stream of code units, for writing out Dalvik bytecode.
+ */
+public interface CodeOutput extends CodeCursor {
+    /**
+     * Writes a code unit.
+     */
+    public void write(short codeUnit);
+
+    /**
+     * Writes two code units.
+     */
+    public void write(short u0, short u1);
+
+    /**
+     * Writes three code units.
+     */
+    public void write(short u0, short u1, short u2);
+
+    /**
+     * Writes four code units.
+     */
+    public void write(short u0, short u1, short u2, short u3);
+
+    /**
+     * Writes five code units.
+     */
+    public void write(short u0, short u1, short u2, short u3, short u4);
+
+    /**
+     * Writes an {@code int}, little-endian.
+     */
+    public void writeInt(int value);
+
+    /**
+     * Writes a {@code long}, little-endian.
+     */
+    public void writeLong(long value);
+
+    /**
+     * Writes the contents of the given array.
+     */
+    public void write(byte[] data);
+
+    /**
+     * Writes the contents of the given array.
+     */
+    public void write(short[] data);
+
+    /**
+     * Writes the contents of the given array.
+     */
+    public void write(int[] data);
+
+    /**
+     * Writes the contents of the given array.
+     */
+    public void write(long[] data);
+}
diff --git a/dx/src/com/android/dx/io/instructions/DecodedInstruction.java b/dx/src/com/android/dx/io/instructions/DecodedInstruction.java
new file mode 100644
index 0000000..a8bc859
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/DecodedInstruction.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+import com.android.dx.io.OpcodeInfo;
+import com.android.dx.io.Opcodes;
+import com.android.dx.util.DexException;
+import com.android.dx.util.Hex;
+import java.io.EOFException;
+
+/**
+ * A decoded Dalvik instruction. This consists of a format codec, a
+ * numeric opcode, an optional index type, and any additional
+ * arguments of the instruction. The additional arguments (if any) are
+ * represented as uninterpreted data.
+ *
+ * <p><b>Note:</b> The names of the arguments are <i>not</i> meant to
+ * match the names given in the Dalvik instruction format
+ * specification, specification which just names fields (somewhat)
+ * arbitrarily alphabetically from A. In this class, non-register
+ * fields are given descriptive names and register fields are
+ * consistently named alphabetically.</p>
+ */
+public abstract class DecodedInstruction {
+    /** non-null; instruction format / codec */
+    private final InstructionCodec format;
+
+    /** opcode number */
+    private final int opcode;
+
+    /** constant index argument */
+    private final int index;
+
+    /** null-ok; index type */
+    private final IndexType indexType;
+
+    /**
+     * target address argument. This is an absolute address, not just
+     * a signed offset. <b>Note:</b> The address is unsigned, even
+     * though it is stored in an {@code int}.
+     */
+    private final int target;
+
+    /**
+     * literal value argument; also used for special verification error
+     * constants (format 20bc) as well as should-be-zero values
+     * (formats 10x, 20t, 30t, and 32x)
+     */
+    private final long literal;
+
+    /**
+     * Decodes an instruction from the given input source.
+     */
+    public static DecodedInstruction decode(CodeInput in) throws EOFException {
+        int opcodeUnit = in.read();
+        int opcode = Opcodes.extractOpcodeFromUnit(opcodeUnit);
+        InstructionCodec format = OpcodeInfo.getFormat(opcode);
+
+        return format.decode(opcodeUnit, in);
+    }
+
+    /**
+     * Decodes an array of instructions. The result has non-null
+     * elements at each offset that represents the start of an
+     * instruction.
+     */
+    public static DecodedInstruction[] decodeAll(short[] encodedInstructions) {
+        int size = encodedInstructions.length;
+        DecodedInstruction[] decoded = new DecodedInstruction[size];
+        ShortArrayCodeInput in = new ShortArrayCodeInput(encodedInstructions);
+
+        try {
+            while (in.hasMore()) {
+                decoded[in.cursor()] = DecodedInstruction.decode(in);
+            }
+        } catch (EOFException ex) {
+            throw new DexException(ex);
+        }
+
+        return decoded;
+    }
+
+    /**
+     * Constructs an instance.
+     */
+    public DecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal) {
+        if (format == null) {
+            throw new NullPointerException("format == null");
+        }
+
+        if (!Opcodes.isValidShape(opcode)) {
+            throw new IllegalArgumentException("invalid opcode");
+        }
+
+        this.format = format;
+        this.opcode = opcode;
+        this.index = index;
+        this.indexType = indexType;
+        this.target = target;
+        this.literal = literal;
+    }
+
+    public final InstructionCodec getFormat() {
+        return format;
+    }
+
+    public final int getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the opcode, as a code unit.
+     */
+    public final short getOpcodeUnit() {
+        return (short) opcode;
+    }
+
+    public final int getIndex() {
+        return index;
+    }
+
+    /**
+     * Gets the index, as a code unit.
+     */
+    public final short getIndexUnit() {
+        return (short) index;
+    }
+
+    public final IndexType getIndexType() {
+        return indexType;
+    }
+
+    /**
+     * Gets the raw target.
+     */
+    public final int getTarget() {
+        return target;
+    }
+
+    /**
+     * Gets the target as a relative offset from the given address.
+     */
+    public final int getTarget(int baseAddress) {
+        return target - baseAddress;
+    }
+
+    /**
+     * Gets the target as a relative offset from the given base
+     * address, as a code unit. This will throw if the value is out of
+     * the range of a signed code unit.
+     */
+    public final short getTargetUnit(int baseAddress) {
+        int relativeTarget = getTarget(baseAddress);
+
+        if (relativeTarget != (short) relativeTarget) {
+            throw new DexException("Target out of range: "
+                    + Hex.s4(relativeTarget));
+        }
+
+        return (short) relativeTarget;
+    }
+
+    /**
+     * Gets the target as a relative offset from the given base
+     * address, masked to be a byte in size. This will throw if the
+     * value is out of the range of a signed byte.
+     */
+    public final int getTargetByte(int baseAddress) {
+        int relativeTarget = getTarget(baseAddress);
+
+        if (relativeTarget != (byte) relativeTarget) {
+            throw new DexException("Target out of range: "
+                    + Hex.s4(relativeTarget));
+        }
+
+        return relativeTarget & 0xff;
+    }
+
+    public final long getLiteral() {
+        return literal;
+    }
+
+    /**
+     * Gets the literal value, masked to be an int in size. This will
+     * throw if the value is out of the range of a signed int.
+     */
+    public final int getLiteralInt() {
+        if (literal != (int) literal) {
+            throw new DexException("Literal out of range: " + Hex.u8(literal));
+        }
+
+        return (int) literal;
+    }
+
+    /**
+     * Gets the literal value, as a code unit. This will throw if the
+     * value is out of the range of a signed code unit.
+     */
+    public final short getLiteralUnit() {
+        if (literal != (short) literal) {
+            throw new DexException("Literal out of range: " + Hex.u8(literal));
+        }
+
+        return (short) literal;
+    }
+
+    /**
+     * Gets the literal value, masked to be a byte in size. This will
+     * throw if the value is out of the range of a signed byte.
+     */
+    public final int getLiteralByte() {
+        if (literal != (byte) literal) {
+            throw new DexException("Literal out of range: " + Hex.u8(literal));
+        }
+
+        return (int) literal & 0xff;
+    }
+
+    /**
+     * Gets the literal value, masked to be a nibble in size. This
+     * will throw if the value is out of the range of a signed nibble.
+     */
+    public final int getLiteralNibble() {
+        if ((literal < -8) || (literal > 7)) {
+            throw new DexException("Literal out of range: " + Hex.u8(literal));
+        }
+
+        return (int) literal & 0xf;
+    }
+
+    public abstract int getRegisterCount();
+
+    public int getA() {
+        return 0;
+    }
+
+    public int getB() {
+        return 0;
+    }
+
+    public int getC() {
+        return 0;
+    }
+
+    public int getD() {
+        return 0;
+    }
+
+    public int getE() {
+        return 0;
+    }
+
+    /**
+     * Gets the register count, as a code unit. This will throw if the
+     * value is out of the range of an unsigned code unit.
+     */
+    public final short getRegisterCountUnit() {
+        int registerCount = getRegisterCount();
+
+        if ((registerCount & ~0xffff) != 0) {
+            throw new DexException("Register count out of range: "
+                    + Hex.u8(registerCount));
+        }
+
+        return (short) registerCount;
+    }
+
+    /**
+     * Gets the A register number, as a code unit. This will throw if the
+     * value is out of the range of an unsigned code unit.
+     */
+    public final short getAUnit() {
+        int a = getA();
+
+        if ((a & ~0xffff) != 0) {
+            throw new DexException("Register A out of range: " + Hex.u8(a));
+        }
+
+        return (short) a;
+    }
+
+    /**
+     * Gets the A register number, as a byte. This will throw if the
+     * value is out of the range of an unsigned byte.
+     */
+    public final short getAByte() {
+        int a = getA();
+
+        if ((a & ~0xff) != 0) {
+            throw new DexException("Register A out of range: " + Hex.u8(a));
+        }
+
+        return (short) a;
+    }
+
+    /**
+     * Gets the A register number, as a nibble. This will throw if the
+     * value is out of the range of an unsigned nibble.
+     */
+    public final short getANibble() {
+        int a = getA();
+
+        if ((a & ~0xf) != 0) {
+            throw new DexException("Register A out of range: " + Hex.u8(a));
+        }
+
+        return (short) a;
+    }
+
+    /**
+     * Gets the B register number, as a code unit. This will throw if the
+     * value is out of the range of an unsigned code unit.
+     */
+    public final short getBUnit() {
+        int b = getB();
+
+        if ((b & ~0xffff) != 0) {
+            throw new DexException("Register B out of range: " + Hex.u8(b));
+        }
+
+        return (short) b;
+    }
+
+    /**
+     * Gets the B register number, as a byte. This will throw if the
+     * value is out of the range of an unsigned byte.
+     */
+    public final short getBByte() {
+        int b = getB();
+
+        if ((b & ~0xff) != 0) {
+            throw new DexException("Register B out of range: " + Hex.u8(b));
+        }
+
+        return (short) b;
+    }
+
+    /**
+     * Gets the B register number, as a nibble. This will throw if the
+     * value is out of the range of an unsigned nibble.
+     */
+    public final short getBNibble() {
+        int b = getB();
+
+        if ((b & ~0xf) != 0) {
+            throw new DexException("Register B out of range: " + Hex.u8(b));
+        }
+
+        return (short) b;
+    }
+
+    /**
+     * Gets the C register number, as a code unit. This will throw if the
+     * value is out of the range of an unsigned code unit.
+     */
+    public final short getCUnit() {
+        int c = getC();
+
+        if ((c & ~0xffff) != 0) {
+            throw new DexException("Register C out of range: " + Hex.u8(c));
+        }
+
+        return (short) c;
+    }
+
+    /**
+     * Gets the C register number, as a byte. This will throw if the
+     * value is out of the range of an unsigned byte.
+     */
+    public final short getCByte() {
+        int c = getC();
+
+        if ((c & ~0xff) != 0) {
+            throw new DexException("Register C out of range: " + Hex.u8(c));
+        }
+
+        return (short) c;
+    }
+
+    /**
+     * Gets the C register number, as a nibble. This will throw if the
+     * value is out of the range of an unsigned nibble.
+     */
+    public final short getCNibble() {
+        int c = getC();
+
+        if ((c & ~0xf) != 0) {
+            throw new DexException("Register C out of range: " + Hex.u8(c));
+        }
+
+        return (short) c;
+    }
+
+    /**
+     * Gets the D register number, as a code unit. This will throw if the
+     * value is out of the range of an unsigned code unit.
+     */
+    public final short getDUnit() {
+        int d = getD();
+
+        if ((d & ~0xffff) != 0) {
+            throw new DexException("Register D out of range: " + Hex.u8(d));
+        }
+
+        return (short) d;
+    }
+
+    /**
+     * Gets the D register number, as a byte. This will throw if the
+     * value is out of the range of an unsigned byte.
+     */
+    public final short getDByte() {
+        int d = getD();
+
+        if ((d & ~0xff) != 0) {
+            throw new DexException("Register D out of range: " + Hex.u8(d));
+        }
+
+        return (short) d;
+    }
+
+    /**
+     * Gets the D register number, as a nibble. This will throw if the
+     * value is out of the range of an unsigned nibble.
+     */
+    public final short getDNibble() {
+        int d = getD();
+
+        if ((d & ~0xf) != 0) {
+            throw new DexException("Register D out of range: " + Hex.u8(d));
+        }
+
+        return (short) d;
+    }
+
+    /**
+     * Gets the E register number, as a nibble. This will throw if the
+     * value is out of the range of an unsigned nibble.
+     */
+    public final short getENibble() {
+        int e = getE();
+
+        if ((e & ~0xf) != 0) {
+            throw new DexException("Register E out of range: " + Hex.u8(e));
+        }
+
+        return (short) e;
+    }
+
+    /**
+     * Encodes this instance to the given output.
+     */
+    public final void encode(CodeOutput out) {
+        format.encode(this, out);
+    }
+
+    /**
+     * Returns an instance just like this one, except with the index replaced
+     * with the given one.
+     */
+    public abstract DecodedInstruction withIndex(int newIndex);
+}
diff --git a/dx/src/com/android/dx/io/instructions/FillArrayDataPayloadDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/FillArrayDataPayloadDecodedInstruction.java
new file mode 100644
index 0000000..64fc55b
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/FillArrayDataPayloadDecodedInstruction.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+/**
+ * A decoded Dalvik instruction which contains the payload for
+ * a {@code packed-switch} instruction.
+ */
+public final class FillArrayDataPayloadDecodedInstruction
+        extends DecodedInstruction {
+    /** data array */
+    private final Object data;
+
+    /** number of elements */
+    private final int size;
+
+    /** element width */
+    private final int elementWidth;
+
+    /**
+     * Constructs an instance. This private instance doesn't check the
+     * type of the data array.
+     */
+    private FillArrayDataPayloadDecodedInstruction(InstructionCodec format,
+            int opcode, Object data, int size, int elementWidth) {
+        super(format, opcode, 0, null, 0, 0L);
+
+        this.data = data;
+        this.size = size;
+        this.elementWidth = elementWidth;
+    }
+
+    /**
+     * Constructs an instance.
+     */
+    public FillArrayDataPayloadDecodedInstruction(InstructionCodec format,
+            int opcode, byte[] data) {
+        this(format, opcode, data, data.length, 1);
+    }
+
+    /**
+     * Constructs an instance.
+     */
+    public FillArrayDataPayloadDecodedInstruction(InstructionCodec format,
+            int opcode, short[] data) {
+        this(format, opcode, data, data.length, 2);
+    }
+
+    /**
+     * Constructs an instance.
+     */
+    public FillArrayDataPayloadDecodedInstruction(InstructionCodec format,
+            int opcode, int[] data) {
+        this(format, opcode, data, data.length, 4);
+    }
+
+    /**
+     * Constructs an instance.
+     */
+    public FillArrayDataPayloadDecodedInstruction(InstructionCodec format,
+            int opcode, long[] data) {
+        this(format, opcode, data, data.length, 8);
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 0;
+    }
+
+    public short getElementWidthUnit() {
+        return (short) elementWidth;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        throw new UnsupportedOperationException("no index in instruction");
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/FiveRegisterDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/FiveRegisterDecodedInstruction.java
new file mode 100644
index 0000000..6e14d34
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/FiveRegisterDecodedInstruction.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+
+/**
+ * A decoded Dalvik instruction which has five register arguments.
+ */
+public final class FiveRegisterDecodedInstruction extends DecodedInstruction {
+    /** register argument "A" */
+    private final int a;
+
+    /** register argument "B" */
+    private final int b;
+
+    /** register argument "C" */
+    private final int c;
+
+    /** register argument "D" */
+    private final int d;
+
+    /** register argument "E" */
+    private final int e;
+
+    /**
+     * Constructs an instance.
+     */
+    public FiveRegisterDecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal,
+            int a, int b, int c, int d, int e) {
+        super(format, opcode, index, indexType, target, literal);
+
+        this.a = a;
+        this.b = b;
+        this.c = c;
+        this.d = d;
+        this.e = e;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 5;
+    }
+
+    /** @inheritDoc */
+    public int getA() {
+        return a;
+    }
+
+    /** @inheritDoc */
+    public int getB() {
+        return b;
+    }
+
+    /** @inheritDoc */
+    public int getC() {
+        return c;
+    }
+
+    /** @inheritDoc */
+    public int getD() {
+        return d;
+    }
+
+    /** @inheritDoc */
+    public int getE() {
+        return e;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        return new FiveRegisterDecodedInstruction(
+                getFormat(), getOpcode(), newIndex, getIndexType(),
+                getTarget(), getLiteral(), a, b, c, d, e);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/FourRegisterDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/FourRegisterDecodedInstruction.java
new file mode 100644
index 0000000..29836d0
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/FourRegisterDecodedInstruction.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+
+/**
+ * A decoded Dalvik instruction which has five register arguments.
+ */
+public final class FourRegisterDecodedInstruction extends DecodedInstruction {
+    /** register argument "A" */
+    private final int a;
+
+    /** register argument "B" */
+    private final int b;
+
+    /** register argument "C" */
+    private final int c;
+
+    /** register argument "D" */
+    private final int d;
+
+    /**
+     * Constructs an instance.
+     */
+    public FourRegisterDecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal,
+            int a, int b, int c, int d) {
+        super(format, opcode, index, indexType, target, literal);
+
+        this.a = a;
+        this.b = b;
+        this.c = c;
+        this.d = d;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 4;
+    }
+
+    /** @inheritDoc */
+    public int getA() {
+        return a;
+    }
+
+    /** @inheritDoc */
+    public int getB() {
+        return b;
+    }
+
+    /** @inheritDoc */
+    public int getC() {
+        return c;
+    }
+
+    /** @inheritDoc */
+    public int getD() {
+        return d;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        return new FourRegisterDecodedInstruction(
+                getFormat(), getOpcode(), newIndex, getIndexType(),
+                getTarget(), getLiteral(), a, b, c, d);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/InstructionCodec.java b/dx/src/com/android/dx/io/instructions/InstructionCodec.java
new file mode 100644
index 0000000..2e803ed
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/InstructionCodec.java
@@ -0,0 +1,966 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+import com.android.dx.io.OpcodeInfo;
+import com.android.dx.io.Opcodes;
+import com.android.dx.util.DexException;
+import com.android.dx.util.Hex;
+
+import java.io.EOFException;
+
+/**
+ * Representation of an instruction format, which knows how to decode into
+ * and encode from instances of {@link DecodedInstruction}.
+ */
+public enum InstructionCodec {
+    FORMAT_00X() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            return new ZeroRegisterDecodedInstruction(
+                    this, opcodeUnit, 0, null,
+                    0, 0L);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(insn.getOpcodeUnit());
+        }
+    },
+
+    FORMAT_10X() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int literal = byte1(opcodeUnit); // should be zero
+            return new ZeroRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(insn.getOpcodeUnit());
+        }
+    },
+
+    FORMAT_12X() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = nibble2(opcodeUnit);
+            int b = nibble3(opcodeUnit);
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, 0L,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcodeUnit(),
+                             makeByte(insn.getA(), insn.getB())));
+        }
+    },
+
+    FORMAT_11N() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = nibble2(opcodeUnit);
+            int literal = (nibble3(opcodeUnit) << 28) >> 28; // sign-extend
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcodeUnit(),
+                             makeByte(insn.getA(), insn.getLiteralNibble())));
+        }
+    },
+
+    FORMAT_11X() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, 0L,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(codeUnit(insn.getOpcode(), insn.getA()));
+        }
+    },
+
+    FORMAT_10T() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.cursor() - 1;
+            int opcode = byte0(opcodeUnit);
+            int target = (byte) byte1(opcodeUnit); // sign-extend
+            return new ZeroRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    baseAddress + target, 0L);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            int relativeTarget = insn.getTargetByte(out.cursor());
+            out.write(codeUnit(insn.getOpcode(), relativeTarget));
+        }
+    },
+
+    FORMAT_20T() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.cursor() - 1;
+            int opcode = byte0(opcodeUnit);
+            int literal = byte1(opcodeUnit); // should be zero
+            int target = (short) in.read(); // sign-extend
+            return new ZeroRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    baseAddress + target, literal);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            short relativeTarget = insn.getTargetUnit(out.cursor());
+            out.write(insn.getOpcodeUnit(), relativeTarget);
+        }
+    },
+
+    FORMAT_20BC() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            // Note: We use the literal field to hold the decoded AA value.
+            int opcode = byte0(opcodeUnit);
+            int literal = byte1(opcodeUnit);
+            int index = in.read();
+            return new ZeroRegisterDecodedInstruction(
+                    this, opcode, index, IndexType.VARIES,
+                    0, literal);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getLiteralByte()),
+                    insn.getIndexUnit());
+        }
+    },
+
+    FORMAT_22X() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int b = in.read();
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, 0L,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    insn.getBUnit());
+        }
+    },
+
+    FORMAT_21T() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.cursor() - 1;
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int target = (short) in.read(); // sign-extend
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    baseAddress + target, 0L,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            short relativeTarget = insn.getTargetUnit(out.cursor());
+            out.write(codeUnit(insn.getOpcode(), insn.getA()), relativeTarget);
+        }
+    },
+
+    FORMAT_21S() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int literal = (short) in.read(); // sign-extend
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    insn.getLiteralUnit());
+        }
+    },
+
+    FORMAT_21H() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            long literal = (short) in.read(); // sign-extend
+
+            /*
+             * Format 21h decodes differently depending on the opcode,
+             * because the "signed hat" might represent either a 32-
+             * or 64- bit value.
+             */
+            literal <<= (opcode == Opcodes.CONST_HIGH16) ? 16 : 48;
+
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            // See above.
+            int opcode = insn.getOpcode();
+            int shift = (opcode == Opcodes.CONST_HIGH16) ? 16 : 48;
+            short literal = (short) (insn.getLiteral() >> shift);
+
+            out.write(codeUnit(opcode, insn.getA()), literal);
+        }
+    },
+
+    FORMAT_21C() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int index = in.read();
+            IndexType indexType = OpcodeInfo.getIndexType(opcode);
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, index, indexType,
+                    0, 0L,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    insn.getIndexUnit());
+        }
+    },
+
+    FORMAT_23X() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int bc = in.read();
+            int b = byte0(bc);
+            int c = byte1(bc);
+            return new ThreeRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, 0L,
+                    a, b, c);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    codeUnit(insn.getB(), insn.getC()));
+        }
+    },
+
+    FORMAT_22B() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int bc = in.read();
+            int b = byte0(bc);
+            int literal = (byte) byte1(bc); // sign-extend
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    codeUnit(insn.getB(),
+                             insn.getLiteralByte()));
+        }
+    },
+
+    FORMAT_22T() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.cursor() - 1;
+            int opcode = byte0(opcodeUnit);
+            int a = nibble2(opcodeUnit);
+            int b = nibble3(opcodeUnit);
+            int target = (short) in.read(); // sign-extend
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    baseAddress + target, 0L,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            short relativeTarget = insn.getTargetUnit(out.cursor());
+            out.write(
+                    codeUnit(insn.getOpcode(),
+                             makeByte(insn.getA(), insn.getB())),
+                    relativeTarget);
+        }
+    },
+
+    FORMAT_22S() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = nibble2(opcodeUnit);
+            int b = nibble3(opcodeUnit);
+            int literal = (short) in.read(); // sign-extend
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(),
+                             makeByte(insn.getA(), insn.getB())),
+                    insn.getLiteralUnit());
+        }
+    },
+
+    FORMAT_22C() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = nibble2(opcodeUnit);
+            int b = nibble3(opcodeUnit);
+            int index = in.read();
+            IndexType indexType = OpcodeInfo.getIndexType(opcode);
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, index, indexType,
+                    0, 0L,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(),
+                             makeByte(insn.getA(), insn.getB())),
+                    insn.getIndexUnit());
+        }
+    },
+
+    FORMAT_22CS() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = nibble2(opcodeUnit);
+            int b = nibble3(opcodeUnit);
+            int index = in.read();
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, index, IndexType.FIELD_OFFSET,
+                    0, 0L,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(
+                    codeUnit(insn.getOpcode(),
+                             makeByte(insn.getA(), insn.getB())),
+                    insn.getIndexUnit());
+        }
+    },
+
+    FORMAT_30T() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.cursor() - 1;
+            int opcode = byte0(opcodeUnit);
+            int literal = byte1(opcodeUnit); // should be zero
+            int target = in.readInt();
+            return new ZeroRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    baseAddress + target, literal);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            int relativeTarget = insn.getTarget(out.cursor());
+            out.write(insn.getOpcodeUnit(),
+                    unit0(relativeTarget), unit1(relativeTarget));
+        }
+    },
+
+    FORMAT_32X() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int literal = byte1(opcodeUnit); // should be zero
+            int a = in.read();
+            int b = in.read();
+            return new TwoRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a, b);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(insn.getOpcodeUnit(), insn.getAUnit(), insn.getBUnit());
+        }
+    },
+
+    FORMAT_31I() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int literal = in.readInt();
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            int literal = insn.getLiteralInt();
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    unit0(literal),
+                    unit1(literal));
+        }
+    },
+
+    FORMAT_31T() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.cursor() - 1;
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int target = baseAddress + in.readInt();
+
+            /*
+             * Switch instructions need to "forward" their addresses to their
+             * payload target instructions.
+             */
+            switch (opcode) {
+                case Opcodes.PACKED_SWITCH:
+                case Opcodes.SPARSE_SWITCH: {
+                    in.setBaseAddress(target, baseAddress);
+                    break;
+                }
+            }
+
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    target, 0L,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            int relativeTarget = insn.getTarget(out.cursor());
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    unit0(relativeTarget), unit1(relativeTarget));
+        }
+    },
+
+    FORMAT_31C() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            int index = in.readInt();
+            IndexType indexType = OpcodeInfo.getIndexType(opcode);
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, index, indexType,
+                    0, 0L,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            int index = insn.getIndex();
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    unit0(index),
+                    unit1(index));
+        }
+    },
+
+    FORMAT_35C() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            return decodeRegisterList(this, opcodeUnit, in);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            encodeRegisterList(insn, out);
+        }
+    },
+
+    FORMAT_35MS() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            return decodeRegisterList(this, opcodeUnit, in);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            encodeRegisterList(insn, out);
+        }
+    },
+
+    FORMAT_35MI() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            return decodeRegisterList(this, opcodeUnit, in);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            encodeRegisterList(insn, out);
+        }
+    },
+
+    FORMAT_3RC() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            return decodeRegisterRange(this, opcodeUnit, in);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            encodeRegisterRange(insn, out);
+        }
+    },
+
+    FORMAT_3RMS() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            return decodeRegisterRange(this, opcodeUnit, in);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            encodeRegisterRange(insn, out);
+        }
+    },
+
+    FORMAT_3RMI() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            return decodeRegisterRange(this, opcodeUnit, in);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            encodeRegisterRange(insn, out);
+        }
+    },
+
+    FORMAT_51L() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int opcode = byte0(opcodeUnit);
+            int a = byte1(opcodeUnit);
+            long literal = in.readLong();
+            return new OneRegisterDecodedInstruction(
+                    this, opcode, 0, null,
+                    0, literal,
+                    a);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            long literal = insn.getLiteral();
+            out.write(
+                    codeUnit(insn.getOpcode(), insn.getA()),
+                    unit0(literal),
+                    unit1(literal),
+                    unit2(literal),
+                    unit3(literal));
+        }
+    },
+
+    FORMAT_PACKED_SWITCH_PAYLOAD() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
+            int size = in.read();
+            int firstKey = in.readInt();
+            int[] targets = new int[size];
+
+            for (int i = 0; i < size; i++) {
+                targets[i] = baseAddress + in.readInt();
+            }
+
+            return new PackedSwitchPayloadDecodedInstruction(
+                    this, opcodeUnit, firstKey, targets);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            PackedSwitchPayloadDecodedInstruction payload =
+                (PackedSwitchPayloadDecodedInstruction) insn;
+            int[] targets = payload.getTargets();
+            int baseAddress = out.baseAddressForCursor();
+
+            out.write(payload.getOpcodeUnit());
+            out.write(asUnsignedUnit(targets.length));
+            out.writeInt(payload.getFirstKey());
+
+            for (int target : targets) {
+                out.writeInt(target - baseAddress);
+            }
+        }
+    },
+
+    FORMAT_SPARSE_SWITCH_PAYLOAD() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
+            int size = in.read();
+            int[] keys = new int[size];
+            int[] targets = new int[size];
+
+            for (int i = 0; i < size; i++) {
+                keys[i] = in.readInt();
+            }
+
+            for (int i = 0; i < size; i++) {
+                targets[i] = baseAddress + in.readInt();
+            }
+
+            return new SparseSwitchPayloadDecodedInstruction(
+                    this, opcodeUnit, keys, targets);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            SparseSwitchPayloadDecodedInstruction payload =
+                (SparseSwitchPayloadDecodedInstruction) insn;
+            int[] keys = payload.getKeys();
+            int[] targets = payload.getTargets();
+            int baseAddress = out.baseAddressForCursor();
+
+            out.write(payload.getOpcodeUnit());
+            out.write(asUnsignedUnit(targets.length));
+
+            for (int key : keys) {
+                out.writeInt(key);
+            }
+
+            for (int target : targets) {
+                out.writeInt(target - baseAddress);
+            }
+        }
+    },
+
+    FORMAT_FILL_ARRAY_DATA_PAYLOAD() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int elementWidth = in.read();
+            int size = in.readInt();
+
+            switch (elementWidth) {
+                case 1: {
+                    byte[] array = new byte[size];
+                    boolean even = true;
+                    for (int i = 0, value = 0; i < size; i++, even = !even) {
+                        if (even) {
+                            value = in.read();
+                        }
+                        array[i] = (byte) (value & 0xff);
+                        value >>= 8;
+                    }
+                    return new FillArrayDataPayloadDecodedInstruction(
+                            this, opcodeUnit, array);
+                }
+                case 2: {
+                    short[] array = new short[size];
+                    for (int i = 0; i < size; i++) {
+                        array[i] = (short) in.read();
+                    }
+                    return new FillArrayDataPayloadDecodedInstruction(
+                            this, opcodeUnit, array);
+                }
+                case 4: {
+                    int[] array = new int[size];
+                    for (int i = 0; i < size; i++) {
+                        array[i] = in.readInt();
+                    }
+                    return new FillArrayDataPayloadDecodedInstruction(
+                            this, opcodeUnit, array);
+                }
+                case 8: {
+                    long[] array = new long[size];
+                    for (int i = 0; i < size; i++) {
+                        array[i] = in.readLong();
+                    }
+                    return new FillArrayDataPayloadDecodedInstruction(
+                            this, opcodeUnit, array);
+                }
+            }
+
+            throw new DexException("bogus element_width: "
+                    + Hex.u2(elementWidth));
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            FillArrayDataPayloadDecodedInstruction payload =
+                (FillArrayDataPayloadDecodedInstruction) insn;
+            short elementWidth = payload.getElementWidthUnit();
+            Object data = payload.getData();
+
+            out.write(payload.getOpcodeUnit());
+            out.write(elementWidth);
+            out.writeInt(payload.getSize());
+
+            switch (elementWidth) {
+                case 1: out.write((byte[]) data);  break;
+                case 2: out.write((short[]) data); break;
+                case 4: out.write((int[]) data);   break;
+                case 8: out.write((long[]) data);  break;
+                default: {
+                    throw new DexException("bogus element_width: "
+                            + Hex.u2(elementWidth));
+                }
+            }
+        }
+    };
+
+    /**
+     * Decodes an instruction specified by the given opcode unit, reading
+     * any required additional code units from the given input source.
+     */
+    public abstract DecodedInstruction decode(int opcodeUnit, CodeInput in)
+        throws EOFException;
+
+    /**
+     * Encodes the given instruction.
+     */
+    public abstract void encode(DecodedInstruction insn, CodeOutput out);
+
+    /**
+     * Helper method that decodes any of the register-list formats.
+     */
+    private static DecodedInstruction decodeRegisterList(
+            InstructionCodec format, int opcodeUnit, CodeInput in)
+            throws EOFException {
+        int opcode = byte0(opcodeUnit);
+        int e = nibble2(opcodeUnit);
+        int registerCount = nibble3(opcodeUnit);
+        int index = in.read();
+        int abcd = in.read();
+        int a = nibble0(abcd);
+        int b = nibble1(abcd);
+        int c = nibble2(abcd);
+        int d = nibble3(abcd);
+        IndexType indexType = OpcodeInfo.getIndexType(opcode);
+
+        // TODO: Having to switch like this is less than ideal.
+        switch (registerCount) {
+            case 0:
+                return new ZeroRegisterDecodedInstruction(
+                        format, opcode, index, indexType,
+                        0, 0L);
+            case 1:
+                return new OneRegisterDecodedInstruction(
+                        format, opcode, index, indexType,
+                        0, 0L,
+                        a);
+            case 2:
+                return new TwoRegisterDecodedInstruction(
+                        format, opcode, index, indexType,
+                        0, 0L,
+                        a, b);
+            case 3:
+                return new ThreeRegisterDecodedInstruction(
+                        format, opcode, index, indexType,
+                        0, 0L,
+                        a, b, c);
+            case 4:
+                return new FourRegisterDecodedInstruction(
+                        format, opcode, index, indexType,
+                        0, 0L,
+                        a, b, c, d);
+            case 5:
+                return new FiveRegisterDecodedInstruction(
+                        format, opcode, index, indexType,
+                        0, 0L,
+                        a, b, c, d, e);
+        }
+
+        throw new DexException("bogus registerCount: "
+                + Hex.uNibble(registerCount));
+    }
+
+    /**
+     * Helper method that encodes any of the register-list formats.
+     */
+    private static void encodeRegisterList(DecodedInstruction insn,
+            CodeOutput out) {
+        out.write(codeUnit(insn.getOpcode(),
+                        makeByte(insn.getE(), insn.getRegisterCount())),
+                insn.getIndexUnit(),
+                codeUnit(insn.getA(), insn.getB(), insn.getC(), insn.getD()));
+    }
+
+    /**
+     * Helper method that decodes any of the three-unit register-range formats.
+     */
+    private static DecodedInstruction decodeRegisterRange(
+            InstructionCodec format, int opcodeUnit, CodeInput in)
+            throws EOFException {
+        int opcode = byte0(opcodeUnit);
+        int registerCount = byte1(opcodeUnit);
+        int index = in.read();
+        int a = in.read();
+        IndexType indexType = OpcodeInfo.getIndexType(opcode);
+        return new RegisterRangeDecodedInstruction(
+                format, opcode, index, indexType,
+                0, 0L,
+                a, registerCount);
+    }
+
+    /**
+     * Helper method that encodes any of the three-unit register-range formats.
+     */
+    private static void encodeRegisterRange(DecodedInstruction insn,
+            CodeOutput out) {
+        out.write(codeUnit(insn.getOpcode(), insn.getRegisterCount()),
+                insn.getIndexUnit(),
+                insn.getAUnit());
+    }
+
+    private static short codeUnit(int lowByte, int highByte) {
+        if ((lowByte & ~0xff) != 0) {
+            throw new IllegalArgumentException("bogus lowByte");
+        }
+
+        if ((highByte & ~0xff) != 0) {
+            throw new IllegalArgumentException("bogus highByte");
+        }
+
+        return (short) (lowByte | (highByte << 8));
+    }
+
+    private static short codeUnit(int nibble0, int nibble1, int nibble2,
+            int nibble3) {
+        if ((nibble0 & ~0xf) != 0) {
+            throw new IllegalArgumentException("bogus nibble0");
+        }
+
+        if ((nibble1 & ~0xf) != 0) {
+            throw new IllegalArgumentException("bogus nibble1");
+        }
+
+        if ((nibble2 & ~0xf) != 0) {
+            throw new IllegalArgumentException("bogus nibble2");
+        }
+
+        if ((nibble3 & ~0xf) != 0) {
+            throw new IllegalArgumentException("bogus nibble3");
+        }
+
+        return (short) (nibble0 | (nibble1 << 4)
+                | (nibble2 << 8) | (nibble3 << 12));
+    }
+
+    private static int makeByte(int lowNibble, int highNibble) {
+        if ((lowNibble & ~0xf) != 0) {
+            throw new IllegalArgumentException("bogus lowNibble");
+        }
+
+        if ((highNibble & ~0xf) != 0) {
+            throw new IllegalArgumentException("bogus highNibble");
+        }
+
+        return lowNibble | (highNibble << 4);
+    }
+
+    private static short asUnsignedUnit(int value) {
+        if ((value & ~0xffff) != 0) {
+            throw new IllegalArgumentException("bogus unsigned code unit");
+        }
+
+        return (short) value;
+    }
+
+    private static short unit0(int value) {
+        return (short) value;
+    }
+
+    private static short unit1(int value) {
+        return (short) (value >> 16);
+    }
+
+    private static short unit0(long value) {
+        return (short) value;
+    }
+
+    private static short unit1(long value) {
+        return (short) (value >> 16);
+    }
+
+    private static short unit2(long value) {
+        return (short) (value >> 32);
+    }
+
+    private static short unit3(long value) {
+        return (short) (value >> 48);
+    }
+
+    private static int byte0(int value) {
+        return value & 0xff;
+    }
+
+    private static int byte1(int value) {
+        return (value >> 8) & 0xff;
+    }
+
+    private static int byte2(int value) {
+        return (value >> 16) & 0xff;
+    }
+
+    private static int byte3(int value) {
+        return value >>> 24;
+    }
+
+    private static int nibble0(int value) {
+        return value & 0xf;
+    }
+
+    private static int nibble1(int value) {
+        return (value >> 4) & 0xf;
+    }
+
+    private static int nibble2(int value) {
+        return (value >> 8) & 0xf;
+    }
+
+    private static int nibble3(int value) {
+        return (value >> 12) & 0xf;
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/OneRegisterDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/OneRegisterDecodedInstruction.java
new file mode 100644
index 0000000..fd38e3b
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/OneRegisterDecodedInstruction.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+
+/**
+ * A decoded Dalvik instruction which has one register argument.
+ */
+public final class OneRegisterDecodedInstruction extends DecodedInstruction {
+    /** register argument "A" */
+    private final int a;
+
+    /**
+     * Constructs an instance.
+     */
+    public OneRegisterDecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal,
+            int a) {
+        super(format, opcode, index, indexType, target, literal);
+
+        this.a = a;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 1;
+    }
+
+    /** @inheritDoc */
+    public int getA() {
+        return a;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        return new OneRegisterDecodedInstruction(
+                getFormat(), getOpcode(), newIndex, getIndexType(),
+                getTarget(), getLiteral(), a);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/PackedSwitchPayloadDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/PackedSwitchPayloadDecodedInstruction.java
new file mode 100644
index 0000000..c31d319
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/PackedSwitchPayloadDecodedInstruction.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+/**
+ * A decoded Dalvik instruction which contains the payload for
+ * a {@code packed-switch} instruction.
+ */
+public final class PackedSwitchPayloadDecodedInstruction
+        extends DecodedInstruction {
+    /** first key value */
+    private final int firstKey;
+
+    /**
+     * array of target addresses. These are absolute, not relative,
+     * addresses.
+     */
+    private final int[] targets;
+
+    /**
+     * Constructs an instance.
+     */
+    public PackedSwitchPayloadDecodedInstruction(InstructionCodec format,
+            int opcode, int firstKey, int[] targets) {
+        super(format, opcode, 0, null, 0, 0L);
+
+        this.firstKey = firstKey;
+        this.targets = targets;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 0;
+    }
+
+    public int getFirstKey() {
+        return firstKey;
+    }
+
+    public int[] getTargets() {
+        return targets;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        throw new UnsupportedOperationException("no index in instruction");
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/RegisterRangeDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/RegisterRangeDecodedInstruction.java
new file mode 100644
index 0000000..f294f63
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/RegisterRangeDecodedInstruction.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+
+/**
+ * A decoded Dalvik instruction which has register range arguments (an
+ * "A" start register and a register count).
+ */
+public final class RegisterRangeDecodedInstruction extends DecodedInstruction {
+    /** register argument "A" */
+    private final int a;
+
+    /** register count */
+    private final int registerCount;
+
+    /**
+     * Constructs an instance.
+     */
+    public RegisterRangeDecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal,
+            int a, int registerCount) {
+        super(format, opcode, index, indexType, target, literal);
+
+        this.a = a;
+        this.registerCount = registerCount;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return registerCount;
+    }
+
+    /** @inheritDoc */
+    public int getA() {
+        return a;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        return new RegisterRangeDecodedInstruction(
+                getFormat(), getOpcode(), newIndex, getIndexType(),
+                getTarget(), getLiteral(), a, registerCount);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/ShortArrayCodeInput.java b/dx/src/com/android/dx/io/instructions/ShortArrayCodeInput.java
new file mode 100644
index 0000000..49ce473
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/ShortArrayCodeInput.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import java.io.EOFException;
+
+/**
+ * Implementation of {@code CodeInput} that reads from a {@code short[]}.
+ */
+public final class ShortArrayCodeInput extends BaseCodeCursor
+        implements CodeInput {
+    /** source array to read from */
+    private final short[] array;
+
+    /**
+     * Constructs an instance.
+     */
+    public ShortArrayCodeInput(short[] array) {
+        if (array == null) {
+            throw new NullPointerException("array == null");
+        }
+
+        this.array = array;
+    }
+
+    /** @inheritDoc */
+    public boolean hasMore() {
+        return cursor() < array.length;
+    }
+
+    /** @inheritDoc */
+    public int read() throws EOFException {
+        try {
+            int value = array[cursor()];
+            advance(1);
+            return value & 0xffff;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            throw new EOFException();
+        }
+    }
+
+    /** @inheritDoc */
+    public int readInt() throws EOFException {
+        int short0 = read();
+        int short1 = read();
+
+        return short0 | (short1 << 16);
+    }
+
+    /** @inheritDoc */
+    public long readLong() throws EOFException {
+        long short0 = read();
+        long short1 = read();
+        long short2 = read();
+        long short3 = read();
+
+        return short0 | (short1 << 16) | (short2 << 32) | (short3 << 48);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/ShortArrayCodeOutput.java b/dx/src/com/android/dx/io/instructions/ShortArrayCodeOutput.java
new file mode 100644
index 0000000..efa7ddd
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/ShortArrayCodeOutput.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+/**
+ * Implementation of {@code CodeOutput} that writes to a {@code short[]}.
+ */
+public final class ShortArrayCodeOutput extends BaseCodeCursor
+        implements CodeOutput {
+    /** array to write to */
+    private final short[] array;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param maxSize the maximum number of code units that will be written
+     */
+    public ShortArrayCodeOutput(int maxSize) {
+        if (maxSize < 0) {
+            throw new IllegalArgumentException("maxSize < 0");
+        }
+
+        this.array = new short[maxSize];
+    }
+
+    /**
+     * Gets the array. The returned array contains exactly the data
+     * written (e.g. no leftover space at the end).
+     */
+    public short[] getArray() {
+        int cursor = cursor();
+
+        if (cursor == array.length) {
+            return array;
+        }
+
+        short[] result = new short[cursor];
+        System.arraycopy(array, 0, result, 0, cursor);
+        return result;
+    }
+
+    /** @inheritDoc */
+    public void write(short codeUnit) {
+        array[cursor()] = codeUnit;
+        advance(1);
+    }
+
+    /** @inheritDoc */
+    public void write(short u0, short u1) {
+        write(u0);
+        write(u1);
+    }
+
+    /** @inheritDoc */
+    public void write(short u0, short u1, short u2) {
+        write(u0);
+        write(u1);
+        write(u2);
+    }
+
+    /** @inheritDoc */
+    public void write(short u0, short u1, short u2, short u3) {
+        write(u0);
+        write(u1);
+        write(u2);
+        write(u3);
+    }
+
+    /** @inheritDoc */
+    public void write(short u0, short u1, short u2, short u3, short u4) {
+        write(u0);
+        write(u1);
+        write(u2);
+        write(u3);
+        write(u4);
+    }
+
+    /** @inheritDoc */
+    public void writeInt(int value) {
+        write((short) value);
+        write((short) (value >> 16));
+    }
+
+    /** @inheritDoc */
+    public void writeLong(long value) {
+        write((short) value);
+        write((short) (value >> 16));
+        write((short) (value >> 32));
+        write((short) (value >> 48));
+    }
+
+    /** @inheritDoc */
+    public void write(byte[] data) {
+        int value = 0;
+        boolean even = true;
+        for (byte b : data) {
+            if (even) {
+                value = b & 0xff;
+                even = false;
+            } else {
+                value |= b << 8;
+                write((short) value);
+                even = true;
+            }
+        }
+
+        if (!even) {
+            write((short) value);
+        }
+    }
+
+    /** @inheritDoc */
+    public void write(short[] data) {
+        for (short unit : data) {
+            write(unit);
+        }
+    }
+
+    /** @inheritDoc */
+    public void write(int[] data) {
+        for (int i : data) {
+            writeInt(i);
+        }
+    }
+
+    /** @inheritDoc */
+    public void write(long[] data) {
+        for (long l : data) {
+            writeLong(l);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/SparseSwitchPayloadDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/SparseSwitchPayloadDecodedInstruction.java
new file mode 100644
index 0000000..bfc47c9
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/SparseSwitchPayloadDecodedInstruction.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+/**
+ * A decoded Dalvik instruction which contains the payload for
+ * a {@code packed-switch} instruction.
+ */
+public final class SparseSwitchPayloadDecodedInstruction
+        extends DecodedInstruction {
+    /** array of key values */
+    private final int[] keys;
+
+    /**
+     * array of target addresses. These are absolute, not relative,
+     * addresses.
+     */
+    private final int[] targets;
+
+    /**
+     * Constructs an instance.
+     */
+    public SparseSwitchPayloadDecodedInstruction(InstructionCodec format,
+            int opcode, int[] keys, int[] targets) {
+        super(format, opcode, 0, null, 0, 0L);
+
+        if (keys.length != targets.length) {
+            throw new IllegalArgumentException("keys/targets length mismatch");
+        }
+
+        this.keys = keys;
+        this.targets = targets;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 0;
+    }
+
+    public int[] getKeys() {
+        return keys;
+    }
+
+    public int[] getTargets() {
+        return targets;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        throw new UnsupportedOperationException("no index in instruction");
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/ThreeRegisterDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/ThreeRegisterDecodedInstruction.java
new file mode 100644
index 0000000..a463677
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/ThreeRegisterDecodedInstruction.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+
+/**
+ * A decoded Dalvik instruction which has three register arguments.
+ */
+public final class ThreeRegisterDecodedInstruction extends DecodedInstruction {
+    /** register argument "A" */
+    private final int a;
+
+    /** register argument "B" */
+    private final int b;
+
+    /** register argument "C" */
+    private final int c;
+
+    /**
+     * Constructs an instance.
+     */
+    public ThreeRegisterDecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal,
+            int a, int b, int c) {
+        super(format, opcode, index, indexType, target, literal);
+
+        this.a = a;
+        this.b = b;
+        this.c = c;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 3;
+    }
+
+    /** @inheritDoc */
+    public int getA() {
+        return a;
+    }
+
+    /** @inheritDoc */
+    public int getB() {
+        return b;
+    }
+
+    /** @inheritDoc */
+    public int getC() {
+        return c;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        return new ThreeRegisterDecodedInstruction(
+                getFormat(), getOpcode(), newIndex, getIndexType(),
+                getTarget(), getLiteral(), a, b, c);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/TwoRegisterDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/TwoRegisterDecodedInstruction.java
new file mode 100644
index 0000000..acb77ba
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/TwoRegisterDecodedInstruction.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+
+/**
+ * A decoded Dalvik instruction which has two register arguments.
+ */
+public final class TwoRegisterDecodedInstruction extends DecodedInstruction {
+    /** register argument "A" */
+    private final int a;
+
+    /** register argument "B" */
+    private final int b;
+
+    /**
+     * Constructs an instance.
+     */
+    public TwoRegisterDecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal,
+            int a, int b) {
+        super(format, opcode, index, indexType, target, literal);
+
+        this.a = a;
+        this.b = b;
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 2;
+    }
+
+    /** @inheritDoc */
+    public int getA() {
+        return a;
+    }
+
+    /** @inheritDoc */
+    public int getB() {
+        return b;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        return new TwoRegisterDecodedInstruction(
+                getFormat(), getOpcode(), newIndex, getIndexType(),
+                getTarget(), getLiteral(), a, b);
+    }
+}
diff --git a/dx/src/com/android/dx/io/instructions/ZeroRegisterDecodedInstruction.java b/dx/src/com/android/dx/io/instructions/ZeroRegisterDecodedInstruction.java
new file mode 100644
index 0000000..172caa4
--- /dev/null
+++ b/dx/src/com/android/dx/io/instructions/ZeroRegisterDecodedInstruction.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.io.instructions;
+
+import com.android.dx.io.IndexType;
+
+/**
+ * A decoded Dalvik instruction which has no register arguments.
+ */
+public final class ZeroRegisterDecodedInstruction extends DecodedInstruction {
+    /**
+     * Constructs an instance.
+     */
+    public ZeroRegisterDecodedInstruction(InstructionCodec format, int opcode,
+            int index, IndexType indexType, int target, long literal) {
+        super(format, opcode, index, indexType, target, literal);
+    }
+
+    /** @inheritDoc */
+    public int getRegisterCount() {
+        return 0;
+    }
+
+    /** @inheritDoc */
+    public DecodedInstruction withIndex(int newIndex) {
+        return new ZeroRegisterDecodedInstruction(
+                getFormat(), getOpcode(), newIndex, getIndexType(),
+                getTarget(), getLiteral());
+    }
+}
diff --git a/dx/src/com/android/dx/merge/CollisionPolicy.java b/dx/src/com/android/dx/merge/CollisionPolicy.java
new file mode 100644
index 0000000..95e9835
--- /dev/null
+++ b/dx/src/com/android/dx/merge/CollisionPolicy.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+/**
+ * What to do when two dex files define the same class.
+ */
+public enum CollisionPolicy {
+
+    /**
+     * Keep the class def from the first dex file and discard the def from the
+     * second dex file. This policy is appropriate for incremental builds.
+     */
+    KEEP_FIRST,
+
+    /**
+     * Forbid collisions. This policy is appropriate for merging libraries.
+     */
+    FAIL
+}
diff --git a/dx/src/com/android/dx/merge/DexMerger.java b/dx/src/com/android/dx/merge/DexMerger.java
new file mode 100644
index 0000000..b7677cf
--- /dev/null
+++ b/dx/src/com/android/dx/merge/DexMerger.java
@@ -0,0 +1,1084 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+import com.android.dx.dex.SizeOf;
+import com.android.dx.dex.TableOfContents;
+import com.android.dx.io.Annotation;
+import com.android.dx.io.ClassData;
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.Code;
+import com.android.dx.io.DexBuffer;
+import com.android.dx.io.DexHasher;
+import com.android.dx.io.FieldId;
+import com.android.dx.io.MethodId;
+import com.android.dx.io.ProtoId;
+import com.android.dx.util.DexException;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Combine two dex files into one.
+ */
+public final class DexMerger {
+    private final DexBuffer dexA;
+    private final DexBuffer dexB;
+    private final CollisionPolicy collisionPolicy;
+    private final WriterSizes writerSizes;
+
+    private final DexBuffer dexOut = new DexBuffer();
+
+    private final DexBuffer.Section headerOut;
+
+    /** All IDs and definitions sections */
+    private final DexBuffer.Section idsDefsOut;
+
+    private final DexBuffer.Section mapListOut;
+
+    private final DexBuffer.Section typeListOut;
+
+    private final DexBuffer.Section classDataOut;
+
+    private final DexBuffer.Section codeOut;
+
+    private final DexBuffer.Section stringDataOut;
+
+    private final DexBuffer.Section debugInfoOut;
+
+    private final DexBuffer.Section encodedArrayOut;
+
+    /** annotations directory on a type */
+    private final DexBuffer.Section annotationsDirectoryOut;
+
+    /** sets of annotations on a member, parameter or type */
+    private final DexBuffer.Section annotationSetOut;
+
+    /** parameter lists */
+    private final DexBuffer.Section annotationSetRefListOut;
+
+    /** individual annotations, each containing zero or more fields */
+    private final DexBuffer.Section annotationOut;
+
+    private final TableOfContents contentsOut;
+
+    private final IndexMap aIndexMap;
+    private final IndexMap bIndexMap;
+    private final InstructionTransformer aInstructionTransformer;
+    private final InstructionTransformer bInstructionTransformer;
+
+    /** minimum number of wasted bytes before it's worthwhile to compact the result */
+    private int compactWasteThreshold = 1024 * 1024; // 1MiB
+
+    public DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy)
+            throws IOException {
+        this(dexA, dexB, collisionPolicy, new WriterSizes(dexA, dexB));
+    }
+
+    private DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy,
+            WriterSizes writerSizes) throws IOException {
+        this.dexA = dexA;
+        this.dexB = dexB;
+        this.collisionPolicy = collisionPolicy;
+        this.writerSizes = writerSizes;
+
+        TableOfContents aContents = dexA.getTableOfContents();
+        TableOfContents bContents = dexB.getTableOfContents();
+        aIndexMap = new IndexMap(dexOut, aContents);
+        bIndexMap = new IndexMap(dexOut, bContents);
+        aInstructionTransformer = new InstructionTransformer(aIndexMap);
+        bInstructionTransformer = new InstructionTransformer(bIndexMap);
+
+        headerOut = dexOut.appendSection(writerSizes.header, "header");
+        idsDefsOut = dexOut.appendSection(writerSizes.idsDefs, "ids defs");
+
+        contentsOut = dexOut.getTableOfContents();
+        contentsOut.dataOff = dexOut.getLength();
+
+        contentsOut.mapList.off = dexOut.getLength();
+        contentsOut.mapList.size = 1;
+        mapListOut = dexOut.appendSection(writerSizes.mapList, "map list");
+
+        contentsOut.typeLists.off = dexOut.getLength();
+        typeListOut = dexOut.appendSection(writerSizes.typeList, "type list");
+
+        contentsOut.annotationSetRefLists.off = dexOut.getLength();
+        annotationSetRefListOut = dexOut.appendSection(
+                writerSizes.annotationsSetRefList, "annotation set ref list");
+
+        contentsOut.annotationSets.off = dexOut.getLength();
+        annotationSetOut = dexOut.appendSection(writerSizes.annotationsSet, "annotation sets");
+
+        contentsOut.classDatas.off = dexOut.getLength();
+        classDataOut = dexOut.appendSection(writerSizes.classData, "class data");
+
+        contentsOut.codes.off = dexOut.getLength();
+        codeOut = dexOut.appendSection(writerSizes.code, "code");
+
+        contentsOut.stringDatas.off = dexOut.getLength();
+        stringDataOut = dexOut.appendSection(writerSizes.stringData, "string data");
+
+        contentsOut.debugInfos.off = dexOut.getLength();
+        debugInfoOut = dexOut.appendSection(writerSizes.debugInfo, "debug info");
+
+        contentsOut.annotations.off = dexOut.getLength();
+        annotationOut = dexOut.appendSection(writerSizes.annotation, "annotation");
+
+        contentsOut.encodedArrays.off = dexOut.getLength();
+        encodedArrayOut = dexOut.appendSection(writerSizes.encodedArray, "encoded array");
+
+        contentsOut.annotationsDirectories.off = dexOut.getLength();
+        annotationsDirectoryOut = dexOut.appendSection(
+                writerSizes.annotationsDirectory, "annotations directory");
+
+        dexOut.noMoreSections();
+        contentsOut.dataSize = dexOut.getLength() - contentsOut.dataOff;
+    }
+
+    public void setCompactWasteThreshold(int compactWasteThreshold) {
+        this.compactWasteThreshold = compactWasteThreshold;
+    }
+
+    private DexBuffer mergeDexBuffers() throws IOException {
+        mergeStringIds();
+        mergeTypeIds();
+        mergeTypeLists();
+        mergeProtoIds();
+        mergeFieldIds();
+        mergeMethodIds();
+        mergeAnnotations();
+        unionAnnotationSetsAndDirectories();
+        mergeClassDefs();
+
+        // write the header
+        contentsOut.header.off = 0;
+        contentsOut.header.size = 1;
+        contentsOut.fileSize = dexOut.getLength();
+        contentsOut.computeSizesFromOffsets();
+        contentsOut.writeHeader(headerOut);
+        contentsOut.writeMap(mapListOut);
+
+        // generate and write the hashes
+        new DexHasher().writeHashes(dexOut);
+
+        return dexOut;
+    }
+
+    public DexBuffer merge() throws IOException {
+        long start = System.nanoTime();
+        DexBuffer result = mergeDexBuffers();
+
+        /*
+         * We use pessimistic sizes when merging dex files. If those sizes
+         * result in too many bytes wasted, compact the result. To compact,
+         * simply merge the result with itself.
+         */
+        WriterSizes compactedSizes = new WriterSizes(this);
+        int wastedByteCount = writerSizes.size() - compactedSizes.size();
+        if (wastedByteCount >  + compactWasteThreshold) {
+            DexMerger compacter = new DexMerger(
+                    dexOut, new DexBuffer(), CollisionPolicy.FAIL, compactedSizes);
+            result = compacter.mergeDexBuffers();
+            System.out.printf("Result compacted from %.1fKiB to %.1fKiB to save %.1fKiB%n",
+                    dexOut.getLength() / 1024f,
+                    result.getLength() / 1024f,
+                    wastedByteCount / 1024f);
+        }
+
+        long elapsed = System.nanoTime() - start;
+        System.out.printf("Merged dex A (%d defs/%.1fKiB) with dex B "
+                + "(%d defs/%.1fKiB). Result is %d defs/%.1fKiB. Took %.1fs%n",
+                dexA.getTableOfContents().classDefs.size,
+                dexA.getLength() / 1024f,
+                dexB.getTableOfContents().classDefs.size,
+                dexB.getLength() / 1024f,
+                result.getTableOfContents().classDefs.size,
+                result.getLength() / 1024f,
+                elapsed / 1000000000f);
+
+        return result;
+    }
+
+    /**
+     * Reads an IDs section of two dex files and writes an IDs section of a
+     * merged dex file. Populates maps from old to new indices in the process.
+     */
+    abstract class IdMerger<T extends Comparable<T>> {
+        private final DexBuffer.Section out;
+
+        protected IdMerger(DexBuffer.Section out) {
+            this.out = out;
+        }
+
+        /**
+         * Merges already-sorted sections, reading only two values into memory
+         * at a time.
+         */
+        public final void mergeSorted() {
+            TableOfContents.Section aSection = getSection(dexA.getTableOfContents());
+            TableOfContents.Section bSection = getSection(dexB.getTableOfContents());
+            getSection(contentsOut).off = out.getPosition();
+
+            DexBuffer.Section inA = aSection.exists() ? dexA.open(aSection.off) : null;
+            DexBuffer.Section inB = bSection.exists() ? dexB.open(bSection.off) : null;
+            int aOffset = -1;
+            int bOffset = -1;
+            int aIndex = 0;
+            int bIndex = 0;
+            int outCount = 0;
+            T a = null;
+            T b = null;
+
+            while (true) {
+                if (a == null && aIndex < aSection.size) {
+                    aOffset = inA.getPosition();
+                    a = read(inA, aIndexMap, aIndex);
+                }
+                if (b == null && bIndex < bSection.size) {
+                    bOffset = inB.getPosition();
+                    b = read(inB, bIndexMap, bIndex);
+                }
+
+                // Write the smaller of a and b. If they're equal, write only once
+                boolean advanceA;
+                boolean advanceB;
+                if (a != null && b != null) {
+                    int compare = a.compareTo(b);
+                    advanceA = compare <= 0;
+                    advanceB = compare >= 0;
+                } else {
+                    advanceA = (a != null);
+                    advanceB = (b != null);
+                }
+
+                T toWrite = null;
+                if (advanceA) {
+                    toWrite = a;
+                    updateIndex(aOffset, aIndexMap, aIndex++, outCount);
+                    a = null;
+                    aOffset = -1;
+                }
+                if (advanceB) {
+                    toWrite = b;
+                    updateIndex(bOffset, bIndexMap, bIndex++, outCount);
+                    b = null;
+                    bOffset = -1;
+                }
+                if (toWrite == null) {
+                    break; // advanceA == false && advanceB == false
+                }
+                write(toWrite);
+                outCount++;
+            }
+
+            getSection(contentsOut).size = outCount;
+        }
+
+        /**
+         * Merges unsorted sections by reading them completely into memory and
+         * sorting in memory.
+         */
+        public final void mergeUnsorted() {
+            getSection(contentsOut).off = out.getPosition();
+
+            List<UnsortedValue> all = new ArrayList<UnsortedValue>();
+            all.addAll(readUnsortedValues(dexA, aIndexMap));
+            all.addAll(readUnsortedValues(dexB, bIndexMap));
+            Collections.sort(all);
+
+            int outCount = 0;
+            for (int i = 0; i < all.size(); ) {
+                UnsortedValue e1 = all.get(i++);
+                updateIndex(e1.offset, getIndexMap(e1.source), e1.index, outCount - 1);
+
+                while (i < all.size() && e1.compareTo(all.get(i)) == 0) {
+                    UnsortedValue e2 = all.get(i++);
+                    updateIndex(e2.offset, getIndexMap(e2.source), e2.index, outCount - 1);
+                }
+
+                write(e1.value);
+                outCount++;
+            }
+
+            getSection(contentsOut).size = outCount;
+        }
+
+        private List<UnsortedValue> readUnsortedValues(DexBuffer source, IndexMap indexMap) {
+            TableOfContents.Section section = getSection(source.getTableOfContents());
+            if (!section.exists()) {
+                return Collections.emptyList();
+            }
+
+            List<UnsortedValue> result = new ArrayList<UnsortedValue>();
+            DexBuffer.Section in = source.open(section.off);
+            for (int i = 0; i < section.size; i++) {
+                int offset = in.getPosition();
+                T value = read(in, indexMap, 0);
+                result.add(new UnsortedValue(source, indexMap, value, i, offset));
+            }
+            return result;
+        }
+
+        abstract TableOfContents.Section getSection(TableOfContents tableOfContents);
+        abstract T read(DexBuffer.Section in, IndexMap indexMap, int index);
+        abstract void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex);
+        abstract void write(T value);
+
+        class UnsortedValue implements Comparable<UnsortedValue> {
+            final DexBuffer source;
+            final IndexMap indexMap;
+            final T value;
+            final int index;
+            final int offset;
+
+            UnsortedValue(DexBuffer source, IndexMap indexMap, T value, int index, int offset) {
+                this.source = source;
+                this.indexMap = indexMap;
+                this.value = value;
+                this.index = index;
+                this.offset = offset;
+            }
+
+            public int compareTo(UnsortedValue unsortedValue) {
+                return value.compareTo(unsortedValue.value);
+            }
+        }
+    }
+
+    private IndexMap getIndexMap(DexBuffer dexBuffer) {
+        if (dexBuffer == dexA) {
+            return aIndexMap;
+        } else if (dexBuffer == dexB) {
+            return bIndexMap;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private void mergeStringIds() {
+        new IdMerger<String>(idsDefsOut) {
+            @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+                return tableOfContents.stringIds;
+            }
+
+            @Override String read(DexBuffer.Section in, IndexMap indexMap, int index) {
+                return in.readString();
+            }
+
+            @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+                indexMap.stringIds[oldIndex] = newIndex;
+            }
+
+            @Override void write(String value) {
+                contentsOut.stringDatas.size++;
+                idsDefsOut.writeInt(stringDataOut.getPosition());
+                stringDataOut.writeStringData(value);
+            }
+        }.mergeSorted();
+    }
+
+    private void mergeTypeIds() {
+        new IdMerger<Integer>(idsDefsOut) {
+            @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+                return tableOfContents.typeIds;
+            }
+
+            @Override Integer read(DexBuffer.Section in, IndexMap indexMap, int index) {
+                int stringIndex = in.readInt();
+                return indexMap.adjustString(stringIndex);
+            }
+
+            @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+                if (newIndex < 0 || newIndex > 0xffff) {
+                    throw new IllegalArgumentException("type ID not in [0, 0xffff]: " + newIndex);
+                }
+                indexMap.typeIds[oldIndex] = (short) newIndex;
+            }
+
+            @Override void write(Integer value) {
+                idsDefsOut.writeInt(value);
+            }
+        }.mergeSorted();
+    }
+
+    private void mergeTypeLists() {
+        new IdMerger<TypeList>(typeListOut) {
+            @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+                return tableOfContents.typeLists;
+            }
+
+            @Override TypeList read(DexBuffer.Section in, IndexMap indexMap, int index) {
+                return indexMap.adjustTypeList(in.readTypeList());
+            }
+
+            @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+                indexMap.putTypeListOffset(offset, typeListOut.getPosition());
+            }
+
+            @Override void write(TypeList value) {
+                typeListOut.writeTypeList(value);
+            }
+        }.mergeUnsorted();
+    }
+
+    private void mergeProtoIds() {
+        new IdMerger<ProtoId>(idsDefsOut) {
+            @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+                return tableOfContents.protoIds;
+            }
+
+            @Override ProtoId read(DexBuffer.Section in, IndexMap indexMap, int index) {
+                return indexMap.adjust(in.readProtoId());
+            }
+
+            @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+                if (newIndex < 0 || newIndex > 0xffff) {
+                    throw new IllegalArgumentException("proto ID not in [0, 0xffff]: " + newIndex);
+                }
+                indexMap.protoIds[oldIndex] = (short) newIndex;
+            }
+
+            @Override void write(ProtoId value) {
+                value.writeTo(idsDefsOut);
+            }
+        }.mergeSorted();
+    }
+
+    private void mergeFieldIds() {
+        new IdMerger<FieldId>(idsDefsOut) {
+            @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+                return tableOfContents.fieldIds;
+            }
+
+            @Override FieldId read(DexBuffer.Section in, IndexMap indexMap, int index) {
+                return indexMap.adjust(in.readFieldId());
+            }
+
+            @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+                if (newIndex < 0 || newIndex > 0xffff) {
+                    throw new IllegalArgumentException("field ID not in [0, 0xffff]: " + newIndex);
+                }
+                indexMap.fieldIds[oldIndex] = (short) newIndex;
+            }
+
+            @Override void write(FieldId value) {
+                value.writeTo(idsDefsOut);
+            }
+        }.mergeSorted();
+    }
+
+    private void mergeMethodIds() {
+        new IdMerger<MethodId>(idsDefsOut) {
+            @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+                return tableOfContents.methodIds;
+            }
+
+            @Override MethodId read(DexBuffer.Section in, IndexMap indexMap, int index) {
+                return indexMap.adjust(in.readMethodId());
+            }
+
+            @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+                if (newIndex < 0 || newIndex > 0xffff) {
+                    throw new IllegalArgumentException("method ID not in [0, 0xffff]: " + newIndex);
+                }
+                indexMap.methodIds[oldIndex] = (short) newIndex;
+            }
+
+            @Override void write(MethodId methodId) {
+                methodId.writeTo(idsDefsOut);
+            }
+        }.mergeSorted();
+    }
+
+    private void mergeAnnotations() {
+        new IdMerger<Annotation>(annotationOut) {
+            @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+                return tableOfContents.annotations;
+            }
+
+            @Override Annotation read(DexBuffer.Section in, IndexMap indexMap, int index) {
+                return indexMap.adjust(in.readAnnotation());
+            }
+
+            @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+                indexMap.putAnnotationOffset(offset, annotationOut.getPosition());
+            }
+
+            @Override void write(Annotation value) {
+                value.writeTo(annotationOut);
+            }
+        }.mergeUnsorted();
+    }
+
+    private void mergeClassDefs() {
+        SortableType[] types = getSortedTypes();
+        contentsOut.classDefs.off = idsDefsOut.getPosition();
+        contentsOut.classDefs.size = types.length;
+
+        for (SortableType type : types) {
+            DexBuffer in = type.getBuffer();
+            IndexMap indexMap = (in == dexA) ? aIndexMap : bIndexMap;
+            transformClassDef(in, type.getClassDef(), indexMap);
+        }
+    }
+
+    /**
+     * Returns the union of classes from both files, sorted in order such that
+     * a class is always preceded by its supertype and implemented interfaces.
+     */
+    private SortableType[] getSortedTypes() {
+        // size is pessimistic; doesn't include arrays
+        SortableType[] sortableTypes = new SortableType[contentsOut.typeIds.size];
+        readSortableTypes(sortableTypes, dexA, aIndexMap);
+        readSortableTypes(sortableTypes, dexB, bIndexMap);
+
+        /*
+         * Populate the depths of each sortable type. This makes D iterations
+         * through all N types, where 'D' is the depth of the deepest type. For
+         * example, the deepest class in libcore is Xalan's KeyIterator, which
+         * is 11 types deep.
+         */
+        while (true) {
+            boolean allDone = true;
+            for (SortableType sortableType : sortableTypes) {
+                if (sortableType != null && !sortableType.isDepthAssigned()) {
+                    allDone &= sortableType.tryAssignDepth(sortableTypes);
+                }
+            }
+            if (allDone) {
+                break;
+            }
+        }
+
+        // Now that all types have depth information, the result can be sorted
+        Arrays.sort(sortableTypes, SortableType.NULLS_LAST_ORDER);
+
+        // Strip nulls from the end
+        int firstNull = Arrays.asList(sortableTypes).indexOf(null);
+        return firstNull != -1
+                ? Arrays.copyOfRange(sortableTypes, 0, firstNull)
+                : sortableTypes;
+    }
+
+    /**
+     * Reads just enough data on each class so that we can sort it and then find
+     * it later.
+     */
+    private void readSortableTypes(SortableType[] sortableTypes, DexBuffer buffer,
+            IndexMap indexMap) {
+        for (ClassDef classDef : buffer.classDefs()) {
+            SortableType sortableType = indexMap.adjust(new SortableType(buffer, classDef));
+            int t = sortableType.getTypeIndex();
+            if (sortableTypes[t] == null) {
+                sortableTypes[t] = sortableType;
+            } else if (collisionPolicy != CollisionPolicy.KEEP_FIRST) {
+                throw new DexException("Multiple dex files define "
+                        + buffer.typeNames().get(classDef.getTypeIndex()));
+            }
+        }
+    }
+
+    /**
+     * Copy annotation sets from each input to the output.
+     *
+     * TODO: this may write multiple copies of the same annotation set.
+     * We should shrink the output by merging rather than unioning
+     */
+    private void unionAnnotationSetsAndDirectories() {
+        transformAnnotationSets(dexA, aIndexMap);
+        transformAnnotationSets(dexB, bIndexMap);
+        transformAnnotationDirectories(dexA, aIndexMap);
+        transformAnnotationDirectories(dexB, bIndexMap);
+        transformStaticValues(dexA, aIndexMap);
+        transformStaticValues(dexB, bIndexMap);
+    }
+
+    private void transformAnnotationSets(DexBuffer in, IndexMap indexMap) {
+        TableOfContents.Section section = in.getTableOfContents().annotationSets;
+        if (section.exists()) {
+            DexBuffer.Section setIn = in.open(section.off);
+            for (int i = 0; i < section.size; i++) {
+                transformAnnotationSet(indexMap, setIn);
+            }
+        }
+    }
+
+    private void transformAnnotationDirectories(DexBuffer in, IndexMap indexMap) {
+        TableOfContents.Section section = in.getTableOfContents().annotationsDirectories;
+        if (section.exists()) {
+            DexBuffer.Section directoryIn = in.open(section.off);
+            for (int i = 0; i < section.size; i++) {
+                transformAnnotationDirectory(in, directoryIn, indexMap);
+            }
+        }
+    }
+
+    private void transformStaticValues(DexBuffer in, IndexMap indexMap) {
+        TableOfContents.Section section = in.getTableOfContents().encodedArrays;
+        if (section.exists()) {
+            DexBuffer.Section staticValuesIn = in.open(section.off);
+            for (int i = 0; i < section.size; i++) {
+                transformStaticValues(staticValuesIn, indexMap);
+            }
+        }
+    }
+
+    /**
+     * Reads a class_def_item beginning at {@code in} and writes the index and
+     * data.
+     */
+    private void transformClassDef(DexBuffer in, ClassDef classDef, IndexMap indexMap) {
+        idsDefsOut.assertFourByteAligned();
+        idsDefsOut.writeInt(classDef.getTypeIndex());
+        idsDefsOut.writeInt(classDef.getAccessFlags());
+        idsDefsOut.writeInt(classDef.getSupertypeIndex());
+        idsDefsOut.writeInt(classDef.getInterfacesOffset());
+
+        int sourceFileIndex = indexMap.adjustString(classDef.getSourceFileIndex());
+        idsDefsOut.writeInt(sourceFileIndex);
+
+        int annotationsOff = classDef.getAnnotationsOffset();
+        idsDefsOut.writeInt(indexMap.adjustAnnotationDirectory(annotationsOff));
+
+        int classDataOff = classDef.getClassDataOffset();
+        if (classDataOff == 0) {
+            idsDefsOut.writeInt(0);
+        } else {
+            idsDefsOut.writeInt(classDataOut.getPosition());
+            ClassData classData = in.readClassData(classDef);
+            transformClassData(in, classData, indexMap);
+        }
+
+        int staticValuesOff = classDef.getStaticValuesOffset();
+        idsDefsOut.writeInt(indexMap.adjustStaticValues(staticValuesOff));
+    }
+
+    /**
+     * Transform all annotations on a class.
+     */
+    private void transformAnnotationDirectory(
+            DexBuffer in, DexBuffer.Section directoryIn, IndexMap indexMap) {
+        contentsOut.annotationsDirectories.size++;
+        annotationsDirectoryOut.assertFourByteAligned();
+        indexMap.putAnnotationDirectoryOffset(
+                directoryIn.getPosition(), annotationsDirectoryOut.getPosition());
+
+        int classAnnotationsOffset = indexMap.adjustAnnotationSet(directoryIn.readInt());
+        annotationsDirectoryOut.writeInt(classAnnotationsOffset);
+
+        int fieldsSize = directoryIn.readInt();
+        annotationsDirectoryOut.writeInt(fieldsSize);
+
+        int methodsSize = directoryIn.readInt();
+        annotationsDirectoryOut.writeInt(methodsSize);
+
+        int parameterListSize = directoryIn.readInt();
+        annotationsDirectoryOut.writeInt(parameterListSize);
+
+        for (int i = 0; i < fieldsSize; i++) {
+            // field index
+            annotationsDirectoryOut.writeInt(indexMap.adjustField(directoryIn.readInt()));
+
+            // annotations offset
+            annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt()));
+        }
+
+        for (int i = 0; i < methodsSize; i++) {
+            // method index
+            annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt()));
+
+            // annotation set offset
+            annotationsDirectoryOut.writeInt(
+                    indexMap.adjustAnnotationSet(directoryIn.readInt()));
+        }
+
+        for (int i = 0; i < parameterListSize; i++) {
+            contentsOut.annotationSetRefLists.size++;
+            annotationSetRefListOut.assertFourByteAligned();
+
+            // method index
+            annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt()));
+
+            // annotations offset
+            annotationsDirectoryOut.writeInt(annotationSetRefListOut.getPosition());
+            DexBuffer.Section refListIn = in.open(directoryIn.readInt());
+
+            // parameters
+            int parameterCount = refListIn.readInt();
+            annotationSetRefListOut.writeInt(parameterCount);
+            for (int p = 0; p < parameterCount; p++) {
+                annotationSetRefListOut.writeInt(indexMap.adjustAnnotationSet(refListIn.readInt()));
+            }
+        }
+    }
+
+    /**
+     * Transform all annotations on a single type, member or parameter.
+     */
+    private void transformAnnotationSet(IndexMap indexMap, DexBuffer.Section setIn) {
+        contentsOut.annotationSets.size++;
+        annotationSetOut.assertFourByteAligned();
+        indexMap.putAnnotationSetOffset(setIn.getPosition(), annotationSetOut.getPosition());
+
+        int size = setIn.readInt();
+        annotationSetOut.writeInt(size);
+
+        for (int j = 0; j < size; j++) {
+            annotationSetOut.writeInt(indexMap.adjustAnnotation(setIn.readInt()));
+        }
+    }
+
+    private void transformClassData(DexBuffer in, ClassData classData, IndexMap indexMap) {
+        contentsOut.classDatas.size++;
+
+        ClassData.Field[] staticFields = classData.getStaticFields();
+        ClassData.Field[] instanceFields = classData.getInstanceFields();
+        ClassData.Method[] directMethods = classData.getDirectMethods();
+        ClassData.Method[] virtualMethods = classData.getVirtualMethods();
+
+        classDataOut.writeUleb128(staticFields.length);
+        classDataOut.writeUleb128(instanceFields.length);
+        classDataOut.writeUleb128(directMethods.length);
+        classDataOut.writeUleb128(virtualMethods.length);
+
+        transformFields(indexMap, staticFields);
+        transformFields(indexMap, instanceFields);
+        transformMethods(in, indexMap, directMethods);
+        transformMethods(in, indexMap, virtualMethods);
+    }
+
+    private void transformFields(IndexMap indexMap, ClassData.Field[] fields) {
+        int lastOutFieldIndex = 0;
+        for (ClassData.Field field : fields) {
+            int outFieldIndex = indexMap.adjustField(field.getFieldIndex());
+            classDataOut.writeUleb128(outFieldIndex - lastOutFieldIndex);
+            lastOutFieldIndex = outFieldIndex;
+            classDataOut.writeUleb128(field.getAccessFlags());
+        }
+    }
+
+    private void transformMethods(DexBuffer in, IndexMap indexMap, ClassData.Method[] methods) {
+        int lastOutMethodIndex = 0;
+        for (ClassData.Method method : methods) {
+            int outMethodIndex = indexMap.adjustMethod(method.getMethodIndex());
+            classDataOut.writeUleb128(outMethodIndex - lastOutMethodIndex);
+            lastOutMethodIndex = outMethodIndex;
+
+            classDataOut.writeUleb128(method.getAccessFlags());
+
+            if (method.getCodeOffset() == 0) {
+                classDataOut.writeUleb128(0);
+            } else {
+                codeOut.alignToFourBytes();
+                classDataOut.writeUleb128(codeOut.getPosition());
+                transformCode(in, in.readCode(method), indexMap);
+            }
+        }
+    }
+
+    private void transformCode(DexBuffer in, Code code, IndexMap indexMap) {
+        contentsOut.codes.size++;
+        codeOut.assertFourByteAligned();
+
+        codeOut.writeUnsignedShort(code.getRegistersSize());
+        codeOut.writeUnsignedShort(code.getInsSize());
+        codeOut.writeUnsignedShort(code.getOutsSize());
+
+        Code.Try[] tries = code.getTries();
+        Code.CatchHandler[] catchHandlers = code.getCatchHandlers();
+        codeOut.writeUnsignedShort(tries.length);
+
+        int debugInfoOffset = code.getDebugInfoOffset();
+        if (debugInfoOffset != 0) {
+            codeOut.writeInt(debugInfoOut.getPosition());
+            transformDebugInfoItem(in.open(debugInfoOffset), indexMap);
+        } else {
+            codeOut.writeInt(0);
+        }
+
+        short[] instructions = code.getInstructions();
+        InstructionTransformer transformer = (in == dexA)
+                ? aInstructionTransformer
+                : bInstructionTransformer;
+        short[] newInstructions = transformer.transform(instructions);
+        codeOut.writeInt(newInstructions.length);
+        codeOut.write(newInstructions);
+
+        if (tries.length > 0) {
+            if (newInstructions.length % 2 == 1) {
+                codeOut.writeShort((short) 0); // padding
+            }
+
+            /*
+             * We can't write the tries until we've written the catch handlers.
+             * Unfortunately they're in the opposite order in the dex file so we
+             * need to transform them out-of-order.
+             */
+            DexBuffer.Section triesSection = dexOut.open(codeOut.getPosition());
+            codeOut.skip(tries.length * SizeOf.TRY_ITEM);
+            int[] offsets = transformCatchHandlers(indexMap, catchHandlers);
+            transformTries(triesSection, tries, offsets);
+        }
+    }
+
+    /**
+     * Writes the catch handlers to {@code codeOut} and returns their indices.
+     */
+    private int[] transformCatchHandlers(IndexMap indexMap, Code.CatchHandler[] catchHandlers) {
+        int baseOffset = codeOut.getPosition();
+        codeOut.writeUleb128(catchHandlers.length);
+        int[] offsets = new int[catchHandlers.length];
+        for (int i = 0; i < catchHandlers.length; i++) {
+            offsets[i] = codeOut.getPosition() - baseOffset;
+            transformEncodedCatchHandler(catchHandlers[i], indexMap);
+        }
+        return offsets;
+    }
+
+    private void transformTries(DexBuffer.Section out, Code.Try[] tries,
+            int[] catchHandlerOffsets) {
+        for (Code.Try tryItem : tries) {
+            out.writeInt(tryItem.getStartAddress());
+            out.writeUnsignedShort(tryItem.getInstructionCount());
+            out.writeUnsignedShort(catchHandlerOffsets[tryItem.getCatchHandlerIndex()]);
+        }
+    }
+
+    private static final byte DBG_END_SEQUENCE = 0x00;
+    private static final byte DBG_ADVANCE_PC = 0x01;
+    private static final byte DBG_ADVANCE_LINE = 0x02;
+    private static final byte DBG_START_LOCAL = 0x03;
+    private static final byte DBG_START_LOCAL_EXTENDED = 0x04;
+    private static final byte DBG_END_LOCAL = 0x05;
+    private static final byte DBG_RESTART_LOCAL = 0x06;
+    private static final byte DBG_SET_PROLOGUE_END = 0x07;
+    private static final byte DBG_SET_EPILOGUE_BEGIN = 0x08;
+    private static final byte DBG_SET_FILE = 0x09;
+
+    private void transformDebugInfoItem(DexBuffer.Section in, IndexMap indexMap) {
+        contentsOut.debugInfos.size++;
+        int lineStart = in.readUleb128();
+        debugInfoOut.writeUleb128(lineStart);
+
+        int parametersSize = in.readUleb128();
+        debugInfoOut.writeUleb128(parametersSize);
+
+        for (int p = 0; p < parametersSize; p++) {
+            int parameterName = in.readUleb128p1();
+            debugInfoOut.writeUleb128p1(indexMap.adjustString(parameterName));
+        }
+
+        int addrDiff;    // uleb128   address delta.
+        int lineDiff;    // sleb128   line delta.
+        int registerNum; // uleb128   register number.
+        int nameIndex;   // uleb128p1 string index.    Needs indexMap adjustment.
+        int typeIndex;   // uleb128p1 type index.      Needs indexMap adjustment.
+        int sigIndex;    // uleb128p1 string index.    Needs indexMap adjustment.
+
+        while (true) {
+            int opcode = in.readByte();
+            debugInfoOut.writeByte(opcode);
+
+            switch (opcode) {
+            case DBG_END_SEQUENCE:
+                return;
+
+            case DBG_ADVANCE_PC:
+                addrDiff = in.readUleb128();
+                debugInfoOut.writeUleb128(addrDiff);
+                break;
+
+            case DBG_ADVANCE_LINE:
+                lineDiff = in.readSleb128();
+                debugInfoOut.writeSleb128(lineDiff);
+                break;
+
+            case DBG_START_LOCAL:
+            case DBG_START_LOCAL_EXTENDED:
+                registerNum = in.readUleb128();
+                debugInfoOut.writeUleb128(registerNum);
+                nameIndex = in.readUleb128p1();
+                debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex));
+                typeIndex = in.readUleb128p1();
+                debugInfoOut.writeUleb128p1(indexMap.adjustType(typeIndex));
+                if (opcode == DBG_START_LOCAL_EXTENDED) {
+                    sigIndex = in.readUleb128p1();
+                    debugInfoOut.writeUleb128p1(indexMap.adjustString(sigIndex));
+                }
+                break;
+
+            case DBG_END_LOCAL:
+            case DBG_RESTART_LOCAL:
+                registerNum = in.readUleb128();
+                debugInfoOut.writeUleb128(registerNum);
+                break;
+
+            case DBG_SET_FILE:
+                nameIndex = in.readUleb128p1();
+                debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex));
+                break;
+
+            case DBG_SET_PROLOGUE_END:
+            case DBG_SET_EPILOGUE_BEGIN:
+            default:
+                break;
+            }
+        }
+    }
+
+    private void transformEncodedCatchHandler(Code.CatchHandler catchHandler, IndexMap indexMap) {
+        int catchAllAddress = catchHandler.getCatchAllAddress();
+        int[] typeIndexes = catchHandler.getTypeIndexes();
+        int[] addresses = catchHandler.getAddresses();
+
+        if (catchAllAddress != -1) {
+            codeOut.writeSleb128(-typeIndexes.length);
+        } else {
+            codeOut.writeSleb128(typeIndexes.length);
+        }
+
+        for (int i = 0; i < typeIndexes.length; i++) {
+            codeOut.writeUleb128(indexMap.adjustType(typeIndexes[i]));
+            codeOut.writeUleb128(addresses[i]);
+        }
+
+        if (catchAllAddress != -1) {
+            codeOut.writeUleb128(catchAllAddress);
+        }
+    }
+
+    private void transformStaticValues(DexBuffer.Section in, IndexMap indexMap) {
+        contentsOut.encodedArrays.size++;
+        indexMap.putStaticValuesOffset(in.getPosition(), encodedArrayOut.getPosition());
+        indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut);
+    }
+
+    /**
+     * Byte counts for the sections written when creating a dex. Target sizes
+     * are defined in one of two ways:
+     * <ul>
+     * <li>By pessimistically guessing how large the union of dex files will be.
+     *     We're pessimistic because we can't predict the amount of duplication
+     *     between dex files, nor can we predict the length of ULEB-encoded
+     *     offsets or indices.
+     * <li>By exactly measuring an existing dex.
+     * </ul>
+     */
+    private static class WriterSizes {
+        private int header = SizeOf.HEADER_ITEM;
+        private int idsDefs;
+        private int mapList;
+        private int typeList;
+        private int classData;
+        private int code;
+        private int stringData;
+        private int debugInfo;
+        private int encodedArray;
+        private int annotationsDirectory;
+        private int annotationsSet;
+        private int annotationsSetRefList;
+        private int annotation;
+
+        /**
+         * Compute sizes for merging a and b.
+         */
+        public WriterSizes(DexBuffer a, DexBuffer b) {
+            plus(a.getTableOfContents(), false);
+            plus(b.getTableOfContents(), false);
+        }
+
+        public WriterSizes(DexMerger dexMerger) {
+            header = dexMerger.headerOut.used();
+            idsDefs = dexMerger.idsDefsOut.used();
+            mapList = dexMerger.mapListOut.used();
+            typeList = dexMerger.typeListOut.used();
+            classData = dexMerger.classDataOut.used();
+            code = dexMerger.codeOut.used();
+            stringData = dexMerger.stringDataOut.used();
+            debugInfo = dexMerger.debugInfoOut.used();
+            encodedArray = dexMerger.encodedArrayOut.used();
+            annotationsDirectory = dexMerger.annotationsDirectoryOut.used();
+            annotationsSet = dexMerger.annotationSetOut.used();
+            annotationsSetRefList = dexMerger.annotationSetRefListOut.used();
+            annotation = dexMerger.annotationOut.used();
+        }
+
+        public void plus(TableOfContents contents, boolean exact) {
+            idsDefs += contents.stringIds.size * SizeOf.STRING_ID_ITEM
+                    + contents.typeIds.size * SizeOf.TYPE_ID_ITEM
+                    + contents.protoIds.size * SizeOf.PROTO_ID_ITEM
+                    + contents.fieldIds.size * SizeOf.MEMBER_ID_ITEM
+                    + contents.methodIds.size * SizeOf.MEMBER_ID_ITEM
+                    + contents.classDefs.size * SizeOf.CLASS_DEF_ITEM;
+            mapList = SizeOf.UINT + (contents.sections.length * SizeOf.MAP_ITEM);
+            typeList += contents.typeLists.byteCount;
+            stringData += contents.stringDatas.byteCount;
+            annotationsDirectory += contents.annotationsDirectories.byteCount;
+            annotationsSet += contents.annotationSets.byteCount;
+            annotationsSetRefList += contents.annotationSetRefLists.byteCount;
+
+            if (exact) {
+                code += contents.codes.byteCount;
+                classData += contents.classDatas.byteCount;
+                encodedArray += contents.encodedArrays.byteCount;
+                annotation += contents.annotations.byteCount;
+                debugInfo += contents.debugInfos.byteCount;
+            } else {
+                // at most 1/4 of the bytes in a code section are uleb/sleb
+                code += (int) Math.ceil(contents.codes.byteCount * 1.25);
+                // at most 1/3 of the bytes in a class data section are uleb/sleb
+                classData += (int) Math.ceil(contents.classDatas.byteCount * 1.34);
+                // all of the bytes in an encoding arrays section may be uleb/sleb
+                encodedArray += contents.encodedArrays.byteCount * 2;
+                // all of the bytes in an annotations section may be uleb/sleb
+                annotation += (int) Math.ceil(contents.annotations.byteCount * 2);
+                // all of the bytes in a debug info section may be uleb/sleb
+                debugInfo += contents.debugInfos.byteCount * 2;
+            }
+
+            typeList = DexBuffer.fourByteAlign(typeList);
+            code = DexBuffer.fourByteAlign(code);
+        }
+
+        public int size() {
+            return header + idsDefs + mapList + typeList + classData + code + stringData + debugInfo
+                    + encodedArray + annotationsDirectory + annotationsSet + annotationsSetRefList
+                    + annotation;
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        if (args.length != 3) {
+            printUsage();
+            return;
+        }
+
+        DexBuffer dexA = new DexBuffer(new File(args[1]));
+        DexBuffer dexB = new DexBuffer(new File(args[2]));
+        DexBuffer merged = new DexMerger(dexA, dexB, CollisionPolicy.KEEP_FIRST).merge();
+        merged.writeTo(new File(args[0]));
+    }
+
+    private static void printUsage() {
+        System.out.println("Usage: DexMerger <out.dex> <a.dex> <b.dex>");
+        System.out.println();
+        System.out.println("If both a and b define the same classes, a's copy will be used.");
+    }
+}
diff --git a/dx/src/com/android/dx/merge/IndexMap.java b/dx/src/com/android/dx/merge/IndexMap.java
new file mode 100644
index 0000000..a5c9584
--- /dev/null
+++ b/dx/src/com/android/dx/merge/IndexMap.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+import com.android.dx.dex.TableOfContents;
+import com.android.dx.io.Annotation;
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.DexBuffer;
+import com.android.dx.io.EncodedValue;
+import com.android.dx.io.EncodedValueReader;
+import com.android.dx.io.FieldId;
+import com.android.dx.io.MethodId;
+import com.android.dx.io.ProtoId;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.ByteInput;
+import com.android.dx.util.ByteOutput;
+import com.android.dx.util.Leb128Utils;
+import com.android.dx.util.Unsigned;
+import java.util.HashMap;
+
+/**
+ * Maps the index offsets from one dex file to those in another. For example, if
+ * you have string #5 in the old dex file, its position in the new dex file is
+ * {@code strings[5]}.
+ */
+public final class IndexMap {
+    private final DexBuffer target;
+    public final int[] stringIds;
+    public final short[] typeIds;
+    public final short[] protoIds;
+    public final short[] fieldIds;
+    public final short[] methodIds;
+    private final HashMap<Integer, Integer> typeListOffsets;
+    private final HashMap<Integer, Integer> annotationOffsets;
+    private final HashMap<Integer, Integer> annotationSetOffsets;
+    private final HashMap<Integer, Integer> annotationDirectoryOffsets;
+    private final HashMap<Integer, Integer> staticValuesOffsets;
+
+    public IndexMap(DexBuffer target, TableOfContents tableOfContents) {
+        this.target = target;
+        this.stringIds = new int[tableOfContents.stringIds.size];
+        this.typeIds = new short[tableOfContents.typeIds.size];
+        this.protoIds = new short[tableOfContents.protoIds.size];
+        this.fieldIds = new short[tableOfContents.fieldIds.size];
+        this.methodIds = new short[tableOfContents.methodIds.size];
+        this.typeListOffsets = new HashMap<Integer, Integer>();
+        this.annotationOffsets = new HashMap<Integer, Integer>();
+        this.annotationSetOffsets = new HashMap<Integer, Integer>();
+        this.annotationDirectoryOffsets = new HashMap<Integer, Integer>();
+        this.staticValuesOffsets = new HashMap<Integer, Integer>();
+
+        /*
+         * A type list, annotation set, annotation directory, or static value at
+         * offset 0 is always empty. Always map offset 0 to 0.
+         */
+        this.typeListOffsets.put(0, 0);
+        this.annotationSetOffsets.put(0, 0);
+        this.annotationDirectoryOffsets.put(0, 0);
+        this.staticValuesOffsets.put(0, 0);
+    }
+
+    public void putTypeListOffset(int oldOffset, int newOffset) {
+        if (oldOffset <= 0 || newOffset <= 0) {
+            throw new IllegalArgumentException();
+        }
+        typeListOffsets.put(oldOffset, newOffset);
+    }
+
+    public void putAnnotationOffset(int oldOffset, int newOffset) {
+        if (oldOffset <= 0 || newOffset <= 0) {
+            throw new IllegalArgumentException();
+        }
+        annotationOffsets.put(oldOffset, newOffset);
+    }
+
+    public void putAnnotationSetOffset(int oldOffset, int newOffset) {
+        if (oldOffset <= 0 || newOffset <= 0) {
+            throw new IllegalArgumentException();
+        }
+        annotationSetOffsets.put(oldOffset, newOffset);
+    }
+
+    public void putAnnotationDirectoryOffset(int oldOffset, int newOffset) {
+        if (oldOffset <= 0 || newOffset <= 0) {
+            throw new IllegalArgumentException();
+        }
+        annotationDirectoryOffsets.put(oldOffset, newOffset);
+    }
+
+    public void putStaticValuesOffset(int oldOffset, int newOffset) {
+        if (oldOffset <= 0 || newOffset <= 0) {
+            throw new IllegalArgumentException();
+        }
+        staticValuesOffsets.put(oldOffset, newOffset);
+    }
+
+    public int adjustString(int stringIndex) {
+        return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex];
+    }
+
+    public int adjustType(int typeIndex) {
+        return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff);
+    }
+
+    public TypeList adjustTypeList(TypeList typeList) {
+        if (typeList == TypeList.EMPTY) {
+            return typeList;
+        }
+        short[] types = typeList.getTypes().clone();
+        for (int i = 0; i < types.length; i++) {
+            types[i] = (short) adjustType(types[i]);
+        }
+        return new TypeList(target, types);
+    }
+
+    public int adjustProto(int protoIndex) {
+        return protoIds[protoIndex] & 0xffff;
+    }
+
+    public int adjustField(int fieldIndex) {
+        return fieldIds[fieldIndex] & 0xffff;
+    }
+
+    public int adjustMethod(int methodIndex) {
+        return methodIds[methodIndex] & 0xffff;
+    }
+
+    public int adjustTypeListOffset(int typeListOffset) {
+        return typeListOffsets.get(typeListOffset);
+    }
+
+    public int adjustAnnotation(int annotationOffset) {
+        return annotationOffsets.get(annotationOffset);
+    }
+
+    public int adjustAnnotationSet(int annotationSetOffset) {
+        return annotationSetOffsets.get(annotationSetOffset);
+    }
+
+    public int adjustAnnotationDirectory(int annotationDirectoryOffset) {
+        return annotationDirectoryOffsets.get(annotationDirectoryOffset);
+    }
+
+    public int adjustStaticValues(int staticValuesOffset) {
+        return staticValuesOffsets.get(staticValuesOffset);
+    }
+
+    public MethodId adjust(MethodId methodId) {
+        return new MethodId(target,
+                adjustType(methodId.getDeclaringClassIndex()),
+                adjustProto(methodId.getProtoIndex()),
+                adjustString(methodId.getNameIndex()));
+    }
+
+    public FieldId adjust(FieldId fieldId) {
+        return new FieldId(target,
+                adjustType(fieldId.getDeclaringClassIndex()),
+                adjustType(fieldId.getTypeIndex()),
+                adjustString(fieldId.getNameIndex()));
+
+    }
+
+    public ProtoId adjust(ProtoId protoId) {
+        return new ProtoId(target,
+                adjustString(protoId.getShortyIndex()),
+                adjustType(protoId.getReturnTypeIndex()),
+                adjustTypeListOffset(protoId.getParametersOffset()));
+    }
+
+    public ClassDef adjust(ClassDef classDef) {
+        return new ClassDef(target, classDef.getOffset(), adjustType(classDef.getTypeIndex()),
+                classDef.getAccessFlags(), adjustType(classDef.getSupertypeIndex()),
+                adjustTypeListOffset(classDef.getInterfacesOffset()), classDef.getSourceFileIndex(),
+                classDef.getAnnotationsOffset(), classDef.getClassDataOffset(),
+                classDef.getStaticValuesOffset());
+    }
+
+    public SortableType adjust(SortableType sortableType) {
+        return new SortableType(sortableType.getBuffer(), adjust(sortableType.getClassDef()));
+    }
+
+    public EncodedValue adjustEncodedValue(EncodedValue encodedValue) {
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32);
+        new EncodedValueTransformer(encodedValue, out).readValue();
+        return new EncodedValue(out.toByteArray());
+    }
+
+    public EncodedValue adjustEncodedArray(EncodedValue encodedArray) {
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32);
+        new EncodedValueTransformer(encodedArray, out).readArray();
+        return new EncodedValue(out.toByteArray());
+    }
+
+    public Annotation adjust(Annotation annotation) {
+        int[] names = annotation.getNames().clone();
+        EncodedValue[] values = annotation.getValues().clone();
+        for (int i = 0; i < names.length; i++) {
+            names[i] = adjustString(names[i]);
+            values[i] = adjustEncodedValue(values[i]);
+        }
+        return new Annotation(target, annotation.getVisibility(),
+                adjustType(annotation.getTypeIndex()), names, values);
+    }
+
+    /**
+     * Adjust an encoded value or array.
+     */
+    private final class EncodedValueTransformer extends EncodedValueReader {
+        private final ByteOutput out;
+
+        public EncodedValueTransformer(EncodedValue encodedValue, ByteOutput out) {
+            super(encodedValue);
+            this.out = out;
+        }
+
+        protected void visitArray(int size) {
+            Leb128Utils.writeUnsignedLeb128(out, size);
+        }
+
+        protected void visitAnnotation(int typeIndex, int size) {
+            Leb128Utils.writeUnsignedLeb128(out, adjustType(typeIndex));
+            Leb128Utils.writeUnsignedLeb128(out, size);
+        }
+
+        protected void visitAnnotationName(int index) {
+            Leb128Utils.writeUnsignedLeb128(out, adjustString(index));
+        }
+
+        protected void visitPrimitive(int argAndType, int type, int arg, int size) {
+            out.writeByte(argAndType);
+            copyBytes(in, out, size);
+        }
+
+        protected void visitString(int type, int index) {
+            writeTypeAndSizeAndIndex(type, adjustString(index));
+        }
+
+        protected void visitType(int type, int index) {
+            writeTypeAndSizeAndIndex(type, adjustType(index));
+        }
+
+        protected void visitField(int type, int index) {
+            writeTypeAndSizeAndIndex(type, adjustField(index));
+        }
+
+        protected void visitMethod(int type, int index) {
+            writeTypeAndSizeAndIndex(type, adjustMethod(index));
+        }
+
+        protected void visitArrayValue(int argAndType) {
+            out.writeByte(argAndType);
+        }
+
+        protected void visitAnnotationValue(int argAndType) {
+            out.writeByte(argAndType);
+        }
+
+        protected void visitEncodedBoolean(int argAndType) {
+            out.writeByte(argAndType);
+        }
+
+        protected void visitEncodedNull(int argAndType) {
+            out.writeByte(argAndType);
+        }
+
+        private void writeTypeAndSizeAndIndex(int type, int index) {
+            int byteCount;
+            if (Unsigned.compare(index, 0xff) <= 0) {
+                byteCount = 1;
+            } else if (Unsigned.compare(index, 0xffff) <= 0) {
+                byteCount = 2;
+            } else if (Unsigned.compare(index, 0xffffff) <= 0) {
+                byteCount = 3;
+            } else {
+                byteCount = 4;
+            }
+            int argAndType = ((byteCount - 1) << 5) | type;
+            out.writeByte(argAndType);
+
+            for (int i = 0; i < byteCount; i++) {
+                out.writeByte(index & 0xff);
+                index >>>= 8;
+            }
+        }
+
+        private void copyBytes(ByteInput in, ByteOutput out, int size) {
+            for (int i = 0; i < size; i++) {
+                out.writeByte(in.readByte());
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/merge/InstructionTransformer.java b/dx/src/com/android/dx/merge/InstructionTransformer.java
new file mode 100644
index 0000000..6051e17
--- /dev/null
+++ b/dx/src/com/android/dx/merge/InstructionTransformer.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+import com.android.dx.io.CodeReader;
+import com.android.dx.io.Opcodes;
+import com.android.dx.io.instructions.DecodedInstruction;
+import com.android.dx.io.instructions.ShortArrayCodeOutput;
+import com.android.dx.util.DexException;
+
+final class InstructionTransformer {
+    private final IndexMap indexMap;
+    private final CodeReader reader;
+    private DecodedInstruction[] mappedInstructions;
+    private int mappedAt;
+
+    public InstructionTransformer(IndexMap indexMap) {
+        this.indexMap = indexMap;
+        this.reader = new CodeReader();
+        this.reader.setAllVisitors(new GenericVisitor());
+        this.reader.setStringVisitor(new StringVisitor());
+        this.reader.setTypeVisitor(new TypeVisitor());
+        this.reader.setFieldVisitor(new FieldVisitor());
+        this.reader.setMethodVisitor(new MethodVisitor());
+    }
+
+    public short[] transform(short[] encodedInstructions) throws DexException {
+        DecodedInstruction[] decodedInstructions =
+            DecodedInstruction.decodeAll(encodedInstructions);
+        int size = decodedInstructions.length;
+
+        mappedInstructions = new DecodedInstruction[size];
+        mappedAt = 0;
+        reader.visitAll(decodedInstructions);
+
+        ShortArrayCodeOutput out = new ShortArrayCodeOutput(size);
+        for (DecodedInstruction instruction : mappedInstructions) {
+            if (instruction != null) {
+                instruction.encode(out);
+            }
+        }
+
+        return out.getArray();
+    }
+
+    private class GenericVisitor implements CodeReader.Visitor {
+        public void visit(DecodedInstruction[] all, DecodedInstruction one) {
+            mappedInstructions[mappedAt++] = one;
+        }
+    }
+
+    private class StringVisitor implements CodeReader.Visitor {
+        public void visit(DecodedInstruction[] all, DecodedInstruction one) {
+            int stringId = one.getIndex();
+            int mappedId = indexMap.adjustString(stringId);
+            boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
+            jumboCheck(isJumbo, mappedId);
+            mappedInstructions[mappedAt++] = one.withIndex(mappedId);
+        }
+    }
+
+    private class FieldVisitor implements CodeReader.Visitor {
+        public void visit(DecodedInstruction[] all, DecodedInstruction one) {
+            int fieldId = one.getIndex();
+            int mappedId = indexMap.adjustField(fieldId);
+            boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
+            jumboCheck(isJumbo, mappedId);
+            mappedInstructions[mappedAt++] = one.withIndex(mappedId);
+        }
+    }
+
+    private class TypeVisitor implements CodeReader.Visitor {
+        public void visit(DecodedInstruction[] all, DecodedInstruction one) {
+            int typeId = one.getIndex();
+            int mappedId = indexMap.adjustType(typeId);
+            boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
+            jumboCheck(isJumbo, mappedId);
+            mappedInstructions[mappedAt++] = one.withIndex(mappedId);
+        }
+    }
+
+    private class MethodVisitor implements CodeReader.Visitor {
+        public void visit(DecodedInstruction[] all, DecodedInstruction one) {
+            int methodId = one.getIndex();
+            int mappedId = indexMap.adjustMethod(methodId);
+            boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO);
+            jumboCheck(isJumbo, mappedId);
+            mappedInstructions[mappedAt++] = one.withIndex(mappedId);
+        }
+    }
+
+    private static void jumboCheck(boolean isJumbo, int newIndex) {
+        if (!isJumbo && (newIndex > 0xffff)) {
+            throw new DexException("Cannot merge new index " + newIndex +
+                                   " into a non-jumbo instruction!");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/merge/SortableType.java b/dx/src/com/android/dx/merge/SortableType.java
new file mode 100644
index 0000000..838ea28
--- /dev/null
+++ b/dx/src/com/android/dx/merge/SortableType.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+import com.android.dx.io.ClassDef;
+import com.android.dx.io.DexBuffer;
+import java.util.Comparator;
+
+/**
+ * Name and structure of a type. Used to order types such that each type is
+ * preceded by its supertype and implemented interfaces.
+ */
+final class SortableType {
+    public static final Comparator<SortableType> NULLS_LAST_ORDER = new Comparator<SortableType>() {
+        public int compare(SortableType a, SortableType b) {
+            if (a == b) {
+                return 0;
+            }
+            if (b == null) {
+                return -1;
+            }
+            if (a == null) {
+                return 1;
+            }
+            if (a.depth != b.depth) {
+                return a.depth - b.depth;
+            }
+            return a.getTypeIndex() - b.getTypeIndex();
+        }
+    };
+
+    private final DexBuffer buffer;
+    private ClassDef classDef;
+    private int depth = -1;
+
+    public SortableType(DexBuffer buffer, ClassDef classDef) {
+        this.buffer = buffer;
+        this.classDef = classDef;
+    }
+
+    public DexBuffer getBuffer() {
+        return buffer;
+    }
+
+    public ClassDef getClassDef() {
+        return classDef;
+    }
+
+    public int getTypeIndex() {
+        return classDef.getTypeIndex();
+    }
+
+    /**
+     * Assigns this type's depth if the depths of its supertype and implemented
+     * interfaces are known. Returns false if the depth couldn't be computed
+     * yet.
+     */
+    public boolean tryAssignDepth(SortableType[] types) {
+        int max;
+        if (classDef.getSupertypeIndex() == ClassDef.NO_INDEX) {
+            max = 0; // this is Object.class or an interface
+        } else {
+            SortableType sortableSupertype = types[classDef.getSupertypeIndex()];
+            if (sortableSupertype == null) {
+                max = 1; // unknown, so assume it's a root.
+            } else if (sortableSupertype.depth == -1) {
+                return false;
+            } else {
+                max = sortableSupertype.depth;
+            }
+        }
+
+        for (short interfaceIndex : classDef.getInterfaces()) {
+            SortableType implemented = types[interfaceIndex];
+            if (implemented == null) {
+                max = Math.max(max, 1); // unknown, so assume it's a root.
+            } else if (implemented.depth == -1) {
+                return false;
+            } else {
+                max = Math.max(max, implemented.depth);
+            }
+        }
+
+        depth = max + 1;
+        return true;
+    }
+
+    public boolean isDepthAssigned() {
+        return depth != -1;
+    }
+}
diff --git a/dx/src/com/android/dx/merge/TypeList.java b/dx/src/com/android/dx/merge/TypeList.java
new file mode 100644
index 0000000..62984e2
--- /dev/null
+++ b/dx/src/com/android/dx/merge/TypeList.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+import com.android.dx.io.DexBuffer;
+import com.android.dx.util.Unsigned;
+import java.util.Arrays;
+
+public final class TypeList implements Comparable<TypeList> {
+
+    public static final TypeList EMPTY = new TypeList(null, new short[0]);
+
+    private final DexBuffer buffer;
+    private final short[] types;
+
+    public TypeList(DexBuffer buffer, short[] types) {
+        this.buffer = buffer;
+        this.types = types;
+    }
+
+    public short[] getTypes() {
+        return types;
+    }
+
+    public int compareTo(TypeList other) {
+        for (int i = 0; i < types.length && i < other.types.length; i++) {
+            if (types[i] != other.types[i]) {
+                return Unsigned.compare(types[i], other.types[i]);
+            }
+        }
+        return Unsigned.compare(types.length, other.types.length);
+    }
+
+    @Override public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("(");
+        for (int i = 0, typesLength = types.length; i < typesLength; i++) {
+            result.append(buffer != null ? buffer.typeNames().get(types[i]) : types[i]);
+        }
+        result.append(")");
+        return result.toString();
+    }
+}
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..8f9e976
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/Annotation.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstString;
+import com.android.dx.rop.cst.CstType;
+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<CstString, 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<CstString, 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");
+        }
+
+        CstString 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..56c4936
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/NameValuePair.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.rop.annotation;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstString;
+
+/**
+ * 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 CstString 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(CstString name, Constant value) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        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 CstString 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..9d99833
--- /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 " + primarySuccessor + " not in successors " + 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..ef79075
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/BasicBlockList.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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..35ce2f2
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.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.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;
+        }
+
+        // Return false if second source isn't a constant
+        if (! (sourceB.getTypeBearer() instanceof CstInteger)) {
+            // Except for rsub-int (reverse sub) where first source is constant
+            if (sourceA.getTypeBearer() instanceof CstInteger &&
+                    opcode.getOpcode() == RegOps.SUB) {
+                CstInteger cst = (CstInteger) sourceA.getTypeBearer();
+                return cst.fitsIn16Bits();
+            } else {
+                return false;
+            }
+        }
+
+        CstInteger cst = (CstInteger) sourceB.getTypeBearer();
+
+        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();
+            // No sub-const insn, so check if equivalent add-const fits
+            case RegOps.SUB:
+                CstInteger cst2 = CstInteger.make(-cst.getValue());
+                return cst2.fitsIn16Bits();
+            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..5031f89
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Insn.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 a 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 withSourceLiteral() {
+        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..d238f21
--- /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.CstString;
+
+/**
+ * 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 CstString name;
+
+    /** {@code null-ok;} local variable signature */
+    private final CstString 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(CstString name, CstString 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(CstString name, CstString 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(CstString a, CstString 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 CstString getName() {
+        return name;
+    }
+
+    /**
+     * Gets signature.
+     *
+     * @return {@code null-ok;} signature
+     */
+    public CstString 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..5d2b995
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalVariableInfo.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.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();
+        if (start.size() != 0) {
+            newStart.intersect(specs, true);
+        } else {
+            newStart = specs.mutableCopy();
+        }
+
+        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..3b52efa
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/PlainInsn.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+import com.android.dx.rop.cst.CstInteger;
+
+/**
+ * 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 withSourceLiteral() {
+        RegisterSpecList sources = getSources();
+        int szSources = sources.size();
+
+        if (szSources == 0) {
+            return this;
+        }
+
+        TypeBearer lastType = sources.get(szSources - 1).getTypeBearer();
+
+        if (!lastType.isConstant()) {
+            // Check for reverse subtraction, where first source is constant
+            TypeBearer firstType = sources.get(0).getTypeBearer();
+            if (szSources == 2 && firstType.isConstant()) {
+                Constant cst = (Constant) firstType;
+                RegisterSpecList newSources = sources.withoutFirst();
+                Rop newRop = Rops.ropFor(getOpcode().getOpcode(), getResult(),
+                                             newSources, cst);
+                return new PlainCstInsn(newRop, getPosition(), getResult(),
+                                            newSources, cst);
+            }
+            return this;
+        } else {
+
+            Constant cst = (Constant) lastType;
+
+            RegisterSpecList newSources = sources.withoutLast();
+
+            Rop newRop;
+            try {
+                // Check for constant subtraction and flip it to be addition
+                int opcode = getOpcode().getOpcode();
+                if (opcode == RegOps.SUB && cst instanceof CstInteger) {
+                    opcode = RegOps.ADD;
+                    cst = CstInteger.make(-((CstInteger)cst).getValue());
+                }
+                newRop = Rops.ropFor(opcode, getResult(), newSources, cst);
+            } 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..c81398d
--- /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 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..e5f908b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.CstString;
+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) {
+        synchronized (theInterns) {
+            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 CstString)) {
+                sb.append(((CstString) type).toQuoted());
+            } else if (human && (type instanceof Constant)) {
+                sb.append(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..3d891fd
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecList.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+import java.util.BitSet;
+
+/**
+ * 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 a new instance, which contains a subset of the elements
+     * specified by the given BitSet. Indexes in the BitSet with a zero
+     * are included, while indexes with a one are excluded. Mutability
+     * of the result is inherited from the original.
+     *
+     * @param exclusionSet {@code non-null;} set of registers to exclude
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecList subset(BitSet exclusionSet) {
+        int newSize = size() - exclusionSet.cardinality();
+
+        if (newSize == 0) {
+            return EMPTY;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(newSize);
+
+        int newIndex = 0;
+        for (int oldIndex = 0; oldIndex < size(); oldIndex++) {
+            if (!exclusionSet.get(oldIndex)) {
+                result.set0(newIndex, get0(oldIndex));
+                newIndex++;
+            }
+        }
+
+        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 incompatible register numbers are renumbered sequentially from
+     * the given base, with the first number duplicated if indicated. If
+     * a null BitSet is given, it indicates all registers are compatible.
+     *
+     * @param base the base register number
+     * @param duplicateFirst whether to duplicate the first number
+     * @param compatRegs {@code null-ok;} either a {@code non-null} set of
+     * compatible registers, or {@code null} to indicate all registers are
+     * compatible
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecList withExpandedRegisters(int base,
+                                                  boolean duplicateFirst,
+                                                  BitSet compatRegs) {
+        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);
+            boolean replace = (compatRegs == null) ? true : !compatRegs.get(i);
+
+            if (replace) {
+                result.set0(i, one.withReg(base));
+                if (!duplicateFirst) {
+                    base += one.getCategory();
+                }
+            } else {
+                result.set0(i, one);
+            }
+
+            if (duplicateFirst) {
+                duplicateFirst = false;
+            }
+        }
+
+        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..14ef778
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * 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..c1f4f46
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Rops.java
@@ -0,0 +1,2090 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+                    default:             break;
+                }
+            }
+            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;
+                    default:             break;
+                }
+            }
+            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;
+                    default:             break;
+                }
+            }
+            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;
+                    default:             break;
+                }
+            }
+        }
+
+        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..11ac992
--- /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.CstString;
+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 CstString 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(CstString 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 CstString 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..dee01b5
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.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.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstString;
+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() {
+        Constant cst = getConstant();
+        String constantString = cst.toHuman();
+        if (cst instanceof CstString) {
+            constantString = ((CstString) cst).toQuoted();
+        }
+        return constantString + " " + 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..2766b5f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstArray.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.
+ */
+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) {
+            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..a4d7180
--- /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;
+        CstString thisDescriptor = getNat().getDescriptor();
+        CstString 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..1775398
--- /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;
+        }
+
+        CstString thisName = nat.getName();
+        CstString 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..cd067e9
--- /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 CstString("TYPE"),
+                   new CstString("Ljava/lang/Class;"));
+
+    /** {@code non-null;} the name */
+    private final CstString name;
+
+    /** {@code non-null;} the descriptor (type) */
+    private final CstString descriptor;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param name {@code non-null;} the name
+     * @param descriptor {@code non-null;} the descriptor
+     */
+    public CstNat(CstString name, CstString 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 CstString getName() {
+        return name;
+    }
+
+    /**
+     * Gets the descriptor.
+     *
+     * @return {@code non-null;} the descriptor
+     */
+    public CstString 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..a778e97
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstString.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Utf8_info} or {@code CONSTANT_String_info}.
+ */
+public final class CstString extends TypedConstant {
+    /**
+     * {@code non-null;} instance representing {@code ""}, that is, the
+     * empty string
+     */
+    public static final CstString EMPTY_STRING = new CstString("");
+
+    /** {@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 MUTF-8 form. MUTF-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 CstString(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 CstString(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 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} */
+    @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();
+    }
+
+    public Type getType() {
+        return Type.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..870fcb4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstType.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 CstString 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) {
+        synchronized (interns) {
+            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 CstString getDescriptor() {
+        if (descriptor == null) {
+            descriptor = new CstString(type.getDescriptor());
+        }
+
+        return descriptor;
+    }
+
+    /**
+     * Returns a human readable package name for this type, like "java.util".
+     * If this is an array type, this returns the package name of the array's
+     * component type. If this is a primitive type, this returns "default".
+     */
+    public String getPackageName() {
+        // descriptor is a string like "[[Ljava/util/String;"
+        String descriptor = getDescriptor().getString();
+        int lastSlash = descriptor.lastIndexOf('/');
+        int lastLeftSquare = descriptor.lastIndexOf('['); // -1 unless this is an array
+        if (lastSlash == -1) {
+            return "default";
+        } else {
+            // +2 to skip the '[' and the 'L' prefix
+            return descriptor.substring(lastLeftSquare + 2, lastSlash).replace('/', '.');
+        }
+    }
+}
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..1c738ee
--- /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 implementation 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..3fc5d82
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Prototype.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 descriptor. 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;
+        synchronized (internTable) {
+            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..c564fab
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Type.java
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+        synchronized (internTable) {
+            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: " + 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: " + descriptor);
+                }
+                case '/': {
+                    if ((i == 1) ||
+                        (i == (length - 1)) ||
+                        (descriptor.charAt(i - 1) == '/')) {
+                        throw new IllegalArgumentException("bad descriptor: " + 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..62d629f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/ConstCollector.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.ThrowingCstInsn;
+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.TypeBearer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+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 || insn.getOpcode() == null) continue;
+
+            RegisterSpec result = insn.getResult();
+            TypeBearer typeBearer = result.getTypeBearer();
+
+            if (!typeBearer.isConstant()) continue;
+
+            TypedConstant cst = (TypedConstant) typeBearer;
+
+            // Find defining instruction for move-result-pseudo instructions
+            if (insn.getOpcode().getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
+                int pred = insn.getBlock().getPredecessors().nextSetBit(0);
+                ArrayList<SsaInsn> predInsns;
+                predInsns = ssaMeth.getBlocks().get(pred).getInsns();
+                insn = predInsns.get(predInsns.size()-1);
+            }
+
+            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;
+            }
+
+            @Override
+            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..07fb553
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/DeadCodeRemover.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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() {
+        pruneDeadInstructions();
+
+        HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
+
+        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);
+    }
+
+    /**
+     * Removes all instructions from every unreachable block.
+     */
+    private void pruneDeadInstructions() {
+        HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
+
+        ssaMeth.computeReachability();
+
+        for (SsaBasicBlock block : ssaMeth.getBlocks()) {
+            if (block.isReachable()) continue;
+
+            // Prune instructions from unreachable blocks
+            for (int i = 0; i < block.getInsns().size(); i++) {
+                SsaInsn insn = block.getInsns().get(i);
+                RegisterSpecList sources = insn.getSources();
+                int sourcesSize = sources.size();
+
+                // Delete this instruction completely if it has sources
+                if (sourcesSize != 0) {
+                    deletedInsns.add(insn);
+                }
+
+                // Delete this instruction from all usage lists.
+                for (int j = 0; j < sourcesSize; j++) {
+                    RegisterSpec source = sources.get(j);
+                    useList[source.getReg()].remove(insn);
+                }
+
+                // Remove this instruction result from the sources of any phis
+                RegisterSpec result = insn.getResult();
+                if (result == null) continue;
+                for (SsaInsn use : useList[result.getReg()]) {
+                    if (use instanceof PhiInsn) {
+                        PhiInsn phiUse = (PhiInsn) use;
+                        phiUse.removePhiRegister(result);
+                    }
+                }
+            }
+        }
+
+        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..b02c929
--- /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.CstString;
+import com.android.dx.rop.cst.CstType;
+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 CstString("<init>"), new CstString("(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..f3976f2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.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.ssa;
+
+import com.android.dx.rop.code.PlainCstInsn;
+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.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Upgrades insn to their literal (constant-immediate) equivalent 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();
+
+                // Replace insns with constant results with const insns
+                if (tryReplacingWithConstant(insn)) return;
+
+                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()), null);
+                    } else if (isConstIntZeroOrKnownNull(sources.get(1))) {
+                        replacePlainInsn(insn, sources.withoutLast(),
+                              opcode.getOpcode(), null);
+                    }
+                } 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();
+                }
+            }
+        });
+    }
+
+    /**
+     * Tries to replace an instruction with a const instruction. The given
+     * instruction must have a constant result for it to be replaced.
+     *
+     * @param insn {@code non-null;} instruction to try to replace
+     * @return true if the instruction was replaced
+     */
+    private boolean tryReplacingWithConstant(NormalSsaInsn insn) {
+        Insn originalRopInsn = insn.getOriginalRopInsn();
+        Rop opcode = originalRopInsn.getOpcode();
+        RegisterSpec result = insn.getResult();
+
+        if (result != null && !ssaMeth.isRegALocal(result) &&
+                opcode.getOpcode() != RegOps.CONST) {
+            TypeBearer type = insn.getResult().getTypeBearer();
+            if (type.isConstant() && type.getBasicType() == Type.BT_INT) {
+                // Replace the instruction with a constant
+                replacePlainInsn(insn, RegisterSpecList.EMPTY,
+                        RegOps.CONST, (Constant) type);
+
+                // Remove the source as well if this is a move-result-pseudo
+                if (opcode.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
+                    int pred = insn.getBlock().getPredecessors().nextSetBit(0);
+                    ArrayList<SsaInsn> predInsns =
+                            ssaMeth.getBlocks().get(pred).getInsns();
+                    NormalSsaInsn sourceInsn =
+                            (NormalSsaInsn) predInsns.get(predInsns.size()-1);
+                    replacePlainInsn(sourceInsn, RegisterSpecList.EMPTY,
+                            RegOps.GOTO, null);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The
+     * new PlainInsn is constructed 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}
+     * @param cst {@code null-ok;} constant for new instruction, if any
+     */
+    private void replacePlainInsn(NormalSsaInsn insn,
+            RegisterSpecList newSources, int newOpcode, Constant cst) {
+
+        Insn originalRopInsn = insn.getOriginalRopInsn();
+        Rop newRop = Rops.ropFor(newOpcode, insn.getResult(), newSources, cst);
+        Insn newRopInsn;
+        if (cst == null) {
+            newRopInsn = new PlainInsn(newRop, originalRopInsn.getPosition(),
+                    insn.getResult(), newSources);
+        } else {
+            newRopInsn = new PlainCstInsn(newRop, originalRopInsn.getPosition(),
+                    insn.getResult(), newSources, cst);
+        }
+        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..cfef400
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/NormalSsaInsn.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.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
+     */
+    @Override
+    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} */
+    @Override
+    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 source
+     * literally. If the upgrade is not possible, this does nothing.
+     *
+     * @see Insn#withSourceLiteral
+     */
+    public void upgradeToLiteral() {
+        RegisterSpecList oldSources = insn.getSources();
+
+        insn = insn.withSourceLiteral();
+        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..42ae166
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/Optimizer.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.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);
+            DeadCodeRemover.process(ssaMeth);
+            needsDeadCodeRemover = false;
+        }
+
+        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..bc9c4b0
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/PhiInsn.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.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} */
+    @Override
+    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;
+    }
+
+    /**
+     * Removes all operand uses of a register from this phi instruction.
+     *
+     * @param registerSpec register spec, including type and reg of operand
+     */
+    public void removePhiRegister(RegisterSpec registerSpec) {
+        ArrayList<Operand> operandsToRemove = new ArrayList<Operand>();
+        for (Operand o : operands) {
+            if (o.regSpec.getReg() == registerSpec.getReg()) {
+                operandsToRemove.add(o);
+            }
+        }
+
+        operands.removeAll(operandsToRemove);
+
+        // 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
+     */
+    @Override
+    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..1c869e1
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SCCP.java
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.PlainInsn;
+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.code.Rops;
+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;
+    /** Worklist of executed basic blocks with phis to be processed */
+    private ArrayList<SsaBasicBlock> cfgPhiWorklist;
+    /** 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;
+    /** Worklist of potential branches to convert to gotos */
+    private ArrayList<SsaInsn> branchWorklist;
+
+    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.cfgPhiWorklist = new ArrayList<SsaBasicBlock>();
+        this.executableBlocks = new BitSet(ssaMeth.getBlocks().size());
+        this.ssaWorklist = new ArrayList<SsaInsn>();
+        this.varyingWorklist = new ArrayList<SsaInsn>();
+        this.branchWorklist = 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();
+    }
+
+    /**
+     * Adds a SSA basic block to the CFG worklist if it's unexecuted, or
+     * to the CFG phi worklist if it's already executed.
+     * @param ssaBlock Block to add
+     */
+    private void addBlockToWorklist(SsaBasicBlock ssaBlock) {
+        if (!executableBlocks.get(ssaBlock.getIndex())) {
+            cfgWorklist.add(ssaBlock);
+            executableBlocks.set(ssaBlock.getIndex());
+        } else {
+            cfgPhiWorklist.add(ssaBlock);
+        }
+    }
+
+    /**
+     * 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 appropriate value.
+     * Meet values:
+     * TOP x anything = TOP
+     * 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)) {
+                continue;
+            }
+
+            if (sourceRegValue == CONSTANT) {
+                if (phiConstant == null) {
+                    phiConstant = latticeConstants[sourceReg];
+                    phiResultValue = CONSTANT;
+                 } else if (!latticeConstants[sourceReg].equals(phiConstant)){
+                    phiResultValue = VARYING;
+                    break;
+                }
+            } else {
+                phiResultValue = sourceRegValue;
+                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);
+            }
+        }
+    }
+
+    /**
+     * Simulate the phis in a block and note the results in the lattice.
+     * @param block Block to visit
+     */
+    private void simulatePhiBlock(SsaBasicBlock block) {
+        for (SsaInsn insn : block.getInsns()) {
+            if (insn instanceof PhiInsn) {
+                simulatePhi((PhiInsn) insn);
+            } else {
+                return;
+            }
+        }
+    }
+
+    private static String latticeValName(int latticeVal) {
+        switch (latticeVal) {
+            case TOP: return "TOP";
+            case CONSTANT: return "CONSTANT";
+            case VARYING: return "VARYING";
+            default: return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Simulates branch insns, if possible. Adds reachable successor blocks
+     * to the CFG worklists.
+     * @param insn branch to simulate
+     */
+    private void simulateBranch(SsaInsn insn) {
+        Rop opcode = insn.getOpcode();
+        RegisterSpecList sources = insn.getSources();
+
+        boolean constantBranch = false;
+        boolean constantSuccessor = false;
+
+        // Check if the insn is a branch with a constant condition
+        if (opcode.getBranchingness() == Rop.BRANCH_IF) {
+            Constant cA = null;
+            Constant cB = null;
+
+            RegisterSpec specA = sources.get(0);
+            int regA = specA.getReg();
+            if (!ssaMeth.isRegALocal(specA) &&
+                    latticeValues[regA] == CONSTANT) {
+                cA = latticeConstants[regA];
+            }
+
+            if (sources.size() == 2) {
+                RegisterSpec specB = sources.get(1);
+                int regB = specB.getReg();
+                if (!ssaMeth.isRegALocal(specB) &&
+                        latticeValues[regB] == CONSTANT) {
+                    cB = latticeConstants[regB];
+                }
+            }
+
+            // Calculate the result of the condition
+            if (cA != null && sources.size() == 1) {
+                switch (((TypedConstant) cA).getBasicType()) {
+                    case Type.BT_INT:
+                        constantBranch = true;
+                        int vA = ((CstInteger) cA).getValue();
+                        switch (opcode.getOpcode()) {
+                            case RegOps.IF_EQ:
+                                constantSuccessor = (vA == 0);
+                                break;
+                            case RegOps.IF_NE:
+                                constantSuccessor = (vA != 0);
+                                break;
+                            case RegOps.IF_LT:
+                                constantSuccessor = (vA < 0);
+                                break;
+                            case RegOps.IF_GE:
+                                constantSuccessor = (vA >= 0);
+                                break;
+                            case RegOps.IF_LE:
+                                constantSuccessor = (vA <= 0);
+                                break;
+                            case RegOps.IF_GT:
+                                constantSuccessor = (vA > 0);
+                                break;
+                            default:
+                                throw new RuntimeException("Unexpected op");
+                        }
+                        break;
+                    default:
+                        // not yet supported
+                }
+            } else if (cA != null && cB != null) {
+                switch (((TypedConstant) cA).getBasicType()) {
+                    case Type.BT_INT:
+                        constantBranch = true;
+                        int vA = ((CstInteger) cA).getValue();
+                        int vB = ((CstInteger) cB).getValue();
+                        switch (opcode.getOpcode()) {
+                            case RegOps.IF_EQ:
+                                constantSuccessor = (vA == vB);
+                                break;
+                            case RegOps.IF_NE:
+                                constantSuccessor = (vA != vB);
+                                break;
+                            case RegOps.IF_LT:
+                                constantSuccessor = (vA < vB);
+                                break;
+                            case RegOps.IF_GE:
+                                constantSuccessor = (vA >= vB);
+                                break;
+                            case RegOps.IF_LE:
+                                constantSuccessor = (vA <= vB);
+                                break;
+                            case RegOps.IF_GT:
+                                constantSuccessor = (vA > vB);
+                                break;
+                            default:
+                                throw new RuntimeException("Unexpected op");
+                        }
+                        break;
+                    default:
+                        // not yet supported
+                }
+            }
+        }
+
+        /*
+         * If condition is constant, add only the target block to the
+         * worklist. Otherwise, add all successors to the worklist.
+         */
+        SsaBasicBlock block = insn.getBlock();
+
+        if (constantBranch) {
+            int successorBlock;
+            if (constantSuccessor) {
+                successorBlock = block.getSuccessorList().get(1);
+            } else {
+                successorBlock = block.getSuccessorList().get(0);
+            }
+            addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock));
+            branchWorklist.add(insn);
+        } else {
+            for (int i = 0; i < block.getSuccessorList().size(); i++) {
+                int successorBlock = block.getSuccessorList().get(i);
+                addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock));
+            }
+        }
+    }
+
+    /**
+     * Simulates math insns, if possible.
+     *
+     * @param insn non-null insn to simulate
+     * @param resultType basic type of the result
+     * @return constant result or null if not simulatable.
+     */
+    private Constant simulateMath(SsaInsn insn, int resultType) {
+        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 (resultType) {
+            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:
+                        // 1 source for reverse sub, 2 sources for regular sub
+                        if (sources.size() == 1) {
+                            vR = vB - vA;
+                        } else {
+                            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:
+                        if (vB == 0) {
+                            skip = true;
+                            vR = 0; // just to hide a warning
+                        } else {
+                            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()) {
+            simulateBranch(insn);
+        }
+
+        int opcode = insn.getOpcode().getOpcode();
+        RegisterSpec result = insn.getResult();
+
+        if (result == null) {
+            // Find move-result-pseudo result for int div and int rem
+            if (opcode == RegOps.DIV || opcode == RegOps.REM) {
+                SsaBasicBlock succ = insn.getBlock().getPrimarySuccessor();
+                result = succ.getInsns().get(0).getResult();
+            } else {
+                return;
+            }
+        }
+
+        int resultReg = result.getReg();
+        int resultValue = VARYING;
+        Constant resultConstant = null;
+
+        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, result.getBasicType());
+                if (resultConstant != null) {
+                    resultValue = CONSTANT;
+                }
+                break;
+            }
+            case RegOps.MOVE_RESULT_PSEUDO: {
+                if (latticeValues[resultReg] == CONSTANT) {
+                    resultValue = latticeValues[resultReg];
+                    resultConstant = latticeConstants[resultReg];
+                }
+                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()
+                || !cfgPhiWorklist.isEmpty()
+                || !ssaWorklist.isEmpty()
+                || !varyingWorklist.isEmpty()) {
+            while (!cfgWorklist.isEmpty()) {
+                int listSize = cfgWorklist.size() - 1;
+                SsaBasicBlock block = cfgWorklist.remove(listSize);
+                simulateBlock(block);
+            }
+
+            while (!cfgPhiWorklist.isEmpty()) {
+                int listSize = cfgPhiWorklist.size() - 1;
+                SsaBasicBlock block = cfgPhiWorklist.remove(listSize);
+                simulatePhiBlock(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();
+        replaceBranches();
+    }
+
+    /**
+     * 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 destination RegisterSpec with the constant value
+            RegisterSpec dest = defn.getResult();
+            RegisterSpec newDest
+                    = dest.withType((TypedConstant)latticeConstants[reg]);
+            defn.setResult(newDest);
+
+            /*
+             * 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);
+            }
+        }
+    }
+
+    /**
+     * Replaces branches that have constant conditions with gotos
+     */
+    private void replaceBranches() {
+        for (SsaInsn insn : branchWorklist) {
+            // Find if a successor block is never executed
+            int oldSuccessor = -1;
+            SsaBasicBlock block = insn.getBlock();
+            int successorSize = block.getSuccessorList().size();
+            for (int i = 0; i < successorSize; i++) {
+                int successorBlock = block.getSuccessorList().get(i);
+                if (!executableBlocks.get(successorBlock)) {
+                    oldSuccessor = successorBlock;
+                }
+            }
+
+            /*
+             * Prune branches that have already been handled and ones that no
+             * longer have constant conditions (no nonexecutable successors)
+             */
+            if (successorSize != 2 || oldSuccessor == -1) continue;
+
+            // Replace branch with goto
+            Insn originalRopInsn = insn.getOriginalRopInsn();
+            block.replaceLastInsn(new PlainInsn(Rops.GOTO,
+                originalRopInsn.getPosition(), null, RegisterSpecList.EMPTY));
+            block.removeSuccessor(oldSuccessor);
+        }
+    }
+}
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/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..9ef95a7
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java
@@ -0,0 +1,1139 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+    /** list of phi instructions seen in this method */
+    private final ArrayList<PhiInsn> phiInsns;
+
+    /** 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>();
+        phiInsns = new ArrayList<PhiInsn>();
+    }
+
+    /** {@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 phis");
+        handlePhiInsns();
+
+        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 = paramRangeEnd;
+
+            boolean done = false;
+            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);
+                if (canMapRegs(specs, ropReg)) {
+                    done = tryMapRegs(specs, ropReg, maxCategory, true);
+                }
+
+                // Increment for next call to findRopRegForLocal.
+                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) {
+        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) {
+        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.
+     * Inserts a move if it can't map the same register to both and the
+     * check cast is not caught.
+     */
+    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();
+
+            /**
+             * 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.
+             */
+            int category = checkRegSpec.getCategory();
+            boolean moveMapped = ssaRegsMapped.get(moveReg);
+            boolean checkMapped = ssaRegsMapped.get(checkReg);
+            if (moveMapped & !checkMapped) {
+                int moveRopReg = mapper.oldToNew(moveReg);
+                checkMapped = tryMapReg(checkRegSpec, moveRopReg, category);
+            }
+            if (checkMapped & !moveMapped) {
+                int checkRopReg = mapper.oldToNew(checkReg);
+                moveMapped = tryMapReg(moveRegSpec, checkRopReg, category);
+            }
+
+            // Map any unmapped registers to anything available
+            if (!moveMapped || !checkMapped) {
+                int ropReg = findNextUnreservedRopReg(paramRangeEnd, category);
+                ArrayList<RegisterSpec> ssaRegs =
+                    new ArrayList<RegisterSpec>(2);
+                ssaRegs.add(moveRegSpec);
+                ssaRegs.add(checkRegSpec);
+
+                while (!tryMapRegs(ssaRegs, ropReg, category, false)) {
+                    ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+                }
+            }
+
+            /*
+             * If source and result have a different mapping, insert a move so
+             * they can have the same mapping. Don't do this if the check cast
+             * is caught, since it will overwrite a potentially live value.
+             */
+            boolean hasExceptionHandlers =
+                checkCastInsn.getOriginalRopInsn().getCatches().size() != 0;
+            int moveRopReg = mapper.oldToNew(moveReg);
+            int checkRopReg = mapper.oldToNew(checkReg);
+            if (moveRopReg != checkRopReg && !hasExceptionHandlers) {
+                ((NormalSsaInsn) checkCastInsn).changeOneSource(0,
+                        insertMoveBefore(checkCastInsn, checkRegSpec));
+                addMapping(checkCastInsn.getSources().get(0), moveRopReg);
+            }
+        }
+    }
+
+    /**
+    * Handles all phi instructions, trying to map them to a common register.
+    */
+    private void handlePhiInsns() {
+        for (PhiInsn insn : phiInsns) {
+            processPhiInsn(insn);
+        }
+    }
+
+    /**
+     * 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(paramRangeEnd, category);
+            while (!canMapReg(ssaSpec, ropReg)) {
+                ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+            }
+
+            addMapping(ssaSpec, ropReg);
+        }
+    }
+
+    /**
+     * Checks to see if a list of SSA registers can all be mapped into
+     * the same rop reg. Ignores registers that have already been mapped,
+     * and checks the interference graph and ensures the range does not
+     * cross the parameter range.
+     *
+     * @param specs {@code non-null;} SSA registers to check
+     * @param ropReg {@code >=0;} rop register to check mapping to
+     * @return {@code true} if all unmapped registers can be mapped
+     */
+    private boolean canMapRegs(ArrayList<RegisterSpec> specs, int ropReg) {
+        for (RegisterSpec spec : specs) {
+            if (ssaRegsMapped.get(spec.getReg())) continue;
+            if (!canMapReg(spec, ropReg)) return false;
+        }
+        return true;
+    }
+
+    /**
+     * 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);
+                    }
+                } else if (insn instanceof PhiInsn) {
+                    phiInsns.add((PhiInsn) 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 = paramRangeEnd;
+        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;
+    }
+
+    /**
+     * Attempts to map the sources and result of a phi to a common register.
+     * Will try existing mappings first, from most to least common. If none
+     * of the registers have mappings yet, a new mapping is created.
+     */
+    private void processPhiInsn(PhiInsn insn) {
+        RegisterSpec result = insn.getResult();
+        int resultReg = result.getReg();
+        int category = result.getCategory();
+
+        RegisterSpecList sources = insn.getSources();
+        int sourcesSize = sources.size();
+
+        // List of phi sources / result that need mapping
+        ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>();
+
+        // Track how many times a particular mapping is found
+        Multiset mapSet = new Multiset(sourcesSize + 1);
+
+        /*
+         * If the result of the phi has an existing mapping, get it.
+         * Otherwise, add it to the list of regs that need mapping.
+         */
+        if (ssaRegsMapped.get(resultReg)) {
+            mapSet.add(mapper.oldToNew(resultReg));
+        } else {
+            ssaRegs.add(result);
+        }
+
+        for (int i = 0; i < sourcesSize; i++) {
+            RegisterSpec source = sources.get(i);
+            SsaInsn def = ssaMeth.getDefinitionForRegister(source.getReg());
+            RegisterSpec sourceDef = def.getResult();
+            int sourceReg = sourceDef.getReg();
+
+            /*
+             * If a source of the phi has an existing mapping, get it.
+             * Otherwise, add it to the list of regs that need mapping.
+             */
+            if (ssaRegsMapped.get(sourceReg)) {
+                mapSet.add(mapper.oldToNew(sourceReg));
+            } else {
+                ssaRegs.add(sourceDef);
+            }
+        }
+
+        // Try all existing mappings, with the most common ones first
+        for (int i = 0; i < mapSet.getSize(); i++) {
+            int maxReg = mapSet.getAndRemoveHighestCount();
+            tryMapRegs(ssaRegs, maxReg, category, false);
+        }
+
+        // Map any remaining unmapped regs with whatever fits
+        int mapReg = findNextUnreservedRopReg(paramRangeEnd, category);
+        while (!tryMapRegs(ssaRegs, mapReg, category, false)) {
+            mapReg = findNextUnreservedRopReg(mapReg + 1, category);
+        }
+    }
+
+    // A set that tracks how often elements are added to it.
+    private static class Multiset {
+        private final int[] reg;
+        private final int[] count;
+        private int size;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param maxSize the maximum distinct elements the set may have
+         */
+        public Multiset(int maxSize) {
+            reg = new int[maxSize];
+            count = new int[maxSize];
+            size = 0;
+        }
+
+        /**
+         * Adds an element to the set.
+         *
+         * @param element element to add
+         */
+        public void add(int element) {
+            for (int i = 0; i < size; i++) {
+                if (reg[i] == element) {
+                    count[i]++;
+                    return;
+                }
+            }
+
+            reg[size] = element;
+            count[size] = 1;
+            size++;
+        }
+
+        /**
+         * Searches the set for the element that has been added the most.
+         * In the case of a tie, the element that was added first is returned.
+         * Then, it clears the count on that element. The size of the set
+         * remains unchanged.
+         *
+         * @return element with the highest count
+         */
+        public int getAndRemoveHighestCount() {
+            int maxIndex = -1;
+            int maxReg = -1;
+            int maxCount = 0;
+
+            for (int i = 0; i < size; i++) {
+                if (maxCount < count[i]) {
+                    maxIndex = i;
+                    maxReg = reg[i];
+                    maxCount = count[i];
+                }
+            }
+
+            count[maxIndex] = 0;
+            return maxReg;
+        }
+
+        /**
+         * Gets the number of distinct elements in the set.
+         *
+         * @return size of the set
+         */
+        public int getSize() {
+            return size;
+        }
+    }
+}
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..0e30250
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/SsaToRop.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.InsnList;
+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.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.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Comparator;
+
+/**
+ * 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 and is reachable.
+        ropBlockCount -= (exitBlock != null && exitBlock.isReachable()) ? 1 : 0;
+
+        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..05ec733
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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, ByteOutput {
+    /** 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(DEFAULT_SIZE);
+    }
+
+    /**
+     * Constructs a "stretchy" instance with initial size {@code size}. The
+     * underlying array may be reallocated. The constructed instance does not
+     * keep annotations by default.
+     */
+    public ByteArrayAnnotatedOutput(int size) {
+        this(new byte[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 writeUleb128(int value) {
+        if (stretchy) {
+            ensureCapacity(cursor + 5); // pessimistic
+        }
+        int cursorBefore = cursor;
+        Leb128Utils.writeUnsignedLeb128(this, value);
+        return (cursor - cursorBefore);
+    }
+
+    /** {@inheritDoc} */
+    public int writeSleb128(int value) {
+        if (stretchy) {
+            ensureCapacity(cursor + 5); // pessimistic
+        }
+        int cursorBefore = cursor;
+        Leb128Utils.writeSignedLeb128(this, value);
+        return (cursor - cursorBefore);
+    }
+
+    /** {@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/ByteArrayByteInput.java b/dx/src/com/android/dx/util/ByteArrayByteInput.java
new file mode 100644
index 0000000..07c543f
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArrayByteInput.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+public final class ByteArrayByteInput implements ByteInput {
+
+    private final byte[] bytes;
+    private int position;
+
+    public ByteArrayByteInput(byte... bytes) {
+        this.bytes = bytes;
+    }
+
+    @Override public byte readByte() {
+        return bytes[position++];
+    }
+}
diff --git a/dx/src/com/android/dx/util/ByteInput.java b/dx/src/com/android/dx/util/ByteInput.java
new file mode 100644
index 0000000..8181e3e
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteInput.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * A byte source.
+ */
+public interface ByteInput {
+
+    /**
+     * Returns a byte.
+     *
+     * @throws IndexOutOfBoundsException if all bytes have been read.
+     */
+    byte readByte();
+}
diff --git a/dx/src/com/android/dx/util/ByteOutput.java b/dx/src/com/android/dx/util/ByteOutput.java
new file mode 100644
index 0000000..512dcf8
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteOutput.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * A byte sink.
+ */
+public interface ByteOutput {
+
+    /**
+     * Writes a byte.
+     *
+     * @throws IndexOutOfBoundsException if all bytes have been written.
+     */
+    void writeByte(int i);
+}
diff --git a/dx/src/com/android/dx/util/DexException.java b/dx/src/com/android/dx/util/DexException.java
new file mode 100644
index 0000000..527b0b9
--- /dev/null
+++ b/dx/src/com/android/dx/util/DexException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Thrown when there's a format problem reading, writing, or generally
+ * processing a dex file.
+ */
+public final class DexException extends ExceptionWithContext {
+    public DexException(String message) {
+        super(message);
+    }
+
+    public DexException(Throwable cause) {
+        super(cause);
+    }
+}
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..bcf6729
--- /dev/null
+++ b/dx/src/com/android/dx/util/FileUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+    }
+
+    /**
+     * Returns true if {@code fileName} names a .zip, .jar, or .apk.
+     */
+    public static boolean hasArchiveSuffix(String fileName) {
+        return fileName.endsWith(".zip")
+                || fileName.endsWith(".jar")
+                || fileName.endsWith(".apk");
+    }
+}
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..be400aa
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntList.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+     * @throws 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
+     * @throws 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
+     * @throws 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..963b5aa
--- /dev/null
+++ b/dx/src/com/android/dx/util/LabeledList.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.util;
+
+import java.util.Arrays;
+
+/**
+ * A list of labeled items, allowing easy lookup by label.
+ */
+public class LabeledList extends FixedSizeList {
+    /**
+     * Sparse array indexed by label to FixedSizeList index;
+     * {@code -1} for an 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
+     */
+    public 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 final 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--)
+            /*empty*/ ;
+
+        int newSize = i + 1;
+
+        labelToIndex.shrink(newSize);
+
+        return newSize;
+    }
+
+    /**
+     * Removes a label from the label-to-index mapping.
+     *
+     * @param oldLabel label to remove
+     */
+    private 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.
+     */
+    private 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 final int indexOfLabel(int label) {
+        if (label >= labelToIndex.size()) {
+            return -1;
+        } else {
+            return labelToIndex.get(label);
+        }
+    }
+
+    /**
+     * Gets an array containing all of the labels used in this instance,
+     * in order. The returned array is freshly-allocated and so may be
+     * modified safely by the caller without impacting this instance.
+     *
+     * @return {@code non-null;} ordered array of labels
+     * @throws NullPointerException thrown if there are any {@code null}
+     * items in this instance
+     */
+    public final int[] getLabelsInOrder() {
+        int sz = size();
+        int[] result = new int[sz];
+
+        for (int i = 0; i < sz; i++) {
+            LabeledItem li = (LabeledItem) get0(i);
+            if (li == null) {
+                throw new NullPointerException("null at index " + i);
+            }
+            result[i] = li.getLabel();
+        }
+
+        Arrays.sort(result);
+        return result;
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void shrinkToFit() {
+        super.shrinkToFit();
+
+        rebuildLabelToIndex();
+    }
+
+    /**
+     * Rebuilds the label-to-index mapping after a {@code shrinkToFit()}.
+     * Note: This assumes that the labels that are in the list are the
+     * same, although the indicies may have changed.
+     */
+    private 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..1037463
--- /dev/null
+++ b/dx/src/com/android/dx/util/Leb128Utils.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.util;
+
+/**
+ * Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3
+ * section 7.6.
+ */
+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;
+    }
+
+    /**
+     * Reads an signed integer from {@code in}.
+     */
+    public static int readSignedLeb128(ByteInput in) {
+        int result = 0;
+        int cur;
+        int count = 0;
+        int signBits = -1;
+
+        do {
+            cur = in.readByte() & 0xff;
+            result |= (cur & 0x7f) << (count * 7);
+            signBits <<= 7;
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new DexException("invalid LEB128 sequence");
+        }
+
+        // Sign extend if appropriate
+        if (((signBits >> 1) & result) != 0 ) {
+            result |= signBits;
+        }
+
+        return result;
+    }
+
+    /**
+     * Reads an unsigned integer from {@code in}.
+     */
+    public static int readUnsignedLeb128(ByteInput in) {
+        int result = 0;
+        int cur;
+        int count = 0;
+
+        do {
+            cur = in.readByte() & 0xff;
+            result |= (cur & 0x7f) << (count * 7);
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new DexException("invalid LEB128 sequence");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes {@code value} as an unsigned integer to {@code out}, starting at
+     * {@code offset}. Returns the number of bytes written.
+     */
+    public static void writeUnsignedLeb128(ByteOutput out, int value) {
+        int remaining = value >>> 7;
+
+        while (remaining != 0) {
+            out.writeByte((byte) ((value & 0x7f) | 0x80));
+            value = remaining;
+            remaining >>>= 7;
+        }
+
+        out.writeByte((byte) (value & 0x7f));
+    }
+
+    /**
+     * Writes {@code value} as a signed integer to {@code out}, starting at
+     * {@code offset}. Returns the number of bytes written.
+     */
+    public static void writeSignedLeb128(ByteOutput out, int value) {
+        int remaining = value >> 7;
+        boolean hasMore = true;
+        int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+        while (hasMore) {
+            hasMore = (remaining != end)
+                    || ((remaining & 1) != ((value >> 6) & 1));
+
+            out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0)));
+            value = remaining;
+            remaining >>= 7;
+        }
+    }
+}
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/Mutf8.java b/dx/src/com/android/dx/util/Mutf8.java
new file mode 100644
index 0000000..fe55724
--- /dev/null
+++ b/dx/src/com/android/dx/util/Mutf8.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.UTFDataFormatException;
+
+/**
+ * Modified UTF-8 as described in the dex file format spec.
+ *
+ * <p>Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8.
+ */
+public final class Mutf8 {
+    private Mutf8() {}
+
+    /**
+     * Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is
+     * encountered. Returns a new string containing the decoded characters.
+     */
+    public static String decode(ByteInput in, char[] out) throws UTFDataFormatException {
+        int s = 0;
+        while (true) {
+            char a = (char) (in.readByte() & 0xff);
+            if (a == 0) {
+                return new String(out, 0, s);
+            }
+            out[s] = a;
+            if (a < '\u0080') {
+                s++;
+            } else if ((a & 0xe0) == 0xc0) {
+                int b = in.readByte() & 0xff;
+                if ((b & 0xC0) != 0x80) {
+                    throw new UTFDataFormatException("bad second byte");
+                }
+                out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
+            } else if ((a & 0xf0) == 0xe0) {
+                int b = in.readByte() & 0xff;
+                int c = in.readByte() & 0xff;
+                if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
+                    throw new UTFDataFormatException("bad second or third byte");
+                }
+                out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
+            } else {
+                throw new UTFDataFormatException("bad byte");
+            }
+        }
+    }
+
+    /**
+     * Returns the number of bytes the modified UTF8 representation of 's' would take.
+     */
+    private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException {
+        long result = 0;
+        final int length = s.length();
+        for (int i = 0; i < length; ++i) {
+            char ch = s.charAt(i);
+            if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+                ++result;
+            } else if (ch <= 2047) {
+                result += 2;
+            } else {
+                result += 3;
+            }
+            if (shortLength && result > 65535) {
+                throw new UTFDataFormatException("String more than 65535 UTF bytes long");
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Encodes the modified UTF-8 bytes corresponding to {@code s} into  {@code
+     * dst}, starting at {@code offset}.
+     */
+    public static void encode(byte[] dst, int offset, String s) {
+        final int length = s.length();
+        for (int i = 0; i < length; i++) {
+            char ch = s.charAt(i);
+            if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+                dst[offset++] = (byte) ch;
+            } else if (ch <= 2047) {
+                dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
+                dst[offset++] = (byte) (0x80 | (0x3f & ch));
+            } else {
+                dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
+                dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6)));
+                dst[offset++] = (byte) (0x80 | (0x3f & ch));
+            }
+        }
+    }
+
+    /**
+     * Returns an array containing the <i>modified UTF-8</i> form of {@code s}.
+     */
+    public static byte[] encode(String s) throws UTFDataFormatException {
+        int utfCount = (int) countBytes(s, true);
+        byte[] result = new byte[utfCount];
+        encode(result, 0, s);
+        return result;
+    }
+}
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..e5956a2
--- /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 extends ByteOutput {
+    /**
+     * 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 writeUleb128(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 writeSleb128(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/Uint.java b/dx/src/com/android/dx/util/Uint.java
new file mode 100644
index 0000000..039756a
--- /dev/null
+++ b/dx/src/com/android/dx/util/Uint.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * An unsigned integer.
+ */
+public final class Uint implements Comparable<Uint> {
+    public final int intValue;
+
+    public Uint(int value) {
+        this.intValue = value;
+    }
+
+    public int compareTo(Uint uint) {
+        return Unsigned.compare(intValue, uint.intValue);
+    }
+}
diff --git a/dx/src/com/android/dx/util/Unsigned.java b/dx/src/com/android/dx/util/Unsigned.java
new file mode 100644
index 0000000..f15bd86
--- /dev/null
+++ b/dx/src/com/android/dx/util/Unsigned.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Unsigned arithmetic over Java's signed types.
+ */
+public final class Unsigned {
+    private Unsigned() {}
+
+    public static int compare(short ushortA, short ushortB) {
+        if (ushortA == ushortB) {
+            return 0;
+        }
+        int a = ushortA & 0xFFFF;
+        int b = ushortB & 0xFFFF;
+        return a < b ? -1 : 1;
+    }
+
+    public static int compare(int uintA, int uintB) {
+        if (uintA == uintB) {
+            return 0;
+        }
+        long a = uintA & 0xFFFFFFFFL;
+        long b = uintB & 0xFFFFFFFFL;
+        return a < b ? -1 : 1;
+    }
+}
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/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/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..f588190
--- /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: string{"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: string{"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..e607900
--- /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: string{"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..e3f7233
--- /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: string{"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..228989a
--- /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: string{"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..c3610e9
--- /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: string{"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..80bc808
--- /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-payload // 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-payload // 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..0a101d5
--- /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-payload // 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-payload // 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-payload // 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-payload // 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..cd45497
--- /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: add-int/lit8 v2, v2, #int -1 // #ff
+  000b: invoke-static {v2}, Blort.test:(I)I
+  000e: move-result v2
+  000f: move v0, v2
+  0010: move v2, v0
+  0011: move v3, v0
+  0012: const/4 v4, #int 2 // #2
+  0013: add-int/lit8 v3, v3, #int -2 // #fe
+  0015: invoke-static {v3}, Blort.test:(I)I
+  0018: move-result v3
+  0019: add-int/2addr v2, v3
+  001a: move v0, v2
+  001b: move v2, v0
+  001c: move v0, v2
+  001d: goto 0006 // -0017
+  001e: move-exception v2
+  001f: move-object v1, v2
+  0020: const/4 v2, #int 2 // #2
+  0021: move v0, v2
+  0022: goto 0006 // -001c
+  catches
+    tries:
+      try 000b..000e
+      catch java.lang.RuntimeException -> 001e
+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: add-int/lit8 v2, v2, #int -1 // #ff
+  000b: invoke-static {v2}, Blort.test:(I)I
+  000e: move-result v2
+  000f: move v0, v2
+  0010: move v2, v0
+  0011: move v3, v0
+  0012: const/4 v4, #int 2 // #2
+  0013: add-int/lit8 v3, v3, #int -2 // #fe
+  0015: invoke-static {v3}, Blort.test:(I)I
+  0018: move-result v3
+  0019: add-int/2addr v2, v3
+  001a: move v0, v2
+  001b: move v2, v0
+  001c: move v0, v2
+  001d: goto 0006 // -0017
+  001e: move-exception v2
+  001f: move-object v1, v2
+  0020: const/4 v2, #int 2 // #2
+  0021: move v0, v2
+  0022: goto 0006 // -001c
+  catches
+    tries:
+      try 000b..000e
+      catch java.lang.RuntimeException -> 001e
+  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
+    0010: line 28
+    001b: line 29
+    001e: line 25
+    0020: 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: add-int/lit8 v2, v2, #int -1 // #ff
+  000b: invoke-static {v2}, Blort.test:(I)I
+  000e: move-result v2
+  000f: move v0, v2
+  0010: move v2, v0
+  0011: move v3, v0
+  0012: const/4 v4, #int 2 // #2
+  0013: add-int/lit8 v3, v3, #int -2 // #fe
+  0015: invoke-static {v3}, Blort.test:(I)I
+  0018: move-result v3
+  0019: add-int/2addr v2, v3
+  001a: move v0, v2
+  001b: move v2, v0
+  001c: move v0, v2
+  001d: goto 0006 // -0017
+  001e: move-exception v2
+  001f: move-object v1, v2
+  0020: const/4 v2, #int 2 // #2
+  0021: move v0, v2
+  0022: goto 0006 // -001c
+  catches
+    tries:
+      try 000b..000e
+      catch java.lang.RuntimeException -> 001e
+  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
+    0010: line 28
+    001b: line 29
+    001e: line 25
+    0020: 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..38a467a
--- /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-payload // 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-payload // 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-payload // 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-payload // 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-payload // 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-payload // 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-payload // 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..dda99cd
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/expected.txt
@@ -0,0 +1,345 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+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 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 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 0000
+  pred 0046
+  live in:{}
+  Blort.java:21@0000: const-int(1) v4:I=1 <- .
+  Blort.java:21@0001: move-int v1:I=1 <- v4:I=1
+  Blort.java:22@0002: const-int(1) v4:I=1 <- .
+  Blort.java:22@0003: move-int v2:I=1 <- v4:I=1
+  Blort.java:23@0004: const-int(0) v4:I=0 <- .
+  Blort.java:23@0005: move-int v3:I=0 <- 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 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 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 0000
+  pred 0002
+  live in:{}
+  Blort.java:42@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0002
+  pred 0004
+  live in:{}
+  Blort.java:42@0000: goto . <- .
+  next 0000
+  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 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 0007
+  pred 0004
+  live in:{}
+  Blort.java:52@0007: new-instance(java.lang.RuntimeException catch) . <- .
+  next 0094
+  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=0 <- 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 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 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 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 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 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 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 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 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 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 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..061e7d3
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/expected.txt
@@ -0,0 +1,58 @@
+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: instance-of/jumbo v22, v22, java.lang.String
+  0065: if-eqz v22, 006c // +0007
+  0067: const/16 v22, #int 0 // #0000
+  0069: sput v22, Blort.i:I
+  006b: return-void
+  006c: const/16 v22, #int 1 // #0001
+  006e: goto 0069 // -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..e6a901c
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/blort.j
@@ -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.
+
+.class blort
+.super java/lang/Object
+
+; 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
+    istore_0
+    jsr j1
+    iload_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..6bba047
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/expected.txt
@@ -0,0 +1,148 @@
+Generated: ./blort.class
+reading blort.class...
+method test_jsr ()Ljava/lang/Object;
+first 005c
+block 0000
+  pred 005c
+  blort.j:@0000: move-object v3:Lblort; <- v0:Lblort;
+  blort.j:@0000: goto . <- .
+  next 0001
+block 0001
+  pred 0000
+  @????: goto . <- .
+  next 0063
+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 0009
+  pred 0004
+  @????: goto . <- .
+  next 006c
+block 000c
+  pred 006e
+  blort.j:@000c: move-int v4:I <- v0:I
+  blort.j:@000c: goto . <- .
+  next 000e
+block 000e
+  pred 000c
+  @????: goto . <- .
+  next 0075
+block 005c
+  blort.j:@0000: move-param-object(0) v0:Lblort; <- .
+  blort.j:@0000: goto . <- .
+  next 0000
+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 0064
+  pred 0063
+  @????: goto . <- .
+  next 0066
+block 0065
+  pred 0069
+  pred 006a
+  @????: goto . <- .
+  next 0004
+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 0067
+  pred 0066
+  blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+  lang/Throwable; <- .
+  blort.j:@002d: goto . <- .
+  next 006b
+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 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 006c
+  pred 0009
+  blort.j:@0012: goto . <- .
+  next 006d
+block 006d
+  pred 006c
+  @????: goto . <- .
+  next 006f
+block 006e
+  pred 0072
+  pred 0073
+  @????: goto . <- .
+  next 000c
+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 0070
+  pred 006f
+  blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+  lang/Throwable; <- .
+  blort.j:@002d: goto . <- .
+  next 0074
+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 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 0075
+  pred 000e
+  blort.j:@002c: move-object v0:Lblort; <- v3:Lblort;
+  blort.j:@002c: goto . <- .
+  next 005d
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..0a09fb1
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/expected.txt
@@ -0,0 +1,343 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+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 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 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 0000
+  pred 0026
+  live in:{}
+  Blort.java:26@0000: const-int(1) v4:I=1 <- .
+  Blort.java:26@0001: move-int v2:I=1 <- v4:I=1
+  Blort.java:26@0001: goto . <- .
+  next 0002
+  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 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 0024
+  pred 0030
+  pred 0031
+  live in:{}
+  Blort.java:33@0011: goto . <- .
+  next 0011
+  live out:{}
+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 0027
+  pred 000f
+  pred 0012
+  live in:{}
+  Blort.java:33@0010: return-int . <- v0:I
+  returns
+  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 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 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 0000
+  pred 0002
+  live in:{}
+  Blort.java:41@0000: goto . <- .
+  next 0003
+  live out:{}
+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 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 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 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 001c
+  pred 0028
+  pred 0029
+  live in:{}
+  Blort.java:51@000d: goto . <- .
+  next 000d
+  live out:{}
+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 001f
+  pred 000e
+  live in:{}
+  Blort.java:53@000e: return-void . <- .
+  returns
+  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 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 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 0000
+  pred 0058
+  live in:{}
+  Blort.java:62@0000: const-int(0) v3:I=0 <- .
+  Blort.java:62@0001: move-int v2:I=0 <- 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 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 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..1c875b9
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/expected.txt
@@ -0,0 +1,1266 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 0113
+  pred 0088
+  live in:{}
+  Blort.java:32@0088: return-void . <- .
+  next 0132
+  live out:{}
+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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 0023
+  pred 0036
+  pred 0037
+  live in:{7}
+  Blort.java:38@000d: goto . <- .
+  next 000d
+  live out:{7}
+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 002d
+  pred 0015
+  live in:{}
+  Blort.java:41@0015: return-void . <- .
+  next 0038
+  live out:{}
+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 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 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 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 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 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 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 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 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 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 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 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 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 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 0097
+  pred 008f
+  live in:{}
+  Blort.java:81@0097: new-instance(java.io.IOException catch) . <- .
+  next 01bf
+  live out:{}
+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 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 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 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 00c9
+  pred 00c5
+  live in:{}
+  Blort.java:90@00c9: new-instance(java.io.IOException catch) . <- .
+  next 01c4
+  live out:{}
+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 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 01af
+  pred 00d6
+  live in:{}
+  Blort.java:93@00d6: return-void . <- .
+  next 01d5
+  live out:{}
+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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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..e00f774
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/expected.txt
@@ -0,0 +1,82 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+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 000a
+  pred 000c
+  Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+  Blort.java:17@0000: goto . <- .
+  next 0000
+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 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 001e
+  pred 0027
+  Blort.java:31@0000: move-param-object(0) v1:LBlort; <- .
+  Blort.java:31@0000: goto . <- .
+  next 0000
+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 0000
+  pred 0000
+  pred 0002
+  Blort.java:41@0000: goto . <- .
+  next 0000
+block 0002
+  pred 0003
+  Blort.java:41@0000: move-param-object(0) v0:LBlort; <- .
+  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..764c7ee
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/expected.txt
@@ -0,0 +1,454 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+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 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 000b
+  pred 0000
+  Blort.java:4@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method testNumeric ()V
+first 005e
+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 005c
+  pred 005e
+  Blort.java:10@0000: move-param-object(0) v4:"this"LBlort; <- .
+  Blort.java:10@0000: goto . <- .
+  next 0000
+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 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 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; v1:Lj
+  ava/lang/String;="foo"
+  next 000e
+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; v1:Lj
+  ava/lang/String;="foo"
+  next 0015
+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; v1:Lj
+  ava/lang/String;="foo"
+  next 001c
+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; v1:Lj
+  ava/lang/String;="foo"
+  next 0023
+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; v1:Lj
+  ava/lang/String;="foo"
+  next 002a
+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; v1:Lj
+  ava/lang/String;="foo"
+  next 0065
+block 0064
+  pred 0078
+  Blort.java:22@0000: move-param-object(0) v2:"this"LBlort; <- .
+  Blort.java:22@0000: goto . <- .
+  next 0000
+block 0065
+  pred 002e
+  Blort.java:30@0032: return-void . <- .
+  returns
+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 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 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 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 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 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 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 0078
+  @????: goto . <- .
+  next 0064
+
+method testCaughtStrings ()V
+first 0094
+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 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; v2:Lj
+  ava/lang/String;="foo"
+  next 000e
+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; v2:Lj
+  ava/lang/String;="foo"
+  next 0015
+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; v2:Lj
+  ava/lang/String;="foo"
+  next 001d
+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 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 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 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 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; v3:Ljava/lang/String;="foo"
+  next 007f
+block 007e
+  pred 0094
+  Blort.java:33@0000: move-param-object(0) v4:"this"LBlort; <- .
+  Blort.java:33@0000: goto . <- .
+  next 0000
+block 007f
+  pred 002e
+  pred 003b
+  Blort.java:45@003e: return-void . <- .
+  returns
+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 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 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 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 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 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 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 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 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 0094
+  @????: goto . <- .
+  next 007e
+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} v1:L
+  java/lang/Throwable; <- .
+  @????: goto . <- .
+  next 0035
+
+method testLocalVars ()V
+first 0004
+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=100 <- v3:I
+  @????: mark-local-int . <- v3:"i"I=100
+  Blort.java:57@001a: goto . <- .
+  next 0003
+block 0002
+  pred 0004
+  Blort.java:49@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:49@0000: goto . <- .
+  next 0000
+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 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 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 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..e3d660a
--- /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 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 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 000b
+  pred 0000
+  Blort.java:2@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method testMultipleIdenticalSuccessors (I)V
+first 0053
+block 0000
+  pred 004a
+  Blort.java:5@0001: switch({1, 2, 3}) . <- v3:I
+  next 001c
+  next 001c
+  next 001c
+  next 004b *
+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 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 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 004b
+  pred 0000
+  pred 0021
+  Blort.java:12@0024: return-void . <- .
+  returns
+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 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 0053
+  @????: goto . <- .
+  next 004a
+
+method testNoPrimarySuccessor ()V
+first 001a
+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 0012
+  pred 001a
+  Blort.java:16@0000: move-param-object(0) v1:"this"LBlort; <- .
+  Blort.java:16@0000: goto . <- .
+  next 0000
+block 0013
+  pred 001b
+  Blort.java:19@0009: return-void . <- .
+  returns
+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 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..0506c4d
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/expected.txt
@@ -0,0 +1,300 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+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 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+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 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 0014
+  pred 0016
+  Blort.java:9@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:9@0000: goto . <- .
+  next 0000
+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 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 0032
+  pred 0034
+  Blort.java:13@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:13@0000: goto . <- .
+  next 0000
+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 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 0036
+  pred 0038
+  Blort.java:24@0000: move-param-object(0) v12:"this"LBlort; <- .
+  Blort.java:24@0000: goto . <- .
+  next 0000
+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 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 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 001b
+  pred 0000
+  Blort.java:34@000c: return-void . <- .
+  returns
+block 001c
+  @????: goto . <- .
+  next 001a
+
+method testTailParams (II)V
+first 0022
+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 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 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 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 0032
+  pred 0034
+  Blort.java:47@0000: move-param-object(0) v11:"this"LBlort; <- .
+  Blort.java:47@0000: goto . <- .
+  next 0000
+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 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 v6:J <- v3:I
+  Blort.java:63@0019: goto . <- .
+  next 003d
+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
+  Blort.java:66@001f: goto . <- .
+  next 003d
+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 003d
+  pred 0017
+  pred 001d
+  Blort.java:66@001f: return-long . <- v6:J
+  returns
+block 0043
+  pred 001a
+  Blort.java:65@001a: Rop{move-result J <- . flows} v6:J <- .
+  Blort.java:65@001a: goto . <- .
+  next 001d
+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..c3187f8
--- /dev/null
+++ b/dx/tests/094-scala-locals/expected.txt
@@ -0,0 +1,85 @@
+reading Blort.class...
+method scalalocals ()V
+first 0025
+block 0000
+  pred 001a
+  live in:{}
+  blort.j:@0000: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0021
+  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 0006
+  pred 0022
+  live in:{}
+  blort.j:@0006: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0023
+  live out:{}
+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 001a
+  pred 0025
+  live in:{}
+  blort.j:@0000: goto . <- .
+  next 0000
+  live out:{}
+block 001b
+  pred 000c
+  live in:{}
+  blort.j:@001b: return-void . <- .
+  next 0026
+  live out:{}
+block 0021
+  pred 0000
+  live in:{}
+  blort.j:@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0022
+  pred 0003
+  live in:{}
+  blort.j:@0003: goto . <- .
+  next 0006
+  live out:{}
+block 0023
+  pred 0006
+  live in:{}
+  blort.j:@0006: goto . <- .
+  next 0009
+  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 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..28552c6
--- /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 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 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 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 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 0018
+  pred 0015
+  ViewDebug.java:573@0018: new-instance(java.io.BufferedReader catch java.lang.
+  Object) . <- .
+  next 0116
+  next 016d *
+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 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 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 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 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 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 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 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 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 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 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 005f
+  pred 003f
+  pred 0054
+  pred 005c
+  @????: goto . <- .
+  next 018b
+block 0062
+  pred 018d
+  ViewDebug.java:589@0062: goto . <- .
+  next 0079
+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 0067
+  pred 0065
+  @????: goto . <- .
+  next 0188
+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 0079
+  pred 0062
+  @????: goto . <- .
+  next 018e
+block 007c
+  pred 0190
+  ViewDebug.java:600@007c: goto . <- .
+  next 00ad
+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 0082
+  pred 0179
+  ViewDebug.java:591@0082: const-object("Connection error: " catch java.lang.Ob
+  ject) . <- .
+  next 0140
+  next 017a *
+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 0089
+  pred 0088
+  @????: goto . <- .
+  next 0182
+block 008c
+  pred 0184
+  ViewDebug.java:600@008c: goto . <- .
+  next 00ad
+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 0091
+  pred 008f
+  @????: goto . <- .
+  next 017c
+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 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 0162
+  ViewDebug.java:564@0000: move-param-object(0) v0:"this"Landroid/view/ViewDebu
+  g$ViewServer; <- .
+  ViewDebug.java:564@0000: goto . <- .
+  next 0000
+block 0163
+  pred 00b0
+  ViewDebug.java:602@00b0: return-void . <- .
+  returns
+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 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 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 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 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 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 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 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 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 0172
+  pred 0033
+  ViewDebug.java:576@0035: Rop{move-result Z <- . flows} v11:Z <- .
+  ViewDebug.java:576@0035: goto . <- .
+  next 0038
+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 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 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 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 0177
+  pred 0051
+  ViewDebug.java:580@0051: Rop{move-result Z <- . flows} v11:Z <- .
+  ViewDebug.java:580@0051: goto . <- .
+  next 0054
+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 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 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 017b
+  pred 0084
+  ViewDebug.java:591@0085: Rop{move-result I <- . flows} v11:I <- .
+  ViewDebug.java:591@0085: goto . <- .
+  next 0088
+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 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 0180
+  pred 017d
+  ViewDebug.java:598@00a1: goto . <- .
+  next 017e
+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 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 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 0186
+  pred 0183
+  ViewDebug.java:598@00a1: goto . <- .
+  next 0184
+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 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 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 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 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 0192
+  pred 018f
+  ViewDebug.java:598@00a1: goto . <- .
+  next 0190
+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
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..0f77225
--- /dev/null
+++ b/dx/tests/100-local-mismatch/expected.txt
@@ -0,0 +1,9 @@
+TEST 1
+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
+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
+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
+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..b5db406
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/expected.txt
@@ -0,0 +1,84 @@
+Generated: ./op_aaload.class
+aaload: expected failure occurred
+Generated: ./op_aastore.class
+aastore: expected failure occurred
+Generated: ./op_aastore2.class
+aastore2: 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_aastore2.j b/dx/tests/105-verify-load-store-ops/op_aastore2.j
new file mode 100644
index 0000000..0083800
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_aastore2.j
@@ -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.
+
+.class op_aastore2
+.super java/lang/Object
+
+.method public static test([I)V
+    .limit locals 2
+    .limit stack 4
+
+    aload_0
+    iconst_0
+    iconst_0
+    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..a6c5acc
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/run
@@ -0,0 +1,69 @@
+#!/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 aastore2 # second aastore test
+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..a4c4af4
--- /dev/null
+++ b/dx/tests/108-string-annotation/expected.txt
@@ -0,0 +1,12 @@
+
+  elements[0]:
+    name
+    value: utf8 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..223a459
--- /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 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/114-value-propagation/blort.j b/dx/tests/114-value-propagation/blort.j
new file mode 100644
index 0000000..2cc8f5b
--- /dev/null
+++ b/dx/tests/114-value-propagation/blort.j
@@ -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.
+
+.class blort
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 1
+    .limit stack 1
+    ldc 99
+    istore_0
+    return
+.end method
diff --git a/dx/tests/114-value-propagation/expected.txt b/dx/tests/114-value-propagation/expected.txt
new file mode 100644
index 0000000..cbd17de
--- /dev/null
+++ b/dx/tests/114-value-propagation/expected.txt
@@ -0,0 +1,3 @@
+Generated: ./blort.class
+  blort.j:@0000: const-int(99) v1:I=99 <- .
+  blort.j:@0002: move-int v0:I=99 <- v1:I=99
diff --git a/dx/tests/114-value-propagation/info.txt b/dx/tests/114-value-propagation/info.txt
new file mode 100644
index 0000000..2ff498d
--- /dev/null
+++ b/dx/tests/114-value-propagation/info.txt
@@ -0,0 +1,2 @@
+Tests propagation of constant values, specifically when going from a
+stack location to a local variable. (regression test)
diff --git a/dx/tests/114-value-propagation/run b/dx/tests/114-value-propagation/run
new file mode 100644
index 0000000..7dde4d7
--- /dev/null
+++ b/dx/tests/114-value-propagation/run
@@ -0,0 +1,18 @@
+#!/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.
+
+jasmin -d . blort.j
+dx --dump --rop-blocks blort.class | grep 'const\|move'
diff --git a/dx/tests/115-merge/com/android/dx/merge/DexMergeTest.java b/dx/tests/115-merge/com/android/dx/merge/DexMergeTest.java
new file mode 100644
index 0000000..c6ffbdc
--- /dev/null
+++ b/dx/tests/115-merge/com/android/dx/merge/DexMergeTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+import com.android.dx.io.DexBuffer;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import junit.framework.TestCase;
+
+/**
+ * Test that DexMerge works by merging dex files, and then loading them into
+ * the current VM.
+ */
+public final class DexMergeTest extends TestCase {
+
+    public void testFillArrayData() throws Exception {
+        ClassLoader loader = mergeAndLoad(
+                "/testdata/Basic.dex",
+                "/testdata/FillArrayData.dex");
+
+        Class<?> basic = loader.loadClass("testdata.Basic");
+        assertEquals(1, basic.getDeclaredMethods().length);
+
+        Class<?> fillArrayData = loader.loadClass("testdata.FillArrayData");
+        assertTrue(Arrays.equals(
+                new byte[] { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, -112, -23, 121 },
+                (byte[]) fillArrayData.getMethod("newByteArray").invoke(null)));
+        assertTrue(Arrays.equals(
+                new char[] { 0xFFFF, 0x4321, 0xABCD, 0, 'a', 'b', 'c' },
+                (char[]) fillArrayData.getMethod("newCharArray").invoke(null)));
+        assertTrue(Arrays.equals(
+                new long[] { 4660046610375530309L, 7540113804746346429L, -6246583658587674878L },
+                (long[]) fillArrayData.getMethod("newLongArray").invoke(null)));
+    }
+
+    public void testTryCatchFinally() throws Exception {
+        ClassLoader loader = mergeAndLoad(
+                "/testdata/Basic.dex",
+                "/testdata/TryCatchFinally.dex");
+
+        Class<?> basic = loader.loadClass("testdata.Basic");
+        assertEquals(1, basic.getDeclaredMethods().length);
+
+        Class<?> tryCatchFinally = loader.loadClass("testdata.TryCatchFinally");
+        tryCatchFinally.getDeclaredMethod("method").invoke(null);
+    }
+
+    public void testStaticValues() throws Exception {
+        ClassLoader loader = mergeAndLoad(
+                "/testdata/Basic.dex",
+                "/testdata/StaticValues.dex");
+
+        Class<?> basic = loader.loadClass("testdata.Basic");
+        assertEquals(1, basic.getDeclaredMethods().length);
+
+        Class<?> staticValues = loader.loadClass("testdata.StaticValues");
+        assertEquals((byte) 1, staticValues.getField("a").get(null));
+        assertEquals((short) 2, staticValues.getField("b").get(null));
+        assertEquals('C', staticValues.getField("c").get(null));
+        assertEquals(0xabcd1234, staticValues.getField("d").get(null));
+        assertEquals(4660046610375530309L,staticValues.getField("e").get(null));
+        assertEquals(0.5f, staticValues.getField("f").get(null));
+        assertEquals(-0.25, staticValues.getField("g").get(null));
+        assertEquals("this is a String", staticValues.getField("h").get(null));
+        assertEquals(String.class, staticValues.getField("i").get(null));
+        assertEquals("[0, 1]", Arrays.toString((int[]) staticValues.getField("j").get(null)));
+        assertEquals(null, staticValues.getField("k").get(null));
+        assertEquals(true, staticValues.getField("l").get(null));
+        assertEquals(false, staticValues.getField("m").get(null));
+    }
+
+    public void testAnnotations() throws Exception {
+        ClassLoader loader = mergeAndLoad(
+                "/testdata/Basic.dex",
+                "/testdata/Annotated.dex");
+
+        Class<?> basic = loader.loadClass("testdata.Basic");
+        assertEquals(1, basic.getDeclaredMethods().length);
+
+        Class<?> annotated = loader.loadClass("testdata.Annotated");
+        Method method = annotated.getMethod("method", String.class, String.class);
+        Field field = annotated.getField("field");
+
+        @SuppressWarnings("unchecked")
+        Class<? extends Annotation> marker
+                = (Class<? extends Annotation>) loader.loadClass("testdata.Annotated$Marker");
+
+        assertEquals("@testdata.Annotated$Marker(a=on class, b=[A, B, C], "
+                + "c=@testdata.Annotated$Nested(e=E1, f=1695938256, g=7264081114510713000), "
+                + "d=[@testdata.Annotated$Nested(e=E2, f=1695938256, g=7264081114510713000)])",
+                annotated.getAnnotation(marker).toString());
+        assertEquals("@testdata.Annotated$Marker(a=on method, b=[], "
+                + "c=@testdata.Annotated$Nested(e=, f=0, g=0), d=[])",
+                method.getAnnotation(marker).toString());
+        assertEquals("@testdata.Annotated$Marker(a=on field, b=[], "
+                + "c=@testdata.Annotated$Nested(e=, f=0, g=0), d=[])",
+                field.getAnnotation(marker).toString());
+        assertEquals("@testdata.Annotated$Marker(a=on parameter, b=[], "
+                + "c=@testdata.Annotated$Nested(e=, f=0, g=0), d=[])",
+                method.getParameterAnnotations()[1][0].toString());
+    }
+
+    /**
+     * Merging dex files uses pessimistic sizes that naturally leave gaps in the
+     * output files. If those gaps grow too large, the merger is supposed to
+     * compact the result. This exercises that by repeatedly merging a dex with
+     * itself.
+     */
+    public void testMergedOutputSizeIsBounded() throws Exception {
+        /*
+         * At the time this test was written, the output would grow ~25% with
+         * each merge. Setting a low 1KiB ceiling on the maximum size caused
+         * the file to be compacted every four merges.
+         */
+        int steps = 100;
+        int compactWasteThreshold = 1024;
+
+        DexBuffer dexA = resourceToDexBuffer("/testdata/Basic.dex");
+        DexBuffer dexB = resourceToDexBuffer("/testdata/TryCatchFinally.dex");
+        DexBuffer merged = new DexMerger(dexA, dexB, CollisionPolicy.KEEP_FIRST).merge();
+
+        int maxLength = 0;
+        for (int i = 0; i < steps; i++) {
+            DexMerger dexMerger = new DexMerger(dexA, merged, CollisionPolicy.KEEP_FIRST);
+            dexMerger.setCompactWasteThreshold(compactWasteThreshold);
+            merged = dexMerger.merge();
+            maxLength = Math.max(maxLength, merged.getLength());
+        }
+
+        int maxExpectedLength = dexA.getLength() + dexB.getLength() + compactWasteThreshold;
+        assertTrue(maxLength + " < " + maxExpectedLength, maxLength < maxExpectedLength);
+    }
+
+    public ClassLoader mergeAndLoad(String dexAResource, String dexBResource) throws Exception {
+        DexBuffer dexA = resourceToDexBuffer(dexAResource);
+        DexBuffer dexB = resourceToDexBuffer(dexBResource);
+        DexBuffer merged = new DexMerger(dexA, dexB, CollisionPolicy.KEEP_FIRST).merge();
+        File mergedDex = File.createTempFile("DexMergeTest", ".classes.dex");
+        merged.writeTo(mergedDex);
+        File mergedJar = dexToJar(mergedDex);
+        // simplify the javac classpath by not depending directly on 'dalvik.system' classes
+        return (ClassLoader) Class.forName("dalvik.system.PathClassLoader")
+                .getConstructor(String.class, ClassLoader.class)
+                .newInstance(mergedJar.getPath(), getClass().getClassLoader());
+    }
+
+    private DexBuffer resourceToDexBuffer(String resource) throws IOException {
+        return new DexBuffer(getClass().getResourceAsStream(resource));
+    }
+
+    private File dexToJar(File dex) throws IOException {
+        File result = File.createTempFile("DexMergeTest", ".jar");
+        result.deleteOnExit();
+        JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result));
+        jarOut.putNextEntry(new JarEntry("classes.dex"));
+        copy(new FileInputStream(dex), jarOut);
+        jarOut.closeEntry();
+        jarOut.close();
+        return result;
+    }
+
+    private void copy(InputStream in, OutputStream out) throws IOException {
+        byte[] buffer = new byte[1024];
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            out.write(buffer, 0, count);
+        }
+        in.close();
+    }
+}
diff --git a/dx/tests/115-merge/expected.txt b/dx/tests/115-merge/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/115-merge/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/115-merge/info.txt b/dx/tests/115-merge/info.txt
new file mode 100644
index 0000000..c1fa2e4
--- /dev/null
+++ b/dx/tests/115-merge/info.txt
@@ -0,0 +1,6 @@
+Merges two dex files into one and then loads the result.
+
+Because it loads the merged dex files, this JUnit test only works on a dalvikvm.
+The run script requires vogar, so you must have vogar on your $PATH to run this
+test. You'll also need a device or host VM for vogar to attach to.
+
diff --git a/dx/tests/115-merge/run b/dx/tests/115-merge/run
new file mode 100644
index 0000000..83e5b3d
--- /dev/null
+++ b/dx/tests/115-merge/run
@@ -0,0 +1,40 @@
+#!/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.
+
+# Find dx.jar from dx in the android dev tree
+prog=`which dx`
+progdir=`dirname "${prog}"`
+dxjar=$progdir/../framework/dx.jar
+
+javac -cp $dxjar `find . -name "*.java"`
+dx --dex --output=test.jar com/android/dx/merge/* $dxjar
+
+# Build a resource .jar containing the .dex files to merge
+dx --dex --output=testdata/Annotated.dex testdata/Annotated*
+dx --dex --output=testdata/Basic.dex testdata/Basic*
+dx --dex --output=testdata/FillArrayData.dex testdata/FillArrayData*
+dx --dex --output=testdata/StaticValues.dex testdata/StaticValues*
+dx --dex --output=testdata/TryCatchFinally.dex testdata/TryCatchFinally*
+jar cfM resources.jar testdata/*.dex
+
+dalvik -classpath test.jar:resources.jar \
+  junit.textui.TestRunner com.android.dx.merge.DexMergeTest > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
diff --git a/dx/tests/115-merge/testdata/Annotated.java b/dx/tests/115-merge/testdata/Annotated.java
new file mode 100644
index 0000000..2e893f2
--- /dev/null
+++ b/dx/tests/115-merge/testdata/Annotated.java
@@ -0,0 +1,31 @@
+package testdata;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Annotated.Marker(a = "on class", b = {"A", "B", "C" },
+        c = @Annotated.Nested(e="E1", f=1695938256, g=7264081114510713000L),
+        d = { @Annotated.Nested(e="E2", f=1695938256, g=7264081114510713000L) })
+public class Annotated {
+
+    @Annotated.Marker(a="on field")
+    public String field;
+
+    @Annotated.Marker(a="on method")
+    public void method(String a, @Annotated.Marker(a="on parameter") String b) {}
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Marker {
+        String a() default "";
+        String[] b() default {};
+        Nested c() default @Nested;
+        Nested[] d() default {};
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Nested {
+        String e() default "";
+        int f() default 0;
+        long g() default 0L;
+    }
+}
diff --git a/dx/tests/115-merge/testdata/Basic.java b/dx/tests/115-merge/testdata/Basic.java
new file mode 100644
index 0000000..01a1635
--- /dev/null
+++ b/dx/tests/115-merge/testdata/Basic.java
@@ -0,0 +1,10 @@
+package testdata;
+
+public class Basic {
+
+    String field = "this is a field";
+
+    String method() {
+        return "this is a method result";
+    }
+}
diff --git a/dx/tests/115-merge/testdata/FillArrayData.java b/dx/tests/115-merge/testdata/FillArrayData.java
new file mode 100644
index 0000000..0ece934
--- /dev/null
+++ b/dx/tests/115-merge/testdata/FillArrayData.java
@@ -0,0 +1,16 @@
+package testdata;
+
+public class FillArrayData {
+
+    public static byte[] newByteArray() {
+        return new byte[] { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, -112, -23, 121 };
+    }
+
+    public static char[] newCharArray() {
+        return new char[] { 0xFFFF, 0x4321, 0xABCD, 0, 'a', 'b', 'c' };
+    }
+
+    public static long[] newLongArray() {
+        return new long[] { 4660046610375530309L, 7540113804746346429L, -6246583658587674878L };
+    }
+}
diff --git a/dx/tests/115-merge/testdata/StaticValues.java b/dx/tests/115-merge/testdata/StaticValues.java
new file mode 100644
index 0000000..1a8648f
--- /dev/null
+++ b/dx/tests/115-merge/testdata/StaticValues.java
@@ -0,0 +1,17 @@
+package testdata;
+
+public class StaticValues {
+    public static final byte a = 1;
+    public static final short b = 2;
+    public static final char c = 'C';
+    public static final int d = 0xabcd1234;
+    public static final long e = 4660046610375530309L;
+    public static final float f = 0.5f;
+    public static final double g = -0.25;
+    public static final String h = "this is a String";
+    public static final Class<?> i = String.class;
+    public static final int[] j = { 0, 1 };
+    public static final String k = null;
+    public static final boolean l = true;
+    public static final boolean m = false;
+}
diff --git a/dx/tests/115-merge/testdata/TryCatchFinally.java b/dx/tests/115-merge/testdata/TryCatchFinally.java
new file mode 100644
index 0000000..4f3769e
--- /dev/null
+++ b/dx/tests/115-merge/testdata/TryCatchFinally.java
@@ -0,0 +1,26 @@
+package testdata;
+
+public class TryCatchFinally {
+
+    public static void method() {
+        int count = 0;
+        try {
+            if (true) {
+                throw new NullPointerException();
+            }
+            throw new AssertionError();
+        } catch (IllegalStateException e) {
+            throw new AssertionError();
+        } catch (NullPointerException expected) {
+            count++;
+        } catch (RuntimeException e) {
+            throw new AssertionError();
+        } finally {
+            count++;
+        }
+
+        if (count != 2) {
+            throw new AssertionError();
+        }
+    }
+}
diff --git a/dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java b/dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java
new file mode 100644
index 0000000..dbc0ea3
--- /dev/null
+++ b/dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+public final class Leb128UtilsTest extends TestCase {
+
+    public void testDecodeUnsignedLeb() throws IOException {
+        assertEquals(0, Leb128Utils.readUnsignedLeb128(new ByteArrayByteInput((byte) 0)));
+        assertEquals(1, Leb128Utils.readUnsignedLeb128(new ByteArrayByteInput((byte) 1)));
+        assertEquals(127, Leb128Utils.readUnsignedLeb128(new ByteArrayByteInput((byte) 0x7f)));
+        assertEquals(16256, Leb128Utils.readUnsignedLeb128(
+                new ByteArrayByteInput((byte) 0x80, (byte) 0x7f)));
+    }
+
+    public void testEncodeUnsignedLeb() throws IOException {
+        assertEquals(new byte[] { 0 }, encodeUnsignedLeb(0));
+        assertEquals(new byte[] { 1 }, encodeUnsignedLeb(1));
+        assertEquals(new byte[] { 0x7f }, encodeUnsignedLeb(127));
+        assertEquals(new byte[] { (byte) 0x80, 0x7f }, encodeUnsignedLeb(16256));
+        assertEquals(new byte[] { (byte) 0xb4, 0x07 }, encodeUnsignedLeb(0x3b4));
+        assertEquals(new byte[] { (byte) 0x8c, 0x08 }, encodeUnsignedLeb(0x40c));
+        assertEquals(new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0xf },
+                encodeUnsignedLeb(0xffffffff));
+    }
+
+    public void testDecodeSignedLeb() throws IOException {
+        assertEquals(0, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 0)));
+        assertEquals(1, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 1)));
+        assertEquals(-1, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 0x7f)));
+        assertEquals(0x3c, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 0x3c)));
+        assertEquals(-128, Leb128Utils.readSignedLeb128(
+                new ByteArrayByteInput((byte) 0x80, (byte) 0x7f)));
+    }
+
+    public void testEncodeSignedLeb() throws IOException {
+        assertEquals(new byte[] { 0 }, encodeSignedLeb(0));
+        assertEquals(new byte[] { 1 }, encodeSignedLeb(1));
+        assertEquals(new byte[] { 0x7f }, encodeSignedLeb(-1));
+        assertEquals(new byte[] { (byte) 0x80, 0x7f }, encodeSignedLeb(-128));
+    }
+
+    private byte[] encodeSignedLeb(int value) {
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(5);
+        Leb128Utils.writeSignedLeb128(out, value);
+        return out.toByteArray();
+    }
+
+    private byte[] encodeUnsignedLeb(int value) {
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(5);
+        Leb128Utils.writeUnsignedLeb128(out, value);
+        return out.toByteArray();
+    }
+
+    private void assertEquals(byte[] expected, byte[] actual) {
+        assertTrue(Arrays.toString(actual), Arrays.equals(expected, actual));
+    }
+}
diff --git a/dx/tests/116-leb128/expected.txt b/dx/tests/116-leb128/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/116-leb128/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/116-leb128/info.txt b/dx/tests/116-leb128/info.txt
new file mode 100644
index 0000000..1603ec3
--- /dev/null
+++ b/dx/tests/116-leb128/info.txt
@@ -0,0 +1,5 @@
+Performs little endian operations.
+
+The run script requires vogar, so you must have vogar on your $PATH to run this
+test. You'll also need a device or host VM for vogar to attach to.
+
diff --git a/dx/tests/116-leb128/run b/dx/tests/116-leb128/run
new file mode 100644
index 0000000..5f36cd9
--- /dev/null
+++ b/dx/tests/116-leb128/run
@@ -0,0 +1,29 @@
+#!/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.
+
+# Find dx.jar from dx in the android dev tree
+prog=`which dx`
+progdir=`dirname "${prog}"`
+dxjar=$progdir/../framework/dx.jar
+
+javac -cp $dxjar `find . -name "*.java"`
+java -classpath $dxjar:. junit.textui.TestRunner com.android.dx.util.Leb128UtilsTest > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
diff --git a/dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java b/dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java
new file mode 100644
index 0000000..8e6188d
--- /dev/null
+++ b/dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+public final class Mutf8Test extends TestCase {
+
+    public void testDecode() throws IOException {
+        ByteInput in = new ByteArrayByteInput(
+                new byte[] { 'A', 'B', 'C', (byte) 0xc0, (byte) 0x80, 0, 'E' });
+        assertEquals('A', in.readByte());
+        assertEquals("BC\u0000", Mutf8.decode(in, new char[3]));
+        assertEquals('E', in.readByte());
+    }
+
+    public void testEncode() throws IOException {
+        assertEquals(Arrays.toString(new byte[] { 'B', 'C', (byte) 0xc0, (byte) 0x80 }),
+                Arrays.toString(Mutf8.encode("BC\u0000")));
+    }
+}
diff --git a/dx/tests/117-modified-utf8/expected.txt b/dx/tests/117-modified-utf8/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/117-modified-utf8/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/117-modified-utf8/info.txt b/dx/tests/117-modified-utf8/info.txt
new file mode 100644
index 0000000..df11d98
--- /dev/null
+++ b/dx/tests/117-modified-utf8/info.txt
@@ -0,0 +1,5 @@
+Performs modified UTF-8 operations.
+
+The run script requires vogar, so you must have vogar on your $PATH to run this
+test. You'll also need a device or host VM for vogar to attach to.
+
diff --git a/dx/tests/117-modified-utf8/run b/dx/tests/117-modified-utf8/run
new file mode 100644
index 0000000..a4c202e
--- /dev/null
+++ b/dx/tests/117-modified-utf8/run
@@ -0,0 +1,29 @@
+#!/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.
+
+# Find dx.jar from dx in the android dev tree
+prog=`which dx`
+progdir=`dirname "${prog}"`
+dxjar=$progdir/../framework/dx.jar
+
+javac -cp $dxjar `find . -name "*.java"`
+java -classpath $dxjar:. junit.textui.TestRunner com.android.dx.util.Mutf8Test > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
diff --git a/dx/tests/118-find-usages/Foo.java b/dx/tests/118-find-usages/Foo.java
new file mode 100644
index 0000000..d5dc0bd
--- /dev/null
+++ b/dx/tests/118-find-usages/Foo.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.Reader;
+import java.io.StreamTokenizer;
+import java.util.AbstractList;
+import java.util.ArrayList;
+
+public final class Foo {
+
+    public void writeStreamTokenizerNval() {
+        new StreamTokenizer((Reader) null).nval = 5;
+    }
+
+    public double readStreamTokenizerNval() {
+        return new StreamTokenizer((Reader) null).nval;
+    }
+
+    public void callStringValueOf() {
+        String.valueOf(5);
+    }
+
+    public void callIntegerValueOf() {
+        Integer.valueOf("5");
+    }
+
+    public void callArrayListRemoveIndex() {
+        new ArrayList<String>().remove(5);
+    }
+
+    public void callArrayListRemoveValue() {
+        new ArrayList<String>().remove("5");
+    }
+
+    static class MyList<T> extends AbstractList<T> {
+        @Override public T get(int index) {
+            return null;
+        }
+        @Override public int size() {
+            return 0;
+        }
+        @Override public boolean remove(Object o) {
+            return false;
+        }
+    }
+}
diff --git a/dx/tests/118-find-usages/expected.txt b/dx/tests/118-find-usages/expected.txt
new file mode 100644
index 0000000..aca2bf1
--- /dev/null
+++ b/dx/tests/118-find-usages/expected.txt
@@ -0,0 +1,9 @@
+StreamTokenizer.nval
+LFoo;.readStreamTokenizerNval: field reference (iget-wide)
+LFoo;.writeStreamTokenizerNval: field reference (iput-wide)
+ArrayList.remove()
+LFoo;.callArrayListRemoveIndex: method reference (invoke-virtual)
+LFoo;.callArrayListRemoveValue: method reference (invoke-virtual)
+Collection.remove()
+String.valueOf()
+LFoo;.callStringValueOf: method reference (invoke-static)
diff --git a/dx/tests/118-find-usages/info.txt b/dx/tests/118-find-usages/info.txt
new file mode 100644
index 0000000..2a4e8a6
--- /dev/null
+++ b/dx/tests/118-find-usages/info.txt
@@ -0,0 +1,3 @@
+Creates a .dex file and runs find usages on it to find references and declarations.
+
+The expected output assumes this bug has not yet been fixed: http://b/3366285
\ No newline at end of file
diff --git a/dx/tests/118-find-usages/run b/dx/tests/118-find-usages/run
new file mode 100644
index 0000000..22f38cc
--- /dev/null
+++ b/dx/tests/118-find-usages/run
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --output=foo.dex --dex *.class
+
+echo "StreamTokenizer.nval"
+dx --find-usages foo.dex "Ljava/io/StreamTokenizer;" nval
+
+echo "ArrayList.remove()"
+dx --find-usages foo.dex "Ljava/util/ArrayList;" remove
+
+echo "Collection.remove()"
+dx --find-usages foo.dex "Ljava/util/Collection;" remove
+
+echo "String.valueOf()"
+dx --find-usages foo.dex "Ljava/lang/String;" valueOf
diff --git a/dx/tests/119-merge-conflict/com/android/dx/merge/MergeConflictTest.java b/dx/tests/119-merge-conflict/com/android/dx/merge/MergeConflictTest.java
new file mode 100644
index 0000000..421d332
--- /dev/null
+++ b/dx/tests/119-merge-conflict/com/android/dx/merge/MergeConflictTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.merge;
+
+import com.android.dx.io.DexBuffer;
+import com.android.dx.util.DexException;
+import java.io.IOException;
+import junit.framework.TestCase;
+
+public final class MergeConflictTest extends TestCase {
+
+    public void testMergeConflict() throws IOException {
+        DexBuffer a = resourceToDexBuffer("/testdata/A.dex");
+        DexBuffer b = resourceToDexBuffer("/testdata/B.dex");
+
+        // a and b don't overlap; this should succeed
+        DexBuffer ab = new DexMerger(a, b, CollisionPolicy.FAIL).merge();
+
+        // a and ab overlap; this should fail
+        DexMerger dexMerger = new DexMerger(a, ab, CollisionPolicy.FAIL);
+        try {
+            dexMerger.merge();
+            fail();
+        } catch (DexException expected) {
+            assertEquals("Multiple dex files define Ltestdata/A;", expected.getMessage());
+        }
+    }
+
+    private DexBuffer resourceToDexBuffer(String resource) throws IOException {
+        return new DexBuffer(getClass().getResourceAsStream(resource));
+    }
+}
diff --git a/dx/tests/119-merge-conflict/expected.txt b/dx/tests/119-merge-conflict/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/119-merge-conflict/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/119-merge-conflict/info.txt b/dx/tests/119-merge-conflict/info.txt
new file mode 100644
index 0000000..012187a
--- /dev/null
+++ b/dx/tests/119-merge-conflict/info.txt
@@ -0,0 +1,5 @@
+Merges dex files with and without conflicts.
+
+The run script requires vogar, so you must have vogar on your $PATH to run this
+test. You'll also need a device or host VM for vogar to attach to.
+
diff --git a/dx/tests/119-merge-conflict/run b/dx/tests/119-merge-conflict/run
new file mode 100644
index 0000000..28c152d
--- /dev/null
+++ b/dx/tests/119-merge-conflict/run
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Find dx.jar from dx in the android dev tree
+prog=`which dx`
+progdir=`dirname "${prog}"`
+dxjar=$progdir/../framework/dx.jar
+
+javac -cp $dxjar `find . -name "*.java"`
+dx --dex --output=test.jar com/android/dx/merge/* $dxjar
+
+# Build a resource .jar containing the .dex files to merge
+dx --dex --output=testdata/A.dex testdata/A.class
+dx --dex --output=testdata/B.dex testdata/B.class
+jar cfM resources.jar testdata/*.dex
+
+dalvik -classpath test.jar:resources.jar \
+  junit.textui.TestRunner com.android.dx.merge.MergeConflictTest > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
+
+
diff --git a/dx/tests/119-merge-conflict/testdata/A.java b/dx/tests/119-merge-conflict/testdata/A.java
new file mode 100644
index 0000000..8ff8341
--- /dev/null
+++ b/dx/tests/119-merge-conflict/testdata/A.java
@@ -0,0 +1,7 @@
+package testdata;
+
+public class A {
+    String hello() {
+        return "hello from A";
+    }
+}
diff --git a/dx/tests/119-merge-conflict/testdata/B.java b/dx/tests/119-merge-conflict/testdata/B.java
new file mode 100644
index 0000000..cadd96f
--- /dev/null
+++ b/dx/tests/119-merge-conflict/testdata/B.java
@@ -0,0 +1,7 @@
+package testdata;
+
+public class B {
+    public static void main(String[] args) {
+        System.out.println(new A().hello());
+    }
+}
diff --git a/dx/tests/120-disable-extended-ops/Blort.java b/dx/tests/120-disable-extended-ops/Blort.java
new file mode 100644
index 0000000..a1cfa36
--- /dev/null
+++ b/dx/tests/120-disable-extended-ops/Blort.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+    private int field0 = 0;
+    private int field1 = 1;
+    private int field2 = 2;
+    private int field3 = 3;
+    private int field4 = 4;
+    private int field5 = 5;
+    private int field6 = 6;
+    private int field7 = 7;
+    private int field8 = 8;
+    private int field9 = 9;
+
+    public void test() {
+        int v0 = field0;
+        int v1 = field1;
+        int v2 = field2;
+        int v3 = field3;
+        int v4 = field4;
+        int v5 = field5;
+        int v6 = field6;
+        int v7 = field7;
+        int v8 = field8;
+        int v9 = field9;
+        int v10 = field0;
+        int v11 = field1;
+        int v12 = field2;
+        int v13 = field3;
+        int v14 = field4;
+        int v15 = field5;
+        int v16 = field6;
+        int v17 = field7;
+        int v18 = field8;
+        int v19 = field9;
+
+        sink(v0);
+        sink(v1);
+        sink(v2);
+        sink(v3);
+        sink(v4);
+        sink(v5);
+        sink(v6);
+        sink(v7);
+        sink(v8);
+        sink(v9);
+        sink(v10);
+        sink(v11);
+        sink(v12);
+        sink(v13);
+        sink(v14);
+        sink(v15);
+        sink(v16);
+        sink(v17);
+        sink(v18);
+        sink(v19);
+    }
+
+    private static void sink(int arg) {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/120-disable-extended-ops/expected.txt b/dx/tests/120-disable-extended-ops/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/dx/tests/120-disable-extended-ops/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/dx/tests/120-disable-extended-ops/info.txt b/dx/tests/120-disable-extended-ops/info.txt
new file mode 100644
index 0000000..213c29c
--- /dev/null
+++ b/dx/tests/120-disable-extended-ops/info.txt
@@ -0,0 +1,3 @@
+This is a test of the dx option --target-api, which is supposed to
+disable the emission of extended-opcode instructions when the target
+API is for Honeycomb or earlier.
diff --git a/dx/tests/120-disable-extended-ops/run b/dx/tests/120-disable-extended-ops/run
new file mode 100644
index 0000000..c0116c8
--- /dev/null
+++ b/dx/tests/120-disable-extended-ops/run
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This test checks to see that without --target-api=11, the result of
+# dx contains at least one "iget/jumbo" opcode (one example of an
+# extended opcode); and that with that option no such opcode is
+# produced.
+
+$JAVAC -d . *.java
+
+count=`dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class | grep "iget/jumbo" | wc -l`
+if [ "$count" = "0" ]; then
+    echo "No iget/jumbo emitted without --target-api"
+fi
+
+count=`dx --debug --dex --no-optimize --positions=none --no-locals \
+    --target-api=11 \
+    --dump-method=Blort.test Blort.class | grep "iget/jumbo" | wc -l`
+if [ "$count" != "0" ]; then
+    echo "Found $count iget/jumbo emitted with --target-api=11"
+fi
+
+echo "Done"
diff --git a/dx/tests/121-sccp/Blort.java b/dx/tests/121-sccp/Blort.java
new file mode 100644
index 0000000..31e7d8e
--- /dev/null
+++ b/dx/tests/121-sccp/Blort.java
@@ -0,0 +1,164 @@
+class Blort {
+
+    // Test integers
+    public static int testIntAddSub() {
+        int a, b, c, d;
+        a = 3;
+        b = 5 - a;
+        while (true) {
+            c = a + b;
+            d = 5;
+            a = d - b;
+            if (c <= d) {
+                c = d + 1;
+            } else {
+                return c;
+            }
+            b = 2;
+        }
+    }
+
+    public static int testIntMult() {
+        int a = 6;
+        int b = 9 - a;
+        int c = b * 4;
+
+        if (c > 10) {
+            c = c - 10;
+        }
+        return c * 2;
+    }
+
+    public static int testIntDiv() {
+        int a = 30;
+        int b = 9 - a / 5;
+        int c = b * 4;
+
+        if (c > 10) {
+            c = c - 10;
+        }
+        return c * (60 / a);
+    }
+
+    public static int testIntMod() {
+        int a = 5;
+        int b = a % 3;
+        int c = a % 0;
+        return b + c;
+    }
+
+    public static int testIntPhi() {
+        int a = 37;
+        int b = 3;
+        int c = (b == 0) ? 0 : (a / b);
+        return c;
+    }
+
+    // Test floats
+    public static float testFloatAddSub() {
+        float a, b, c, d;
+        a = 3;
+        b = 5 - a;
+        while (true) {
+            c = a + b;
+            d = 5;
+            a = d - b;
+            if (c <= d) {
+                c = d + 1;
+            } else {
+                return c;
+            }
+            b = 2;
+        }
+    }
+
+    public static float testFloatMult() {
+        float a = 6;
+        float b = 9 - a;
+        float c = b * 4;
+
+        if (c > 10) {
+            c = c - 10;
+        }
+        return c * 2;
+    }
+
+    public static float testFloatDiv() {
+        float a = 30;
+        float b = 9 - a / 5;
+        float c = b * 4;
+
+        if (c > 10) {
+            c = c - 10;
+        }
+        return c * (60 / a);
+    }
+
+    public static float testFloatMod() {
+        float a = 5;
+        float b = a % 3;
+        float c = a % 0;
+        return b + c;
+    }
+
+    public static float testFloatPhi() {
+        float a = 37;
+        float b = 3;
+        float c = (b == 0) ? 0 : (a / b);
+        return c;
+    }
+
+    // Test doubles
+    public static double testDoubleAddSub() {
+        double a, b, c, d;
+        a = 3;
+        b = 5 - a;
+        while (true) {
+            c = a + b;
+            d = 5;
+            a = d - b;
+            if (c <= d) {
+                c = d + 1;
+            } else {
+                return c;
+            }
+            b = 2;
+        }
+    }
+
+    public static double testDoubleMult() {
+        double a = 6;
+        double b = 9 - a;
+        double c = b * 4;
+
+        if (c > 10) {
+            c = c - 10;
+        }
+        return c * 2;
+    }
+
+    public static double testDoubleDiv() {
+        double a = 30;
+        double b = 9 - a / 5;
+        double c = b * 4;
+
+        if (c > 10) {
+            c = c - 10;
+        }
+        return c * (60 / a);
+    }
+
+    public static double testDoubleMod() {
+        double a = 5;
+        double b = a % 3;
+        double c = a % 0;
+        return b + c;
+    }
+
+    public static double testDoublePhi() {
+        double a = 37;
+        double b = 3;
+        double c = (b == 0) ? 0 : (a / b);
+        return c;
+    }
+}
diff --git a/dx/tests/121-sccp/expected.txt b/dx/tests/121-sccp/expected.txt
new file mode 100644
index 0000000..afc02c7
--- /dev/null
+++ b/dx/tests/121-sccp/expected.txt
@@ -0,0 +1,715 @@
+Blort.testDoubleAddSub:()D:
+regs: 0008; ins: 0000; outs: 0000
+  0000: const-wide/high16 v6, #double 5.0 // #4014000000000000
+  0002: const-wide/high16 v2, #double 3.0 // #4008000000000000
+  0004: sub-double v0, v6, v2
+  0006: add-double v4, v2, v0
+  0008: sub-double v2, v6, v0
+  000a: cmpg-double v0, v4, v6
+  000c: if-gtz v0, 0014 // +0008
+  000e: const-wide/high16 v0, #double 1.0 // #3ff0000000000000
+  0010: add-double/2addr v0, v6
+  0011: const-wide/high16 v0, #double 2.0 // #4000000000000000
+  0013: goto 0006 // -000d
+  0014: return-wide v4
+  debug info
+    line_start: 114
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 114
+    0004: line 115
+    0006: line 117
+    0008: line 119
+    000a: line 120
+    000e: line 121
+    0011: line 125
+    0014: line 123
+    end sequence
+  source file: "Blort.java"
+Blort.testDoubleDiv:()D:
+regs: 0008; ins: 0000; outs: 0000
+  0000: const-wide/high16 v6, #double 10.0 // #4024000000000000
+  0002: const-wide/high16 v2, #double 30.0 // #403e000000000000
+  0004: const-wide/high16 v0, #double 9.0 // #4022000000000000
+  0006: const-wide/high16 v4, #double 5.0 // #4014000000000000
+  0008: div-double v4, v2, v4
+  000a: sub-double/2addr v0, v4
+  000b: const-wide/high16 v4, #double 4.0 // #4010000000000000
+  000d: mul-double/2addr v0, v4
+  000e: cmpl-double v4, v0, v6
+  0010: if-lez v4, 0013 // +0003
+  0012: sub-double/2addr v0, v6
+  0013: const-wide/high16 v4, #double 60.0 // #404e000000000000
+  0015: div-double v2, v4, v2
+  0017: mul-double/2addr v0, v2
+  0018: return-wide v0
+  debug info
+    line_start: 141
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 141
+    0004: line 142
+    000b: line 143
+    000e: line 145
+    0012: line 146
+    0013: line 148
+    end sequence
+  source file: "Blort.java"
+Blort.testDoubleMod:()D:
+regs: 0006; ins: 0000; outs: 0000
+  0000: const-wide/high16 v0, #double 5.0 // #4014000000000000
+  0002: const-wide/high16 v2, #double 3.0 // #4008000000000000
+  0004: rem-double v2, v0, v2
+  0006: const-wide/16 v4, #double 0.0 // #0000
+  0008: rem-double/2addr v0, v4
+  0009: add-double/2addr v0, v2
+  000a: return-wide v0
+  debug info
+    line_start: 152
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 152
+    0002: line 153
+    0006: line 154
+    0009: line 155
+    end sequence
+  source file: "Blort.java"
+Blort.testDoubleMult:()D:
+regs: 0006; ins: 0000; outs: 0000
+  0000: const-wide/high16 v4, #double 10.0 // #4024000000000000
+  0002: const-wide/high16 v0, #double 6.0 // #4018000000000000
+  0004: const-wide/high16 v2, #double 9.0 // #4022000000000000
+  0006: sub-double v0, v2, v0
+  0008: const-wide/high16 v2, #double 4.0 // #4010000000000000
+  000a: mul-double/2addr v0, v2
+  000b: cmpl-double v2, v0, v4
+  000d: if-lez v2, 0010 // +0003
+  000f: sub-double/2addr v0, v4
+  0010: const-wide/high16 v2, #double 2.0 // #4000000000000000
+  0012: mul-double/2addr v0, v2
+  0013: return-wide v0
+  debug info
+    line_start: 130
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 130
+    0004: line 131
+    0008: line 132
+    000b: line 134
+    000f: line 135
+    0010: line 137
+    end sequence
+  source file: "Blort.java"
+Blort.testDoublePhi:()D:
+regs: 0007; ins: 0000; outs: 0000
+  0000: const-wide/16 v0, #double 0.0 // #0000
+  0002: const-wide v2, #double 37.0 // #4042800000000000
+  0007: const-wide/high16 v4, #double 3.0 // #4008000000000000
+  0009: cmpl-double v6, v4, v0
+  000b: if-nez v6, 000e // +0003
+  000d: return-wide v0
+  000e: div-double v0, v2, v4
+  0010: goto 000d // -0003
+  debug info
+    line_start: 159
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 159
+    0007: line 160
+    0009: line 161
+    000d: line 162
+    000e: line 161
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatAddSub:()F:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/high16 v3, #float 5.0 // #40a00000
+  0002: const/high16 v1, #float 3.0 // #40400000
+  0004: sub-float v0, v3, v1
+  0006: add-float v2, v1, v0
+  0008: sub-float v1, v3, v0
+  000a: cmpg-float v0, v2, v3
+  000c: if-gtz v0, 0014 // +0008
+  000e: const/high16 v0, #float 1.0 // #3f800000
+  0010: add-float/2addr v0, v3
+  0011: const/high16 v0, #float 2.0 // #40000000
+  0013: goto 0006 // -000d
+  0014: return v2
+  debug info
+    line_start: 60
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 60
+    0004: line 61
+    0006: line 63
+    0008: line 65
+    000a: line 66
+    000e: line 67
+    0011: line 71
+    0014: line 69
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatDiv:()F:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/high16 v3, #float 10.0 // #41200000
+  0002: const/high16 v1, #float 30.0 // #41f00000
+  0004: const/high16 v0, #float 9.0 // #41100000
+  0006: const/high16 v2, #float 5.0 // #40a00000
+  0008: div-float v2, v1, v2
+  000a: sub-float/2addr v0, v2
+  000b: const/high16 v2, #float 4.0 // #40800000
+  000d: mul-float/2addr v0, v2
+  000e: cmpl-float v2, v0, v3
+  0010: if-lez v2, 0013 // +0003
+  0012: sub-float/2addr v0, v3
+  0013: const/high16 v2, #float 60.0 // #42700000
+  0015: div-float v1, v2, v1
+  0017: mul-float/2addr v0, v1
+  0018: return v0
+  debug info
+    line_start: 87
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 87
+    0004: line 88
+    000b: line 89
+    000e: line 91
+    0012: line 92
+    0013: line 94
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatMod:()F:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/high16 v0, #float 5.0 // #40a00000
+  0002: const/high16 v1, #float 3.0 // #40400000
+  0004: rem-float v1, v0, v1
+  0006: const/4 v2, #float 0.0 // #0
+  0007: rem-float/2addr v0, v2
+  0008: add-float/2addr v0, v1
+  0009: return v0
+  debug info
+    line_start: 98
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 98
+    0002: line 99
+    0006: line 100
+    0008: line 101
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatMult:()F:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/high16 v2, #float 10.0 // #41200000
+  0002: const/high16 v0, #float 6.0 // #40c00000
+  0004: const/high16 v1, #float 9.0 // #41100000
+  0006: sub-float v0, v1, v0
+  0008: const/high16 v1, #float 4.0 // #40800000
+  000a: mul-float/2addr v0, v1
+  000b: cmpl-float v1, v0, v2
+  000d: if-lez v1, 0010 // +0003
+  000f: sub-float/2addr v0, v2
+  0010: const/high16 v1, #float 2.0 // #40000000
+  0012: mul-float/2addr v0, v1
+  0013: return v0
+  debug info
+    line_start: 76
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 76
+    0004: line 77
+    0008: line 78
+    000b: line 80
+    000f: line 81
+    0010: line 83
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatPhi:()F:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v0, #float 0.0 // #0
+  0001: const/high16 v1, #float 37.0 // #42140000
+  0003: const/high16 v2, #float 3.0 // #40400000
+  0005: cmpl-float v3, v2, v0
+  0007: if-nez v3, 000a // +0003
+  0009: return v0
+  000a: div-float v0, v1, v2
+  000c: goto 0009 // -0003
+  debug info
+    line_start: 105
+    parameters_size: 0000
+    0000: prologue end
+    0001: line 105
+    0003: line 106
+    0005: line 107
+    0009: line 108
+    000a: line 107
+    end sequence
+  source file: "Blort.java"
+Blort.testIntAddSub:()I:
+regs: 0000; ins: 0000; outs: 0000
+  0000: goto/32 0000 // +0000
+  debug info
+    line_start: 17
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 17
+    end sequence
+  source file: "Blort.java"
+Blort.testIntDiv:()I:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/4 v0, #int 4 // #4
+  0001: return v0
+  debug info
+    line_start: 40
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 40
+    end sequence
+  source file: "Blort.java"
+Blort.testIntMod:()I:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/4 v0, #int 5 // #5
+  0001: rem-int/lit8 v0, v0, #int 0 // #00
+  0003: add-int/lit8 v0, v0, #int 2 // #02
+  0005: return v0
+  debug info
+    line_start: 44
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 44
+    0001: line 46
+    0003: line 47
+    end sequence
+  source file: "Blort.java"
+Blort.testIntMult:()I:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/4 v0, #int 4 // #4
+  0001: return v0
+  debug info
+    line_start: 29
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 29
+    end sequence
+  source file: "Blort.java"
+Blort.testIntPhi:()I:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/16 v0, #int 12 // #000c
+  0002: return v0
+  debug info
+    line_start: 53
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 53
+    0002: line 54
+    end sequence
+  source file: "Blort.java"
+Blort.testDoubleAddSub:()D:
+regs: 000a; ins: 0000; outs: 0000
+  0000: const-wide/high16 v0, #double 3.0 // #4008000000000000
+  0002: const-wide/high16 v8, #double 5.0 // #4014000000000000
+  0004: sub-double v2, v8, v0
+  0006: add-double v4, v0, v2
+  0008: const-wide/high16 v6, #double 5.0 // #4014000000000000
+  000a: sub-double v0, v6, v2
+  000c: cmpg-double v8, v4, v6
+  000e: if-gtz v8, 0017 // +0009
+  0010: const-wide/high16 v8, #double 1.0 // #3ff0000000000000
+  0012: add-double v4, v6, v8
+  0014: const-wide/high16 v2, #double 2.0 // #4000000000000000
+  0016: goto 0006 // -0010
+  0017: return-wide v4
+  debug info
+    line_start: 114
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 114
+    0002: line 115
+    0002: +local v0 a double
+    0006: line 117
+    0006: +local v2 b double
+    0008: line 118
+    0008: +local v4 c double
+    000a: line 119
+    000a: +local v6 d double
+    000c: line 120
+    0010: line 121
+    0014: line 125
+    0017: line 123
+    end sequence
+  source file: "Blort.java"
+Blort.testDoubleDiv:()D:
+regs: 000c; ins: 0000; outs: 0000
+  0000: const-wide/high16 v10, #double 10.0 // #4024000000000000
+  0002: const-wide/high16 v0, #double 30.0 // #403e000000000000
+  0004: const-wide/high16 v6, #double 9.0 // #4022000000000000
+  0006: const-wide/high16 v8, #double 5.0 // #4014000000000000
+  0008: div-double v8, v0, v8
+  000a: sub-double v2, v6, v8
+  000c: const-wide/high16 v6, #double 4.0 // #4010000000000000
+  000e: mul-double v4, v2, v6
+  0010: cmpl-double v6, v4, v10
+  0012: if-lez v6, 0015 // +0003
+  0014: sub-double/2addr v4, v10
+  0015: const-wide/high16 v6, #double 60.0 // #404e000000000000
+  0017: div-double/2addr v6, v0
+  0018: mul-double/2addr v6, v4
+  0019: return-wide v6
+  debug info
+    line_start: 141
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 141
+    0004: line 142
+    0004: +local v0 a double
+    000c: line 143
+    000c: +local v2 b double
+    0010: line 145
+    0010: +local v4 c double
+    0014: line 146
+    0015: line 148
+    end sequence
+  source file: "Blort.java"
+Blort.testDoubleMod:()D:
+regs: 0008; ins: 0000; outs: 0000
+  0000: const-wide/high16 v0, #double 5.0 // #4014000000000000
+  0002: const-wide/high16 v6, #double 3.0 // #4008000000000000
+  0004: rem-double v2, v0, v6
+  0006: const-wide/16 v6, #double 0.0 // #0000
+  0008: rem-double v4, v0, v6
+  000a: add-double v6, v2, v4
+  000c: return-wide v6
+  debug info
+    line_start: 152
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 152
+    0002: line 153
+    0002: +local v0 a double
+    0006: line 154
+    0006: +local v2 b double
+    000a: line 155
+    000a: +local v4 c double
+    end sequence
+  source file: "Blort.java"
+Blort.testDoubleMult:()D:
+regs: 000a; ins: 0000; outs: 0000
+  0000: const-wide/high16 v8, #double 10.0 // #4024000000000000
+  0002: const-wide/high16 v0, #double 6.0 // #4018000000000000
+  0004: const-wide/high16 v6, #double 9.0 // #4022000000000000
+  0006: sub-double v2, v6, v0
+  0008: const-wide/high16 v6, #double 4.0 // #4010000000000000
+  000a: mul-double v4, v2, v6
+  000c: cmpl-double v6, v4, v8
+  000e: if-lez v6, 0011 // +0003
+  0010: sub-double/2addr v4, v8
+  0011: const-wide/high16 v6, #double 2.0 // #4000000000000000
+  0013: mul-double/2addr v6, v4
+  0014: return-wide v6
+  debug info
+    line_start: 130
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 130
+    0004: line 131
+    0004: +local v0 a double
+    0008: line 132
+    0008: +local v2 b double
+    000c: line 134
+    000c: +local v4 c double
+    0010: line 135
+    0011: line 137
+    end sequence
+  source file: "Blort.java"
+Blort.testDoublePhi:()D:
+regs: 0007; ins: 0000; outs: 0000
+  0000: const-wide/16 v4, #double 0.0 // #0000
+  0002: const-wide v0, #double 37.0 // #4042800000000000
+  0007: const-wide/high16 v2, #double 3.0 // #4008000000000000
+  0009: cmpl-double v6, v2, v4
+  000b: if-nez v6, 000e // +0003
+  000d: return-wide v4
+  000e: div-double v4, v0, v2
+  0010: goto 000d // -0003
+  debug info
+    line_start: 159
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 159
+    0007: line 160
+    0007: +local v0 a double
+    0009: line 161
+    0009: +local v2 b double
+    000d: line 162
+    000d: +local v4 c double
+    000e: line 161
+    000e: -local v4 c double
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatAddSub:()F:
+regs: 0005; ins: 0000; outs: 0000
+  0000: const/high16 v0, #float 3.0 // #40400000
+  0002: const/high16 v4, #float 5.0 // #40a00000
+  0004: sub-float v1, v4, v0
+  0006: add-float v2, v0, v1
+  0008: const/high16 v3, #float 5.0 // #40a00000
+  000a: sub-float v0, v3, v1
+  000c: cmpg-float v4, v2, v3
+  000e: if-gtz v4, 0017 // +0009
+  0010: const/high16 v4, #float 1.0 // #3f800000
+  0012: add-float v2, v3, v4
+  0014: const/high16 v1, #float 2.0 // #40000000
+  0016: goto 0006 // -0010
+  0017: return v2
+  debug info
+    line_start: 60
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 60
+    0002: line 61
+    0002: +local v0 a float
+    0006: line 63
+    0006: +local v1 b float
+    0008: line 64
+    0008: +local v2 c float
+    000a: line 65
+    000a: +local v3 d float
+    000c: line 66
+    0010: line 67
+    0014: line 71
+    0017: line 69
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatDiv:()F:
+regs: 0006; ins: 0000; outs: 0000
+  0000: const/high16 v5, #float 10.0 // #41200000
+  0002: const/high16 v0, #float 30.0 // #41f00000
+  0004: const/high16 v3, #float 9.0 // #41100000
+  0006: const/high16 v4, #float 5.0 // #40a00000
+  0008: div-float v4, v0, v4
+  000a: sub-float v1, v3, v4
+  000c: const/high16 v3, #float 4.0 // #40800000
+  000e: mul-float v2, v1, v3
+  0010: cmpl-float v3, v2, v5
+  0012: if-lez v3, 0015 // +0003
+  0014: sub-float/2addr v2, v5
+  0015: const/high16 v3, #float 60.0 // #42700000
+  0017: div-float/2addr v3, v0
+  0018: mul-float/2addr v3, v2
+  0019: return v3
+  debug info
+    line_start: 87
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 87
+    0004: line 88
+    0004: +local v0 a float
+    000c: line 89
+    000c: +local v1 b float
+    0010: line 91
+    0010: +local v2 c float
+    0014: line 92
+    0015: line 94
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatMod:()F:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/high16 v0, #float 5.0 // #40a00000
+  0002: const/high16 v3, #float 3.0 // #40400000
+  0004: rem-float v1, v0, v3
+  0006: const/4 v3, #float 0.0 // #0
+  0007: rem-float v2, v0, v3
+  0009: add-float v3, v1, v2
+  000b: return v3
+  debug info
+    line_start: 98
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 98
+    0002: line 99
+    0002: +local v0 a float
+    0006: line 100
+    0006: +local v1 b float
+    0009: line 101
+    0009: +local v2 c float
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatMult:()F:
+regs: 0005; ins: 0000; outs: 0000
+  0000: const/high16 v4, #float 10.0 // #41200000
+  0002: const/high16 v0, #float 6.0 // #40c00000
+  0004: const/high16 v3, #float 9.0 // #41100000
+  0006: sub-float v1, v3, v0
+  0008: const/high16 v3, #float 4.0 // #40800000
+  000a: mul-float v2, v1, v3
+  000c: cmpl-float v3, v2, v4
+  000e: if-lez v3, 0011 // +0003
+  0010: sub-float/2addr v2, v4
+  0011: const/high16 v3, #float 2.0 // #40000000
+  0013: mul-float/2addr v3, v2
+  0014: return v3
+  debug info
+    line_start: 76
+    parameters_size: 0000
+    0000: prologue end
+    0002: line 76
+    0004: line 77
+    0004: +local v0 a float
+    0008: line 78
+    0008: +local v1 b float
+    000c: line 80
+    000c: +local v2 c float
+    0010: line 81
+    0011: line 83
+    end sequence
+  source file: "Blort.java"
+Blort.testFloatPhi:()F:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v2, #float 0.0 // #0
+  0001: const/high16 v0, #float 37.0 // #42140000
+  0003: const/high16 v1, #float 3.0 // #40400000
+  0005: cmpl-float v3, v1, v2
+  0007: if-nez v3, 000a // +0003
+  0009: return v2
+  000a: div-float v2, v0, v1
+  000c: goto 0009 // -0003
+  debug info
+    line_start: 105
+    parameters_size: 0000
+    0000: prologue end
+    0001: line 105
+    0003: line 106
+    0003: +local v0 a float
+    0005: line 107
+    0005: +local v1 b float
+    0009: line 108
+    0009: +local v2 c float
+    000a: line 107
+    000a: -local v2 c float
+    end sequence
+  source file: "Blort.java"
+Blort.testIntAddSub:()I:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v0, #int 3 // #3
+  0001: rsub-int/lit8 v1, v0, #int 5 // #05
+  0003: add-int/lit8 v2, v0, #int 2 // #02
+  0005: const/4 v3, #int 5 // #5
+  0006: add-int/lit8 v0, v3, #int -2 // #fe
+  0008: if-gt v2, v3, 000e // +0006
+  000a: add-int/lit8 v2, v3, #int 1 // #01
+  000c: const/4 v1, #int 2 // #2
+  000d: goto 0003 // -000a
+  000e: return v2
+  debug info
+    line_start: 6
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 6
+    0001: line 7
+    0001: +local v0 a int
+    0003: line 9
+    0003: +local v1 b int
+    0005: line 10
+    0005: +local v2 c int
+    0006: line 11
+    0006: +local v3 d int
+    0008: line 12
+    000a: line 13
+    000c: line 17
+    000e: line 15
+    end sequence
+  source file: "Blort.java"
+Blort.testIntDiv:()I:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/16 v0, #int 30 // #001e
+  0002: const/4 v3, #int 6 // #6
+  0003: rsub-int/lit8 v1, v3, #int 9 // #09
+  0005: mul-int/lit8 v2, v1, #int 4 // #04
+  0007: const/16 v3, #int 10 // #000a
+  0009: if-le v2, v3, 000d // +0004
+  000b: add-int/lit8 v2, v2, #int -10 // #f6
+  000d: mul-int/lit8 v3, v2, #int 2 // #02
+  000f: return v3
+  debug info
+    line_start: 33
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 33
+    0002: line 34
+    0002: +local v0 a int
+    0005: line 35
+    0005: +local v1 b int
+    0007: line 37
+    0007: +local v2 c int
+    000b: line 38
+    000d: line 40
+    end sequence
+  source file: "Blort.java"
+Blort.testIntMod:()I:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v0, #int 5 // #5
+  0001: rem-int/lit8 v1, v0, #int 3 // #03
+  0003: rem-int/lit8 v2, v0, #int 0 // #00
+  0005: add-int/lit8 v3, v2, #int 2 // #02
+  0007: return v3
+  debug info
+    line_start: 44
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 44
+    0001: line 45
+    0001: +local v0 a int
+    0003: line 46
+    0003: +local v1 b int
+    0005: line 47
+    0005: +local v2 c int
+    end sequence
+  source file: "Blort.java"
+Blort.testIntMult:()I:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v0, #int 6 // #6
+  0001: rsub-int/lit8 v1, v0, #int 9 // #09
+  0003: mul-int/lit8 v2, v1, #int 4 // #04
+  0005: const/16 v3, #int 10 // #000a
+  0007: if-le v2, v3, 000b // +0004
+  0009: add-int/lit8 v2, v2, #int -10 // #f6
+  000b: mul-int/lit8 v3, v2, #int 2 // #02
+  000d: return v3
+  debug info
+    line_start: 22
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 22
+    0001: line 23
+    0001: +local v0 a int
+    0003: line 24
+    0003: +local v1 b int
+    0005: line 26
+    0005: +local v2 c int
+    0009: line 27
+    000b: line 29
+    end sequence
+  source file: "Blort.java"
+Blort.testIntPhi:()I:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/16 v0, #int 37 // #0025
+  0002: const/4 v1, #int 3 // #3
+  0003: if-nez v1, 0007 // +0004
+  0005: const/4 v2, #int 0 // #0
+  0006: return v2
+  0007: const/16 v2, #int 12 // #000c
+  0009: goto 0006 // -0003
+  debug info
+    line_start: 51
+    parameters_size: 0000
+    0000: prologue end
+    0000: line 51
+    0002: line 52
+    0002: +local v0 a int
+    0003: line 53
+    0003: +local v1 b int
+    0006: line 54
+    0006: +local v2 c int
+    0007: line 53
+    0007: -local v2 c int
+    end sequence
+  source file: "Blort.java"
diff --git a/dx/tests/121-sccp/info.txt b/dx/tests/121-sccp/info.txt
new file mode 100644
index 0000000..76e6bcd
--- /dev/null
+++ b/dx/tests/121-sccp/info.txt
@@ -0,0 +1,6 @@
+This tests various aspects of the SCCP pass for correctness, and to
+ensure there aren't any regressions.
+
+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/121-sccp/run b/dx/tests/121-sccp/run
new file mode 100644
index 0000000..4f65461
--- /dev/null
+++ b/dx/tests/121-sccp/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 -d . *.java
+dx --debug --dex --dump-method=Blort.test'*' Blort.class
+$JAVAC -g -d . *.java
+dx --debug --dex --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/122-switch-with-high-register/Blort.java b/dx/tests/122-switch-with-high-register/Blort.java
new file mode 100644
index 0000000..c0fb1d0
--- /dev/null
+++ b/dx/tests/122-switch-with-high-register/Blort.java
@@ -0,0 +1,30 @@
+class Blort {
+
+    public static int switchWithHighRegister(
+            int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10, int param11, int param12, int param13, int param14, int param15, int param16, int param17, int param18, int param19, int param20, int param21, int param22, int param23, int param24, int param25, int param26, int param27, int param28, int param29, int param30, int param31, int param32, int param33, int param34, int param35, int param36, int param37, int param38, int param39, int param40, int param41, int param42, int param43, int param44, int param45, int param46, int param47, int param48, int param49, int param50, int param51, int param52, int param53, int param54, int param55, int param56, int param57, int param58, int param59, int param60, int param61, int param62, int param63, int param64, int param65, int param66, int param67, int param68, int param69, int param70, int param71, int param72, int param73, int param74, int param75, int param76, int param77, int param78, int param79, int param80, int param81, int param82, int param83, int param84, int param85, int param86, int param87, int param88, int param89, int param90, int param91, int param92, int param93, int param94, int param95, int param96, int param97, int param98, int param99, int param100, int param101, int param102, int param103, int param104, int param105, int param106, int param107, int param108, int param109, int param110, int param111, int param112, int param113, int param114, int param115, int param116, int param117, int param118, int param119, int param120, int param121, int param122, int param123, int param124, int param125, int param126, int param127, int param128, int param129, int param130, int param131, int param132, int param133, int param134, int param135, int param136, int param137, int param138, int param139, int param140, int param141, int param142, int param143, int param144, int param145, int param146, int param147, int param148, int param149, int param150, int param151, int param152, int param153, int param154, int param155, int param156, int param157, int param158, int param159, int param160, int param161, int param162, int param163, int param164, int param165, int param166, int param167, int param168, int param169, int param170, int param171, int param172, int param173, int param174, int param175, int param176, int param177, int param178, int param179, int param180, int param181, int param182, int param183, int param184, int param185, int param186, int param187, int param188, int param189, int param190, int param191, int param192, int param193, int param194, int param195, int param196, int param197, int param198, int param199, int param200, int param201, int param202, int param203, int param204, int param205, int param206, int param207, int param208, int param209, int param210, int param211, int param212, int param213, int param214, int param215, int param216, int param217, int param218, int param219, int param220, int param221, int param222, int param223, int param224, int param225, int param226, int param227, int param228, int param229, int param230, int param231, int param232, int param233, int param234, int param235, int param236, int param237, int param238, int param239, int param240, int param241, int param242, int param243, int param244, int param245, int param246, int param247, int param248, int param249, int param250, int param251, int param252, int param253, int param254,
+            int param255) {
+
+        // this should add a new non-parameter register, and force the register
+        // for param255 over v255
+        int i=1;
+
+        // make sure that it can't re-use one of the parameter registers for i
+        System.out.println(Integer.toString(i+param1+param2+param3+param4+param5+param6+param7+param8+param9+param10+param11+param12+param13+param14+param15+param16+param17+param18+param19+param20+param21+param22+param23+param24+param25+param26+param27+param28+param29+param30+param31+param32+param33+param34+param35+param36+param37+param38+param39+param40+param41+param42+param43+param44+param45+param46+param47+param48+param49+param50+param51+param52+param53+param54+param55+param56+param57+param58+param59+param60+param61+param62+param63+param64+param65+param66+param67+param68+param69+param70+param71+param72+param73+param74+param75+param76+param77+param78+param79+param80+param81+param82+param83+param84+param85+param86+param87+param88+param89+param90+param91+param92+param93+param94+param95+param96+param97+param98+param99+param100+param101+param102+param103+param104+param105+param106+param107+param108+param109+param110+param111+param112+param113+param114+param115+param116+param117+param118+param119+param120+param121+param122+param123+param124+param125+param126+param127+param128+param129+param130+param131+param132+param133+param134+param135+param136+param137+param138+param139+param140+param141+param142+param143+param144+param145+param146+param147+param148+param149+param150+param151+param152+param153+param154+param155+param156+param157+param158+param159+param160+param161+param162+param163+param164+param165+param166+param167+param168+param169+param170+param171+param172+param173+param174+param175+param176+param177+param178+param179+param180+param181+param182+param183+param184+param185+param186+param187+param188+param189+param190+param191+param192+param193+param194+param195+param196+param197+param198+param199+param200+param201+param202+param203+param204+param205+param206+param207+param208+param209+param210+param211+param212+param213+param214+param215+param216+param217+param218+param219+param220+param221+param222+param223+param224+param225+param226+param227+param228+param229+param230+param231+param232+param233+param234+param235+param236+param237+param238+param239+param240+param241+param242+param243+param244+param245+param246+param247+param248+param249+param250+param251+param252+param253+param254));
+
+        // the register for param255 should be greater than v255, forcing dx
+        // to generate a move instruction to move it to a lower register
+        switch (param255) {
+            case 0:
+                return 0;
+            case 1:
+                return 1;
+            case 2:
+                return 2;
+            case 3:
+                return 3;
+            case 4:
+                return 4;
+        }
+        return -1;
+    }
+}
diff --git a/dx/tests/122-switch-with-high-register/expected.txt b/dx/tests/122-switch-with-high-register/expected.txt
new file mode 100644
index 0000000..7f09421
--- /dev/null
+++ b/dx/tests/122-switch-with-high-register/expected.txt
@@ -0,0 +1,1117 @@
+Blort.switchWithHighRegister:(IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)I:
+regs: 0102; ins: 00ff; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: sget-object v2, java.lang.System.out:Ljava/io/PrintStream;
+  0003: add-int/2addr v1, v3
+  0004: add-int/2addr v1, v4
+  0005: add-int/2addr v1, v5
+  0006: add-int/2addr v1, v6
+  0007: add-int/2addr v1, v7
+  0008: add-int/2addr v1, v8
+  0009: add-int/2addr v1, v9
+  000a: add-int/2addr v1, v10
+  000b: add-int/2addr v1, v11
+  000c: add-int/2addr v1, v12
+  000d: add-int/2addr v1, v13
+  000e: add-int/2addr v1, v14
+  000f: add-int/2addr v1, v15
+  0010: add-int v1, v1, v16
+  0012: add-int v1, v1, v17
+  0014: add-int v1, v1, v18
+  0016: add-int v1, v1, v19
+  0018: add-int v1, v1, v20
+  001a: add-int v1, v1, v21
+  001c: add-int v1, v1, v22
+  001e: add-int v1, v1, v23
+  0020: add-int v1, v1, v24
+  0022: add-int v1, v1, v25
+  0024: add-int v1, v1, v26
+  0026: add-int v1, v1, v27
+  0028: add-int v1, v1, v28
+  002a: add-int v1, v1, v29
+  002c: add-int v1, v1, v30
+  002e: add-int v1, v1, v31
+  0030: add-int v1, v1, v32
+  0032: add-int v1, v1, v33
+  0034: add-int v1, v1, v34
+  0036: add-int v1, v1, v35
+  0038: add-int v1, v1, v36
+  003a: add-int v1, v1, v37
+  003c: add-int v1, v1, v38
+  003e: add-int v1, v1, v39
+  0040: add-int v1, v1, v40
+  0042: add-int v1, v1, v41
+  0044: add-int v1, v1, v42
+  0046: add-int v1, v1, v43
+  0048: add-int v1, v1, v44
+  004a: add-int v1, v1, v45
+  004c: add-int v1, v1, v46
+  004e: add-int v1, v1, v47
+  0050: add-int v1, v1, v48
+  0052: add-int v1, v1, v49
+  0054: add-int v1, v1, v50
+  0056: add-int v1, v1, v51
+  0058: add-int v1, v1, v52
+  005a: add-int v1, v1, v53
+  005c: add-int v1, v1, v54
+  005e: add-int v1, v1, v55
+  0060: add-int v1, v1, v56
+  0062: add-int v1, v1, v57
+  0064: add-int v1, v1, v58
+  0066: add-int v1, v1, v59
+  0068: add-int v1, v1, v60
+  006a: add-int v1, v1, v61
+  006c: add-int v1, v1, v62
+  006e: add-int v1, v1, v63
+  0070: add-int v1, v1, v64
+  0072: add-int v1, v1, v65
+  0074: add-int v1, v1, v66
+  0076: add-int v1, v1, v67
+  0078: add-int v1, v1, v68
+  007a: add-int v1, v1, v69
+  007c: add-int v1, v1, v70
+  007e: add-int v1, v1, v71
+  0080: add-int v1, v1, v72
+  0082: add-int v1, v1, v73
+  0084: add-int v1, v1, v74
+  0086: add-int v1, v1, v75
+  0088: add-int v1, v1, v76
+  008a: add-int v1, v1, v77
+  008c: add-int v1, v1, v78
+  008e: add-int v1, v1, v79
+  0090: add-int v1, v1, v80
+  0092: add-int v1, v1, v81
+  0094: add-int v1, v1, v82
+  0096: add-int v1, v1, v83
+  0098: add-int v1, v1, v84
+  009a: add-int v1, v1, v85
+  009c: add-int v1, v1, v86
+  009e: add-int v1, v1, v87
+  00a0: add-int v1, v1, v88
+  00a2: add-int v1, v1, v89
+  00a4: add-int v1, v1, v90
+  00a6: add-int v1, v1, v91
+  00a8: add-int v1, v1, v92
+  00aa: add-int v1, v1, v93
+  00ac: add-int v1, v1, v94
+  00ae: add-int v1, v1, v95
+  00b0: add-int v1, v1, v96
+  00b2: add-int v1, v1, v97
+  00b4: add-int v1, v1, v98
+  00b6: add-int v1, v1, v99
+  00b8: add-int v1, v1, v100
+  00ba: add-int v1, v1, v101
+  00bc: add-int v1, v1, v102
+  00be: add-int v1, v1, v103
+  00c0: add-int v1, v1, v104
+  00c2: add-int v1, v1, v105
+  00c4: add-int v1, v1, v106
+  00c6: add-int v1, v1, v107
+  00c8: add-int v1, v1, v108
+  00ca: add-int v1, v1, v109
+  00cc: add-int v1, v1, v110
+  00ce: add-int v1, v1, v111
+  00d0: add-int v1, v1, v112
+  00d2: add-int v1, v1, v113
+  00d4: add-int v1, v1, v114
+  00d6: add-int v1, v1, v115
+  00d8: add-int v1, v1, v116
+  00da: add-int v1, v1, v117
+  00dc: add-int v1, v1, v118
+  00de: add-int v1, v1, v119
+  00e0: add-int v1, v1, v120
+  00e2: add-int v1, v1, v121
+  00e4: add-int v1, v1, v122
+  00e6: add-int v1, v1, v123
+  00e8: add-int v1, v1, v124
+  00ea: add-int v1, v1, v125
+  00ec: add-int v1, v1, v126
+  00ee: add-int v1, v1, v127
+  00f0: add-int v1, v1, v128
+  00f2: add-int v1, v1, v129
+  00f4: add-int v1, v1, v130
+  00f6: add-int v1, v1, v131
+  00f8: add-int v1, v1, v132
+  00fa: add-int v1, v1, v133
+  00fc: add-int v1, v1, v134
+  00fe: add-int v1, v1, v135
+  0100: add-int v1, v1, v136
+  0102: add-int v1, v1, v137
+  0104: add-int v1, v1, v138
+  0106: add-int v1, v1, v139
+  0108: add-int v1, v1, v140
+  010a: add-int v1, v1, v141
+  010c: add-int v1, v1, v142
+  010e: add-int v1, v1, v143
+  0110: add-int v1, v1, v144
+  0112: add-int v1, v1, v145
+  0114: add-int v1, v1, v146
+  0116: add-int v1, v1, v147
+  0118: add-int v1, v1, v148
+  011a: add-int v1, v1, v149
+  011c: add-int v1, v1, v150
+  011e: add-int v1, v1, v151
+  0120: add-int v1, v1, v152
+  0122: add-int v1, v1, v153
+  0124: add-int v1, v1, v154
+  0126: add-int v1, v1, v155
+  0128: add-int v1, v1, v156
+  012a: add-int v1, v1, v157
+  012c: add-int v1, v1, v158
+  012e: add-int v1, v1, v159
+  0130: add-int v1, v1, v160
+  0132: add-int v1, v1, v161
+  0134: add-int v1, v1, v162
+  0136: add-int v1, v1, v163
+  0138: add-int v1, v1, v164
+  013a: add-int v1, v1, v165
+  013c: add-int v1, v1, v166
+  013e: add-int v1, v1, v167
+  0140: add-int v1, v1, v168
+  0142: add-int v1, v1, v169
+  0144: add-int v1, v1, v170
+  0146: add-int v1, v1, v171
+  0148: add-int v1, v1, v172
+  014a: add-int v1, v1, v173
+  014c: add-int v1, v1, v174
+  014e: add-int v1, v1, v175
+  0150: add-int v1, v1, v176
+  0152: add-int v1, v1, v177
+  0154: add-int v1, v1, v178
+  0156: add-int v1, v1, v179
+  0158: add-int v1, v1, v180
+  015a: add-int v1, v1, v181
+  015c: add-int v1, v1, v182
+  015e: add-int v1, v1, v183
+  0160: add-int v1, v1, v184
+  0162: add-int v1, v1, v185
+  0164: add-int v1, v1, v186
+  0166: add-int v1, v1, v187
+  0168: add-int v1, v1, v188
+  016a: add-int v1, v1, v189
+  016c: add-int v1, v1, v190
+  016e: add-int v1, v1, v191
+  0170: add-int v1, v1, v192
+  0172: add-int v1, v1, v193
+  0174: add-int v1, v1, v194
+  0176: add-int v1, v1, v195
+  0178: add-int v1, v1, v196
+  017a: add-int v1, v1, v197
+  017c: add-int v1, v1, v198
+  017e: add-int v1, v1, v199
+  0180: add-int v1, v1, v200
+  0182: add-int v1, v1, v201
+  0184: add-int v1, v1, v202
+  0186: add-int v1, v1, v203
+  0188: add-int v1, v1, v204
+  018a: add-int v1, v1, v205
+  018c: add-int v1, v1, v206
+  018e: add-int v1, v1, v207
+  0190: add-int v1, v1, v208
+  0192: add-int v1, v1, v209
+  0194: add-int v1, v1, v210
+  0196: add-int v1, v1, v211
+  0198: add-int v1, v1, v212
+  019a: add-int v1, v1, v213
+  019c: add-int v1, v1, v214
+  019e: add-int v1, v1, v215
+  01a0: add-int v1, v1, v216
+  01a2: add-int v1, v1, v217
+  01a4: add-int v1, v1, v218
+  01a6: add-int v1, v1, v219
+  01a8: add-int v1, v1, v220
+  01aa: add-int v1, v1, v221
+  01ac: add-int v1, v1, v222
+  01ae: add-int v1, v1, v223
+  01b0: add-int v1, v1, v224
+  01b2: add-int v1, v1, v225
+  01b4: add-int v1, v1, v226
+  01b6: add-int v1, v1, v227
+  01b8: add-int v1, v1, v228
+  01ba: add-int v1, v1, v229
+  01bc: add-int v1, v1, v230
+  01be: add-int v1, v1, v231
+  01c0: add-int v1, v1, v232
+  01c2: add-int v1, v1, v233
+  01c4: add-int v1, v1, v234
+  01c6: add-int v1, v1, v235
+  01c8: add-int v1, v1, v236
+  01ca: add-int v1, v1, v237
+  01cc: add-int v1, v1, v238
+  01ce: add-int v1, v1, v239
+  01d0: add-int v1, v1, v240
+  01d2: add-int v1, v1, v241
+  01d4: add-int v1, v1, v242
+  01d6: add-int v1, v1, v243
+  01d8: add-int v1, v1, v244
+  01da: add-int v1, v1, v245
+  01dc: add-int v1, v1, v246
+  01de: add-int v1, v1, v247
+  01e0: add-int v1, v1, v248
+  01e2: add-int v1, v1, v249
+  01e4: add-int v1, v1, v250
+  01e6: add-int v1, v1, v251
+  01e8: add-int v1, v1, v252
+  01ea: add-int v1, v1, v253
+  01ec: add-int v1, v1, v254
+  01ee: add-int v1, v1, v255
+  01f0: move/from16 v0, v256
+  01f2: add-int/2addr v1, v0
+  01f3: invoke-static {v1}, java.lang.Integer.toString:(I)Ljava/lang/String;
+  01f6: move-result-object v1
+  01f7: invoke-virtual {v2, v1}, java.io.PrintStream.println:(Ljava/lang/String;)V
+  01fa: move/from16 v0, v257
+  01fc: packed-switch v0, 020c // +0010
+  01ff: const/4 v1, #int -1 // #f
+  0200: return v1
+  0201: const/4 v1, #int 0 // #0
+  0202: goto 0200 // -0002
+  0203: const/4 v1, #int 1 // #1
+  0204: goto 0200 // -0004
+  0205: const/4 v1, #int 2 // #2
+  0206: goto 0200 // -0006
+  0207: const/4 v1, #int 3 // #3
+  0208: goto 0200 // -0008
+  0209: const/4 v1, #int 4 // #4
+  020a: goto 0200 // -000a
+  020b: nop // spacer
+  020c: packed-switch-payload // for switch @ 01fc
+          0: 00000201 // +00000005
+          1: 00000203 // +00000007
+          2: 00000205 // +00000009
+          3: 00000207 // +0000000b
+          4: 00000209 // +0000000d
+  debug info
+    line_start: 9
+    parameters_size: 00ff
+    parameter <unnamed> v3
+    parameter <unnamed> v4
+    parameter <unnamed> v5
+    parameter <unnamed> v6
+    parameter <unnamed> v7
+    parameter <unnamed> v8
+    parameter <unnamed> v9
+    parameter <unnamed> v10
+    parameter <unnamed> v11
+    parameter <unnamed> v12
+    parameter <unnamed> v13
+    parameter <unnamed> v14
+    parameter <unnamed> v15
+    parameter <unnamed> v16
+    parameter <unnamed> v17
+    parameter <unnamed> v18
+    parameter <unnamed> v19
+    parameter <unnamed> v20
+    parameter <unnamed> v21
+    parameter <unnamed> v22
+    parameter <unnamed> v23
+    parameter <unnamed> v24
+    parameter <unnamed> v25
+    parameter <unnamed> v26
+    parameter <unnamed> v27
+    parameter <unnamed> v28
+    parameter <unnamed> v29
+    parameter <unnamed> v30
+    parameter <unnamed> v31
+    parameter <unnamed> v32
+    parameter <unnamed> v33
+    parameter <unnamed> v34
+    parameter <unnamed> v35
+    parameter <unnamed> v36
+    parameter <unnamed> v37
+    parameter <unnamed> v38
+    parameter <unnamed> v39
+    parameter <unnamed> v40
+    parameter <unnamed> v41
+    parameter <unnamed> v42
+    parameter <unnamed> v43
+    parameter <unnamed> v44
+    parameter <unnamed> v45
+    parameter <unnamed> v46
+    parameter <unnamed> v47
+    parameter <unnamed> v48
+    parameter <unnamed> v49
+    parameter <unnamed> v50
+    parameter <unnamed> v51
+    parameter <unnamed> v52
+    parameter <unnamed> v53
+    parameter <unnamed> v54
+    parameter <unnamed> v55
+    parameter <unnamed> v56
+    parameter <unnamed> v57
+    parameter <unnamed> v58
+    parameter <unnamed> v59
+    parameter <unnamed> v60
+    parameter <unnamed> v61
+    parameter <unnamed> v62
+    parameter <unnamed> v63
+    parameter <unnamed> v64
+    parameter <unnamed> v65
+    parameter <unnamed> v66
+    parameter <unnamed> v67
+    parameter <unnamed> v68
+    parameter <unnamed> v69
+    parameter <unnamed> v70
+    parameter <unnamed> v71
+    parameter <unnamed> v72
+    parameter <unnamed> v73
+    parameter <unnamed> v74
+    parameter <unnamed> v75
+    parameter <unnamed> v76
+    parameter <unnamed> v77
+    parameter <unnamed> v78
+    parameter <unnamed> v79
+    parameter <unnamed> v80
+    parameter <unnamed> v81
+    parameter <unnamed> v82
+    parameter <unnamed> v83
+    parameter <unnamed> v84
+    parameter <unnamed> v85
+    parameter <unnamed> v86
+    parameter <unnamed> v87
+    parameter <unnamed> v88
+    parameter <unnamed> v89
+    parameter <unnamed> v90
+    parameter <unnamed> v91
+    parameter <unnamed> v92
+    parameter <unnamed> v93
+    parameter <unnamed> v94
+    parameter <unnamed> v95
+    parameter <unnamed> v96
+    parameter <unnamed> v97
+    parameter <unnamed> v98
+    parameter <unnamed> v99
+    parameter <unnamed> v100
+    parameter <unnamed> v101
+    parameter <unnamed> v102
+    parameter <unnamed> v103
+    parameter <unnamed> v104
+    parameter <unnamed> v105
+    parameter <unnamed> v106
+    parameter <unnamed> v107
+    parameter <unnamed> v108
+    parameter <unnamed> v109
+    parameter <unnamed> v110
+    parameter <unnamed> v111
+    parameter <unnamed> v112
+    parameter <unnamed> v113
+    parameter <unnamed> v114
+    parameter <unnamed> v115
+    parameter <unnamed> v116
+    parameter <unnamed> v117
+    parameter <unnamed> v118
+    parameter <unnamed> v119
+    parameter <unnamed> v120
+    parameter <unnamed> v121
+    parameter <unnamed> v122
+    parameter <unnamed> v123
+    parameter <unnamed> v124
+    parameter <unnamed> v125
+    parameter <unnamed> v126
+    parameter <unnamed> v127
+    parameter <unnamed> v128
+    parameter <unnamed> v129
+    parameter <unnamed> v130
+    parameter <unnamed> v131
+    parameter <unnamed> v132
+    parameter <unnamed> v133
+    parameter <unnamed> v134
+    parameter <unnamed> v135
+    parameter <unnamed> v136
+    parameter <unnamed> v137
+    parameter <unnamed> v138
+    parameter <unnamed> v139
+    parameter <unnamed> v140
+    parameter <unnamed> v141
+    parameter <unnamed> v142
+    parameter <unnamed> v143
+    parameter <unnamed> v144
+    parameter <unnamed> v145
+    parameter <unnamed> v146
+    parameter <unnamed> v147
+    parameter <unnamed> v148
+    parameter <unnamed> v149
+    parameter <unnamed> v150
+    parameter <unnamed> v151
+    parameter <unnamed> v152
+    parameter <unnamed> v153
+    parameter <unnamed> v154
+    parameter <unnamed> v155
+    parameter <unnamed> v156
+    parameter <unnamed> v157
+    parameter <unnamed> v158
+    parameter <unnamed> v159
+    parameter <unnamed> v160
+    parameter <unnamed> v161
+    parameter <unnamed> v162
+    parameter <unnamed> v163
+    parameter <unnamed> v164
+    parameter <unnamed> v165
+    parameter <unnamed> v166
+    parameter <unnamed> v167
+    parameter <unnamed> v168
+    parameter <unnamed> v169
+    parameter <unnamed> v170
+    parameter <unnamed> v171
+    parameter <unnamed> v172
+    parameter <unnamed> v173
+    parameter <unnamed> v174
+    parameter <unnamed> v175
+    parameter <unnamed> v176
+    parameter <unnamed> v177
+    parameter <unnamed> v178
+    parameter <unnamed> v179
+    parameter <unnamed> v180
+    parameter <unnamed> v181
+    parameter <unnamed> v182
+    parameter <unnamed> v183
+    parameter <unnamed> v184
+    parameter <unnamed> v185
+    parameter <unnamed> v186
+    parameter <unnamed> v187
+    parameter <unnamed> v188
+    parameter <unnamed> v189
+    parameter <unnamed> v190
+    parameter <unnamed> v191
+    parameter <unnamed> v192
+    parameter <unnamed> v193
+    parameter <unnamed> v194
+    parameter <unnamed> v195
+    parameter <unnamed> v196
+    parameter <unnamed> v197
+    parameter <unnamed> v198
+    parameter <unnamed> v199
+    parameter <unnamed> v200
+    parameter <unnamed> v201
+    parameter <unnamed> v202
+    parameter <unnamed> v203
+    parameter <unnamed> v204
+    parameter <unnamed> v205
+    parameter <unnamed> v206
+    parameter <unnamed> v207
+    parameter <unnamed> v208
+    parameter <unnamed> v209
+    parameter <unnamed> v210
+    parameter <unnamed> v211
+    parameter <unnamed> v212
+    parameter <unnamed> v213
+    parameter <unnamed> v214
+    parameter <unnamed> v215
+    parameter <unnamed> v216
+    parameter <unnamed> v217
+    parameter <unnamed> v218
+    parameter <unnamed> v219
+    parameter <unnamed> v220
+    parameter <unnamed> v221
+    parameter <unnamed> v222
+    parameter <unnamed> v223
+    parameter <unnamed> v224
+    parameter <unnamed> v225
+    parameter <unnamed> v226
+    parameter <unnamed> v227
+    parameter <unnamed> v228
+    parameter <unnamed> v229
+    parameter <unnamed> v230
+    parameter <unnamed> v231
+    parameter <unnamed> v232
+    parameter <unnamed> v233
+    parameter <unnamed> v234
+    parameter <unnamed> v235
+    parameter <unnamed> v236
+    parameter <unnamed> v237
+    parameter <unnamed> v238
+    parameter <unnamed> v239
+    parameter <unnamed> v240
+    parameter <unnamed> v241
+    parameter <unnamed> v242
+    parameter <unnamed> v243
+    parameter <unnamed> v244
+    parameter <unnamed> v245
+    parameter <unnamed> v246
+    parameter <unnamed> v247
+    parameter <unnamed> v248
+    parameter <unnamed> v249
+    parameter <unnamed> v250
+    parameter <unnamed> v251
+    parameter <unnamed> v252
+    parameter <unnamed> v253
+    parameter <unnamed> v254
+    parameter <unnamed> v255
+    parameter <unnamed> v256
+    parameter <unnamed> v257
+    0000: prologue end
+    0000: line 9
+    0001: line 12
+    01fa: advance pc
+    01fa: line 16
+    line = 28
+    01ff: line 28
+    line = 18
+    0201: line 18
+    0203: line 20
+    0205: line 22
+    0207: line 24
+    0209: line 26
+    line = 16
+    020b: line 16
+    end sequence
+  source file: "Blort.java"
+Blort.switchWithHighRegister:(IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII)I:
+regs: 0103; ins: 00ff; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: sget-object v2, java.lang.System.out:Ljava/io/PrintStream;
+  0003: add-int v3, v1, v4
+  0005: add-int/2addr v3, v5
+  0006: add-int/2addr v3, v6
+  0007: add-int/2addr v3, v7
+  0008: add-int/2addr v3, v8
+  0009: add-int/2addr v3, v9
+  000a: add-int/2addr v3, v10
+  000b: add-int/2addr v3, v11
+  000c: add-int/2addr v3, v12
+  000d: add-int/2addr v3, v13
+  000e: add-int/2addr v3, v14
+  000f: add-int/2addr v3, v15
+  0010: add-int v3, v3, v16
+  0012: add-int v3, v3, v17
+  0014: add-int v3, v3, v18
+  0016: add-int v3, v3, v19
+  0018: add-int v3, v3, v20
+  001a: add-int v3, v3, v21
+  001c: add-int v3, v3, v22
+  001e: add-int v3, v3, v23
+  0020: add-int v3, v3, v24
+  0022: add-int v3, v3, v25
+  0024: add-int v3, v3, v26
+  0026: add-int v3, v3, v27
+  0028: add-int v3, v3, v28
+  002a: add-int v3, v3, v29
+  002c: add-int v3, v3, v30
+  002e: add-int v3, v3, v31
+  0030: add-int v3, v3, v32
+  0032: add-int v3, v3, v33
+  0034: add-int v3, v3, v34
+  0036: add-int v3, v3, v35
+  0038: add-int v3, v3, v36
+  003a: add-int v3, v3, v37
+  003c: add-int v3, v3, v38
+  003e: add-int v3, v3, v39
+  0040: add-int v3, v3, v40
+  0042: add-int v3, v3, v41
+  0044: add-int v3, v3, v42
+  0046: add-int v3, v3, v43
+  0048: add-int v3, v3, v44
+  004a: add-int v3, v3, v45
+  004c: add-int v3, v3, v46
+  004e: add-int v3, v3, v47
+  0050: add-int v3, v3, v48
+  0052: add-int v3, v3, v49
+  0054: add-int v3, v3, v50
+  0056: add-int v3, v3, v51
+  0058: add-int v3, v3, v52
+  005a: add-int v3, v3, v53
+  005c: add-int v3, v3, v54
+  005e: add-int v3, v3, v55
+  0060: add-int v3, v3, v56
+  0062: add-int v3, v3, v57
+  0064: add-int v3, v3, v58
+  0066: add-int v3, v3, v59
+  0068: add-int v3, v3, v60
+  006a: add-int v3, v3, v61
+  006c: add-int v3, v3, v62
+  006e: add-int v3, v3, v63
+  0070: add-int v3, v3, v64
+  0072: add-int v3, v3, v65
+  0074: add-int v3, v3, v66
+  0076: add-int v3, v3, v67
+  0078: add-int v3, v3, v68
+  007a: add-int v3, v3, v69
+  007c: add-int v3, v3, v70
+  007e: add-int v3, v3, v71
+  0080: add-int v3, v3, v72
+  0082: add-int v3, v3, v73
+  0084: add-int v3, v3, v74
+  0086: add-int v3, v3, v75
+  0088: add-int v3, v3, v76
+  008a: add-int v3, v3, v77
+  008c: add-int v3, v3, v78
+  008e: add-int v3, v3, v79
+  0090: add-int v3, v3, v80
+  0092: add-int v3, v3, v81
+  0094: add-int v3, v3, v82
+  0096: add-int v3, v3, v83
+  0098: add-int v3, v3, v84
+  009a: add-int v3, v3, v85
+  009c: add-int v3, v3, v86
+  009e: add-int v3, v3, v87
+  00a0: add-int v3, v3, v88
+  00a2: add-int v3, v3, v89
+  00a4: add-int v3, v3, v90
+  00a6: add-int v3, v3, v91
+  00a8: add-int v3, v3, v92
+  00aa: add-int v3, v3, v93
+  00ac: add-int v3, v3, v94
+  00ae: add-int v3, v3, v95
+  00b0: add-int v3, v3, v96
+  00b2: add-int v3, v3, v97
+  00b4: add-int v3, v3, v98
+  00b6: add-int v3, v3, v99
+  00b8: add-int v3, v3, v100
+  00ba: add-int v3, v3, v101
+  00bc: add-int v3, v3, v102
+  00be: add-int v3, v3, v103
+  00c0: add-int v3, v3, v104
+  00c2: add-int v3, v3, v105
+  00c4: add-int v3, v3, v106
+  00c6: add-int v3, v3, v107
+  00c8: add-int v3, v3, v108
+  00ca: add-int v3, v3, v109
+  00cc: add-int v3, v3, v110
+  00ce: add-int v3, v3, v111
+  00d0: add-int v3, v3, v112
+  00d2: add-int v3, v3, v113
+  00d4: add-int v3, v3, v114
+  00d6: add-int v3, v3, v115
+  00d8: add-int v3, v3, v116
+  00da: add-int v3, v3, v117
+  00dc: add-int v3, v3, v118
+  00de: add-int v3, v3, v119
+  00e0: add-int v3, v3, v120
+  00e2: add-int v3, v3, v121
+  00e4: add-int v3, v3, v122
+  00e6: add-int v3, v3, v123
+  00e8: add-int v3, v3, v124
+  00ea: add-int v3, v3, v125
+  00ec: add-int v3, v3, v126
+  00ee: add-int v3, v3, v127
+  00f0: add-int v3, v3, v128
+  00f2: add-int v3, v3, v129
+  00f4: add-int v3, v3, v130
+  00f6: add-int v3, v3, v131
+  00f8: add-int v3, v3, v132
+  00fa: add-int v3, v3, v133
+  00fc: add-int v3, v3, v134
+  00fe: add-int v3, v3, v135
+  0100: add-int v3, v3, v136
+  0102: add-int v3, v3, v137
+  0104: add-int v3, v3, v138
+  0106: add-int v3, v3, v139
+  0108: add-int v3, v3, v140
+  010a: add-int v3, v3, v141
+  010c: add-int v3, v3, v142
+  010e: add-int v3, v3, v143
+  0110: add-int v3, v3, v144
+  0112: add-int v3, v3, v145
+  0114: add-int v3, v3, v146
+  0116: add-int v3, v3, v147
+  0118: add-int v3, v3, v148
+  011a: add-int v3, v3, v149
+  011c: add-int v3, v3, v150
+  011e: add-int v3, v3, v151
+  0120: add-int v3, v3, v152
+  0122: add-int v3, v3, v153
+  0124: add-int v3, v3, v154
+  0126: add-int v3, v3, v155
+  0128: add-int v3, v3, v156
+  012a: add-int v3, v3, v157
+  012c: add-int v3, v3, v158
+  012e: add-int v3, v3, v159
+  0130: add-int v3, v3, v160
+  0132: add-int v3, v3, v161
+  0134: add-int v3, v3, v162
+  0136: add-int v3, v3, v163
+  0138: add-int v3, v3, v164
+  013a: add-int v3, v3, v165
+  013c: add-int v3, v3, v166
+  013e: add-int v3, v3, v167
+  0140: add-int v3, v3, v168
+  0142: add-int v3, v3, v169
+  0144: add-int v3, v3, v170
+  0146: add-int v3, v3, v171
+  0148: add-int v3, v3, v172
+  014a: add-int v3, v3, v173
+  014c: add-int v3, v3, v174
+  014e: add-int v3, v3, v175
+  0150: add-int v3, v3, v176
+  0152: add-int v3, v3, v177
+  0154: add-int v3, v3, v178
+  0156: add-int v3, v3, v179
+  0158: add-int v3, v3, v180
+  015a: add-int v3, v3, v181
+  015c: add-int v3, v3, v182
+  015e: add-int v3, v3, v183
+  0160: add-int v3, v3, v184
+  0162: add-int v3, v3, v185
+  0164: add-int v3, v3, v186
+  0166: add-int v3, v3, v187
+  0168: add-int v3, v3, v188
+  016a: add-int v3, v3, v189
+  016c: add-int v3, v3, v190
+  016e: add-int v3, v3, v191
+  0170: add-int v3, v3, v192
+  0172: add-int v3, v3, v193
+  0174: add-int v3, v3, v194
+  0176: add-int v3, v3, v195
+  0178: add-int v3, v3, v196
+  017a: add-int v3, v3, v197
+  017c: add-int v3, v3, v198
+  017e: add-int v3, v3, v199
+  0180: add-int v3, v3, v200
+  0182: add-int v3, v3, v201
+  0184: add-int v3, v3, v202
+  0186: add-int v3, v3, v203
+  0188: add-int v3, v3, v204
+  018a: add-int v3, v3, v205
+  018c: add-int v3, v3, v206
+  018e: add-int v3, v3, v207
+  0190: add-int v3, v3, v208
+  0192: add-int v3, v3, v209
+  0194: add-int v3, v3, v210
+  0196: add-int v3, v3, v211
+  0198: add-int v3, v3, v212
+  019a: add-int v3, v3, v213
+  019c: add-int v3, v3, v214
+  019e: add-int v3, v3, v215
+  01a0: add-int v3, v3, v216
+  01a2: add-int v3, v3, v217
+  01a4: add-int v3, v3, v218
+  01a6: add-int v3, v3, v219
+  01a8: add-int v3, v3, v220
+  01aa: add-int v3, v3, v221
+  01ac: add-int v3, v3, v222
+  01ae: add-int v3, v3, v223
+  01b0: add-int v3, v3, v224
+  01b2: add-int v3, v3, v225
+  01b4: add-int v3, v3, v226
+  01b6: add-int v3, v3, v227
+  01b8: add-int v3, v3, v228
+  01ba: add-int v3, v3, v229
+  01bc: add-int v3, v3, v230
+  01be: add-int v3, v3, v231
+  01c0: add-int v3, v3, v232
+  01c2: add-int v3, v3, v233
+  01c4: add-int v3, v3, v234
+  01c6: add-int v3, v3, v235
+  01c8: add-int v3, v3, v236
+  01ca: add-int v3, v3, v237
+  01cc: add-int v3, v3, v238
+  01ce: add-int v3, v3, v239
+  01d0: add-int v3, v3, v240
+  01d2: add-int v3, v3, v241
+  01d4: add-int v3, v3, v242
+  01d6: add-int v3, v3, v243
+  01d8: add-int v3, v3, v244
+  01da: add-int v3, v3, v245
+  01dc: add-int v3, v3, v246
+  01de: add-int v3, v3, v247
+  01e0: add-int v3, v3, v248
+  01e2: add-int v3, v3, v249
+  01e4: add-int v3, v3, v250
+  01e6: add-int v3, v3, v251
+  01e8: add-int v3, v3, v252
+  01ea: add-int v3, v3, v253
+  01ec: add-int v3, v3, v254
+  01ee: add-int v3, v3, v255
+  01f0: move/from16 v0, v256
+  01f2: add-int/2addr v3, v0
+  01f3: move/from16 v0, v257
+  01f5: add-int/2addr v3, v0
+  01f6: invoke-static {v3}, java.lang.Integer.toString:(I)Ljava/lang/String;
+  01f9: move-result-object v3
+  01fa: invoke-virtual {v2, v3}, java.io.PrintStream.println:(Ljava/lang/String;)V
+  01fd: move/from16 v0, v258
+  01ff: packed-switch v0, 020e // +000f
+  0202: const/4 v2, #int -1 // #f
+  0203: return v2
+  0204: const/4 v2, #int 0 // #0
+  0205: goto 0203 // -0002
+  0206: const/4 v2, #int 1 // #1
+  0207: goto 0203 // -0004
+  0208: const/4 v2, #int 2 // #2
+  0209: goto 0203 // -0006
+  020a: const/4 v2, #int 3 // #3
+  020b: goto 0203 // -0008
+  020c: const/4 v2, #int 4 // #4
+  020d: goto 0203 // -000a
+  020e: packed-switch-payload // for switch @ 01ff
+          0: 00000204 // +00000005
+          1: 00000206 // +00000007
+          2: 00000208 // +00000009
+          3: 0000020a // +0000000b
+          4: 0000020c // +0000000d
+  debug info
+    line_start: 9
+    parameters_size: 00ff
+    parameter param1 v4
+    parameter param2 v5
+    parameter param3 v6
+    parameter param4 v7
+    parameter param5 v8
+    parameter param6 v9
+    parameter param7 v10
+    parameter param8 v11
+    parameter param9 v12
+    parameter param10 v13
+    parameter param11 v14
+    parameter param12 v15
+    parameter param13 v16
+    parameter param14 v17
+    parameter param15 v18
+    parameter param16 v19
+    parameter param17 v20
+    parameter param18 v21
+    parameter param19 v22
+    parameter param20 v23
+    parameter param21 v24
+    parameter param22 v25
+    parameter param23 v26
+    parameter param24 v27
+    parameter param25 v28
+    parameter param26 v29
+    parameter param27 v30
+    parameter param28 v31
+    parameter param29 v32
+    parameter param30 v33
+    parameter param31 v34
+    parameter param32 v35
+    parameter param33 v36
+    parameter param34 v37
+    parameter param35 v38
+    parameter param36 v39
+    parameter param37 v40
+    parameter param38 v41
+    parameter param39 v42
+    parameter param40 v43
+    parameter param41 v44
+    parameter param42 v45
+    parameter param43 v46
+    parameter param44 v47
+    parameter param45 v48
+    parameter param46 v49
+    parameter param47 v50
+    parameter param48 v51
+    parameter param49 v52
+    parameter param50 v53
+    parameter param51 v54
+    parameter param52 v55
+    parameter param53 v56
+    parameter param54 v57
+    parameter param55 v58
+    parameter param56 v59
+    parameter param57 v60
+    parameter param58 v61
+    parameter param59 v62
+    parameter param60 v63
+    parameter param61 v64
+    parameter param62 v65
+    parameter param63 v66
+    parameter param64 v67
+    parameter param65 v68
+    parameter param66 v69
+    parameter param67 v70
+    parameter param68 v71
+    parameter param69 v72
+    parameter param70 v73
+    parameter param71 v74
+    parameter param72 v75
+    parameter param73 v76
+    parameter param74 v77
+    parameter param75 v78
+    parameter param76 v79
+    parameter param77 v80
+    parameter param78 v81
+    parameter param79 v82
+    parameter param80 v83
+    parameter param81 v84
+    parameter param82 v85
+    parameter param83 v86
+    parameter param84 v87
+    parameter param85 v88
+    parameter param86 v89
+    parameter param87 v90
+    parameter param88 v91
+    parameter param89 v92
+    parameter param90 v93
+    parameter param91 v94
+    parameter param92 v95
+    parameter param93 v96
+    parameter param94 v97
+    parameter param95 v98
+    parameter param96 v99
+    parameter param97 v100
+    parameter param98 v101
+    parameter param99 v102
+    parameter param100 v103
+    parameter param101 v104
+    parameter param102 v105
+    parameter param103 v106
+    parameter param104 v107
+    parameter param105 v108
+    parameter param106 v109
+    parameter param107 v110
+    parameter param108 v111
+    parameter param109 v112
+    parameter param110 v113
+    parameter param111 v114
+    parameter param112 v115
+    parameter param113 v116
+    parameter param114 v117
+    parameter param115 v118
+    parameter param116 v119
+    parameter param117 v120
+    parameter param118 v121
+    parameter param119 v122
+    parameter param120 v123
+    parameter param121 v124
+    parameter param122 v125
+    parameter param123 v126
+    parameter param124 v127
+    parameter param125 v128
+    parameter param126 v129
+    parameter param127 v130
+    parameter param128 v131
+    parameter param129 v132
+    parameter param130 v133
+    parameter param131 v134
+    parameter param132 v135
+    parameter param133 v136
+    parameter param134 v137
+    parameter param135 v138
+    parameter param136 v139
+    parameter param137 v140
+    parameter param138 v141
+    parameter param139 v142
+    parameter param140 v143
+    parameter param141 v144
+    parameter param142 v145
+    parameter param143 v146
+    parameter param144 v147
+    parameter param145 v148
+    parameter param146 v149
+    parameter param147 v150
+    parameter param148 v151
+    parameter param149 v152
+    parameter param150 v153
+    parameter param151 v154
+    parameter param152 v155
+    parameter param153 v156
+    parameter param154 v157
+    parameter param155 v158
+    parameter param156 v159
+    parameter param157 v160
+    parameter param158 v161
+    parameter param159 v162
+    parameter param160 v163
+    parameter param161 v164
+    parameter param162 v165
+    parameter param163 v166
+    parameter param164 v167
+    parameter param165 v168
+    parameter param166 v169
+    parameter param167 v170
+    parameter param168 v171
+    parameter param169 v172
+    parameter param170 v173
+    parameter param171 v174
+    parameter param172 v175
+    parameter param173 v176
+    parameter param174 v177
+    parameter param175 v178
+    parameter param176 v179
+    parameter param177 v180
+    parameter param178 v181
+    parameter param179 v182
+    parameter param180 v183
+    parameter param181 v184
+    parameter param182 v185
+    parameter param183 v186
+    parameter param184 v187
+    parameter param185 v188
+    parameter param186 v189
+    parameter param187 v190
+    parameter param188 v191
+    parameter param189 v192
+    parameter param190 v193
+    parameter param191 v194
+    parameter param192 v195
+    parameter param193 v196
+    parameter param194 v197
+    parameter param195 v198
+    parameter param196 v199
+    parameter param197 v200
+    parameter param198 v201
+    parameter param199 v202
+    parameter param200 v203
+    parameter param201 v204
+    parameter param202 v205
+    parameter param203 v206
+    parameter param204 v207
+    parameter param205 v208
+    parameter param206 v209
+    parameter param207 v210
+    parameter param208 v211
+    parameter param209 v212
+    parameter param210 v213
+    parameter param211 v214
+    parameter param212 v215
+    parameter param213 v216
+    parameter param214 v217
+    parameter param215 v218
+    parameter param216 v219
+    parameter param217 v220
+    parameter param218 v221
+    parameter param219 v222
+    parameter param220 v223
+    parameter param221 v224
+    parameter param222 v225
+    parameter param223 v226
+    parameter param224 v227
+    parameter param225 v228
+    parameter param226 v229
+    parameter param227 v230
+    parameter param228 v231
+    parameter param229 v232
+    parameter param230 v233
+    parameter param231 v234
+    parameter param232 v235
+    parameter param233 v236
+    parameter param234 v237
+    parameter param235 v238
+    parameter param236 v239
+    parameter param237 v240
+    parameter param238 v241
+    parameter param239 v242
+    parameter param240 v243
+    parameter param241 v244
+    parameter param242 v245
+    parameter param243 v246
+    parameter param244 v247
+    parameter param245 v248
+    parameter param246 v249
+    parameter param247 v250
+    parameter param248 v251
+    parameter param249 v252
+    parameter param250 v253
+    parameter param251 v254
+    parameter param252 v255
+    parameter param253 v256
+    parameter param254 v257
+    parameter param255 v258
+    0000: prologue end
+    0000: line 9
+    0001: line 12
+    0001: +local v1 i int
+    01fd: advance pc
+    01fd: line 16
+    line = 28
+    0202: line 28
+    line = 18
+    0204: line 18
+    0206: line 20
+    0208: line 22
+    020a: line 24
+    020c: line 26
+    line = 16
+    020e: line 16
+    end sequence
+  source file: "Blort.java"
diff --git a/dx/tests/122-switch-with-high-register/info.txt b/dx/tests/122-switch-with-high-register/info.txt
new file mode 100644
index 0000000..ca2a795
--- /dev/null
+++ b/dx/tests/122-switch-with-high-register/info.txt
@@ -0,0 +1,6 @@
+This test ensures that dx correctly handles switches on data in a high
+register (>v255)
+
+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/122-switch-with-high-register/run b/dx/tests/122-switch-with-high-register/run
new file mode 100644
index 0000000..2b6eb66
--- /dev/null
+++ b/dx/tests/122-switch-with-high-register/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 -d . *.java
+dx --debug --dex --dump-method=Blort.switchWithHighRegister Blort.class
+$JAVAC -g -d . *.java
+dx --debug --dex --dump-method=Blort.switchWithHighRegister Blort.class
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..bb8d03b
--- /dev/null
+++ b/libdex/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+dex_src_files := \
+	CmdUtils.cpp \
+	DexCatch.cpp \
+	DexClass.cpp \
+	DexDataMap.cpp \
+	DexDebugInfo.cpp \
+	DexFile.cpp \
+	DexInlines.cpp \
+	DexOptData.cpp \
+	DexOpcodes.cpp \
+	DexProto.cpp \
+	DexSwapVerify.cpp \
+	DexUtf.cpp \
+	InstrUtils.cpp \
+	Leb128.cpp \
+	OptInvocation.cpp \
+	sha1.cpp \
+	SysUtil.cpp \
+	ZipArchive.cpp
+
+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_CFLAGS += -UNDEBUG -DDEBUG=1
+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.cpp b/libdex/CmdUtils.cpp
new file mode 100644
index 0000000..ff737a3
--- /dev/null
+++ b/libdex/CmdUtils.cpp
@@ -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.
+ */
+/*
+ * 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 <strings.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/*
+ * 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.
+             *
+             * On the device we must use /sdcard because most other
+             * directories aren't writable (either because of permissions
+             * or because the volume is mounted read-only).  On desktop
+             * it's nice to use the designated temp directory.
+             */
+            if (access("/tmp", W_OK) == 0) {
+                sprintf(tempNameBuf, "/tmp/dex-temp-%d", getpid());
+            } else if (access("/sdcard", W_OK) == 0) {
+                sprintf(tempNameBuf, "/sdcard/dex-temp-%d", getpid());
+            } else {
+                fprintf(stderr,
+                    "NOTE: /tmp and /sdcard unavailable for temp files\n");
+                sprintf(tempNameBuf, "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.
+     */
+    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((u1*) 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 = kUTFRSuccess;
+
+bail:
+    if (fd >= 0)
+        close(fd);
+    if (removeTemp) {
+        /* this will fail if the OS doesn't allow removal of a mapped file */
+        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..887eed9
--- /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_H_
+#define LIBDEX_CMDUTILS_H_
+
+/* encode the result of unzipping to a file */
+enum UnzipToFileResult {
+    kUTFRSuccess = 0,
+    kUTFRGenericFailure,
+    kUTFRBadArgs,
+    kUTFRNotZip,
+    kUTFRNoClassesDex,
+    kUTFROutputFileProblem,
+    kUTFRBadZip,
+};
+
+/*
+ * 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_H_
diff --git a/libdex/DexCatch.cpp b/libdex/DexCatch.cpp
new file mode 100644
index 0000000..ed97e87
--- /dev/null
+++ b/libdex/DexCatch.cpp
@@ -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..cfea2d9
--- /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_H_
+#define LIBDEX_DEXCATCH_H_
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/*
+ * Catch handler entry, used while iterating over catch_handler_items.
+ */
+struct DexCatchHandler {
+    u4          typeIdx;    /* type index of the caught exception type */
+    u4          address;    /* handler address */
+};
+
+/* 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.
+ */
+struct DexCatchIterator {
+    const u1* pEncodedData;
+    bool catchesAll;
+    u4 countRemaining;
+    DexCatchHandler handler;
+};
+
+/* 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  // LIBDEX_DEXCATCH_H_
diff --git a/libdex/DexClass.cpp b/libdex/DexClass.cpp
new file mode 100644
index 0000000..552229c
--- /dev/null
+++ b/libdex/DexClass.cpp
@@ -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 = (DexClassData*) 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 = (DexClassData*) 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..11b3b0e
--- /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_H_
+#define LIBDEX_DEXCLASS_H_
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/* expanded form of a class_data_item header */
+struct DexClassDataHeader {
+    u4 staticFieldsSize;
+    u4 instanceFieldsSize;
+    u4 directMethodsSize;
+    u4 virtualMethodsSize;
+};
+
+/* expanded form of encoded_field */
+struct DexField {
+    u4 fieldIdx;    /* index to a field_id_item */
+    u4 accessFlags;
+};
+
+/* expanded form of encoded_method */
+struct DexMethod {
+    u4 methodIdx;    /* index to a method_id_item */
+    u4 accessFlags;
+    u4 codeOff;      /* file offset to a code_item */
+};
+
+/* 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. */
+struct DexClassData {
+    DexClassDataHeader header;
+    DexField*          staticFields;
+    DexField*          instanceFields;
+    DexMethod*         directMethods;
+    DexMethod*         virtualMethods;
+};
+
+/* 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  // LIBDEX_DEXCLASS_H_
diff --git a/libdex/DexDataMap.cpp b/libdex/DexDataMap.cpp
new file mode 100644
index 0000000..8405e8c
--- /dev/null
+++ b/libdex/DexDataMap.cpp
@@ -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 = (DexDataMap*) 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)) {
+        ALOGE("Out-of-order data map offset: %#x then %#x",
+                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) {
+        ALOGE("No data map entry found @ %#x; expected %x",
+                offset, type);
+    } else {
+        ALOGE("Unexpected data map entry @ %#x: expected %x, found %x",
+                offset, type, found);
+    }
+
+    return false;
+}
diff --git a/libdex/DexDataMap.h b/libdex/DexDataMap.h
new file mode 100644
index 0000000..7e43dc9
--- /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_H_
+#define LIBDEX_DEXDATAMAP_H_
+
+#include "DexFile.h"
+
+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 */
+};
+
+/*
+ * 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_H_
diff --git a/libdex/DexDebugInfo.cpp b/libdex/DexDebugInfo.cpp
new file mode 100644
index 0000000..6a7b6bb
--- /dev/null
+++ b/libdex/DexDebugInfo.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handling of method debug info in a .dex file.
+ */
+
+#include "DexDebugInfo.h"
+#include "DexProto.h"
+#include "Leb128.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * 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);
+    }
+}
+
+struct LocalInfo {
+    const char *name;
+    const char *descriptor;
+    const char *signature;
+    u2 startAddress;
+    bool live;
+};
+
+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 );
+    }
+}
+
+static void invalidStream(const char* classDescriptor, const DexProto* proto) {
+    IF_ALOGE() {
+        char* methodDescriptor = dexProtoCopyMethodDescriptor(proto);
+        ALOGE("Invalid debug info stream. class %s; proto %s",
+                classDescriptor, methodDescriptor);
+        free(methodDescriptor);
+    }
+}
+
+static void dexDecodeDebugInfo0(
+            const DexFile* pDexFile,
+            const DexCode* pCode,
+            const char* classDescriptor,
+            u4 protoIdx,
+            u4 accessFlags,
+            DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+            void* cnxt,
+            const u1* stream,
+            LocalInfo* localInReg)
+{
+    DexProto proto = { pDexFile, protoIdx };
+    u4 insnsSize = pCode->insnsSize;
+    u4 line = readUnsignedLeb128(&stream);
+    u4 parametersSize = readUnsignedLeb128(&stream);
+    u2 argReg = pCode->registersSize - pCode->insSize;
+    u4 address = 0;
+
+    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)) {
+            invalidStream(classDescriptor, &proto);
+            return;
+        }
+
+        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:
+                return;
+
+            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) {
+                    invalidStream(classDescriptor, &proto);
+                    return;
+                }
+
+                // 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) {
+                    invalidStream(classDescriptor, &proto);
+                    return;
+                }
+
+                emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
+                localInReg[reg].live = false;
+                break;
+
+            case DBG_RESTART_LOCAL:
+                reg = readUnsignedLeb128(&stream);
+                if (reg > pCode->registersSize) {
+                    invalidStream(classDescriptor, &proto);
+                    return;
+                }
+
+                if (localInReg[reg].name == NULL
+                        || localInReg[reg].descriptor == NULL) {
+                    invalidStream(classDescriptor, &proto);
+                    return;
+                }
+
+                /*
+                 * 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
+                        return;
+                    }
+                }
+                break;
+            }
+        }
+    }
+}
+
+// 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);
+    LocalInfo localInReg[pCode->registersSize];
+
+    memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
+
+    if (stream != NULL) {
+        dexDecodeDebugInfo0(pDexFile, pCode, classDescriptor, protoIdx, accessFlags,
+            posCb, localCb, cnxt, stream, localInReg);
+    }
+
+    for (int reg = 0; reg < pCode->registersSize; reg++) {
+        emitLocalCbIfLive(cnxt, reg, pCode->insnsSize, localInReg, localCb);
+    }
+}
diff --git a/libdex/DexDebugInfo.h b/libdex/DexDebugInfo.h
new file mode 100644
index 0000000..bd0954c
--- /dev/null
+++ b/libdex/DexDebugInfo.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handling of method debug info in a .dex file.
+ */
+
+#ifndef LIBDEX_DEXDEBUGINFO_H_
+#define LIBDEX_DEXDEBUGINFO_H_
+
+#include "DexFile.h"
+
+/*
+ * 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);
+
+#endif  // LIBDEX_DEXDEBUGINFO_H_
diff --git a/libdex/DexFile.cpp b/libdex/DexFile.cpp
new file mode 100644
index 0000000..54070a2
--- /dev/null
+++ b/libdex/DexFile.cpp
@@ -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.
+ */
+
+/*
+ * 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;
+
+/* (documented in header) */
+char dexGetPrimitiveTypeDescriptorChar(PrimitiveType type) {
+    const char* string = dexGetPrimitiveTypeDescriptor(type);
+
+    return (string == NULL) ? '\0' : string[0];
+}
+
+/* (documented in header) */
+const char* dexGetPrimitiveTypeDescriptor(PrimitiveType type) {
+    switch (type) {
+        case PRIM_VOID:    return "V";
+        case PRIM_BOOLEAN: return "Z";
+        case PRIM_BYTE:    return "B";
+        case PRIM_SHORT:   return "S";
+        case PRIM_CHAR:    return "C";
+        case PRIM_INT:     return "I";
+        case PRIM_LONG:    return "J";
+        case PRIM_FLOAT:   return "F";
+        case PRIM_DOUBLE:  return "D";
+        default:           return NULL;
+    }
+
+    return NULL;
+}
+
+/* (documented in header) */
+const char* dexGetBoxedTypeDescriptor(PrimitiveType type) {
+    switch (type) {
+        case PRIM_VOID:    return NULL;
+        case PRIM_BOOLEAN: return "Ljava/lang/Boolean;";
+        case PRIM_BYTE:    return "Ljava/lang/Byte;";
+        case PRIM_SHORT:   return "Ljava/lang/Short;";
+        case PRIM_CHAR:    return "Ljava/lang/Character;";
+        case PRIM_INT:     return "Ljava/lang/Integer;";
+        case PRIM_LONG:    return "Ljava/lang/Long;";
+        case PRIM_FLOAT:   return "Ljava/lang/Float;";
+        case PRIM_DOUBLE:  return "Ljava/lang/Double;";
+        default:           return NULL;
+    }
+}
+
+/* (documented in header) */
+PrimitiveType dexGetPrimitiveTypeFromDescriptorChar(char descriptorChar) {
+    switch (descriptorChar) {
+        case 'V': return PRIM_VOID;
+        case 'Z': return PRIM_BOOLEAN;
+        case 'B': return PRIM_BYTE;
+        case 'S': return PRIM_SHORT;
+        case 'C': return PRIM_CHAR;
+        case 'I': return PRIM_INT;
+        case 'J': return PRIM_LONG;
+        case 'F': return PRIM_FLOAT;
+        case 'D': return PRIM_DOUBLE;
+        default:  return PRIM_NOT;
+    }
+}
+
+/* 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)
+    //    ALOGW("classLookupAdd: probes=%d", probes);
+
+    pLookup->table[idx].classDescriptorHash = hash;
+    pLookup->table[idx].classDescriptorOffset = stringOff;
+    pLookup->table[idx].classDefOffset = classDefOff;
+    *pNumProbes = probes;
+}
+
+/*
+ * 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;
+    }
+
+    ALOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
+         " total=%d max=%d",
+        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)) {
+        ALOGE("too short to be a valid .dex");
+        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) {
+            ALOGE("bad opt version (0x%02x %02x %02x %02x)",
+                 magic[4], magic[5], magic[6], magic[7]);
+            goto bail;
+        }
+
+        pDexFile->pOptHeader = (const DexOptHeader*) data;
+        ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",
+            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) {
+            ALOGE("File truncated? stored len=%d, rem len=%d",
+                pDexFile->pOptHeader->dexLength, (int) length);
+            goto bail;
+        }
+        length = pDexFile->pOptHeader->dexLength;
+    }
+
+    dexFileSetupBasicPointers(pDexFile, data);
+    pHeader = pDexFile->pHeader;
+
+    if (!dexHasValidMagic(pHeader)) {
+        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) {
+            ALOGE("ERROR: bad checksum (%08x vs %08x)",
+                adler, pHeader->checksum);
+            if (!(flags & kDexParseContinueOnError))
+                goto bail;
+        } else {
+            ALOGV("+++ adler32 checksum (%08x) verified", adler);
+        }
+
+        const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+        if (pOptHeader != NULL) {
+            adler = dexComputeOptChecksum(pOptHeader);
+            if (adler != pOptHeader->checksum) {
+                ALOGE("ERROR: bad opt checksum (%08x vs %08x)",
+                    adler, pOptHeader->checksum);
+                if (!(flags & kDexParseContinueOnError))
+                    goto bail;
+            } else {
+                ALOGV("+++ adler32 opt checksum (%08x) verified", 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];
+            ALOGE("ERROR: bad SHA1 digest (%s vs %s)",
+                dexSHA1DigestToStr(sha1Digest, tmpBuf1),
+                dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
+            if (!(flags & kDexParseContinueOnError))
+                goto bail;
+        } else {
+            ALOGV("+++ sha1 digest verified");
+        }
+    }
+
+    if (pHeader->fileSize != length) {
+        ALOGE("ERROR: stored file size (%d) != expected (%d)",
+            (int) pHeader->fileSize, (int) length);
+        if (!(flags & kDexParseContinueOnError))
+            goto bail;
+    }
+
+    if (pHeader->classDefsSize == 0) {
+        ALOGE("ERROR: DEX file has no classes in it, failing");
+        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);
+
+    //ALOGD("+++ pCode=%p handlerData=%p last offset=%d",
+    //    pCode, handlerData, offset);
+
+    /* return the size of the catch handler + everything before it */
+    return (handlerData - (u1*) pCode) + offset;
+}
+
+/*
+ * 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;
+}
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
new file mode 100644
index 0000000..809d450
--- /dev/null
+++ b/libdex/DexFile.h
@@ -0,0 +1,984 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_H_
+#define LIBDEX_DEXFILE_H_
+
+#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"
+
+/* current version, encoded in 4 bytes of ASCII */
+#define DEX_MAGIC_VERS  "036\0"
+
+/*
+ * older but still-recognized version (corresponding to Android API
+ * levels 13 and earlier
+ */
+#define DEX_MAGIC_VERS_API_13  "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 */
+};
+
+/*
+ * Enumeration of all the primitive types.
+ */
+enum PrimitiveType {
+    PRIM_NOT        = 0,       /* value is a reference type, not a primitive type */
+    PRIM_VOID       = 1,
+    PRIM_BOOLEAN    = 2,
+    PRIM_BYTE       = 3,
+    PRIM_SHORT      = 4,
+    PRIM_CHAR       = 5,
+    PRIM_INT        = 6,
+    PRIM_LONG       = 7,
+    PRIM_FLOAT      = 8,
+    PRIM_DOUBLE     = 9,
+};
+
+/*
+ * 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.
+ */
+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;
+};
+
+/*
+ * Direct-mapped "map_item".
+ */
+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 */
+};
+
+/*
+ * Direct-mapped "map_list".
+ */
+struct DexMapList {
+    u4  size;               /* #of entries in list */
+    DexMapItem list[1];     /* entries */
+};
+
+/*
+ * Direct-mapped "string_id_item".
+ */
+struct DexStringId {
+    u4 stringDataOff;      /* file offset to string_data_item */
+};
+
+/*
+ * Direct-mapped "type_id_item".
+ */
+struct DexTypeId {
+    u4  descriptorIdx;      /* index into stringIds list for type descriptor */
+};
+
+/*
+ * Direct-mapped "field_id_item".
+ */
+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 */
+};
+
+/*
+ * Direct-mapped "method_id_item".
+ */
+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 */
+};
+
+/*
+ * Direct-mapped "proto_id_item".
+ */
+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 */
+};
+
+/*
+ * Direct-mapped "class_def_item".
+ */
+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 */
+};
+
+/*
+ * Direct-mapped "type_item".
+ */
+struct DexTypeItem {
+    u2  typeIdx;            /* index into typeIds */
+};
+
+/*
+ * Direct-mapped "type_list".
+ */
+struct DexTypeList {
+    u4  size;               /* #of entries in list */
+    DexTypeItem list[1];    /* entries */
+};
+
+/*
+ * 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.
+ */
+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] */
+};
+
+/*
+ * Direct-mapped "try_item".
+ */
+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 */
+};
+
+/*
+ * Link table.  Currently undefined.
+ */
+struct DexLink {
+    u1  bleargh;
+};
+
+
+/*
+ * Direct-mapped "annotations_directory_item".
+ */
+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] */
+};
+
+/*
+ * Direct-mapped "field_annotations_item".
+ */
+struct DexFieldAnnotationsItem {
+    u4  fieldIdx;
+    u4  annotationsOff;             /* offset to DexAnnotationSetItem */
+};
+
+/*
+ * Direct-mapped "method_annotations_item".
+ */
+struct DexMethodAnnotationsItem {
+    u4  methodIdx;
+    u4  annotationsOff;             /* offset to DexAnnotationSetItem */
+};
+
+/*
+ * Direct-mapped "parameter_annotations_item".
+ */
+struct DexParameterAnnotationsItem {
+    u4  methodIdx;
+    u4  annotationsOff;             /* offset to DexAnotationSetRefList */
+};
+
+/*
+ * Direct-mapped "annotation_set_ref_item".
+ */
+struct DexAnnotationSetRefItem {
+    u4  annotationsOff;             /* offset to DexAnnotationSetItem */
+};
+
+/*
+ * Direct-mapped "annotation_set_ref_list".
+ */
+struct DexAnnotationSetRefList {
+    u4  size;
+    DexAnnotationSetRefItem list[1];
+};
+
+/*
+ * Direct-mapped "annotation_set_item".
+ */
+struct DexAnnotationSetItem {
+    u4  size;
+    u4  entries[1];                 /* offset to DexAnnotationItem */
+};
+
+/*
+ * Direct-mapped "annotation_item".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+struct DexAnnotationItem {
+    u1  visibility;
+    u1  annotation[1];              /* data in encoded_annotation format */
+};
+
+/*
+ * Direct-mapped "encoded_array".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+struct DexEncodedArray {
+    u1  array[1];                   /* data in encoded_array format */
+};
+
+/*
+ * 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.
+ */
+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];
+};
+
+/*
+ * 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.
+ */
+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 */
+};
+
+#define DEX_OPT_FLAG_BIG            (1<<1)  /* swapped to big-endian */
+
+#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.
+ */
+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;
+};
+
+/*
+ * 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);
+
+/*
+ * Check to see if the file magic and format version in the given
+ * header are recognized as valid. Returns true if they are
+ * acceptable.
+ */
+bool dexHasValidMagic(const DexHeader* pHeader);
+
+/*
+ * 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 ((((uintptr_t) 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;
+    }
+}
+
+/* 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)
+{
+    if (offset == 0) {
+        return NULL;
+    }
+    return (const DexAnnotationSetItem*) (pDexFile->baseAddr + offset);
+}
+/* get the class' annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetClassAnnotationSet(
+    const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    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)
+{
+    if (pItem->annotationsOff == 0) {
+        return NULL;
+    }
+    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)
+{
+    u4 offset = dexGetAnnotationOff(pAnnoSet, idx);
+    if (offset == 0) {
+        return NULL;
+    }
+    return (const DexAnnotationItem*) (pDexFile->baseAddr + offset);
+}
+
+/*
+ * Get the type descriptor character associated with a given primitive
+ * type. This returns '\0' if the type is invalid.
+ */
+char dexGetPrimitiveTypeDescriptorChar(PrimitiveType type);
+
+/*
+ * Get the type descriptor string associated with a given primitive
+ * type.
+ */
+const char* dexGetPrimitiveTypeDescriptor(PrimitiveType type);
+
+/*
+ * Get the boxed type descriptor string associated with a given
+ * primitive type. This returns NULL for an invalid type, including
+ * particularly for type "void". In the latter case, even though there
+ * is a class Void, there's no such thing as a boxed instance of it.
+ */
+const char* dexGetBoxedTypeDescriptor(PrimitiveType type);
+
+/*
+ * Get the primitive type constant from the given descriptor character.
+ * This returns PRIM_NOT (note: this is a 0) if the character is invalid
+ * as a primitive type descriptor.
+ */
+PrimitiveType dexGetPrimitiveTypeFromDescriptorChar(char descriptorChar);
+
+#endif  // LIBDEX_DEXFILE_H_
diff --git a/libdex/DexInlines.cpp b/libdex/DexInlines.cpp
new file mode 100644
index 0000000..cbedb62
--- /dev/null
+++ b/libdex/DexInlines.cpp
@@ -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.
+ */
+
+/*
+ * 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 "DexUtf.h"
+#include "DexOpcodes.h"
+#include "DexProto.h"
+#include "InstrUtils.h"
+#include "Leb128.h"
+#include "ZipArchive.h"
diff --git a/libdex/DexOpcodes.cpp b/libdex/DexOpcodes.cpp
new file mode 100644
index 0000000..bdcc558
--- /dev/null
+++ b/libdex/DexOpcodes.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.
+ *
+ * IMPORTANT NOTE: The contents of this file are mostly generated
+ * automatically by the opcode-gen tool. Any edits to the generated
+ * sections will get wiped out the next time the tool is run.
+ */
+
+#include "DexOpcodes.h"
+#include <assert.h>
+
+/*
+ * Dalvik opcode names.
+ */
+static const char* gOpNames[kNumPackedOpcodes] = {
+    // BEGIN(libdex-opcode-names); GENERATED AUTOMATICALLY BY opcode-gen
+    "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",
+    "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",
+    "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",
+    "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-3e",
+    "unused-3f",
+    "unused-40",
+    "unused-41",
+    "unused-42",
+    "unused-43",
+    "aget",
+    "aget-wide",
+    "aget-object",
+    "aget-boolean",
+    "aget-byte",
+    "aget-char",
+    "aget-short",
+    "aput",
+    "aput-wide",
+    "aput-object",
+    "aput-boolean",
+    "aput-byte",
+    "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",
+    "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",
+    "invoke-direct",
+    "invoke-static",
+    "invoke-interface",
+    "unused-73",
+    "invoke-virtual/range",
+    "invoke-super/range",
+    "invoke-direct/range",
+    "invoke-static/range",
+    "invoke-interface/range",
+    "unused-79",
+    "unused-7a",
+    "neg-int",
+    "not-int",
+    "neg-long",
+    "not-long",
+    "neg-float",
+    "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",
+    "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",
+    "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",
+    "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",
+    "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",
+    "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",
+    "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",
+    "^throw-verification-error",
+    "+execute-inline",
+    "+execute-inline/range",
+    "+invoke-object-init/range",
+    "+return-void-barrier",
+    "+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-ff",
+    // END(libdex-opcode-names)
+};
+
+/*
+ * Return the name of an opcode.
+ */
+const char* dexGetOpcodeName(Opcode op)
+{
+    assert(op >= 0 && op < kNumPackedOpcodes);
+    return gOpNames[op];
+}
diff --git a/libdex/DexOpcodes.h b/libdex/DexOpcodes.h
new file mode 100644
index 0000000..8a17841
--- /dev/null
+++ b/libdex/DexOpcodes.h
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 information.
+ *
+ * IMPORTANT NOTE: The contents of this file are mostly generated
+ * automatically by the opcode-gen tool. Any edits to the generated
+ * sections will get wiped out the next time the tool is run.
+ *
+ * See the file opcode-gen/README.txt for information about updating
+ * opcodes and instruction formats.
+ */
+
+#ifndef LIBDEX_DEXOPCODES_H_
+#define LIBDEX_DEXOPCODES_H_
+
+#include "DexFile.h"
+
+/*
+ * kMaxOpcodeValue: the highest possible raw (unpacked) opcode value
+ *
+ * kNumPackedOpcodes: the highest possible packed opcode value of a
+ * valid Dalvik opcode, plus one
+ *
+ * TODO: Change this once the rest of the code is prepared to deal with
+ * extended opcodes.
+ */
+// BEGIN(libdex-maximum-values); GENERATED AUTOMATICALLY BY opcode-gen
+#define kMaxOpcodeValue 0xffff
+#define kNumPackedOpcodes 0x100
+// END(libdex-maximum-values); GENERATED AUTOMATICALLY BY opcode-gen
+
+/*
+ * Switch table and array data signatures are a code unit consisting
+ * of "NOP" (0x00) in the low-order byte and a non-zero identifying
+ * code in the high-order byte. (A true NOP is 0x0000.)
+ */
+#define kPackedSwitchSignature  0x0100
+#define kSparseSwitchSignature  0x0200
+#define kArrayDataSignature     0x0300
+
+/*
+ * Enumeration of all Dalvik opcodes, where the enumeration value
+ * associated with each is the corresponding packed opcode number.
+ * This is different than the opcode value from the Dalvik bytecode
+ * spec for opcode values >= 0xff; see dexOpcodeFromCodeUnit() below.
+ *
+ * A note about the "breakpoint" opcode. This 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.)
+ */
+enum Opcode {
+    // BEGIN(libdex-opcode-enum); GENERATED AUTOMATICALLY BY opcode-gen
+    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,
+    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,
+    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,
+    OP_BREAKPOINT                   = 0xec,
+    OP_THROW_VERIFICATION_ERROR     = 0xed,
+    OP_EXECUTE_INLINE               = 0xee,
+    OP_EXECUTE_INLINE_RANGE         = 0xef,
+    OP_INVOKE_OBJECT_INIT_RANGE     = 0xf0,
+    OP_RETURN_VOID_BARRIER          = 0xf1,
+    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,
+    // END(libdex-opcode-enum)
+};
+
+/*
+ * Macro used to generate a computed goto table for use in implementing
+ * an interpreter in C.
+ */
+#define DEFINE_GOTO_TABLE(_name) \
+    static const void* _name[kNumPackedOpcodes] = {                      \
+        /* BEGIN(libdex-goto-table); GENERATED AUTOMATICALLY BY opcode-gen */ \
+        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),                                                         \
+        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),                                                     \
+        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),                                                    \
+        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),                                                      \
+        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),                                                      \
+        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),                                                     \
+        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),                                                   \
+        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),                                                      \
+        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),                                                   \
+        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),                                                       \
+        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),                                                     \
+        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),                                                 \
+        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),                                               \
+        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),                                                   \
+        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),                                           \
+        H(OP_INVOKE_OBJECT_INIT_RANGE),                                       \
+        H(OP_RETURN_VOID_BARRIER),                                            \
+        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),                                                      \
+        /* END(libdex-goto-table) */                                          \
+    };
+
+/*
+ * Return the Opcode for a given raw opcode code unit (which may
+ * include data payload). The packed index is a zero-based index which
+ * can be used to point into various opcode-related tables. The Dalvik
+ * opcode space is inherently sparse, in that the opcode unit is 16
+ * bits wide, but for most opcodes, eight of those bits are for data.
+ */
+DEX_INLINE Opcode dexOpcodeFromCodeUnit(u2 codeUnit) {
+    /*
+     * This will want to become table-driven should the opcode layout
+     * get more complicated.
+     *
+     * Note: This has to match the corresponding code in opcode-gen, so
+     * that data tables get generated in a consistent way.
+     */
+    int lowByte = codeUnit & 0xff;
+    if (lowByte != 0xff) {
+        return (Opcode) lowByte;
+    } else {
+        return (Opcode) ((codeUnit >> 8) | 0x100);
+    }
+}
+
+/*
+ * Return the name of an opcode.
+ */
+const char* dexGetOpcodeName(Opcode op);
+
+#endif  // LIBDEX_DEXOPCODES_H_
diff --git a/libdex/DexOptData.cpp b/libdex/DexOptData.cpp
new file mode 100644
index 0000000..dd41751
--- /dev/null
+++ b/libdex/DexOptData.cpp
@@ -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) && (((uintptr_t) 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 = (const u4*) 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)) {
+        ALOGE("Bogus opt data start pointer");
+        return false;
+    }
+
+    /* Make sure that the opt data length is a whole number of words. */
+    if ((optLength & 3) != 0) {
+        ALOGE("Unaligned opt data area end");
+        return false;
+    }
+
+    /*
+     * Make sure that the opt data area is large enough to have at least
+     * one chunk header.
+     */
+    if (optLength < 8) {
+        ALOGE("Undersized opt data area (%u)", optLength);
+        return false;
+    }
+
+    /* Process chunks until we see the end marker. */
+    while (*pOpt != kDexChunkEnd) {
+        if (!isValidPointer(pOpt + 2, pOptStart, pOptEnd)) {
+            ALOGE("Bogus opt data content pointer at offset %u",
+                    ((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)) {
+            ALOGE("Opt data area problem for chunk of size %u at offset %u",
+                    size, ((const u1*) pOpt) - data);
+            return false;
+        }
+
+        switch (*pOpt) {
+        case kDexChunkClassLookup:
+            pDexFile->pClassLookup = (const DexClassLookup*) pOptData;
+            break;
+        case kDexChunkRegisterMaps:
+            ALOGV("+++ found register maps, size=%u", size);
+            pDexFile->pRegisterMapPool = pOptData;
+            break;
+        default:
+            ALOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in opt data area",
+                *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.cpp b/libdex/DexProto.cpp
new file mode 100644
index 0000000..06c59b3
--- /dev/null
+++ b/libdex/DexProto.cpp
@@ -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.
+ */
+
+/*
+ * 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.
+ */
+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 = (char*) 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);
+}
+
+/* (documented in header file) */
+const char* dexProtoGetShorty(const DexProto* pProto) {
+    const DexProtoId* protoId = getProtoId(pProto);
+
+    return dexStringById(pProto->dexFile, protoId->shortyIdx);
+}
+
+/* (documented in header file) */
+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;
+}
+
+/* (documented in header file) */
+char* dexProtoCopyMethodDescriptor(const DexProto* pProto) {
+    DexStringCache cache;
+
+    dexStringCacheInit(&cache);
+    return dexStringCacheAbandon(&cache,
+            dexProtoGetMethodDescriptor(pProto, &cache));
+}
+
+/* (documented in header file) */
+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;
+}
+
+/* (documented in header file) */
+const char* dexProtoGetReturnType(const DexProto* pProto) {
+    const DexProtoId* protoId = getProtoId(pProto);
+    return dexStringByTypeIdx(pProto->dexFile, protoId->returnTypeIdx);
+}
+
+/* (documented in header file) */
+size_t dexProtoGetParameterCount(const DexProto* pProto) {
+    const DexProtoId* protoId = getProtoId(pProto);
+    const DexTypeList* typeList =
+        dexGetProtoParameters(pProto->dexFile, protoId);
+    return (typeList == NULL) ? 0 : typeList->size;
+}
+
+/* (documented in header file) */
+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;
+        }
+    }
+}
+
+/* (documented in header file) */
+int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
+    return protoCompare(pProto1, pProto2, true);
+}
+
+/* (documented in header file) */
+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;
+}
+
+/*
+ * Common implementation for dexProtoCompareToDescriptor() and
+ * dexProtoCompareToParameterDescriptors(). The descriptor argument
+ * can be either a full method descriptor (with parens and a return
+ * type) or an unadorned concatenation of types (e.g. a list of
+ * argument types).
+ */
+static int protoCompareToParameterDescriptors(const DexProto* proto,
+        const char* descriptor, bool expectParens) {
+    char expectedEndChar = expectParens ? ')' : '\0';
+    DexParameterIterator iterator;
+    dexParameterIteratorInit(&iterator, proto);
+
+    if (expectParens) {
+        // Skip the '('.
+        assert (*descriptor == '(');
+        descriptor++;
+    }
+
+    for (;;) {
+        const char* protoDesc = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (*descriptor == expectedEndChar) {
+            // 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);
+        assert(nextDesc != NULL);
+
+        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.
+         */
+    }
+}
+
+/* (documented in header file) */
+int dexProtoCompareToDescriptor(const DexProto* proto,
+        const char* descriptor) {
+    // First compare the return types.
+
+    const char *returnType = methodDescriptorReturnType(descriptor);
+    assert(returnType != NULL);
+
+    int result = strcmp(dexProtoGetReturnType(proto), returnType);
+
+    if (result != 0) {
+        return result;
+    }
+
+    // The return types match, so we have to check arguments.
+    return protoCompareToParameterDescriptors(proto, descriptor, true);
+}
+
+/* (documented in header file) */
+int dexProtoCompareToParameterDescriptors(const DexProto* proto,
+        const char* descriptors) {
+    return protoCompareToParameterDescriptors(proto, descriptors, false);
+}
+
+
+
+
+
+
+/*
+ * ===========================================================================
+ *      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..dccae6c
--- /dev/null
+++ b/libdex/DexProto.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.
+ */
+
+/*
+ * Functions for dealing with method prototypes
+ */
+
+#ifndef LIBDEX_DEXPROTO_H_
+#define LIBDEX_DEXPROTO_H_
+
+#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.
+ */
+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 */
+};
+
+/*
+ * Make sure that the given cache can hold a string of the given length,
+ * including the final '\0' byte.
+ */
+void dexStringCacheAlloc(DexStringCache* pCache, size_t length);
+
+/*
+ * 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.
+ */
+struct DexProto {
+    const DexFile* dexFile;     /* file the idx refers to */
+    u4 protoIdx;                /* index into proto_ids table of dexFile */
+};
+
+/*
+ * 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, ignoring return type. 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);
+
+/*
+ * Compare a prototype and a concatenation of type descriptors. The
+ * comparison is done as if the descriptors were converted to a
+ * prototype and compared with dexProtoCompareParameters().
+ */
+int dexProtoCompareToParameterDescriptors(const DexProto* proto,
+        const char* descriptors);
+
+/*
+ * Single-thread prototype parameter iterator. This structure holds a
+ * pointer to a prototype and its parts, along with a cursor.
+ */
+struct DexParameterIterator {
+    const DexProto* proto;
+    const DexTypeList* parameters;
+    int parameterCount;
+    int cursor;
+};
+
+/*
+ * 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_H_
diff --git a/libdex/DexSwapVerify.cpp b/libdex/DexSwapVerify.cpp
new file mode 100644
index 0000000..24a86f9
--- /dev/null
+++ b/libdex/DexSwapVerify.cpp
@@ -0,0 +1,2959 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "DexUtf.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.
+ */
+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
+};
+
+/*
+ * 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)) {
+        ALOGW("Bad offset range for %s: %#x..%#x", 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 = (const u1*) filePointer(state, (_start));     \
+        const u1* _endPtr = (const u1*) 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)) {                                         \
+            ALOGW("Bad index: %s(%u) > %s(%u)",                              \
+                #_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)) {              \
+            ALOGW("Bad index: %s(%u) > %s(%u)",                              \
+                #_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) {
+        ALOGE("Unexpected endian_tag: %#x", 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) {
+        ALOGE("Multiple header items");
+        return false;
+    }
+
+    if (sectionOffset != 0) {
+        ALOGE("Header at %#x; not at start of file", sectionOffset);
+        return false;
+    }
+
+    const DexHeader* pHeader = (const DexHeader*) 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: {
+            ALOGE("Unknown map item type %04x", 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) {
+            ALOGE("Out-of-order map item: %#x then %#x",
+                    lastOffset, item->offset);
+            return false;
+        }
+
+        if (item->offset >= state->pHeader->fileSize) {
+            ALOGE("Map item after end of file: %x, size %#x",
+                    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) {
+                ALOGE("Unrealistically many items in the data section: "
+                        "at least %d", dataItemCount + icount);
+                return false;
+            }
+
+            dataItemsLeft -= icount;
+            dataItemCount += icount;
+        }
+
+        u4 bit = mapTypeToBitMask(item->type);
+
+        if (bit == 0) {
+            return false;
+        }
+
+        if ((usedBits & bit) != 0) {
+            ALOGE("Duplicate map section of type %#x", item->type);
+            return false;
+        }
+
+        usedBits |= bit;
+        lastOffset = item->offset;
+        item++;
+    }
+
+    if ((usedBits & mapTypeToBitMask(kDexTypeHeaderItem)) == 0) {
+        ALOGE("Map is missing header entry");
+        return false;
+    }
+
+    if ((usedBits & mapTypeToBitMask(kDexTypeMapList)) == 0) {
+        ALOGE("Map is missing map_list entry");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeStringIdItem)) == 0)
+            && ((state->pHeader->stringIdsOff != 0)
+                    || (state->pHeader->stringIdsSize != 0))) {
+        ALOGE("Map is missing string_ids entry");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem)) == 0)
+            && ((state->pHeader->typeIdsOff != 0)
+                    || (state->pHeader->typeIdsSize != 0))) {
+        ALOGE("Map is missing type_ids entry");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem)) == 0)
+            && ((state->pHeader->protoIdsOff != 0)
+                    || (state->pHeader->protoIdsSize != 0))) {
+        ALOGE("Map is missing proto_ids entry");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem)) == 0)
+            && ((state->pHeader->fieldIdsOff != 0)
+                    || (state->pHeader->fieldIdsSize != 0))) {
+        ALOGE("Map is missing field_ids entry");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem)) == 0)
+            && ((state->pHeader->methodIdsOff != 0)
+                    || (state->pHeader->methodIdsSize != 0))) {
+        ALOGE("Map is missing method_ids entry");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeClassDefItem)) == 0)
+            && ((state->pHeader->classDefsOff != 0)
+                    || (state->pHeader->classDefsSize != 0))) {
+        ALOGE("Map is missing class_defs entry");
+        return false;
+    }
+
+    state->pDataMap = dexDataMapAlloc(dataItemCount);
+    if (state->pDataMap == NULL) {
+        ALOGE("Unable to allocate data map (size %#x)", 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) {
+        ALOGE("Multiple map list items");
+        return false;
+    }
+
+    if (sectionOffset != state->pHeader->mapOff) {
+        ALOGE("Map not at header-defined offset: %#x, expected %#x",
+                sectionOffset, state->pHeader->mapOff);
+        return false;
+    }
+
+    const DexMapList* pMap = (const DexMapList*) 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 = (DexStringId*) 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 = (const DexStringId*) ptr;
+
+    if (!dexDataMapVerify(state->pDataMap,
+                    item->stringDataOff, kDexTypeStringDataItem)) {
+        return NULL;
+    }
+
+    const DexStringId* item0 = (const DexStringId*) 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) {
+            ALOGE("Out-of-order string_ids: '%s' then '%s'", 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 = (DexTypeId*) 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 = (const DexTypeId*) ptr;
+    const char* descriptor =
+        dexStringById(state->pDexFile, item->descriptorIdx);
+
+    if (!dexIsValidTypeDescriptor(descriptor)) {
+        ALOGE("Invalid type descriptor: '%s'", descriptor);
+        return NULL;
+    }
+
+    const DexTypeId* item0 = (const DexTypeId*) state->previousItem;
+    if (item0 != NULL) {
+        // Check ordering. This relies on string_ids being in order.
+        if (item0->descriptorIdx >= item->descriptorIdx) {
+            ALOGE("Out-of-order type_ids: %#x then %#x",
+                    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 = (DexProtoId*) 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) {
+                ALOGE("Invalid use of void");
+                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')) {
+                ALOGE("Shorty vs. primitive type mismatch: '%c', '%s'",
+                        shorty, descriptor);
+                return false;
+            }
+            break;
+        }
+        case 'L': {
+            if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+                ALOGE("Shorty vs. type mismatch: '%c', '%s'",
+                        shorty, descriptor);
+                return false;
+            }
+            break;
+        }
+        default: {
+            ALOGE("Bogus shorty: '%c'", shorty);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/* Perform cross-item verification of proto_id_item. */
+static void* crossVerifyProtoIdItem(const CheckState* state, void* ptr) {
+    const DexProtoId* item = (const DexProtoId*) 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') {
+            ALOGE("Shorty is too short");
+            return NULL;
+        }
+
+        if (!shortyDescMatch(*shorty, desc, false)) {
+            return NULL;
+        }
+
+        shorty++;
+    }
+
+    if (*shorty != '\0') {
+        ALOGE("Shorty is too long");
+        return NULL;
+    }
+
+    const DexProtoId* item0 = (const DexProtoId*) state->previousItem;
+    if (item0 != NULL) {
+        // Check ordering. This relies on type_ids being in order.
+        if (item0->returnTypeIdx > item->returnTypeIdx) {
+            ALOGE("Out-of-order proto_id return types");
+            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) {
+                ALOGE("Out-of-order proto_id arguments");
+                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 = (DexFieldId*) 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 = (const DexFieldId*) ptr;
+    const char* s;
+
+    s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+    if (!dexIsClassDescriptor(s)) {
+        ALOGE("Invalid descriptor for class_idx: '%s'", s);
+        return NULL;
+    }
+
+    s = dexStringByTypeIdx(state->pDexFile, item->typeIdx);
+    if (!dexIsFieldDescriptor(s)) {
+        ALOGE("Invalid descriptor for type_idx: '%s'", s);
+        return NULL;
+    }
+
+    s = dexStringById(state->pDexFile, item->nameIdx);
+    if (!dexIsValidMemberName(s)) {
+        ALOGE("Invalid name: '%s'", s);
+        return NULL;
+    }
+
+    const DexFieldId* item0 = (const DexFieldId*) 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) {
+            ALOGE("Out-of-order field_ids");
+            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 = (DexMethodId*) 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 = (const DexMethodId*) ptr;
+    const char* s;
+
+    s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+    if (!dexIsReferenceDescriptor(s)) {
+        ALOGE("Invalid descriptor for class_idx: '%s'", s);
+        return NULL;
+    }
+
+    s = dexStringById(state->pDexFile, item->nameIdx);
+    if (!dexIsValidMemberName(s)) {
+        ALOGE("Invalid name: '%s'", s);
+        return NULL;
+    }
+
+    const DexMethodId* item0 = (const DexMethodId*) 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) {
+            ALOGE("Out-of-order method_ids");
+            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 = (DexClassDef*) 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 = (const u1*) 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 =
+        (const DexAnnotationsDirectoryItem*) 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 = (const DexClassDef*) ptr;
+    u4 classIdx = item->classIdx;
+    const char* descriptor = dexStringByTypeIdx(state->pDexFile, classIdx);
+
+    if (!dexIsClassDescriptor(descriptor)) {
+        ALOGE("Invalid class: '%s'", descriptor);
+        return NULL;
+    }
+
+    if (setDefinedClassBit(state, classIdx)) {
+        ALOGE("Duplicate class definition: '%s'", 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)) {
+            ALOGE("Invalid superclass: '%s'", 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)) {
+                ALOGE("Invalid interface: '%s'", 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) {
+                    ALOGE("Duplicate interface: '%s'",
+                            dexStringByTypeIdx(state->pDexFile, idx1));
+                    return NULL;
+                }
+            }
+        }
+    }
+
+    if (!verifyClassDataIsForDef(state, item->classDataOff, item->classIdx)) {
+        ALOGE("Invalid class_data_item");
+        return NULL;
+    }
+
+    if (!verifyAnnotationsDirectoryIsForDef(state, item->annotationsOff,
+                    item->classIdx)) {
+        ALOGE("Invalid annotations_directory_item");
+        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) {
+            ALOGE("Out-of-order field_idx: %#x then %#x", 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) {
+            ALOGE("Out-of-order method_idx: %#x then %#x", 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) {
+            ALOGE("Out-of-order method_idx: %#x then %#x", 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 = (DexAnnotationsDirectoryItem*) 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 = (const DexAnnotationsDirectoryItem*) 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 = (DexTypeList*) 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 = (DexAnnotationSetRefList*) 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 = (const DexAnnotationSetRefList*) 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 = (DexAnnotationSetItem*) 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 = (const DexAnnotationSetItem*) 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) {
+            ALOGE("Out-of-order entry types: %#x then %#x",
+                    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) {
+            ALOGE("Field in wrong list @ %d", i);
+            return false;
+        }
+
+        if ((accessFlags & ~ACC_FIELD_MASK) != 0) {
+            ALOGE("Bogus field access flags %x @ %d", 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) {
+            ALOGE("Method in wrong list @ %d", i);
+            return false;
+        }
+
+        if (((accessFlags & ~ACC_METHOD_MASK) != 0)
+                || (isSynchronized && !allowSynchronized)) {
+            ALOGE("Bogus method access flags %x @ %d", accessFlags, i);
+            return false;
+        }
+
+        if (expectCode) {
+            if (method->codeOff == 0) {
+                ALOGE("Unexpected zero code_off for access_flags %x",
+                        accessFlags);
+                return false;
+            }
+        } else if (method->codeOff != 0) {
+            ALOGE("Unexpected non-zero code_off %#x for access_flags %x",
+                    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) {
+        ALOGE("Trouble with static fields");
+        return false;
+    }
+
+    verifyFields(state, classData->header.instanceFieldsSize,
+            classData->instanceFields, false);
+
+    if (!okay) {
+        ALOGE("Trouble with instance fields");
+        return false;
+    }
+
+    okay = verifyMethods(state, classData->header.directMethodsSize,
+            classData->directMethods, true);
+
+    if (!okay) {
+        ALOGE("Trouble with direct methods");
+        return false;
+    }
+
+    okay = verifyMethods(state, classData->header.virtualMethodsSize,
+            classData->virtualMethods, false);
+
+    if (!okay) {
+        ALOGE("Trouble with virtual methods");
+        return false;
+    }
+
+    return true;
+}
+
+/* Perform intra-item verification on class_data_item. */
+static void* intraVerifyClassDataItem(const CheckState* state, void* ptr) {
+    const u1* data = (const u1*) ptr;
+    DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+
+    if (classData == NULL) {
+        ALOGE("Unable to parse class_data_item");
+        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 = (const u1*) 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) {
+            ALOGE("Bogus size");
+            return 0;
+        }
+
+        if ((size < -65536) || (size > 65536)) {
+            ALOGE("Invalid size: %d", 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) {
+                ALOGE("Bogus type_idx");
+                return 0;
+            }
+
+            CHECK_INDEX(typeIdx, state->pHeader->typeIdsSize);
+
+            u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+            if (!okay) {
+                ALOGE("Bogus addr");
+                return 0;
+            }
+
+            if (addr >= code->insnsSize) {
+                ALOGE("Invalid addr: %#x", addr);
+                return 0;
+            }
+        }
+
+        if (catchAll) {
+            u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+            if (!okay) {
+                ALOGE("Bogus catch_all_addr");
+                return 0;
+            }
+
+            if (addr >= code->insnsSize) {
+                ALOGE("Invalid catch_all_addr: %#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) {
+        ALOGE("Bogus handlers_size");
+        return NULL;
+    }
+
+    if ((handlersSize == 0) || (handlersSize >= 65536)) {
+        ALOGE("Invalid handlers_size: %d", 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) {
+            ALOGE("Out-of-order try");
+            return NULL;
+        }
+
+        if (tries->startAddr >= code->insnsSize) {
+            ALOGE("Invalid start_addr: %#x", tries->startAddr);
+            return NULL;
+        }
+
+        for (i = 0; i < handlersSize; i++) {
+            if (tries->handlerOff == handlerOffs[i]) {
+                break;
+            }
+        }
+
+        if (i == handlersSize) {
+            ALOGE("Bogus handler offset: %#x", tries->handlerOff);
+            return NULL;
+        }
+
+        lastEnd = tries->startAddr + tries->insnCount;
+
+        if (lastEnd > code->insnsSize) {
+            ALOGE("Invalid insn_count: %#x (end addr %#x)",
+                    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 = (DexCode*) 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) {
+        ALOGE("insSize (%u) > registersSize (%u)", 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.
+         */
+        ALOGE("outsSize (%u) > registersSize (%u)", 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 ((((uintptr_t) insns) & 3) != 0) {
+            // Four-byte alignment for the tries. Verify the spacer is a 0.
+            if (*insns != 0) {
+                ALOGE("Non-zero padding: %#x", (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 = (const u1*) ptr;
+    bool okay = true;
+    u4 utf16Size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+    u4 i;
+
+    if (!okay) {
+        ALOGE("Bogus utf16_size");
+        return NULL;
+    }
+
+    for (i = 0; i < utf16Size; i++) {
+        if (data >= fileEnd) {
+            ALOGE("String data would go beyond end-of-file");
+            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) {
+                    ALOGE("String shorter than indicated utf16_size %#x",
+                            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.
+                 */
+                ALOGE("Illegal start byte %#x", byte1);
+                return NULL;
+            }
+            case 0x0e: {
+                // Bit pattern 1110, so there are two additional bytes.
+                u1 byte2 = *(data++);
+                if ((byte2 & 0xc0) != 0x80) {
+                    ALOGE("Illegal continuation byte %#x", byte2);
+                    return NULL;
+                }
+                u1 byte3 = *(data++);
+                if ((byte3 & 0xc0) != 0x80) {
+                    ALOGE("Illegal continuation byte %#x", byte3);
+                    return NULL;
+                }
+                u2 value = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6)
+                    | (byte3 & 0x3f);
+                if (value < 0x800) {
+                    ALOGE("Illegal representation for value %x", value);
+                    return NULL;
+                }
+                break;
+            }
+            case 0x0c:
+            case 0x0d: {
+                // Bit pattern 110x, so there is one additional byte.
+                u1 byte2 = *(data++);
+                if ((byte2 & 0xc0) != 0x80) {
+                    ALOGE("Illegal continuation byte %#x", byte2);
+                    return NULL;
+                }
+                u2 value = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
+                if ((value != 0) && (value < 0x80)) {
+                    ALOGE("Illegal representation for value %x", value);
+                    return NULL;
+                }
+                break;
+            }
+        }
+    }
+
+    if (*(data++) != '\0') {
+        ALOGE("String longer than indicated utf16_size %#x", 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 = (const u1*) ptr;
+    bool okay = true;
+    u4 i;
+
+    readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+    if (!okay) {
+        ALOGE("Bogus line_start");
+        return NULL;
+    }
+
+    u4 parametersSize =
+        readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+    if (!okay) {
+        ALOGE("Bogus parameters_size");
+        return NULL;
+    }
+
+    if (parametersSize > 65536) {
+        ALOGE("Invalid parameters_size: %#x", parametersSize);
+        return NULL;
+    }
+
+    for (i = 0; i < parametersSize; i++) {
+        u4 parameterName =
+            readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+        if (!okay) {
+            ALOGE("Bogus parameter_name");
+            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) {
+            ALOGE("Bogus syntax for opcode %02x", 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) {
+        ALOGE("Bogus encoded_array size");
+        return NULL;
+    }
+
+    while (size--) {
+        data = verifyEncodedValue(state, data, crossVerify);
+        if (data == NULL) {
+            ALOGE("Bogus encoded_array value");
+            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) {
+                ALOGE("Bogus byte size %#x", valueArg);
+                return NULL;
+            }
+            data++;
+            break;
+        }
+        case kDexAnnotationShort:
+        case kDexAnnotationChar: {
+            if (valueArg > 1) {
+                ALOGE("Bogus char/short size %#x", valueArg);
+                return NULL;
+            }
+            data += valueArg + 1;
+            break;
+        }
+        case kDexAnnotationInt:
+        case kDexAnnotationFloat: {
+            if (valueArg > 3) {
+                ALOGE("Bogus int/float size %#x", valueArg);
+                return NULL;
+            }
+            data += valueArg + 1;
+            break;
+        }
+        case kDexAnnotationLong:
+        case kDexAnnotationDouble: {
+            data += valueArg + 1;
+            break;
+        }
+        case kDexAnnotationString: {
+            if (valueArg > 3) {
+                ALOGE("Bogus string size %#x", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+            break;
+        }
+        case kDexAnnotationType: {
+            if (valueArg > 3) {
+                ALOGE("Bogus type size %#x", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+            break;
+        }
+        case kDexAnnotationField:
+        case kDexAnnotationEnum: {
+            if (valueArg > 3) {
+                ALOGE("Bogus field/enum size %#x", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->fieldIdsSize);
+            break;
+        }
+        case kDexAnnotationMethod: {
+            if (valueArg > 3) {
+                ALOGE("Bogus method size %#x", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->methodIdsSize);
+            break;
+        }
+        case kDexAnnotationArray: {
+            if (valueArg != 0) {
+                ALOGE("Bogus array value_arg %#x", valueArg);
+                return NULL;
+            }
+            data = verifyEncodedArray(state, data, crossVerify);
+            break;
+        }
+        case kDexAnnotationAnnotation: {
+            if (valueArg != 0) {
+                ALOGE("Bogus annotation value_arg %#x", valueArg);
+                return NULL;
+            }
+            data = verifyEncodedAnnotation(state, data, crossVerify);
+            break;
+        }
+        case kDexAnnotationNull: {
+            if (valueArg != 0) {
+                ALOGE("Bogus null value_arg %#x", valueArg);
+                return NULL;
+            }
+            // Nothing else to do for this type.
+            break;
+        }
+        case kDexAnnotationBoolean: {
+            if (valueArg > 1) {
+                ALOGE("Bogus boolean value_arg %#x", valueArg);
+                return NULL;
+            }
+            // Nothing else to do for this type.
+            break;
+        }
+        default: {
+            ALOGE("Bogus value_type %#x", 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) {
+        ALOGE("Bogus encoded_annotation type_idx");
+        return NULL;
+    }
+
+    CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+
+    if (crossVerify) {
+        const char* descriptor = dexStringByTypeIdx(state->pDexFile, idx);
+        if (!dexIsClassDescriptor(descriptor)) {
+            ALOGE("Bogus annotation type: '%s'", descriptor);
+            return NULL;
+        }
+    }
+
+    u4 size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+    u4 lastIdx = 0;
+    bool first = true;
+
+    if (!okay) {
+        ALOGE("Bogus encoded_annotation size");
+        return NULL;
+    }
+
+    while (size--) {
+        idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+        if (!okay) {
+            ALOGE("Bogus encoded_annotation name_idx");
+            return NULL;
+        }
+
+        CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+
+        if (crossVerify) {
+            const char* name = dexStringById(state->pDexFile, idx);
+            if (!dexIsValidMemberName(name)) {
+                ALOGE("Bogus annotation member name: '%s'", name);
+                return NULL;
+            }
+        }
+
+        if (first) {
+            first = false;
+        } else if (lastIdx >= idx) {
+            ALOGE("Out-of-order encoded_annotation name_idx: %#x then %#x",
+                    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 = (const u1*) ptr;
+
+    CHECK_PTR_RANGE(data, data + 1);
+
+    switch (*(data++)) {
+        case kDexVisibilityBuild:
+        case kDexVisibilityRuntime:
+        case kDexVisibilitySystem: {
+            break;
+        }
+        default: {
+            ALOGE("Bogus annotation visibility: %#x", *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 = (const u1*) 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 = (u1*) filePointer(state, newOffset);
+
+        if (offset < newOffset) {
+            ptr = (u1*) filePointer(state, offset);
+            if (offset < newOffset) {
+                CHECK_OFFSET_RANGE(offset, newOffset);
+                while (offset < newOffset) {
+                    if (*ptr != '\0') {
+                        ALOGE("Non-zero padding 0x%02x @ %x", *ptr, offset);
+                        return false;
+                    }
+                    ptr++;
+                    offset++;
+                }
+            }
+        }
+
+        u1* newPtr = (u1*) func(state, ptr);
+        newOffset = fileOffset(state, newPtr);
+
+        if (newPtr == NULL) {
+            ALOGE("Trouble with item %d @ offset %#x", i, offset);
+            return false;
+        }
+
+        if (newOffset > state->fileLen) {
+            ALOGE("Item %d @ offset %#x ends out of bounds", 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) {
+        ALOGE("Bogus offset for section: got %#x; expected %#x",
+                offset, expectedOffset);
+        return false;
+    }
+
+    if (count != expectedCount) {
+        ALOGE("Bogus size for section: got %#x; expected %#x",
+                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)) {
+        ALOGE("Bogus offset for data subsection: %#x", offset);
+        return false;
+    }
+
+    if (!iterateSectionWithOptionalUpdate(state, offset, count, func,
+                    alignment, nextOffset, mapType)) {
+        return false;
+    }
+
+    if (*nextOffset > dataEnd) {
+        ALOGE("Out-of-bounds end of data subsection: %#x", *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 = (const u1*) filePointer(state, lastOffset);
+            while (lastOffset < sectionOffset) {
+                if (*ptr != '\0') {
+                    ALOGE("Non-zero padding 0x%02x before section start @ %x",
+                            *ptr, lastOffset);
+                    okay = false;
+                    break;
+                }
+                ptr++;
+                lastOffset++;
+            }
+        } else if (lastOffset > sectionOffset) {
+            ALOGE("Section overlap or out-of-order map: %x, %x",
+                    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: {
+                ALOGE("Unknown map item type %04x", type);
+                return false;
+            }
+        }
+
+        if (!okay) {
+            ALOGE("Swap of section type %04x failed", 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: {
+                ALOGE("Unknown map item type %04x", item->type);
+                return false;
+            }
+        }
+
+        if (!okay) {
+            ALOGE("Cross-item verify of section type %04x failed",
+                    item->type);
+        }
+
+        item++;
+    }
+
+    return okay;
+}
+
+/* (documented in header file) */
+bool dexHasValidMagic(const DexHeader* pHeader)
+{
+    const u1* magic = pHeader->magic;
+    const u1* version = &magic[4];
+
+    if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+        ALOGE("ERROR: unrecognized magic number (%02x %02x %02x %02x)",
+            magic[0], magic[1], magic[2], magic[3]);
+        return false;
+    }
+
+    if ((memcmp(version, DEX_MAGIC_VERS, 4) != 0) &&
+            (memcmp(version, DEX_MAGIC_VERS_API_13, 4) != 0)) {
+        /*
+         * Magic was correct, but this is an unsupported older or
+         * newer format variant.
+         */
+        ALOGE("ERROR: unsupported dex version (%02x %02x %02x %02x)",
+            version[0], version[1], version[2], version[3]);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * 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));
+    ALOGV("+++ swapping and verifying");
+
+    /*
+     * Note: The caller must have verified that "len" is at least as
+     * large as a dex file header.
+     */
+    pHeader = (DexHeader*) addr;
+
+    if (!dexHasValidMagic(pHeader)) {
+        okay = false;
+    }
+
+    if (okay) {
+        int expectedLen = (int) SWAP4(pHeader->fileSize);
+        if (len < expectedLen) {
+            ALOGE("ERROR: Bad length: expected %d, got %d", expectedLen, len);
+            okay = false;
+        } else if (len != expectedLen) {
+            ALOGW("WARNING: Odd length: expected %d, got %d", 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) {
+            ALOGE("ERROR: bad checksum (%08lx, expected %08x)",
+                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)) {
+            ALOGE("ERROR: Small header size %d, struct %d",
+                    pHeader->headerSize, (int) sizeof(DexHeader));
+            okay = false;
+        } else if (pHeader->headerSize > sizeof(DexHeader)) {
+            ALOGW("WARNING: Large header size %d, struct %d",
+                    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 {
+            ALOGE("ERROR: No map found; impossible to byte-swap and verify");
+            okay = false;
+        }
+    }
+
+    if (!okay) {
+        ALOGE("ERROR: Byte swap + verify failed");
+    }
+
+    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);
+    }
+
+    ALOGE("ERROR: Bad magic number (0x%02x %02x %02x %02x)",
+             addr[0], addr[1], addr[2], addr[3]);
+
+    return 1;
+}
diff --git a/libdex/DexUtf.cpp b/libdex/DexUtf.cpp
new file mode 100644
index 0000000..df49d18
--- /dev/null
+++ b/libdex/DexUtf.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Validate and manipulate MUTF-8 encoded string data.
+ */
+
+#include "DexUtf.h"
+
+/* 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). This is all specified in the dex format
+     * document.
+     */
+
+    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;
+        }
+    }
+}
+
+/* Helper for validating type descriptors and class names, which is parametric
+ * with respect to type vs. class and dot vs. slash. */
+static bool isValidTypeDescriptorOrClassName(const char* s, bool isClassName,
+        bool dotSeparator) {
+    int arrayCount = 0;
+
+    while (*s == '[') {
+        arrayCount++;
+        s++;
+    }
+
+    if (arrayCount > 255) {
+        // Arrays may have no more than 255 dimensions.
+        return false;
+    }
+
+    if (arrayCount != 0) {
+        /*
+         * If we're looking at an array of some sort, then it doesn't
+         * matter if what is being asked for is a class name; the
+         * format looks the same as a type descriptor in that case, so
+         * treat it as such.
+         */
+        isClassName = false;
+    }
+
+    if (!isClassName) {
+        /*
+         * We are looking for a descriptor. Either validate it as a
+         * single-character primitive type, or continue on to check the
+         * embedded class name (bracketed by "L" and ";").
+         */
+        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': {
+                // Non-array void is valid, but you can't have an array of void.
+                return (arrayCount == 0) && (*s == '\0');
+            }
+            case 'L': {
+                // Class name: Break out and continue below.
+                break;
+            }
+            default: {
+                // Oddball descriptor character.
+                return false;
+            }
+        }
+    }
+
+    /*
+     * We just consumed the 'L' that introduces a class name as part
+     * of a type descriptor, or we are looking for an unadorned class
+     * name.
+     */
+
+    bool sepOrFirst = true; // first character or just encountered a separator.
+    for (;;) {
+        u1 c = (u1) *s;
+        switch (c) {
+            case '\0': {
+                /*
+                 * Premature end for a type descriptor, but valid for
+                 * a class name as long as we haven't encountered an
+                 * empty component (including the degenerate case of
+                 * the empty string "").
+                 */
+                return isClassName && !sepOrFirst;
+            }
+            case ';': {
+                /*
+                 * Invalid character for a class name, but the
+                 * legitimate end of a type descriptor. In the latter
+                 * 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 !isClassName && !sepOrFirst && (s[1] == '\0');
+            }
+            case '/':
+            case '.': {
+                if (dotSeparator != (c == '.')) {
+                    // The wrong separator character.
+                    return false;
+                }
+                if (sepOrFirst) {
+                    // Separator at start or two separators in a row.
+                    return false;
+                }
+                sepOrFirst = true;
+                s++;
+                break;
+            }
+            default: {
+                if (!dexIsValidMemberNameUtf8(&s)) {
+                    return false;
+                }
+                sepOrFirst = false;
+                break;
+            }
+        }
+    }
+}
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s) {
+    return isValidTypeDescriptorOrClassName(s, false, false);
+}
+
+/* (documented in header) */
+bool dexIsValidClassName(const char* s, bool dotSeparator) {
+    return isValidTypeDescriptorOrClassName(s, true, dotSeparator);
+}
+
+/* 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';
+}
+
diff --git a/libdex/DexUtf.h b/libdex/DexUtf.h
new file mode 100644
index 0000000..cb3d919
--- /dev/null
+++ b/libdex/DexUtf.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Validate and manipulate MUTF-8 (modified UTF-8) encoded string data.
+ */
+
+#ifndef LIBDEX_DEXUTF_H_
+#define LIBDEX_DEXUTF_H_
+
+#include "DexFile.h"
+
+/*
+ * 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 internal-form class
+ * name, with components separated either by dots or slashes as
+ * specified. A class name is like a type descriptor, except that it
+ * can't name a primitive type (including void). In terms of syntax,
+ * the form is either (a) the name of the class without adornment
+ * (that is, not bracketed by "L" and ";"); or (b) identical to the
+ * type descriptor syntax for array types. */
+bool dexIsValidClassName(const char* s, bool dotSeparator);
+
+/* 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_DEXUTF_H_
diff --git a/libdex/InstrUtils.cpp b/libdex/InstrUtils.cpp
new file mode 100644
index 0000000..be343f0
--- /dev/null
+++ b/libdex/InstrUtils.cpp
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.
+ *
+ * IMPORTANT NOTE: Much of the contents of this file are generated
+ * automatically by the opcode-gen tool. Any edits to the generated
+ * sections will get wiped out the next time the tool is run.
+ */
+
+#include "InstrUtils.h"
+#include <stdlib.h>
+
+/*
+ * Table that maps each opcode to the full width of instructions that
+ * use that opcode, in (16-bit) code units. Unimplemented opcodes as
+ * well as the "breakpoint" opcode have a width of zero.
+ */
+static InstructionWidth gInstructionWidthTable[kNumPackedOpcodes] = {
+    // BEGIN(libdex-widths); GENERATED AUTOMATICALLY BY opcode-gen
+    1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 2, 3, 2, 2, 3, 5, 2, 2, 3, 2, 1, 1, 2,
+    2, 1, 2, 2, 3, 3, 3, 1, 1, 2, 3, 3, 3, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0,
+    0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3,
+    3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 3, 3,
+    3, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 0,
+    // END(libdex-widths)
+};
+
+/*
+ * Table that maps each opcode to the flags associated with that
+ * opcode.
+ */
+static u1 gOpcodeFlagsTable[kNumPackedOpcodes] = {
+    // BEGIN(libdex-flags); GENERATED AUTOMATICALLY BY opcode-gen
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanReturn,
+    kInstrCanReturn,
+    kInstrCanReturn,
+    kInstrCanReturn,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue,
+    kInstrCanThrow,
+    kInstrCanBranch,
+    kInstrCanBranch,
+    kInstrCanBranch,
+    kInstrCanContinue|kInstrCanSwitch,
+    kInstrCanContinue|kInstrCanSwitch,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    kInstrCanContinue|kInstrCanBranch,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    0,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    0,
+    0,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    0,
+    kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanReturn,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
+    0,
+    // END(libdex-flags)
+};
+
+/*
+ * Table that maps each opcode to the instruction format associated
+ * that opcode.
+ */
+static u1 gInstructionFormatTable[kNumPackedOpcodes] = {
+    // BEGIN(libdex-formats); GENERATED AUTOMATICALLY BY opcode-gen
+    kFmt10x,  kFmt12x,  kFmt22x,  kFmt32x,  kFmt12x,  kFmt22x,  kFmt32x,
+    kFmt12x,  kFmt22x,  kFmt32x,  kFmt11x,  kFmt11x,  kFmt11x,  kFmt11x,
+    kFmt10x,  kFmt11x,  kFmt11x,  kFmt11x,  kFmt11n,  kFmt21s,  kFmt31i,
+    kFmt21h,  kFmt21s,  kFmt31i,  kFmt51l,  kFmt21h,  kFmt21c,  kFmt31c,
+    kFmt21c,  kFmt11x,  kFmt11x,  kFmt21c,  kFmt22c,  kFmt12x,  kFmt21c,
+    kFmt22c,  kFmt35c,  kFmt3rc,  kFmt31t,  kFmt11x,  kFmt10t,  kFmt20t,
+    kFmt30t,  kFmt31t,  kFmt31t,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt22t,  kFmt22t,  kFmt22t,  kFmt22t,  kFmt22t,  kFmt22t,
+    kFmt21t,  kFmt21t,  kFmt21t,  kFmt21t,  kFmt21t,  kFmt21t,  kFmt00x,
+    kFmt00x,  kFmt00x,  kFmt00x,  kFmt00x,  kFmt00x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt22c,  kFmt22c,
+    kFmt22c,  kFmt22c,  kFmt22c,  kFmt22c,  kFmt22c,  kFmt22c,  kFmt22c,
+    kFmt22c,  kFmt22c,  kFmt22c,  kFmt22c,  kFmt22c,  kFmt21c,  kFmt21c,
+    kFmt21c,  kFmt21c,  kFmt21c,  kFmt21c,  kFmt21c,  kFmt21c,  kFmt21c,
+    kFmt21c,  kFmt21c,  kFmt21c,  kFmt21c,  kFmt21c,  kFmt35c,  kFmt35c,
+    kFmt35c,  kFmt35c,  kFmt35c,  kFmt00x,  kFmt3rc,  kFmt3rc,  kFmt3rc,
+    kFmt3rc,  kFmt3rc,  kFmt00x,  kFmt00x,  kFmt12x,  kFmt12x,  kFmt12x,
+    kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,
+    kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,
+    kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt23x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,  kFmt23x,
+    kFmt23x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,
+    kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,
+    kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,
+    kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,
+    kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt12x,  kFmt22s,  kFmt22s,
+    kFmt22s,  kFmt22s,  kFmt22s,  kFmt22s,  kFmt22s,  kFmt22s,  kFmt22b,
+    kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,
+    kFmt22b,  kFmt22b,  kFmt22b,  kFmt22c,  kFmt22c,  kFmt21c,  kFmt21c,
+    kFmt22c,  kFmt22c,  kFmt22c,  kFmt21c,  kFmt21c,  kFmt00x,  kFmt20bc,
+    kFmt35mi, kFmt3rmi, kFmt35c,  kFmt10x,  kFmt22cs, kFmt22cs, kFmt22cs,
+    kFmt22cs, kFmt22cs, kFmt22cs, kFmt35ms, kFmt3rms, kFmt35ms, kFmt3rms,
+    kFmt22c,  kFmt21c,  kFmt21c,  kFmt00x,
+    // END(libdex-formats)
+};
+
+/*
+ * Table that maps each opcode to the index type implied by that
+ * opcode.
+ */
+static u1 gInstructionIndexTypeTable[kNumPackedOpcodes] = {
+    // BEGIN(libdex-index-types); GENERATED AUTOMATICALLY BY opcode-gen
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexStringRef,
+    kIndexStringRef,    kIndexTypeRef,      kIndexNone,
+    kIndexNone,         kIndexTypeRef,      kIndexTypeRef,
+    kIndexNone,         kIndexTypeRef,      kIndexTypeRef,
+    kIndexTypeRef,      kIndexTypeRef,      kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexUnknown,
+    kIndexUnknown,      kIndexUnknown,      kIndexUnknown,
+    kIndexUnknown,      kIndexUnknown,      kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexMethodRef,
+    kIndexMethodRef,    kIndexMethodRef,    kIndexMethodRef,
+    kIndexMethodRef,    kIndexUnknown,      kIndexMethodRef,
+    kIndexMethodRef,    kIndexMethodRef,    kIndexMethodRef,
+    kIndexMethodRef,    kIndexUnknown,      kIndexUnknown,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexNone,
+    kIndexNone,         kIndexNone,         kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexUnknown,
+    kIndexVaries,       kIndexInlineMethod, kIndexInlineMethod,
+    kIndexMethodRef,    kIndexNone,         kIndexFieldOffset,
+    kIndexFieldOffset,  kIndexFieldOffset,  kIndexFieldOffset,
+    kIndexFieldOffset,  kIndexFieldOffset,  kIndexVtableOffset,
+    kIndexVtableOffset, kIndexVtableOffset, kIndexVtableOffset,
+    kIndexFieldRef,     kIndexFieldRef,     kIndexFieldRef,
+    kIndexUnknown,
+    // END(libdex-index-types)
+};
+
+/*
+ * Global InstructionInfoTables struct.
+ */
+InstructionInfoTables gDexOpcodeInfo = {
+    gInstructionFormatTable,
+    gInstructionIndexTypeTable,
+    gOpcodeFlagsTable,
+    gInstructionWidthTable
+};
+
+/*
+ * Handy macros for helping decode instructions.
+ */
+#define FETCH(_offset)      (insns[(_offset)])
+#define FETCH_u4(_offset)   (fetch_u4_impl((_offset), insns))
+#define INST_A(_inst)       (((u2)(_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((u2)(_inst) >> 12)
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/* Helper for FETCH_u4, above. */
+static inline u4 fetch_u4_impl(u4 offset, const u2* insns) {
+    return insns[offset] | ((u4) insns[offset+1] << 16);
+}
+
+/*
+ * 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 u2* insns, DecodedInstruction* pDec)
+{
+    u2 inst = *insns;
+    Opcode opcode = dexOpcodeFromCodeUnit(inst);
+    InstructionFormat format = dexGetFormatFromOpcode(opcode);
+
+    pDec->opcode = opcode;
+    pDec->indexType = dexGetIndexTypeFromOpcode(opcode);
+
+    switch (format) {
+    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:      // [opt] 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_u4(1);                     // signed 32-bit value
+        break;
+    case kFmt31t:       // op vAA, +BBBBBBBB
+    case kFmt31c:       // op vAA, string@BBBBBBBB
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH_u4(1);                     // 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_u4(1);                     // signed 32-bit value
+        break;
+    case kFmt35c:       // op {vC, vD, vE, vF, vG}, thing@BBBB
+    case kFmt35ms:      // [opt] invoke-virtual+super
+    case kFmt35mi:      // [opt] inline invoke
+        {
+            /*
+             * Note that the fields mentioned in the spec don't appear in
+             * their "usual" positions here compared to most formats. This
+             * was done so that the field names for the argument count and
+             * reference index match between this format and the corresponding
+             * range formats (3rc and friends).
+             *
+             * Bottom line: The argument count is always in vA, and the
+             * method constant (or equivalent) is always in vB.
+             */
+            u2 regList;
+            int i, count;
+
+            pDec->vA = INST_B(inst); // This is labeled A in the spec.
+            pDec->vB = FETCH(1);
+            regList = FETCH(2);
+
+            count = pDec->vA;
+
+            /*
+             * Copy the argument registers into the arg[] array, and
+             * also copy the first argument (if any) into vC. (The
+             * DecodedInstruction structure doesn't have separate
+             * fields for {vD, vE, vF, vG}, so there's no need to make
+             * copies of those.) Note that cases 5..2 fall through.
+             */
+            switch (count) {
+            case 5: {
+                if (format == kFmt35mi) {
+                    /* A fifth arg is verboten for inline invokes. */
+                    ALOGW("Invalid arg count in 35mi (5)");
+                    goto bail;
+                }
+                /*
+                 * Per note at the top of this format decoder, the
+                 * fifth argument comes from the A field in the
+                 * instruction, but it's labeled G in the spec.
+                 */
+                pDec->arg[4] = INST_A(inst);
+            }
+            case 4: pDec->arg[3] = (regList >> 12) & 0x0f;
+            case 3: pDec->arg[2] = (regList >> 8) & 0x0f;
+            case 2: pDec->arg[1] = (regList >> 4) & 0x0f;
+            case 1: pDec->vC = pDec->arg[0] = regList & 0x0f; break;
+            case 0: break; // Valid, but no need to do anything.
+            default:
+                ALOGW("Invalid arg count in 35c/35ms/35mi (%d)", count);
+                goto bail;
+            }
+        }
+        break;
+    case kFmt3rc:       // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+    case kFmt3rms:      // [opt] invoke-virtual+super/range
+    case kFmt3rmi:      // [opt] execute-inline/range
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH(1);
+        pDec->vC = FETCH(2);
+        break;
+    case kFmt51l:       // op vAA, #+BBBBBBBBBBBBBBBB
+        pDec->vA = INST_AA(inst);
+        pDec->vB_wide = FETCH_u4(1) | ((u8) FETCH_u4(3) << 32);
+        break;
+    default:
+        ALOGW("Can't decode unexpected format %d (op=%d)", format, 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 dexGetWidthFromInstruction(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);
+        // The plus 1 is to round up for odd size and width.
+        width = 4 + (elemWidth * len + 1) / 2;
+    } else {
+        width = dexGetWidthFromOpcode(dexOpcodeFromCodeUnit(insns[0]));
+    }
+
+    return width;
+}
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
new file mode 100644
index 0000000..76993a5
--- /dev/null
+++ b/libdex/InstrUtils.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_H_
+#define LIBDEX_INSTRUTILS_H_
+
+#include "DexFile.h"
+#include "DexOpcodes.h"
+
+/*
+ * Possible instruction formats associated with Dalvik opcodes.
+ *
+ * See the file opcode-gen/README.txt for information about updating
+ * opcodes and instruction formats.
+ */
+enum InstructionFormat {
+    kFmt00x = 0,    // unknown format (also used for "breakpoint" opcode)
+    kFmt10x,        // op
+    kFmt12x,        // op vA, vB
+    kFmt11n,        // op vA, #+B
+    kFmt11x,        // op vAA
+    kFmt10t,        // op +AA
+    kFmt20bc,       // [opt] 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
+    kFmt30t,        // op +AAAAAAAA
+    kFmt32x,        // op vAAAA, vBBBB
+    kFmt31i,        // op vAA, #+BBBBBBBB
+    kFmt31t,        // op vAA, +BBBBBBBB
+    kFmt31c,        // op vAA, string@BBBBBBBB
+    kFmt35c,        // op {vC,vD,vE,vF,vG}, thing@BBBB
+    kFmt35ms,       // [opt] invoke-virtual+super
+    kFmt3rc,        // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
+    kFmt3rms,       // [opt] invoke-virtual+super/range
+    kFmt51l,        // op vAA, #+BBBBBBBBBBBBBBBB
+    kFmt35mi,       // [opt] inline invoke
+    kFmt3rmi,       // [opt] inline invoke/range
+};
+
+/*
+ * Types of indexed reference that are associated with opcodes whose
+ * formats include such an indexed reference (e.g., 21c and 35c).
+ */
+enum InstructionIndexType {
+    kIndexUnknown = 0,
+    kIndexNone,         // has no index
+    kIndexVaries,       // "It depends." Used for throw-verification-error
+    kIndexTypeRef,      // type reference index
+    kIndexStringRef,    // string reference index
+    kIndexMethodRef,    // method reference index
+    kIndexFieldRef,     // field reference index
+    kIndexInlineMethod, // inline method index (for inline linked methods)
+    kIndexVtableOffset, // vtable offset (for static linked methods)
+    kIndexFieldOffset   // field offset (for static linked fields)
+};
+
+/*
+ * Instruction width implied by an opcode's format; a value in the
+ * range 0 to 5. Note that there are special "pseudo-instructions"
+ * which are used to encode switch and data tables, and these don't
+ * have a fixed width. See dexGetWidthFromInstruction(), below.
+ */
+typedef u1 InstructionWidth;
+
+/*
+ * Opcode control flow flags, used by the verifier and JIT.
+ */
+typedef u1 OpcodeFlags;
+enum OpcodeFlagsBits {
+    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
+};
+
+/*
+ * Struct that includes a pointer to each of the opcode information
+ * tables.
+ *
+ * Note: We use "u1*" here instead of the names of the enumerated
+ * types to guarantee that elements don't use much space. We hold out
+ * hope for a standard way to indicate the size of an enumerated type
+ * that works for both C and C++, but in the mean time, this will
+ * suffice.
+ */
+struct InstructionInfoTables {
+    u1*                formats;    /* InstructionFormat elements */
+    u1*                indexTypes; /* InstructionIndexType elements */
+    OpcodeFlags*       flags;
+    InstructionWidth*  widths;
+};
+
+/*
+ * Global InstructionInfoTables struct.
+ */
+extern InstructionInfoTables gDexOpcodeInfo;
+
+/*
+ * Holds the contents of a decoded instruction.
+ */
+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;
+    InstructionIndexType indexType;
+};
+
+/*
+ * Return the instruction width of the specified opcode, or 0 if not defined.
+ */
+DEX_INLINE size_t dexGetWidthFromOpcode(Opcode opcode)
+{
+    assert((u4) opcode < kNumPackedOpcodes);
+    return gDexOpcodeInfo.widths[opcode];
+}
+
+/*
+ * 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 dexGetWidthFromInstruction(const u2* insns);
+
+/*
+ * Returns the flags for the specified opcode.
+ */
+DEX_INLINE OpcodeFlags dexGetFlagsFromOpcode(Opcode opcode)
+{
+    assert((u4) opcode < kNumPackedOpcodes);
+    return gDexOpcodeInfo.flags[opcode];
+}
+
+/*
+ * Returns true if the given flags represent a goto (unconditional branch).
+ */
+DEX_INLINE bool dexIsGoto(OpcodeFlags flags)
+{
+    return (flags & (kInstrCanBranch | kInstrCanContinue)) == kInstrCanBranch;
+}
+
+/*
+ * Return the instruction format for the specified opcode.
+ */
+DEX_INLINE InstructionFormat dexGetFormatFromOpcode(Opcode opcode)
+{
+    assert((u4) opcode < kNumPackedOpcodes);
+    return (InstructionFormat) gDexOpcodeInfo.formats[opcode];
+}
+
+/*
+ * Return the instruction index type for the specified opcode.
+ */
+DEX_INLINE InstructionIndexType dexGetIndexTypeFromOpcode(Opcode opcode)
+{
+    assert((u4) opcode < kNumPackedOpcodes);
+    return (InstructionIndexType) gDexOpcodeInfo.indexTypes[opcode];
+}
+
+/*
+ * Decode the instruction pointed to by "insns".
+ */
+void dexDecodeInstruction(const u2* insns, DecodedInstruction* pDec);
+
+#endif  // LIBDEX_INSTRUTILS_H_
diff --git a/libdex/Leb128.cpp b/libdex/Leb128.cpp
new file mode 100644
index 0000000..ed09e19
--- /dev/null
+++ b/libdex/Leb128.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * 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..21f4eda
--- /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_H_
+#define LIBDEX_LEB128_H_
+
+#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/OptInvocation.cpp b/libdex/OptInvocation.cpp
new file mode 100644
index 0000000..bac2f24
--- /dev/null
+++ b/libdex/OptInvocation.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Utility functions for dealing with optimized dex files.
+ */
+
+#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* kCacheDirectoryName = "dalvik-cache";
+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];
+    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) {
+            ALOGE("Can't get CWD while opening jar file");
+            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, kCacheDirectoryName);
+
+    /* Tack on the file name for the actual cache file path.
+     */
+    strncat(nameBuf, absoluteFile, kBufLen);
+
+    ALOGV("Cache file for '%s' '%s' is '%s'", 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;
+        ALOGE("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..3f32b94
--- /dev/null
+++ b/libdex/OptInvocation.h
@@ -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.
+ */
+
+/*
+ * Utility functions related to "dexopt".
+ */
+#ifndef LIBDEX_OPTINVOCATION_H_
+#define LIBDEX_OPTINVOCATION_H_
+
+/*
+ * Utility routines, used by the VM.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName,
+    const char* subFileName);
+int dexOptCreateEmptyHeader(int fd);
+
+#endif  // LIBDEX_OPTINVOCATION_H_
diff --git a/libdex/SysUtil.cpp b/libdex/SysUtil.cpp
new file mode 100644
index 0000000..ba82de2
--- /dev/null
+++ b/libdex/SysUtil.cpp
@@ -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.
+ */
+
+/*
+ * 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) {
+        ALOGW("mmap(%d, RW, SHARED|ANON) failed: %s", (int) length,
+            strerror(errno));
+        return NULL;
+    }
+
+    return ptr;
+#else
+    ALOGE("sysCreateAnonShmem not implemented.");
+    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) {
+        ALOGE("could not determine length of file");
+        return -1;
+    }
+
+    length = end - start;
+    if (length == 0) {
+        ALOGE("file is empty");
+        return -1;
+    }
+
+    *start_ = start;
+    *length_ = length;
+
+    return 0;
+}
+
+#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) {
+        ALOGW("read(fd=%d, start=%d, length=%d) failed: %s", (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 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) {
+        ALOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s", (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;
+        ALOGV("mprotect(%p, %d, PROT_READ) failed: %s",
+            memPtr, length, strerror(err));
+        ALOGD("mprotect(RO) failed (%d), file will remain read-write", 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) {
+        ALOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s",
+            (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",
+        (int) start, (int) length,
+        pMap->baseAddr, (int) pMap->baseLength,
+        pMap->addr, (int) pMap->length);
+
+    return 0;
+#else
+    ALOGE("sysMapFileSegmentInShmem not implemented.");
+    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)
+    {
+        ALOGE("Attempted to change %p; map is %p - %p",
+            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*) ((uintptr_t) addr & ~(SYSTEM_PAGE_SIZE-1));
+    size_t alignLength = length + ((u1*) addr - alignAddr);
+
+    //ALOGI("%p/%zd --> %p/%zd", addr, length, alignAddr, alignLength);
+    int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
+    if (mprotect(alignAddr, alignLength, prot) != 0) {
+        int err = errno;
+        ALOGV("mprotect (%p,%zd,%d) failed: %s",
+            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) {
+        ALOGW("munmap(%p, %d) failed: %s",
+            pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
+    } else {
+        ALOGV("munmap(%p, %d) succeeded", 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;
+            ALOGE("%s: write failed: %s", logMsg, strerror(err));
+            return err;
+        } else if (actual != (ssize_t) count) {
+            ALOGD("%s: partial write (will retry): (%d of %zd)",
+                logMsg, (int) actual, count);
+            buf = (const void*) (((const u1*) buf) + actual);
+        }
+        count -= actual;
+    }
+
+    return 0;
+}
+
+/* See documentation comment in header file. */
+int sysCopyFileToFile(int outFd, int inFd, size_t count)
+{
+    const size_t kBufSize = 32768;
+    unsigned char buf[kBufSize];
+
+    while (count != 0) {
+        size_t getSize = (count > kBufSize) ? kBufSize : count;
+
+        ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize));
+        if (actual != (ssize_t) getSize) {
+            ALOGW("sysCopyFileToFile: copy read failed (%d vs %zd)",
+                (int) actual, getSize);
+            return -1;
+        }
+
+        if (sysWriteFully(outFd, buf, getSize, "sysCopyFileToFile") != 0)
+            return -1;
+
+        count -= getSize;
+    }
+
+    return 0;
+}
diff --git a/libdex/SysUtil.h b/libdex/SysUtil.h
new file mode 100644
index 0000000..c02ec6e
--- /dev/null
+++ b/libdex/SysUtil.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_H_
+#define LIBDEX_SYSUTIL_H_
+
+#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.
+ */
+#ifdef PAGE_SHIFT
+#define SYSTEM_PAGE_SIZE        (1<<PAGE_SHIFT)
+#else
+#define SYSTEM_PAGE_SIZE        4096
+#endif
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+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 */
+};
+
+/*
+ * Copy a map.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src);
+
+/*
+ * 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);
+
+/*
+ * Map part of a file into a shared, read-only memory segment.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+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);
+
+/*
+ * Copy the given number of bytes from one fd to another. Returns
+ * 0 on success, -1 on failure.
+ */
+int sysCopyFileToFile(int outFd, int inFd, size_t count);
+
+#endif  // LIBDEX_SYSUTIL_H_
diff --git a/libdex/ZipArchive.cpp b/libdex/ZipArchive.cpp
new file mode 100644
index 0000000..1cccc3f
--- /dev/null
+++ b/libdex/ZipArchive.cpp
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/*
+ * 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)
+    {
+        ALOGW("Zip: invalid ZipEntry %p (%ld)", 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;
+}
+
+static int mapCentralDirectory0(int fd, const char* debugFileName,
+        ZipArchive* pArchive, off_t fileLength, size_t readAmount, u1* scanBuf)
+{
+    off_t searchStart = fileLength - readAmount;
+
+    if (lseek(fd, searchStart, SEEK_SET) != searchStart) {
+        ALOGW("Zip: seek %ld failed: %s", (long) searchStart, strerror(errno));
+        return -1;
+    }
+    ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount));
+    if (actual != (ssize_t) readAmount) {
+        ALOGW("Zip: read %zd failed: %s", readAmount, strerror(errno));
+        return -1;
+    }
+
+    /*
+     * 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) {
+            ALOGV("+++ Found EOCD at buf+%d", i);
+            break;
+        }
+    }
+    if (i < 0) {
+        ALOGD("Zip: EOCD not found, %s is not zip", debugFileName);
+        return -1;
+    }
+
+    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) {
+        ALOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)",
+            (long) dirOffset, dirSize, (long) eocdOffset);
+        return -1;
+    }
+    if (numEntries == 0) {
+        ALOGW("Zip: empty archive?");
+        return -1;
+    }
+
+    ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d",
+        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)
+    {
+        ALOGW("Zip: cd map failed");
+        return -1;
+    }
+
+    pArchive->mNumEntries = numEntries;
+    pArchive->mDirectoryOffset = dirOffset;
+
+    return 0;
+}
+
+/*
+ * 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)
+{
+    /*
+     * Get and test file length.
+     */
+    off_t fileLength = lseek(fd, 0, SEEK_END);
+    if (fileLength < kEOCDLen) {
+        ALOGV("Zip: length %ld is too small to be zip", (long) fileLength);
+        return -1;
+    }
+
+    /*
+     * 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 (fileLength < off_t(readAmount))
+        readAmount = fileLength;
+
+    u1* scanBuf = (u1*) malloc(readAmount);
+    if (scanBuf == NULL) {
+        return -1;
+    }
+
+    int result = mapCentralDirectory0(fd, debugFileName, pArchive,
+            fileLength, readAmount, scanBuf);
+
+    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) {
+            ALOGW("Zip: missed a central dir sig (at %d)", i);
+            goto bail;
+        }
+        if (ptr + kCDELen > cdPtr + cdLength) {
+            ALOGW("Zip: ran off the end (at %d)", i);
+            goto bail;
+        }
+
+        long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+        if (localHdrOffset >= pArchive->mDirectoryOffset) {
+            ALOGW("Zip: bad LFH offset %ld at entry %d", 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) {
+            ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d",
+                (int) (ptr - cdPtr), cdLength, i);
+            goto bail;
+        }
+    }
+    ALOGV("+++ zip good scan %d entries", 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;
+
+    ALOGV("Opening as zip '%s' %p", fileName, pArchive);
+
+    memset(pArchive, 0, sizeof(ZipArchive));
+
+    fd = open(fileName, O_RDONLY | O_BINARY, 0);
+    if (fd < 0) {
+        err = errno ? errno : -1;
+        ALOGV("Unable to open '%s': %s", 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) {
+        ALOGV("Zip: parsing '%s' failed", 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)
+{
+    ALOGV("Closing archive %p", 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) {
+        ALOGW("Invalid index %d", 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) {
+            ALOGW("Zip: bad local hdr offset in zip");
+            return -1;
+        }
+
+        u1 lfhBuf[kLFHLen];
+        if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+            ALOGW("Zip: failed seeking to lfh at offset %ld", localHdrOffset);
+            return -1;
+        }
+        ssize_t actual =
+            TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf)));
+        if (actual != sizeof(lfhBuf)) {
+            ALOGW("Zip: failed reading lfh from offset %ld", localHdrOffset);
+            return -1;
+        }
+
+        if (get4LE(lfhBuf) != kLFHSignature) {
+            ALOGW("Zip: didn't find signature at start of lfh, offset=%ld",
+                localHdrOffset);
+            return -1;
+        }
+
+        off_t dataOffset = localHdrOffset + kLFHLen
+            + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
+        if (dataOffset >= cdOffset) {
+            ALOGW("Zip: bad data offset %ld in zip", (long) dataOffset);
+            return -1;
+        }
+
+        /* check lengths */
+        if ((off_t)(dataOffset + compLen) > cdOffset) {
+            ALOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)",
+                (long) dataOffset, compLen, (long) cdOffset);
+            return -1;
+        }
+
+        if (method == kCompressStored &&
+            (off_t)(dataOffset + uncompLen) > cdOffset)
+        {
+            ALOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)",
+                (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 outFd, int inFd, 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) {
+            ALOGE("Installed zlib is not compatible with linked version (%s)",
+                ZLIB_VERSION);
+        } else {
+            ALOGW("Call to inflateInit2 failed (zerr=%d)", 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) {
+                ALOGW("Zip: inflate read failed (%d vs %zd)",
+                    (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) {
+            ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
+                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) {
+        ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)",
+            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;
+}
+
+/*
+ * 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) {
+        ALOGW("Zip: extract can't find entry %p", 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) {
+        ALOGW("Zip: lseek to data at %ld failed", (long) dataOffset);
+        goto bail;
+    }
+
+    if (method == kCompressStored) {
+        if (sysCopyFileToFile(fd, pArchive->mFd, uncompLen) != 0)
+            goto bail;
+    } else {
+        if (inflateToFile(fd, pArchive->mFd, 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..df5c49a
--- /dev/null
+++ b/libdex/ZipArchive.h
@@ -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.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#ifndef LIBDEX_ZIPARCHIVE_H_
+#define LIBDEX_ZIPARCHIVE_H_
+
+#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.
+ */
+struct ZipHashEntry {
+    const char*     name;
+    unsigned short  nameLen;
+};
+
+/*
+ * 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.
+ */
+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;
+};
+
+/* 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_H_
diff --git a/libdex/sha1.cpp b/libdex/sha1.cpp
new file mode 100644
index 0000000..15a81cc
--- /dev/null
+++ b/libdex/sha1.cpp
@@ -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;
+union CHAR64LONG16 {
+    unsigned char c[64];
+    unsigned long l[16];
+};
+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..28907de
--- /dev/null
+++ b/libdex/sha1.h
@@ -0,0 +1,20 @@
+/*
+ * See "sha1.cpp" for author info.
+ */
+#ifndef LIBDEX_SHA1_H_
+#define LIBDEX_SHA1_H_
+
+struct SHA1_CTX {
+    unsigned long state[5];
+    unsigned long count[2];
+    unsigned char buffer[64];
+};
+
+#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  // LIBDEX_SHA1_H_
diff --git a/opcode-gen/README.txt b/opcode-gen/README.txt
new file mode 100644
index 0000000..a210842
--- /dev/null
+++ b/opcode-gen/README.txt
@@ -0,0 +1,74 @@
+# 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.
+
+---------------------------------------------------------------------
+Notes on updating the sets of defined opcodes and instruction formats
+---------------------------------------------------------------------
+
+##########
+
+If you want to add, delete, or change opcodes:
+
+* Update the file bytecode.txt, in this directory.
+
+* Run the regen-all script, in this directory. This will regenerate a
+  number of tables, definitions, and declarations in the code, in
+  dalvik/dx, dalvik/libdex, and libcore/dalvik.
+
+* Implement/update the opcode in C in vm/mterp/c/...
+  * Verify new code by running with "dalvik.vm.execution-mode = int:portable"
+    or "-Xint:portable".
+
+* Implement/update the instruction in assembly in vm/mterp/{arm*,x86*}/...
+  * Verify by enabling the assembly (e.g. ARM) handler for that instruction
+    in mterp/config-* and running "int:fast" as above.
+
+* Implement/update the instruction in
+  vm/compiler/codegen/{arm,x86}/CodegenDriver.c.
+
+* Rebuild the interpreter code. See the notes in vm/mterp/ReadMe.txt for
+  details.
+
+* Look in the directory vm/analysis at the files CodeVerify.c,
+  DexVerify.c, and Optimize.c. You may need to update them to account
+  for your changes.
+  * If you change anything here, be sure to try running the system with
+    the verifier enabled (which is in fact the default).
+
+##########
+
+If you want to add, delete, or change instruction formats:
+
+This is a more manual affair than changing opcodes.
+
+* Update the file bytecode.txt, and run regen-all, as per above.
+
+* Update the instruction format list in libdex/InstrUtils.h.
+
+* Update dexDecodeInstruction() in libdex/InstrUtils.c.
+
+* Update dumpInstruction() and its helper code in dexdump/DexDump.c.
+
+* Update the switch inside dvmCompilerMIR2LIR() in
+  vm/compiler/codegen/{arm,x86}/CodegenDriver.c. (There may be other
+  architectures to deal with too.)
+
+##########
+
+Testing your work:
+
+The Dalvik VM tests (in the vm/tests directory) 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.
diff --git a/opcode-gen/bytecode.txt b/opcode-gen/bytecode.txt
new file mode 100644
index 0000000..6b7b9b1
--- /dev/null
+++ b/opcode-gen/bytecode.txt
@@ -0,0 +1,346 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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
+#
+
+# One line per instruction format family. Each line consists of a
+# series of instruction formats that all take (potentially) compatible
+# arguments. The order is the preferred order (most to least
+# preferable) of formats, when more than one opcode could be used for
+# a given instruction's arguments.
+#
+# Note: The family that starts with 12x has a mix of both two- and
+# three- register formats. This is because some of the two-register
+# opcodes effectively take three, with a destination and two sources
+# where the destination and one of the sources have to be the same.
+
+# Regular formats
+format 10t 20t 30t
+format 10x
+format 11n 21s 21h 31i 51l
+format 11x
+format 12x 22x 23x 32x  # See note, above.
+format 21c 31c
+format 21t 31t
+format 22b 22s
+format 22c
+format 22t
+format 35c 3rc
+
+# Optimized formats
+format 00x 
+format 20bc
+format 22cs
+format 35mi
+format 35ms
+format 3rmi
+format 3rms
+
+# One line per opcode. Columns are:
+#   hex for opcode
+#   opcode name
+#   format
+#   has result register; one of:
+#     y
+#     n
+#   index type; one of:
+#     unknown -- used for undefined opcodes and breakpoint
+#     none
+#     varies
+#     type-ref
+#     string-ref
+#     method-ref
+#     field-ref
+#     inline-method
+#     vtable-offset
+#     field-offset
+#   flags; pipe-combined combo of one or more of:
+#     optimized     -- optimized; not to be included in unoptimized dex files
+#     branch        -- might branch to an address
+#     continue      -- might continue to the next address in sequence
+#     switch        -- is a switch
+#     throw         -- might throw an exception
+#     return        -- is a return from method
+#     invoke        -- is a method invoke; this is only used for true
+#                      method invokes and notably *not* vm-implemented
+#                      execute-inline nor the nop-equivalent
+#                      invoke-direct-empty
+
+#
+# Regular opcodes (with a couple holes)
+#
+
+op   00 nop                         10x  n none          continue
+op   01 move                        12x  y none          continue
+op   02 move/from16                 22x  y none          continue
+op   03 move/16                     32x  y none          continue
+op   04 move-wide                   12x  y none          continue
+op   05 move-wide/from16            22x  y none          continue
+op   06 move-wide/16                32x  y none          continue
+op   07 move-object                 12x  y none          continue
+op   08 move-object/from16          22x  y none          continue
+op   09 move-object/16              32x  y none          continue
+op   0a move-result                 11x  y none          continue
+op   0b move-result-wide            11x  y none          continue
+op   0c move-result-object          11x  y none          continue
+op   0d move-exception              11x  y none          continue
+op   0e return-void                 10x  n none          return
+op   0f return                      11x  n none          return
+op   10 return-wide                 11x  n none          return
+op   11 return-object               11x  n none          return
+op   12 const/4                     11n  y none          continue
+op   13 const/16                    21s  y none          continue
+op   14 const                       31i  y none          continue
+op   15 const/high16                21h  y none          continue
+op   16 const-wide/16               21s  y none          continue
+op   17 const-wide/32               31i  y none          continue
+op   18 const-wide                  51l  y none          continue
+op   19 const-wide/high16           21h  y none          continue
+op   1a const-string                21c  y string-ref    continue|throw
+op   1b const-string/jumbo          31c  y string-ref    continue|throw
+op   1c const-class                 21c  y type-ref      continue|throw
+op   1d monitor-enter               11x  n none          continue|throw
+op   1e monitor-exit                11x  n none          continue|throw
+op   1f check-cast                  21c  y type-ref      continue|throw
+op   20 instance-of                 22c  y type-ref      continue|throw
+op   21 array-length                12x  y none          continue|throw
+op   22 new-instance                21c  y type-ref      continue|throw
+op   23 new-array                   22c  y type-ref      continue|throw
+op   24 filled-new-array            35c  n type-ref      continue|throw
+op   25 filled-new-array/range      3rc  n type-ref      continue|throw
+op   26 fill-array-data             31t  n none          continue
+op   27 throw                       11x  n none          throw
+op   28 goto                        10t  n none          branch
+op   29 goto/16                     20t  n none          branch
+op   2a goto/32                     30t  n none          branch
+op   2b packed-switch               31t  n none          continue|switch
+op   2c sparse-switch               31t  n none          continue|switch
+op   2d cmpl-float                  23x  y none          continue
+op   2e cmpg-float                  23x  y none          continue
+op   2f cmpl-double                 23x  y none          continue
+op   30 cmpg-double                 23x  y none          continue
+op   31 cmp-long                    23x  y none          continue
+op   32 if-eq                       22t  n none          continue|branch
+op   33 if-ne                       22t  n none          continue|branch
+op   34 if-lt                       22t  n none          continue|branch
+op   35 if-ge                       22t  n none          continue|branch
+op   36 if-gt                       22t  n none          continue|branch
+op   37 if-le                       22t  n none          continue|branch
+op   38 if-eqz                      21t  n none          continue|branch
+op   39 if-nez                      21t  n none          continue|branch
+op   3a if-ltz                      21t  n none          continue|branch
+op   3b if-gez                      21t  n none          continue|branch
+op   3c if-gtz                      21t  n none          continue|branch
+op   3d if-lez                      21t  n none          continue|branch
+# unused: op 3e..43
+op   44 aget                        23x  y none          continue|throw
+op   45 aget-wide                   23x  y none          continue|throw
+op   46 aget-object                 23x  y none          continue|throw
+op   47 aget-boolean                23x  y none          continue|throw
+op   48 aget-byte                   23x  y none          continue|throw
+op   49 aget-char                   23x  y none          continue|throw
+op   4a aget-short                  23x  y none          continue|throw
+op   4b aput                        23x  n none          continue|throw
+op   4c aput-wide                   23x  n none          continue|throw
+op   4d aput-object                 23x  n none          continue|throw
+op   4e aput-boolean                23x  n none          continue|throw
+op   4f aput-byte                   23x  n none          continue|throw
+op   50 aput-char                   23x  n none          continue|throw
+op   51 aput-short                  23x  n none          continue|throw
+op   52 iget                        22c  y field-ref     continue|throw
+op   53 iget-wide                   22c  y field-ref     continue|throw
+op   54 iget-object                 22c  y field-ref     continue|throw
+op   55 iget-boolean                22c  y field-ref     continue|throw
+op   56 iget-byte                   22c  y field-ref     continue|throw
+op   57 iget-char                   22c  y field-ref     continue|throw
+op   58 iget-short                  22c  y field-ref     continue|throw
+op   59 iput                        22c  n field-ref     continue|throw
+op   5a iput-wide                   22c  n field-ref     continue|throw
+op   5b iput-object                 22c  n field-ref     continue|throw
+op   5c iput-boolean                22c  n field-ref     continue|throw
+op   5d iput-byte                   22c  n field-ref     continue|throw
+op   5e iput-char                   22c  n field-ref     continue|throw
+op   5f iput-short                  22c  n field-ref     continue|throw
+op   60 sget                        21c  y field-ref     continue|throw
+op   61 sget-wide                   21c  y field-ref     continue|throw
+op   62 sget-object                 21c  y field-ref     continue|throw
+op   63 sget-boolean                21c  y field-ref     continue|throw
+op   64 sget-byte                   21c  y field-ref     continue|throw
+op   65 sget-char                   21c  y field-ref     continue|throw
+op   66 sget-short                  21c  y field-ref     continue|throw
+op   67 sput                        21c  n field-ref     continue|throw
+op   68 sput-wide                   21c  n field-ref     continue|throw
+op   69 sput-object                 21c  n field-ref     continue|throw
+op   6a sput-boolean                21c  n field-ref     continue|throw
+op   6b sput-byte                   21c  n field-ref     continue|throw
+op   6c sput-char                   21c  n field-ref     continue|throw
+op   6d sput-short                  21c  n field-ref     continue|throw
+op   6e invoke-virtual              35c  n method-ref    continue|throw|invoke
+op   6f invoke-super                35c  n method-ref    continue|throw|invoke
+op   70 invoke-direct               35c  n method-ref    continue|throw|invoke
+op   71 invoke-static               35c  n method-ref    continue|throw|invoke
+op   72 invoke-interface            35c  n method-ref    continue|throw|invoke
+# unused: op 73
+op   74 invoke-virtual/range        3rc  n method-ref    continue|throw|invoke
+op   75 invoke-super/range          3rc  n method-ref    continue|throw|invoke
+op   76 invoke-direct/range         3rc  n method-ref    continue|throw|invoke
+op   77 invoke-static/range         3rc  n method-ref    continue|throw|invoke
+op   78 invoke-interface/range      3rc  n method-ref    continue|throw|invoke
+# unused: op 79..7a
+op   7b neg-int                     12x  y none          continue
+op   7c not-int                     12x  y none          continue
+op   7d neg-long                    12x  y none          continue
+op   7e not-long                    12x  y none          continue
+op   7f neg-float                   12x  y none          continue
+op   80 neg-double                  12x  y none          continue
+op   81 int-to-long                 12x  y none          continue
+op   82 int-to-float                12x  y none          continue
+op   83 int-to-double               12x  y none          continue
+op   84 long-to-int                 12x  y none          continue
+op   85 long-to-float               12x  y none          continue
+op   86 long-to-double              12x  y none          continue
+op   87 float-to-int                12x  y none          continue
+op   88 float-to-long               12x  y none          continue
+op   89 float-to-double             12x  y none          continue
+op   8a double-to-int               12x  y none          continue
+op   8b double-to-long              12x  y none          continue
+op   8c double-to-float             12x  y none          continue
+op   8d int-to-byte                 12x  y none          continue
+op   8e int-to-char                 12x  y none          continue
+op   8f int-to-short                12x  y none          continue
+op   90 add-int                     23x  y none          continue
+op   91 sub-int                     23x  y none          continue
+op   92 mul-int                     23x  y none          continue
+op   93 div-int                     23x  y none          continue|throw
+op   94 rem-int                     23x  y none          continue|throw
+op   95 and-int                     23x  y none          continue
+op   96 or-int                      23x  y none          continue
+op   97 xor-int                     23x  y none          continue
+op   98 shl-int                     23x  y none          continue
+op   99 shr-int                     23x  y none          continue
+op   9a ushr-int                    23x  y none          continue
+op   9b add-long                    23x  y none          continue
+op   9c sub-long                    23x  y none          continue
+op   9d mul-long                    23x  y none          continue
+op   9e div-long                    23x  y none          continue|throw
+op   9f rem-long                    23x  y none          continue|throw
+op   a0 and-long                    23x  y none          continue
+op   a1 or-long                     23x  y none          continue
+op   a2 xor-long                    23x  y none          continue
+op   a3 shl-long                    23x  y none          continue
+op   a4 shr-long                    23x  y none          continue
+op   a5 ushr-long                   23x  y none          continue
+op   a6 add-float                   23x  y none          continue
+op   a7 sub-float                   23x  y none          continue
+op   a8 mul-float                   23x  y none          continue
+op   a9 div-float                   23x  y none          continue
+op   aa rem-float                   23x  y none          continue
+op   ab add-double                  23x  y none          continue
+op   ac sub-double                  23x  y none          continue
+op   ad mul-double                  23x  y none          continue
+op   ae div-double                  23x  y none          continue
+op   af rem-double                  23x  y none          continue
+op   b0 add-int/2addr               12x  y none          continue
+op   b1 sub-int/2addr               12x  y none          continue
+op   b2 mul-int/2addr               12x  y none          continue
+op   b3 div-int/2addr               12x  y none          continue|throw
+op   b4 rem-int/2addr               12x  y none          continue|throw
+op   b5 and-int/2addr               12x  y none          continue
+op   b6 or-int/2addr                12x  y none          continue
+op   b7 xor-int/2addr               12x  y none          continue
+op   b8 shl-int/2addr               12x  y none          continue
+op   b9 shr-int/2addr               12x  y none          continue
+op   ba ushr-int/2addr              12x  y none          continue
+op   bb add-long/2addr              12x  y none          continue
+op   bc sub-long/2addr              12x  y none          continue
+op   bd mul-long/2addr              12x  y none          continue
+op   be div-long/2addr              12x  y none          continue|throw
+op   bf rem-long/2addr              12x  y none          continue|throw
+op   c0 and-long/2addr              12x  y none          continue
+op   c1 or-long/2addr               12x  y none          continue
+op   c2 xor-long/2addr              12x  y none          continue
+op   c3 shl-long/2addr              12x  y none          continue
+op   c4 shr-long/2addr              12x  y none          continue
+op   c5 ushr-long/2addr             12x  y none          continue
+op   c6 add-float/2addr             12x  y none          continue
+op   c7 sub-float/2addr             12x  y none          continue
+op   c8 mul-float/2addr             12x  y none          continue
+op   c9 div-float/2addr             12x  y none          continue
+op   ca rem-float/2addr             12x  y none          continue
+op   cb add-double/2addr            12x  y none          continue
+op   cc sub-double/2addr            12x  y none          continue
+op   cd mul-double/2addr            12x  y none          continue
+op   ce div-double/2addr            12x  y none          continue
+op   cf rem-double/2addr            12x  y none          continue
+op   d0 add-int/lit16               22s  y none          continue
+op   d1 rsub-int                    22s  y none          continue
+op   d2 mul-int/lit16               22s  y none          continue
+op   d3 div-int/lit16               22s  y none          continue|throw
+op   d4 rem-int/lit16               22s  y none          continue|throw
+op   d5 and-int/lit16               22s  y none          continue
+op   d6 or-int/lit16                22s  y none          continue
+op   d7 xor-int/lit16               22s  y none          continue
+op   d8 add-int/lit8                22b  y none          continue
+op   d9 rsub-int/lit8               22b  y none          continue
+op   da mul-int/lit8                22b  y none          continue
+op   db div-int/lit8                22b  y none          continue|throw
+op   dc rem-int/lit8                22b  y none          continue|throw
+op   dd and-int/lit8                22b  y none          continue
+op   de or-int/lit8                 22b  y none          continue
+op   df xor-int/lit8                22b  y none          continue
+op   e0 shl-int/lit8                22b  y none          continue
+op   e1 shr-int/lit8                22b  y none          continue
+op   e2 ushr-int/lit8               22b  y none          continue
+
+#
+# Optimized opcodes (not valid in an unoptimized dex file)
+#
+
+op   e3 +iget-volatile              22c  y field-ref     optimized|continue|throw
+op   e4 +iput-volatile              22c  n field-ref     optimized|continue|throw
+op   e5 +sget-volatile              21c  y field-ref     optimized|continue|throw
+op   e6 +sput-volatile              21c  n field-ref     optimized|continue|throw
+op   e7 +iget-object-volatile       22c  y field-ref     optimized|continue|throw
+op   e8 +iget-wide-volatile         22c  y field-ref     optimized|continue|throw
+op   e9 +iput-wide-volatile         22c  n field-ref     optimized|continue|throw
+op   ea +sget-wide-volatile         21c  y field-ref     optimized|continue|throw
+op   eb +sput-wide-volatile         21c  n field-ref     optimized|continue|throw
+
+# Technically "breakpoint" isn't really an optimized opcode, but it
+# fits the label in terms of not being valid in regular dex files.
+op   ec ^breakpoint                 00x  n unknown       optimized
+
+op   ed ^throw-verification-error   20bc n varies        optimized|throw
+op   ee +execute-inline             35mi n inline-method optimized|continue|throw
+op   ef +execute-inline/range       3rmi n inline-method optimized|continue|throw
+
+op   f0 +invoke-object-init/range   35c  n method-ref    optimized|continue|throw|invoke
+op   f1 +return-void-barrier        10x  n none          optimized|return
+op   f2 +iget-quick                 22cs y field-offset  optimized|continue|throw
+op   f3 +iget-wide-quick            22cs y field-offset  optimized|continue|throw
+op   f4 +iget-object-quick          22cs y field-offset  optimized|continue|throw
+op   f5 +iput-quick                 22cs n field-offset  optimized|continue|throw
+op   f6 +iput-wide-quick            22cs n field-offset  optimized|continue|throw
+op   f7 +iput-object-quick          22cs n field-offset  optimized|continue|throw
+op   f8 +invoke-virtual-quick       35ms n vtable-offset optimized|continue|throw|invoke
+op   f9 +invoke-virtual-quick/range 3rms n vtable-offset optimized|continue|throw|invoke
+op   fa +invoke-super-quick         35ms n vtable-offset optimized|continue|throw|invoke
+op   fb +invoke-super-quick/range   3rms n vtable-offset optimized|continue|throw|invoke
+op   fc +iput-object-volatile       22c  n field-ref     optimized|continue|throw
+op   fd +sget-object-volatile       21c  y field-ref     optimized|continue|throw
+op   fe +sput-object-volatile       21c  n field-ref     optimized|continue|throw
+
+# unused: op ff
diff --git a/opcode-gen/opcode-gen b/opcode-gen/opcode-gen
new file mode 100755
index 0000000..b8e4397
--- /dev/null
+++ b/opcode-gen/opcode-gen
@@ -0,0 +1,67 @@
+#!/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>
+#
+# This script uses the file bytecodes.txt (in this directory) to
+# generate code inside the given <file>, based on the directives found
+# in that file. Refer to those files to understand what's being
+# generated. (Look for comments that say "BEGIN(name)" where "name" is
+# one of the "emission" directives defined in opcode-gen.awk in this
+# directory.)
+
+file="$1"
+tmpfile="/tmp/$$.txt"
+
+echo "processing `basename $1`" 1>&2
+
+if [ "x$1" = "x" ]; then
+    echo "must specify a file" 1>&2
+    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" -f "$progdir/opcode-gen.awk" \
+    "$file" > "$tmpfile"
+
+if [ "$?" = "0" ]; then
+    cp "$tmpfile" "$file"
+    rm "$tmpfile"
+else
+    echo "error running awk" 1>&2
+    exit 1
+fi
diff --git a/opcode-gen/opcode-gen.awk b/opcode-gen/opcode-gen.awk
new file mode 100644
index 0000000..e26a60c
--- /dev/null
+++ b/opcode-gen/opcode-gen.awk
@@ -0,0 +1,538 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Awk helper script for opcode-gen.
+#
+
+#
+# Initialization.
+#
+
+BEGIN {
+    MAX_OPCODE = 65535;
+    MAX_PACKED_OPCODE = 511;
+    MAX_PACKED_OPCODE = 255; # TODO: Not for long!
+    initIndexTypes();
+    initFlags();
+    if (readBytecodes()) exit 1;
+    deriveOpcodeChains();
+    createPackedTables();
+    consumeUntil = "";
+    emission = "";
+}
+
+#
+# General control (must appear above directive handlers).
+#
+
+# Clear out the preexisting output within a directive section.
+consumeUntil != "" {
+    if (index($0, consumeUntil) != 0) {
+        consumeUntil = "";
+        print;
+    }
+
+    next;
+}
+
+# Detect directives.
+/BEGIN\([a-z-]*\)/ {
+    i = match($0, /BEGIN\([a-z-]*\)/);
+    emission = substr($0, i + 6, RLENGTH - 7);
+    consumeUntil = "END(" emission ")";
+    emissionHandled = 0;
+}
+
+# Most lines just get copied from the source as-is, including the start
+# comment for directives.
+{
+    print;
+}
+
+#
+# Handlers for all of the directives.
+#
+
+emission == "opcodes" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i) || isOptimized(i)) continue;
+        printf("    public static final int %s = 0x%s;\n",
+               constName[i], hex[i]);
+    }
+}
+
+emission == "first-opcodes" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i) || isOptimized(i)) continue;
+        if (isFirst[i] == "true") {
+            printf("    //     Opcodes.%s\n", constName[i]);
+        }
+    }
+}
+
+emission == "dops" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i) || isOptimized(i)) continue;
+
+        nextOp = nextOpcode[i];
+        nextOp = (nextOp == -1) ? "NO_NEXT" : constName[nextOp];
+
+        printf("    public static final Dop %s =\n" \
+               "        new Dop(Opcodes.%s, Opcodes.%s,\n" \
+               "            Opcodes.%s, Form%s.THE_ONE, %s);\n\n",
+               constName[i], constName[i], family[i], nextOp, format[i],
+               hasResult[i]);
+    }
+}
+
+emission == "opcode-info-defs" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i) || isOptimized(i)) continue;
+
+        itype = toupper(indexType[i]);
+        gsub(/-/, "_", itype);
+
+        printf("    public static final Info %s =\n" \
+               "        new Info(Opcodes.%s, \"%s\",\n" \
+               "            InstructionCodec.FORMAT_%s, IndexType.%s);\n\n", \
+               constName[i], constName[i], name[i], toupper(format[i]), itype);
+    }
+}
+
+emission == "dops-init" || emission == "opcode-info-init" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i) || isOptimized(i)) continue;
+        printf("        set(%s);\n", constName[i]);
+    }
+}
+
+emission == "libcore-opcodes" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i) || isOptimized(i)) continue;
+        printf("    int OP_%-28s = 0x%04x;\n", constName[i], i);
+    }
+}
+
+emission == "libcore-maximum-values" {
+    emissionHandled = 1;
+
+    printf("        MAXIMUM_VALUE = %d;\n", MAX_OPCODE);
+    printf("        MAXIMUM_PACKED_VALUE = %d;\n", MAX_PACKED_OPCODE);
+}
+
+emission == "libdex-maximum-values" {
+    emissionHandled = 1;
+
+    printf("#define kMaxOpcodeValue 0x%x\n", MAX_OPCODE);
+    printf("#define kNumPackedOpcodes 0x%x\n", MAX_PACKED_OPCODE + 1);
+}
+
+emission == "libdex-opcode-enum" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        printf("    OP_%-28s = 0x%02x,\n", packedConstName[i], i);
+    }
+}
+
+emission == "libdex-goto-table" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        content = sprintf("        H(OP_%s),", packedConstName[i]);
+        printf("%-78s\\\n", content);
+    }
+}
+
+emission == "libdex-opcode-names" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        printf("    \"%s\",\n", packedName[i]);
+    }
+}
+
+emission == "libdex-widths" {
+    emissionHandled = 1;
+
+    col = 1;
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        value = sprintf("%d,", packedWidth[i]);
+        col = colPrint(value, (i == MAX_PACKED_OPCODE), col, 16, 2, "    ");
+    }
+}
+
+emission == "libdex-flags" {
+    emissionHandled = 1;
+
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        value = flagsToC(packedFlags[i]);
+        printf("    %s,\n", value);
+    }
+}
+
+emission == "libdex-formats" {
+    emissionHandled = 1;
+
+    col = 1;
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        value = sprintf("kFmt%s,", packedFormat[i]);
+        col = colPrint(value, (i == MAX_PACKED_OPCODE), col, 7, 9, "    ");
+    }
+}
+
+emission == "libdex-index-types" {
+    emissionHandled = 1;
+
+    col = 1;
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        value = sprintf("%s,", indexTypeValues[packedIndexType[i]]);
+        col = colPrint(value, (i == MAX_PACKED_OPCODE), col, 3, 19, "    ");
+    }
+}
+
+# Handle the end of directive processing (must appear after the directive
+# clauses).
+emission != "" {
+    if (!emissionHandled) {
+        printf("WARNING: unknown tag \"%s\"\n", emission) >"/dev/stderr";
+        consumeUntil = "";
+    }
+
+    emission = "";
+}
+
+#
+# Helper functions.
+#
+
+# Helper to print out an element in a multi-column fashion. It returns
+# the (one-based) column number that the next element will be printed
+# in.
+function colPrint(value, isLast, col, numCols, colWidth, linePrefix) {
+    isLast = (isLast || (col == numCols));
+    printf("%s%-*s%s",
+        (col == 1) ? linePrefix : " ",
+        isLast ? 1 : colWidth, value,
+        isLast ? "\n" : "");
+
+    return (col % numCols) + 1;
+}
+
+# Read the bytecode description file.
+function readBytecodes(i, parts, line, cmd, status, count) {
+    # locals: parts, line, cmd, status, count
+    for (;;) {
+        # Read a line.
+        status = getline line <bytecodeFile;
+        if (status == 0) break;
+        if (status < 0) {
+            print "trouble reading bytecode file";
+            exit 1;
+        }
+
+        # Clean up the line and extract the command.
+        gsub(/  */, " ", line);
+        sub(/ *#.*$/, "", line);
+        sub(/ $/, "", line);
+        sub(/^ /, "", line);
+        count = split(line, parts);
+        if (count == 0) continue; # Blank or comment line.
+        cmd = parts[1];
+        sub(/^[a-z][a-z]* */, "", line); # Remove the command from line.
+
+        if (cmd == "op") {
+            status = defineOpcode(line);
+        } else if (cmd == "format") {
+            status = defineFormat(line);
+        } else {
+            status = -1;
+        }
+
+        if (status != 0) {
+            printf("syntax error on line: %s\n", line) >"/dev/stderr";
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+# Define an opcode.
+function defineOpcode(line, count, parts, idx) {
+    # locals: count, parts, idx
+    count = split(line, parts);
+    if (count != 6)  return -1;
+    idx = parseHex(parts[1]);
+    if (idx < 0) return -1;
+
+    # Extract directly specified values from the line.
+    hex[idx] = parts[1];
+    name[idx] = parts[2];
+    format[idx] = parts[3];
+    hasResult[idx] = (parts[4] == "n") ? "false" : "true";
+    indexType[idx] = parts[5];
+    flags[idx] = parts[6];
+
+    # Calculate derived values.
+
+    constName[idx] = toupper(name[idx]);
+    gsub("[/-]", "_", constName[idx]);   # Dash and slash become underscore.
+    gsub("[+^]", "", constName[idx]);    # Plus and caret are removed.
+    split(name[idx], parts, "/");
+
+    family[idx] = toupper(parts[1]);
+    gsub("-", "_", family[idx]);         # Dash becomes underscore.
+    gsub("[+^]", "", family[idx]);       # Plus and caret are removed.
+
+    split(format[idx], parts, "");       # Width is the first format char.
+    width[idx] = parts[1];
+
+    # This association is used when computing "next" opcodes.
+    familyFormat[family[idx],format[idx]] = idx;
+
+    # Verify values.
+
+    if (nextFormat[format[idx]] == "") {
+        printf("unknown format: %s\n", format[idx]) >"/dev/stderr";
+        return 1;
+    }
+
+    if (indexTypeValues[indexType[idx]] == "") {
+        printf("unknown index type: %s\n", indexType[idx]) >"/dev/stderr";
+        return 1;
+    }
+
+    if (flagsToC(flags[idx]) == "") {
+        printf("bogus flags: %s\n", flags[idx]) >"/dev/stderr";
+        return 1;
+    }
+
+    return 0;
+}
+
+# Define a format family.
+function defineFormat(line, count, parts, i) {
+    # locals: count, parts, i
+    count = split(line, parts);
+    if (count < 1)  return -1;
+    formats[parts[1]] = line;
+
+    parts[count + 1] = "none";
+    for (i = 1; i <= count; i++) {
+        nextFormat[parts[i]] = parts[i + 1];
+    }
+
+    return 0;
+}
+
+# Produce the nextOpcode and isFirst arrays. The former indicates, for
+# each opcode, which one should be tried next when doing instruction
+# fitting. The latter indicates which opcodes are at the head of an
+# instruction fitting chain.
+function deriveOpcodeChains(i, op) {
+    # locals: i, op
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i)) continue;
+        isFirst[i] = "true";
+    }
+
+    for (i = 0; i <= MAX_OPCODE; i++) {
+        if (isUnused(i)) continue;
+        op = findNextOpcode(i);
+        nextOpcode[i] = op;
+        if (op != -1) {
+            isFirst[op] = "false";
+        }
+    }
+}
+
+# Given an opcode by index, find the next opcode in the same family
+# (that is, with the same base name) to try when matching instructions
+# to opcodes. This simply walks the nextFormat chain looking for a
+# match. This returns the index of the matching opcode or -1 if there
+# is none.
+function findNextOpcode(idx, fam, fmt, result) {
+    # locals: fam, fmt, result
+    fam = family[idx];
+    fmt = format[idx];
+
+    # Not every opcode has a version with every possible format, so
+    # we have to iterate down the chain until we find one or run out of
+    # formats to try.
+    for (fmt = nextFormat[format[idx]]; fmt != "none"; fmt = nextFormat[fmt]) {
+        result = familyFormat[fam,fmt];
+        if (result != "") {
+            return result;
+        }
+    }
+
+    return -1;
+}
+
+# Construct the tables of info indexed by packed opcode. The packed opcode
+# values are in the range 0-0x1ff, whereas the unpacked opcodes sparsely
+# span the range 0-0xffff.
+function createPackedTables(i, op) {
+    # locals: i, op
+    for (i = 0; i <= MAX_PACKED_OPCODE; i++) {
+        op = unpackOpcode(i);
+        if (isUnused(op)) {
+            packedName[i]      = unusedName(op);
+            packedConstName[i] = unusedConstName(op);
+            packedFormat[i]    = "00x";
+            packedFlags[i]     = 0;
+            packedWidth[i]     = 0;
+            packedIndexType[i] = "unknown";
+        } else {
+            packedName[i]      = name[op];
+            packedConstName[i] = constName[op];
+            packedFormat[i]    = format[op];
+            packedFlags[i]     = flags[op];
+            packedWidth[i]     = width[op];
+            packedIndexType[i] = indexType[op];
+        }
+    }
+}
+
+# Given a packed opcode, returns the raw (unpacked) opcode value.
+function unpackOpcode(idx) {
+    # Note: This must be the inverse of the corresponding code in
+    # libdex/DexOpcodes.h.
+    if (idx <= 255) {
+        return idx;
+    } else {
+        idx -= 256;
+        return (idx * 256) + 255;
+    }
+}
+
+# Returns the "unused" name of the given opcode (by index).
+# That is, this is the human-oriented name to use for an opcode
+# definition in cases
+# where the opcode isn't used.
+function unusedName(idx) {
+    if (idx <= 255) {
+         return sprintf("unused-%02x", idx);
+    } else {
+         return sprintf("unused-%04x", idx);
+    }
+}
+
+# Returns the "unused" constant name of the given opcode (by index).
+# That is, this is the name to use for a constant definition in cases
+# where the opcode isn't used.
+function unusedConstName(idx) {
+    if (idx <= 255) {
+         return toupper(sprintf("UNUSED_%02x", idx));
+    } else {
+         return toupper(sprintf("UNUSED_%04x", idx));
+    }
+}
+
+# Convert a hex value to an int.
+function parseHex(hex, result, chars, count, c, i) {
+    # locals: result, chars, count, c, i
+    hex = tolower(hex);
+    count = split(hex, chars, "");
+    result = 0;
+    for (i = 1; i <= count; i++) {
+        c = index("0123456789abcdef", chars[i]);
+        if (c == 0) {
+            printf("bogus hex value: %s\n", hex) >"/dev/stderr";
+            return -1;
+        }
+        result = (result * 16) + c - 1;
+    }
+    return result;
+}
+
+# Initialize the indexTypes data.
+function initIndexTypes() {
+    indexTypeValues["unknown"]       = "kIndexUnknown";
+    indexTypeValues["none"]          = "kIndexNone";
+    indexTypeValues["varies"]        = "kIndexVaries";
+    indexTypeValues["type-ref"]      = "kIndexTypeRef";
+    indexTypeValues["string-ref"]    = "kIndexStringRef";
+    indexTypeValues["method-ref"]    = "kIndexMethodRef";
+    indexTypeValues["field-ref"]     = "kIndexFieldRef";
+    indexTypeValues["inline-method"] = "kIndexInlineMethod";
+    indexTypeValues["vtable-offset"] = "kIndexVtableOffset";
+    indexTypeValues["field-offset"]  = "kIndexFieldOffset";
+}
+
+# Initialize the flags data.
+function initFlags() {
+    flagValues["branch"]        = "kInstrCanBranch";
+    flagValues["continue"]      = "kInstrCanContinue";
+    flagValues["switch"]        = "kInstrCanSwitch";
+    flagValues["throw"]         = "kInstrCanThrow";
+    flagValues["return"]        = "kInstrCanReturn";
+    flagValues["invoke"]        = "kInstrInvoke";
+    flagValues["optimized"]     = "0"; # Not represented in C output
+    flagValues["0"]             = "0";
+}
+
+# Translate the given flags into the equivalent C expression. Returns
+# "" on error.
+function flagsToC(f, parts, result, i) {
+    # locals: parts, result, i
+    count = split(f, parts, /\|/); # Split input at pipe characters.
+    result = "0";
+
+    for (i = 1; i <= count; i++) {
+        f = flagValues[parts[i]];
+        if (f == "") {
+            printf("bogus flag: %s\n", f) >"/dev/stderr";
+            return ""; # Bogus flag name.
+        } else if (f == "0") {
+            # Nothing to append for this case.
+        } else if (result == "0") {
+            result = f;
+        } else {
+            result = result "|" f;
+        }
+    }
+
+    return result;
+}
+
+# Returns true if the given opcode (by index) is an "optimized" opcode.
+function isOptimized(idx, parts, f) {
+    # locals: parts, f
+    split(flags[idx], parts, /\|/); # Split flags[idx] at pipes.
+    for (f in parts) {
+        if (parts[f] == "optimized") return 1;
+    }
+    return 0;
+}
+
+# Returns true if there is no definition for the given opcode (by index).
+function isUnused(idx) {
+    return (name[idx] == "");
+}
diff --git a/opcode-gen/regen-all b/opcode-gen/regen-all
new file mode 100755
index 0000000..161cda9
--- /dev/null
+++ b/opcode-gen/regen-all
@@ -0,0 +1,50 @@
+#!/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
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+
+# Be in the parent of the progdir when running the rest of the script.
+cd ".."
+
+${progdir}/opcode-gen dx/src/com/android/dx/dex/code/Dops.java
+${progdir}/opcode-gen dx/src/com/android/dx/dex/code/RopToDop.java
+${progdir}/opcode-gen dx/src/com/android/dx/io/OpcodeInfo.java
+${progdir}/opcode-gen dx/src/com/android/dx/io/Opcodes.java
+${progdir}/opcode-gen libdex/DexOpcodes.cpp
+${progdir}/opcode-gen libdex/DexOpcodes.h
+${progdir}/opcode-gen libdex/InstrUtils.cpp
+
+# It's a minor shame that these files live in a different top-level project.
+# So it goes.
+${progdir}/opcode-gen \
+    ../libcore/dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java
+${progdir}/opcode-gen \
+    ../libcore/dalvik/src/main/java/dalvik/bytecode/Opcodes.java
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/build b/tests/004-annotations/build
new file mode 100644
index 0000000..c147cb2
--- /dev/null
+++ b/tests/004-annotations/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+
+# android.test.anno.MissingAnnotation is available at compile time...
+${JAVAC} -d classes `find src -name '*.java'`
+
+# ...but not at run time.
+rm classes/android/test/anno/MissingAnnotation.class
+dx -JXmx256m --debug --dex --output=test.jar classes
diff --git a/tests/004-annotations/expected.txt b/tests/004-annotations/expected.txt
new file mode 100644
index 0000000..dc13a98
--- /dev/null
+++ b/tests/004-annotations/expected.txt
@@ -0,0 +1,103 @@
+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 $Proxy13
+    --> nombre is 'fubar'
+
+SimplyNoted.get(AnnoSimpleType) = @android.test.anno.AnnoSimpleType()
+SubNoted.get(AnnoSimpleType) = @android.test.anno.AnnoSimpleType()
+
+Package annotations:
+      @android.test.anno.AnnoSimplePackage()
+        interface android.test.anno.AnnoSimplePackage
+Package declared annotations:
+      @android.test.anno.AnnoSimplePackage()
+        interface android.test.anno.AnnoSimplePackage
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/AnnoSimplePackage1.java b/tests/004-annotations/src/android/test/AnnoSimplePackage1.java
new file mode 100644
index 0000000..629cb7d
--- /dev/null
+++ b/tests/004-annotations/src/android/test/AnnoSimplePackage1.java
@@ -0,0 +1,8 @@
+package android.test;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.PACKAGE)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimplePackage1 {}
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/MissingAnnotation.java b/tests/004-annotations/src/android/test/anno/MissingAnnotation.java
new file mode 100644
index 0000000..56da15a
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/MissingAnnotation.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(RUNTIME) public @interface MissingAnnotation {
+}
+
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..b757465
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/SimplyNoted.java
@@ -0,0 +1,36 @@
+package android.test.anno;
+
+@AnnoSimpleType
+@AnnoSimpleType2
+@AnnoSimpleTypeInvis
+@MissingAnnotation
+public class SimplyNoted {
+    @AnnoSimpleField
+    @MissingAnnotation
+    public int mFoo;
+
+    @AnnoSimpleField
+    @MissingAnnotation
+    public static int mOneFoo;
+
+    @AnnoSimpleConstructor
+    @MissingAnnotation
+    SimplyNoted() {
+        mFoo = 0;
+    }
+
+    @AnnoSimpleConstructor
+    @MissingAnnotation
+    SimplyNoted(@AnnoSimpleParameter int xyzzy) {
+        mFoo = xyzzy;
+    }
+
+    @AnnoSimpleMethod
+    @MissingAnnotation
+    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..4eabb12
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/TestAnnotations.java
@@ -0,0 +1,186 @@
+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);
+
+        System.out.println();
+
+        // Package annotations aren't inherited, so getAnnotations and getDeclaredAnnotations are
+        // the same.
+        System.out.println("Package annotations:");
+        printAnnotationArray("    ", TestAnnotations.class.getPackage().getAnnotations());
+        System.out.println("Package declared annotations:");
+        printAnnotationArray("    ", TestAnnotations.class.getPackage().getDeclaredAnnotations());
+    }
+}
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/004-annotations/src/android/test/package-info.java b/tests/004-annotations/src/android/test/package-info.java
new file mode 100644
index 0000000..e188cda
--- /dev/null
+++ b/tests/004-annotations/src/android/test/package-info.java
@@ -0,0 +1,2 @@
+@AnnoSimplePackage1
+package android.test;
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..724786e
--- /dev/null
+++ b/tests/011-array-copy/expected.txt
@@ -0,0 +1,15 @@
+string -> object
+object -> string
+object -> string (modified)
+caught ArrayStoreException (expected)
+copy: 0,0,0: [0, 1, 2, 3, 4, 5, 6, 7]
+copy: 0,0,8: [0, 1, 2, 3, 4, 5, 6, 7]
+copy: 0,2,4: [0, 1, 0, 1, 2, 3, 6, 7]
+copy: 2,0,4: [2, 3, 4, 5, 4, 5, 6, 7]
+copy: 1,3,4: [0, 1, 2, 1, 2, 3, 4, 7]
+copy: 3,1,4: [0, 3, 4, 5, 6, 5, 6, 7]
+copy: 3,1,5: [0, 3, 4, 5, 6, 7, 6, 7]
+copy: 1,3,5: [0, 1, 2, 1, 2, 3, 4, 5]
+copy: 0,3,5: [0, 1, 2, 0, 1, 2, 3, 4]
+copy: 3,0,5: [3, 4, 5, 6, 7, 5, 6, 7]
+copy: 0,5,1: [0, 1, 2, 3, 4, 0, 6, 7]
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..505d8b0
--- /dev/null
+++ b/tests/011-array-copy/src/Main.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.Arrays;
+
+/**
+ * System.arraycopy cases
+ */
+public class Main {
+    public static void main(String args[]) {
+        testObjectCopy();
+        testOverlappingMoves();
+    }
+
+    public static void testObjectCopy() {
+        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)");
+        }
+    }
+
+    static final int ARRAY_SIZE = 8;
+
+    static void initByteArray(byte[] array) {
+        for (int i = 0; i < ARRAY_SIZE; i++) {
+            array[i] = (byte) i;
+        }
+    }
+    static void initShortArray(short[] array) {
+        for (int i = 0; i < ARRAY_SIZE; i++) {
+            array[i] = (short) i;
+        }
+    }
+    static void initIntArray(int[] array) {
+        for (int i = 0; i < ARRAY_SIZE; i++) {
+            array[i] = (int) i;
+        }
+    }
+    static void initLongArray(long[] array) {
+        for (int i = 0; i < ARRAY_SIZE; i++) {
+            array[i] = (long) i;
+        }
+    }
+
+    /*
+     * Perform an array copy operation on primitive arrays with different
+     * element widths.
+     */
+    static void makeCopies(int srcPos, int dstPos, int length) {
+        byte[] byteArray = new byte[ARRAY_SIZE];
+        short[] shortArray = new short[ARRAY_SIZE];
+        int[] intArray = new int[ARRAY_SIZE];
+        long[] longArray = new long[ARRAY_SIZE];
+
+        initByteArray(byteArray);
+        initShortArray(shortArray);
+        initIntArray(intArray);
+        initLongArray(longArray);
+
+        System.arraycopy(byteArray, srcPos, byteArray, dstPos, length);
+        System.arraycopy(shortArray, srcPos, shortArray, dstPos, length);
+        System.arraycopy(intArray, srcPos, intArray, dstPos, length);
+        System.arraycopy(longArray, srcPos, longArray, dstPos, length);
+
+        for (int i = 0; i < ARRAY_SIZE; i++) {
+            if (intArray[i] != byteArray[i]) {
+                System.out.println("mismatch int vs byte at " + i + " : " +
+                    Arrays.toString(byteArray));
+                break;
+            } else if (intArray[i] != shortArray[i]) {
+                System.out.println("mismatch int vs short at " + i + " : " +
+                    Arrays.toString(shortArray));
+                break;
+            } else if (intArray[i] != longArray[i]) {
+                System.out.println("mismatch int vs long at " + i + " : " +
+                    Arrays.toString(longArray));
+                break;
+            }
+        }
+
+        System.out.println("copy: " + srcPos + "," + dstPos + "," + length +
+            ": " + Arrays.toString(intArray));
+    }
+
+    public static void testOverlappingMoves() {
+        /* do nothing */
+        makeCopies(0, 0, 0);
+
+        /* do more nothing */
+        makeCopies(0, 0, ARRAY_SIZE);
+
+        /* copy forward, even alignment */
+        makeCopies(0, 2, 4);
+
+        /* copy backward, even alignment */
+        makeCopies(2, 0, 4);
+
+        /* copy forward, odd alignment */
+        makeCopies(1, 3, 4);
+
+        /* copy backward, odd alignment */
+        makeCopies(3, 1, 4);
+
+        /* copy backward, odd length */
+        makeCopies(3, 1, 5);
+
+        /* copy forward, odd length */
+        makeCopies(1, 3, 5);
+
+        /* copy forward, mixed alignment */
+        makeCopies(0, 3, 5);
+
+        /* copy backward, mixed alignment */
+        makeCopies(3, 0, 5);
+
+        /* copy forward, mixed alignment, trivial length */
+        makeCopies(0, 5, 1);
+    }
+}
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..3b1f8da
--- /dev/null
+++ b/tests/029-assert/src/Main.java
@@ -0,0 +1,19 @@
+// 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");
+        }
+
+        // exercise this code path
+        ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
+    }
+}
diff --git a/tests/030-bad-finalizer/expected.txt b/tests/030-bad-finalizer/expected.txt
new file mode 100644
index 0000000..88b1896
--- /dev/null
+++ b/tests/030-bad-finalizer/expected.txt
@@ -0,0 +1,7 @@
+Constructed object.
+Nulled. Requestion gc.
+Finalizer started and spinning...
+Finalizer done spinning.
+Finalizer sleeping forever now.
+Requesting another GC.
+Requesting another GC.
diff --git a/tests/030-bad-finalizer/info.txt b/tests/030-bad-finalizer/info.txt
new file mode 100644
index 0000000..26f4993
--- /dev/null
+++ b/tests/030-bad-finalizer/info.txt
@@ -0,0 +1,15 @@
+The finalizer for this class never finishes.  Dalvik is expected to detect
+this situation and abort the VM (so you will likely see a stacktrace like
+the following in the log output).
+
+java.util.concurrent.TimeoutException
+	at java.lang.VMThread.sleep(Native Method)
+	at java.lang.Thread.sleep(Thread.java:1031)
+	at java.lang.Thread.sleep(Thread.java:1013)
+	at BadFinalizer.snooze(BadFinalizer.java:9)
+	at BadFinalizer.finalize(BadFinalizer.java:29)
+	at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:182)
+	at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:168)
+	at java.lang.Thread.run(Thread.java:856)
+Calling exit(2)
+
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..db80a87
--- /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(4000);
+            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..afa3416
--- /dev/null
+++ b/tests/031-class-attributes/expected.txt
@@ -0,0 +1,211 @@
+***** class ClassAttrs:
+  name: ClassAttrs
+  canonical: ClassAttrs
+  simple: ClassAttrs
+  genericSignature: null
+  super: class java.lang.Object
+  genericSuperclass: 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
+  genericInterfaces: [0]
+  typeParameters: [0]
+***** class OtherClass:
+  name: OtherClass
+  canonical: OtherClass
+  simple: OtherClass
+  genericSignature: null
+  super: class java.lang.Object
+  genericSuperclass: 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
+  genericInterfaces: [0]
+  typeParameters: [0]
+***** class otherpackage.OtherPackageClass:
+  name: otherpackage.OtherPackageClass
+  canonical: otherpackage.OtherPackageClass
+  simple: OtherPackageClass
+  genericSignature: null
+  super: class java.lang.Object
+  genericSuperclass: 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
+  genericInterfaces: [0]
+  typeParameters: [0]
+***** class ClassAttrs$1InnerNamed:
+  name: ClassAttrs$1InnerNamed
+  canonical: null
+  simple: InnerNamed
+  genericSignature: null
+  super: class java.lang.Object
+  genericSuperclass: 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
+  genericInterfaces: [0]
+  typeParameters: [0]
+***** class ClassAttrs$1ConsInnerNamed:
+  name: ClassAttrs$1ConsInnerNamed
+  canonical: null
+  simple: ConsInnerNamed
+  genericSignature: null
+  super: class java.lang.Object
+  genericSuperclass: 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
+  genericInterfaces: [0]
+  typeParameters: [0]
+***** class ClassAttrs$1:
+  name: ClassAttrs$1
+  canonical: null
+  simple: 
+  genericSignature: null
+  super: class OtherClass
+  genericSuperclass: 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
+  genericInterfaces: [0]
+  typeParameters: [0]
+***** class ClassAttrs$MemberClass:
+  name: ClassAttrs$MemberClass
+  canonical: ClassAttrs.MemberClass
+  simple: MemberClass
+  genericSignature: <XYZ:Ljava/lang/Object;>Ljava/lang/Object;
+  super: class java.lang.Object
+  genericSuperclass: 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
+  genericInterfaces: [0]
+  typeParameters: [1] XYZ
+***** class FancyClass:
+  name: FancyClass
+  canonical: FancyClass
+  simple: FancyClass
+  genericSignature: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/HashMap<TK;TV;>;Ljava/util/Map<TK;TV;>;
+  super: class java.util.HashMap
+  genericSuperclass: java.util.HashMap<K, V>
+  declaring: null
+  enclosing: null
+  enclosingCon: null
+  enclosingMeth: null
+  modifiers: 1
+  package: null
+  declaredClasses: [0]
+  member classes: [2] class java.util.AbstractMap$SimpleEntry, class java.util.AbstractMap$SimpleImmutableEntry
+  isAnnotation: false
+  isAnonymous: false
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: false
+  isMemberClass: false
+  isPrimitive: false
+  isSynthetic: false
+  genericInterfaces: [1] java.util.Map<K, V>
+  typeParameters: [2] K, V
+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..d93a925
--- /dev/null
+++ b/tests/031-class-attributes/src/ClassAttrs.java
@@ -0,0 +1,208 @@
+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;
+import java.lang.reflect.TypeVariable;
+
+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);
+
+        /* fancy */
+        printClassAttrs(FancyClass.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) {
+        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());
+        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());
+
+        System.out.println("  genericInterfaces: "
+            + stringifyTypeArray(clazz.getGenericInterfaces()));
+
+        TypeVariable<Class<?>>[] typeParameters = clazz.getTypeParameters();
+        System.out.println("  typeParameters: "
+            + stringifyTypeArray(typeParameters));
+    }
+
+    /*
+     * 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/FancyClass.java b/tests/031-class-attributes/src/FancyClass.java
new file mode 100644
index 0000000..a58b6a6
--- /dev/null
+++ b/tests/031-class-attributes/src/FancyClass.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class FancyClass<K,V> extends HashMap<K,V> implements Map<K,V> {
+}
+
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..182d0da
--- /dev/null
+++ b/tests/033-class-init-deadlock/expected.txt
@@ -0,0 +1,7 @@
+Deadlock test starting.
+A initializing...
+B initializing...
+Deadlock test interrupting 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..1e3f897
--- /dev/null
+++ b/tests/033-class-init-deadlock/src/Main.java
@@ -0,0 +1,53 @@
+// 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();
+        // Give thread1 a chance to start before starting thread2.
+        try { Thread.sleep(1000); } catch (InterruptedException ie) { }
+        thread2.start();
+
+        try { Thread.sleep(6000); } catch (InterruptedException ie) { }
+
+        System.out.println("Deadlock test interrupting 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..19f86f4
--- /dev/null
+++ b/tests/034-call-null/expected.txt
@@ -0,0 +1 @@
+done
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..8fe88bd
--- /dev/null
+++ b/tests/034-call-null/src/Main.java
@@ -0,0 +1,19 @@
+// 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;
+        try {
+            instance.doStuff();
+            throw new RuntimeException("fail");
+        } catch (NullPointerException npe) { }
+
+        System.out.println("done");
+    }
+}
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..29c1e42
--- /dev/null
+++ b/tests/038-inner-null/expected.txt
@@ -0,0 +1,2 @@
+new Special()
+done
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..1239248
--- /dev/null
+++ b/tests/038-inner-null/src/Main.java
@@ -0,0 +1,31 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Main {
+    public static void main(String[] args) {
+        Special special = new Special();
+        special.callInner();
+        System.out.println("done");
+    }
+
+    public static class Special {
+        Blort mBlort = null;
+
+        Special() {
+            System.out.println("new Special()");
+        }
+
+        public void callInner() {
+            try {
+                mBlort.repaint();
+                throw new RuntimeException("fail");
+            } catch (NullPointerException npe) {}
+        }
+    }
+
+    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..bb1b80c
--- /dev/null
+++ b/tests/042-new-instance/expected.txt
@@ -0,0 +1,9 @@
+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
+Cons got expected PackageAccess2 complaint
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..e43c5a5
--- /dev/null
+++ b/tests/042-new-instance/src/Main.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+/**
+ * Test instance creation.
+ */
+public class Main {
+    private static boolean FULL_ACCESS_CHECKS = false;  // b/5861201
+
+    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) {
+            // constructor isn't public
+            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();
+        }
+
+        // should fail
+        try {
+            Class c = Class.forName("otherpackage.PackageAccess2");
+            Constructor cons = c.getConstructor((Class[]) null);
+            if (!FULL_ACCESS_CHECKS) { throw new IllegalAccessException(); }
+            Object obj = cons.newInstance();
+            System.err.println("ERROR: Cons PackageAccess2 succeeded unexpectedly");
+        } catch (IllegalAccessException iae) {
+            // constructor is public, but class has package scope
+            System.out.println("Cons got expected PackageAccess2 complaint");
+        } catch (Exception ex) {
+            System.err.println("Cons got unexpected PackageAccess2 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..f4541f2
--- /dev/null
+++ b/tests/042-new-instance/src/otherpackage/PackageAccess.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.
+ */
+
+package otherpackage;
+
+class PackageAccess {
+    /*package*/ PackageAccess() {
+        System.out.println("created PackageAccess");
+    }
+}
+
+class PackageAccess2 {
+    public PackageAccess2() {
+        System.out.println("created PackageAccess2");
+    }
+}
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..4be26cf
--- /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 java.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..5990b34
--- /dev/null
+++ b/tests/045-reflect-array/expected.txt
@@ -0,0 +1,9 @@
+ReflectArrayTest.testSingleInt passed
+ReflectArrayTest.testSingleChar passed
+ReflectArrayTest.testSingleShort passed
+ReflectArrayTest.testSingleLong 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..36f8ac3
--- /dev/null
+++ b/tests/045-reflect-array/src/Main.java
@@ -0,0 +1,258 @@
+/*
+ * 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();
+        testSingleChar();
+        testSingleShort();
+        testSingleLong();
+        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) { }
+        try {
+            Array.setInt(intArray, 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");
+        }
+
+        Integer x123 = Integer.valueOf(123);
+        Integer x456 = Integer.valueOf(456);
+
+        Array.set(intArray, 0, x123);
+        Array.set(intArray, 1, x456);
+        if (!Array.get(intArray, 0).equals(x123) || !Array.get(intArray, 1).equals(x456)) {
+            throw new RuntimeException("bad 123 or 456");
+        }
+
+        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 testSingleChar() {
+        Object charArray = Array.newInstance(Character.TYPE, 7);
+
+        char[] array = (char[]) charArray;
+        array[0] = '0';
+        array[1] = 'W';
+        array[2] = '2';
+        array[3] = '3';
+        array[4] = 'X';
+        array[5] = '5';
+        array[6] = '6';
+        Array.setChar(charArray, 1, '1');
+        Array.setChar(charArray, 4, '4');
+        try {
+            Array.setShort(charArray, 3, (short) 'Y');
+            throw new RuntimeException("shouldn't allow short in char array");
+        } catch (IllegalArgumentException iae) {}
+        try {
+            Array.setInt(charArray, 5, 'Z');
+            throw new RuntimeException("shouldn't allow int in char array");
+        } catch (IllegalArgumentException iae) {}
+
+        try {
+            for (int i = 0; i < array.length; i++) {
+                if (Array.getInt(charArray, i) - '0' != i) {
+                    throw new RuntimeException("mismatch: " + i + " is " + array[i]);
+                }
+            }
+
+            if (Array.getInt(charArray, 4) != '4') {
+                throw new RuntimeException("load should have worked");
+            }
+        } catch (IllegalArgumentException iae) {
+            System.err.println("Couldn't Array.getInt(charArray)");
+        }
+        try {
+            Array.getByte(charArray, 2);
+            throw new RuntimeException("shouldn't allow read of char as byte");
+        } catch (IllegalArgumentException iae) {}
+
+        Array.setChar(charArray, 3, (char) 0xffff);
+        try {
+            if (Array.getInt(charArray, 3) != 0xffff) {
+                throw new RuntimeException("char got sign-extended? "
+                    + Array.getInt(charArray, 3));
+            }
+        } catch (IllegalArgumentException iae) {
+            System.err.println("Couldn't Array.getInt(charArray)");
+        }
+
+        System.out.println("ReflectArrayTest.testSingleChar passed");
+    }
+
+    static void testSingleShort() {
+        Object shortArray = Array.newInstance(Short.TYPE, 1);
+        Array.setShort(shortArray, 0, (short) -1);
+        if (Array.getInt(shortArray, 0) != -1) {
+            throw new RuntimeException("short didn't get sign-extended");
+        }
+
+        Short box = (Short) Array.get(shortArray, 0);
+
+        System.out.println("ReflectArrayTest.testSingleShort passed");
+    }
+
+    static void testSingleLong() {
+        Object longArray = Array.newInstance(Long.TYPE, 2);
+        Array.setInt(longArray, 0, 123);
+        Array.setLong(longArray, 1, 0x1122334455667788L);
+        try {
+            Array.getInt(longArray, 0);
+            throw new RuntimeException("shouldn't allow read of long as int");
+        } catch (IllegalArgumentException iae) {}
+
+        long[] array = (long[]) longArray;
+        if (array[0] != 123 || array[1] != 0x1122334455667788L) {
+            throw new RuntimeException();
+        }
+
+        float f = Array.getFloat(longArray, 0);
+        if (f < 122.9 || f > 123.1) {
+            throw new RuntimeException("long-as-float failed - " + f);
+        }
+        if (Array.getLong(longArray, 1) != 0x1122334455667788L) {
+            throw new RuntimeException("long1 failed");
+        }
+
+        System.out.println("ReflectArrayTest.testSingleLong 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");
+        try {
+            Array.set(strArray, 2, "entry two");
+            throw new RuntimeException("store should have failed");
+        } catch (ArrayIndexOutOfBoundsException abe) { }
+
+        //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");
+        }
+
+        try {
+            Array.set(strArray, 0, new Integer(5));
+            throw new RuntimeException("store of Integer should have failed");
+        } catch (IllegalArgumentException iae) {}
+        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..309b076
--- /dev/null
+++ b/tests/046-reflect/expected.txt
@@ -0,0 +1,102 @@
+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
+  cantTouchThis is now 88
+cons modifiers=1
+SuperTarget constructor ()V
+Target constructor (IF)V : ii=7 ff=3.3333
+myMethod (I)I
+ arg=17 anInt=7
+ReflectTest done!
+public method
+checkType invoking null
+checkType got expected exception
+got methods
+NoisyInitUser is initializing
+NoisyInit is initializing
+
+generic field: java.util.List<java.lang.String>
+generic method fancyMethod params='[1] java.util.ArrayList<java.lang.String>' ret='java.util.Map<java.lang.Integer, java.lang.String>'
+generic ctor Main params='[1] java.util.ArrayList<java.lang.Integer>'
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..e2a3929
--- /dev/null
+++ b/tests/046-reflect/src/Main.java
@@ -0,0 +1,555 @@
+// Copyright 2006 The Android Open Source Project
+
+import java.lang.reflect.*;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Reflection test.
+ */
+public class Main {
+    private static boolean FULL_ACCESS_CHECKS = false;  // b/5861201
+    public Main() {}
+    public Main(ArrayList<Integer> stuff) {}
+
+    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 static void checkAccess() {
+        try {
+            Class target = otherpackage.Other.class;
+            Object instance = new otherpackage.Other();
+            Method meth;
+
+            meth = target.getMethod("publicMethod", (Class[]) null);
+            meth.invoke(instance);
+
+            try {
+                meth = target.getMethod("packageMethod", (Class[]) null);
+                System.err.println("succeeded on package-scope method");
+            } catch (NoSuchMethodException nsme) {
+                // good
+            }
+
+
+            instance = otherpackage.Other.getInnerClassInstance();
+            target = instance.getClass();
+            meth = target.getMethod("innerMethod", (Class[]) null);
+            try {
+                if (!FULL_ACCESS_CHECKS) { throw new IllegalAccessException(); }
+                meth.invoke(instance);
+                System.err.println("inner-method invoke unexpectedly worked");
+            } catch (IllegalAccessException iae) {
+                // good
+            }
+
+            Field field = target.getField("innerField");
+            try {
+                int x = field.getInt(instance);
+                if (!FULL_ACCESS_CHECKS) { throw new IllegalAccessException(); }
+                System.err.println("field get unexpectedly worked: " + x);
+            } catch (IllegalAccessException iae) {
+                // good
+            }
+        } catch (Exception ex) {
+            System.out.println("----- unexpected exception -----");
+            ex.printStackTrace();
+        }
+    }
+
+    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");
+            } catch (IllegalAccessException iae) {
+                System.out.println("  got expected set-final failure");
+            }
+            intVal = field.getInt(instance);
+            System.out.println("  cantTouchThis is now " + intVal);
+
+            field.setAccessible(true);
+            field.setInt(instance, 87);     // exercise int version
+            field.set(instance, 88);        // exercise Object version
+            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();
+
+        System.out.println("");
+    }
+
+
+    /*
+     * Test some generic type stuff.
+     */
+    public List<String> dummy;
+    public Map<Integer,String> fancyMethod(ArrayList<String> blah) { return null; }
+    public static void checkGeneric() {
+        Field field;
+        try {
+            field = Main.class.getField("dummy");
+        } catch (NoSuchFieldException nsfe) {
+            throw new RuntimeException(nsfe);
+        }
+        Type listType = field.getGenericType();
+        System.out.println("generic field: " + listType);
+
+        Method method;
+        try {
+            method = Main.class.getMethod("fancyMethod",
+                new Class[] { ArrayList.class });
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+        Type[] parmTypes = method.getGenericParameterTypes();
+        Type ret = method.getGenericReturnType();
+        System.out.println("generic method " + method.getName() + " params='"
+            + stringifyTypeArray(parmTypes) + "' ret='" + ret + "'");
+
+        Constructor ctor;
+        try {
+            ctor = Main.class.getConstructor(new Class[] { ArrayList.class });
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+        parmTypes = ctor.getGenericParameterTypes();
+        System.out.println("generic ctor " + ctor.getName() + " params='"
+            + stringifyTypeArray(parmTypes) + "'");
+    }
+
+    /*
+     * 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();
+    }
+
+
+    public static void main(String[] args) {
+        Main test = new Main();
+        test.run();
+
+        checkAccess();
+        checkType();
+        checkInit();
+        checkGeneric();
+    }
+}
+
+
+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/046-reflect/src/otherpackage/Other.java b/tests/046-reflect/src/otherpackage/Other.java
new file mode 100644
index 0000000..702ab6d
--- /dev/null
+++ b/tests/046-reflect/src/otherpackage/Other.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package otherpackage;
+
+public class Other {
+    public void publicMethod() {
+        System.out.println("public method");
+    }
+    void packageMethod() {
+        System.out.println("package method");
+    }
+
+    public static InnerOther getInnerClassInstance() {
+        return new InnerOther();
+    }
+
+    private static class InnerOther {
+        public void innerMethod() {
+            System.out.println("inner method");
+        }
+
+        public int innerField = 7;
+    }
+}
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..418f5f4
--- /dev/null
+++ b/tests/050-sync-test/src/Main.java
@@ -0,0 +1,169 @@
+// 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();
+
+        try {
+            Thread.sleep(100);
+        } catch (InterruptedException ie) {
+            System.out.println("INTERRUPT!");
+            ie.printStackTrace();
+        }
+
+        two.start();
+
+        //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());
+
+        synchronized (mSyncable) {
+            for (int i = 0; i < 10; i++) {
+                output(mNumber);
+            }
+
+            System.out.print("Final result: ");
+            System.out.println(mCount);
+        }
+    }
+
+    void output(int num) {
+        int 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..8e6b153
--- /dev/null
+++ b/tests/051-thread/expected.txt
@@ -0,0 +1,8 @@
+Initializing System.out...
+Thread count: 512
+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..ea587af
--- /dev/null
+++ b/tests/051-thread/src/Main.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+
+/**
+ * Test some basic thread stuff.
+ */
+public class Main {
+    public static void main(String[] args) throws Exception {
+        System.out.println("Initializing System.out...");
+
+        MyThread[] threads = new MyThread[512];
+        for (int i = 0; i < 512; i++) {
+            threads[i] = new MyThread();
+        }
+
+        for (MyThread thread : threads) {
+            thread.start();
+        }
+        for (MyThread thread : threads) {
+            thread.join();
+        }
+
+        System.out.println("Thread count: " + MyThread.mCount);
+
+        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 {
+        static int mCount = 0;
+        public void run() {
+            synchronized (MyThread.class) {
+                ++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..ceb6bc4
--- /dev/null
+++ b/tests/055-enum-performance/expected.txt
@@ -0,0 +1,12 @@
+FOUR
+ONE
+FOURTEEN
+NINE
+FIVE
+TWELVE
+SamePackagePublicEnum
+basis: performed 10000 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..43f45f1
--- /dev/null
+++ b/tests/055-enum-performance/src/Main.java
@@ -0,0 +1,195 @@
+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(500);
+        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 * 10)) {
+            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 final String[] BASIS_COMPARE_ARRAY = {
+        "ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT",
+        "NINE", "TEN", "ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN", "FIFTEEN",
+        "SIXTEEN", "SEVENTEEN", "EIGHTEEN", "NINETEEN"
+    };
+
+    static public int basis(int iters) {
+        for (int i = iters; i > 0; i--) {
+            basisValueOf("ZERO");
+            basisValueOf("ONE");
+            basisValueOf("TWO");
+            basisValueOf("THREE");
+            basisValueOf("FOUR");
+            basisValueOf("FIVE");
+            basisValueOf("SIX");
+            basisValueOf("SEVEN");
+            basisValueOf("EIGHT");
+            basisValueOf("NINE");
+            basisValueOf("TEN");
+            basisValueOf("ELEVEN");
+            basisValueOf("TWELVE");
+            basisValueOf("THIRTEEN");
+            basisValueOf("FOURTEEN");
+            basisValueOf("FIFTEEN");
+            basisValueOf("SIXTEEN");
+            basisValueOf("SEVENTEEN");
+            basisValueOf("EIGHTEEN");
+            basisValueOf("NINETEEN");
+        }
+
+        return iters * 20;
+    }
+
+    static String basisValueOf(String key) {
+        for (String s : BASIS_COMPARE_ARRAY) {
+            if (s.equals(key)) {
+                return s;
+            }
+        }
+        throw new IllegalArgumentException();
+    }
+
+    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/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..ca87629
--- /dev/null
+++ b/tests/061-out-of-memory/expected.txt
@@ -0,0 +1,7 @@
+tests beginning
+Got expected huge-array OOM
+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..c812c81
--- /dev/null
+++ b/tests/061-out-of-memory/src/Main.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.Arrays;
+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");
+        testHugeArray();
+        testOomeLarge();
+        testOomeSmall();
+        System.out.println("tests succeeded");
+    }
+
+    private static void testHugeArray() {
+        try {
+            final int COUNT = 32768*32768 + 4;
+            int[] tooBig = new int[COUNT];
+
+            Arrays.fill(tooBig, 0xdd);
+        } catch (OutOfMemoryError oom) {
+            System.out.println("Got expected huge-array OOM");
+        }
+    }
+
+    private static void testOomeLarge() {
+        System.out.println("testOomeLarge beginning");
+
+        Boolean sawEx = false;
+        byte[] a;
+
+        try {
+            // Just shy of the typical max heap size so that it will actually
+            // try to allocate it instead of short-circuiting.
+            a = new byte[(int) Runtime.getRuntime().maxMemory() - 32];
+        } 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 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 < Runtime.getRuntime().maxMemory() / 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..68bf878
--- /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("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..d04083a
--- /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..ddb45af
--- /dev/null
+++ b/tests/070-nio-buffer/expected.txt
@@ -0,0 +1,6 @@
+Direct byte buffer has array: true
+Got expected buffer overflow exception
+Got expected out-of-bounds exception
+Got expected buffer overflow exception
+00fbfb2ec03000001234567840490fd01122334455667788000000000000000100000000
+ccfb2efb30c0cccc78563412d00f494088776655443322110100000000000000cccccccc
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..a7433b8
--- /dev/null
+++ b/tests/070-nio-buffer/src/Main.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+
+public class Main {
+    public static void main(String[] args) {
+        ByteBuffer buf = ByteBuffer.allocateDirect(16);
+        System.out.println("Direct byte buffer has array: " + buf.hasArray());
+
+        intFloatTest();
+        basicShortTest();
+        primTest();
+    }
+
+    /*
+     * 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);
+    }
+
+    /*
+     * Exercise all "view buffer" classes, in both byte orders.
+     */
+    public static void primTest() {
+        ByteBuffer directBuf = ByteBuffer.allocateDirect(36);
+        directBuf.order(ByteOrder.BIG_ENDIAN);
+        storeValues(directBuf);
+
+        for (int i = 0; i < 36; i++) {
+            directBuf.put(i, (byte) 0xcc);
+        }
+
+        directBuf.order(ByteOrder.LITTLE_ENDIAN);
+        storeValues(directBuf);
+    }
+
+    static void storeValues(ByteBuffer directBuf) {
+        directBuf.position(0);
+        ShortBuffer shortBuf = directBuf.asShortBuffer();
+        CharBuffer charBuf = directBuf.asCharBuffer();
+        IntBuffer intBuf = directBuf.asIntBuffer();
+        FloatBuffer floatBuf = directBuf.asFloatBuffer();
+        LongBuffer longBuf = directBuf.asLongBuffer();
+        DoubleBuffer doubleBuf = directBuf.asDoubleBuffer();
+
+        final byte byteValue = -5;
+        final short shortValue = -1234;
+        final char charValue = 49200;
+        final int intValue = 0x12345678;
+        final float floatValue = 3.14159f;
+        final long longValue = 0x1122334455667788L;
+        final double doubleValue = Double.MIN_VALUE;
+
+        if (directBuf.put(1, byteValue).get(1) != byteValue) {
+            throw new RuntimeException("byte get/store failed");
+        }
+        if (shortBuf.put(1, shortValue).get(1) != shortValue) {
+            throw new RuntimeException("short get/store failed");
+        }
+        if (charBuf.put(2, charValue).get(2) != charValue) {
+            throw new RuntimeException("char get/store failed");
+        }
+        if (intBuf.put(2, intValue).get(2) != intValue) {
+            throw new RuntimeException("int get/store failed");
+        }
+        if (floatBuf.put(3, floatValue).get(3) != floatValue) {
+            throw new RuntimeException("float get/store failed");
+        }
+        if (longBuf.put(2, longValue).get(2) != longValue) {
+            throw new RuntimeException("long get/store failed");
+        }
+        if (doubleBuf.put(3, doubleValue).get(3) != doubleValue) {
+            throw new RuntimeException("double get/store failed");
+        }
+
+        directBuf.position(0);
+        char[] outBuf = new char[directBuf.limit() * 2];
+        for (int i = 0; i < directBuf.limit(); i++) {
+            byte b = directBuf.get();
+            outBuf[i*2] = hexChar((byte) ((b >> 4) & 0x0f));
+            outBuf[i*2+1] = hexChar((byte) (b & 0x0f));
+        }
+        System.out.println(new String(outBuf));
+    }
+
+    static char hexChar(byte b) {
+        if (b < 10) {
+            return (char) ('0' + b);
+        } else {
+            return (char) ('a' + b - 10);
+        }
+    }
+}
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..7f9f14c
--- /dev/null
+++ b/tests/083-jit-regressions/expected.txt
@@ -0,0 +1,6 @@
+b2296099 passes
+b2302318 passes
+b2487514 passes
+b5884080 passes
+longDivTest passes
+longModTest passes
diff --git a/tests/083-jit-regressions/info.txt b/tests/083-jit-regressions/info.txt
new file mode 100644
index 0000000..00c24ee
--- /dev/null
+++ b/tests/083-jit-regressions/info.txt
@@ -0,0 +1,11 @@
+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
+5884080 ICS JIT regression in nested loop formation
diff --git a/tests/083-jit-regressions/src/Main.java b/tests/083-jit-regressions/src/Main.java
new file mode 100644
index 0000000..68bfa37
--- /dev/null
+++ b/tests/083-jit-regressions/src/Main.java
@@ -0,0 +1,157 @@
+/*
+ * 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();
+        b5884080Test();
+        zeroTest();
+    }
+
+    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)");
+        }
+    }
+
+    static void b5884080Test() {
+        int vA = 1;
+
+        int l = 0;
+        do {
+            int k = 0;
+            do
+                vA += 1;
+            while(++k < 100);
+        } while (++l < 1000);
+        if (vA == 100001) {
+            System.out.println("b5884080 passes");
+        }
+        else {
+            System.out.println("b5884080 fails: vA is " + vA +
+                               " (expecting 100001)");
+        }
+    }
+
+    static void zeroTest() throws Exception {
+        ZeroTests zt = new ZeroTests();
+        try {
+            zt.longDivTest();
+        } catch (Throwable th) {
+            th.printStackTrace();
+        }
+        try {
+            zt.longModTest();
+        } catch (Throwable th) {
+            th.printStackTrace();
+        }
+    }
+}
+
+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/083-jit-regressions/src/ZeroTests.java b/tests/083-jit-regressions/src/ZeroTests.java
new file mode 100644
index 0000000..57ca151
--- /dev/null
+++ b/tests/083-jit-regressions/src/ZeroTests.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Random;
+
+/**
+ * Tests long division by zero for both / and %.
+ */
+public class ZeroTests {
+    public void longDivTest() throws Exception {
+      longTest("longDivTest", true);
+    }
+
+    public void longModTest() throws Exception {
+      longTest("longModTest", false);
+    }
+
+    private static void longTest(String name, boolean divide) throws Exception {
+      // Warm up JIT.
+      for (int i = 0; i < 10000; ++i) {
+        try {
+          // We won't JIT code that hasn't completed successfully, so rhs can't be 0 here!
+          if (divide) {
+            longDiv(1, 1);
+          } else {
+            longMod(1, 1);
+          }
+        } catch (ArithmeticException expected) {
+          throw new AssertionError(name + " threw during warmup");
+        }
+      }
+
+      // Boom?
+      int catchCount = 0;
+      for (int i = 0; i < 10000; ++i) {
+        try {
+          if (divide) {
+            longDiv(1, 0);
+          } else {
+            longMod(1, 0);
+          }
+          throw new AssertionError(name + " failed to throw: " + i);
+        } catch (ArithmeticException expected) {
+          ++catchCount;
+        }
+      }
+      if (catchCount != 10000) throw new AssertionError(name + " failed: " + catchCount);
+
+      System.out.println(name + " passes");
+    }
+
+    private static long longDiv(long lhs, long rhs) {
+      return lhs / rhs;
+    }
+
+    private static long longMod(long lhs, long rhs) {
+      return lhs % rhs;
+    }
+}
diff --git a/tests/084-class-init/expected.txt b/tests/084-class-init/expected.txt
new file mode 100644
index 0000000..68a2b38
--- /dev/null
+++ b/tests/084-class-init/expected.txt
@@ -0,0 +1,9 @@
+Got expected EIIE for FIELD0
+Got expected NCDFE for FIELD0
+Got expected NCDFE for FIELD1
+Got expected 'hello!' from Exploder
+SlowInit static block pre-sleep
+SlowInit static block post-sleep
+MethodThread message
+Fields (child thread): 111222333444
+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/Exploder.java b/tests/084-class-init/src/Exploder.java
new file mode 100644
index 0000000..911e5fe
--- /dev/null
+++ b/tests/084-class-init/src/Exploder.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Throws an Error rather than an exception from its class initializer.
+ */
+public class Exploder {
+    public static final Object FIELD = new AssertThrower();
+    static class AssertThrower {
+        AssertThrower() {
+            throw new AssertionError("hello!");
+        }
+    }
+}
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..29681ce
--- /dev/null
+++ b/tests/084-class-init/src/Main.java
@@ -0,0 +1,112 @@
+/*
+ * 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");
+        }
+
+        try {
+            System.out.println(Exploder.FIELD);
+            System.err.println("Load of FIELD succeeded unexpectedly");
+        } catch (AssertionError expected) {
+            System.out.println("Got expected '" + expected.getMessage() + "' from Exploder");
+        }
+    }
+
+    static void checkTiming() {
+        FieldThread fieldThread = new FieldThread();
+        MethodThread methodThread = new MethodThread();
+
+        fieldThread.start();
+        methodThread.start();
+
+        /* start class init */
+        IntHolder zero = SlowInit.FIELD0;
+
+        /* wait for children to complete */
+        try {
+            fieldThread.join();
+            methodThread.join();
+        } catch (InterruptedException ie) {
+            System.err.println(ie);
+        }
+
+        /* 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 SlowInit's <clinit> to start */
+            Main.sleep(1000);
+
+            /* collect fields; should delay until class init completes */
+            int field0, field1, field2, field3;
+            field0 = SlowInit.FIELD0.getValue();
+            field1 = SlowInit.FIELD1.getValue();
+            field2 = SlowInit.FIELD2.getValue();
+            field3 = SlowInit.FIELD3.getValue();
+
+            /* let MethodThread print first */
+            Main.sleep(5000);
+            System.out.println("Fields (child thread): " +
+                field0 + field1 + field2 + field3);
+        }
+    }
+
+    static class MethodThread extends Thread {
+        public void run() {
+            /* allow SlowInit's <clinit> to start */
+            Main.sleep(1000);
+
+            /* 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..f0c6919
--- /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(4000);
+        printMsg("SlowInit static block post-sleep");
+        FIELD2.setValue(333);
+        FIELD3.setValue(444);
+    };
+}
diff --git a/tests/085-old-style-inner-class/build b/tests/085-old-style-inner-class/build
new file mode 100644
index 0000000..dc6f3bb
--- /dev/null
+++ b/tests/085-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/085-old-style-inner-class/expected.txt b/tests/085-old-style-inner-class/expected.txt
new file mode 100644
index 0000000..63a0076
--- /dev/null
+++ b/tests/085-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/085-old-style-inner-class/info.txt b/tests/085-old-style-inner-class/info.txt
new file mode 100644
index 0000000..9e5c4f9
--- /dev/null
+++ b/tests/085-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/085-old-style-inner-class/src/Main.java b/tests/085-old-style-inner-class/src/Main.java
new file mode 100644
index 0000000..c9a5b72
--- /dev/null
+++ b/tests/085-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..82237c5
--- /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..11fb2d3
--- /dev/null
+++ b/tests/087-gc-after-link/src/Main.java
@@ -0,0 +1,176 @@
+/*
+ * 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
+        {
+            Object dexFile = null;
+            Class dexClass = null;
+
+            try {
+                try {
+                    /*
+                     * Find the DexFile class, and construct a DexFile object
+                     * through reflection, then call loadClass on it.
+                     */
+                    dexClass = ClassLoader.getSystemClassLoader().
+                            loadClass("dalvik.system.DexFile");
+                    Constructor ctor = dexClass.
+                            getConstructor(new Class[] {String.class});
+                    dexFile = ctor.newInstance(DEX_FILE);
+                    Method meth = dexClass.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(dexFile, name, this);
+                } finally {
+                    if (dexFile != null) {
+                        /* close the DexFile to make CloseGuard happy */
+                        Method meth = dexClass.getMethod("close", (Class[]) null);
+                        meth.invoke(dexFile);
+                    }
+                }
+            } 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/088-monitor-verification/expected.txt b/tests/088-monitor-verification/expected.txt
new file mode 100644
index 0000000..07f5b0b
--- /dev/null
+++ b/tests/088-monitor-verification/expected.txt
@@ -0,0 +1,7 @@
+recursiveSync ok
+nestedMayThrow ok
+constantLock ok
+excessiveNesting ok
+notNested ok
+twoPath ok
+triplet ok
diff --git a/tests/088-monitor-verification/info.txt b/tests/088-monitor-verification/info.txt
new file mode 100644
index 0000000..c00cb1c
--- /dev/null
+++ b/tests/088-monitor-verification/info.txt
@@ -0,0 +1,2 @@
+Try different arrangements of "synchronized" to exercise the structured
+lock checks in the bytecode verifier.
diff --git a/tests/088-monitor-verification/src/Main.java b/tests/088-monitor-verification/src/Main.java
new file mode 100644
index 0000000..aa90b92
--- /dev/null
+++ b/tests/088-monitor-verification/src/Main.java
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * Entry point and tests that are expected to succeed.
+ */
+public class Main {
+    /**
+     * Drives tests.
+     */
+    public static void main(String[] args) {
+        Main m = new Main();
+
+        m.recursiveSync(0);
+
+        m.nestedMayThrow(false);
+        try {
+            m.nestedMayThrow(true);
+            System.err.println("nestedThrow(true) did not throw");
+        } catch (MyException me) {}
+        System.out.println("nestedMayThrow ok");
+
+        m.constantLock();
+        System.out.println("constantLock ok");
+
+        m.notExcessiveNesting();
+        if (false) {    // TODO: remove when verification is turned on
+        try {
+            TooDeep.excessiveNesting();
+            System.err.println("excessiveNesting did not throw");
+        } catch (VerifyError ve) {}
+        }
+        System.out.println("excessiveNesting ok");
+
+        m.notNested();
+        System.out.println("notNested ok");
+
+        Object obj1 = new Object();
+        Object obj2 = new Object();
+
+        m.twoPath(obj1, obj2, 0);
+        System.out.println("twoPath ok");
+
+        m.triplet(obj1, obj2, 0);
+        System.out.println("triplet ok");
+    }
+
+    /**
+     * Recursive synchronized method.
+     */
+    synchronized void recursiveSync(int iter) {
+        if (iter < 40) {
+            recursiveSync(iter+1);
+        } else {
+            System.out.println("recursiveSync ok");
+        }
+    }
+
+    /**
+     * Tests simple nesting, with and without a throw.
+     */
+    void nestedMayThrow(boolean doThrow) {
+        synchronized (this) {
+            synchronized (Main.class) {
+                synchronized (new Object()) {
+                    synchronized(Class.class) {
+                        if (doThrow) {
+                            throw new MyException();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Exercises bug 3215458.
+     */
+    void constantLock() {
+        Class thing = Thread.class;
+        synchronized (Thread.class) {}
+    }
+
+    /**
+     * Confirms that we can have 32 nested monitors on one method.
+     */
+    void notExcessiveNesting() {
+        synchronized (this) {   // 1
+        synchronized (this) {   // 2
+        synchronized (this) {   // 3
+        synchronized (this) {   // 4
+        synchronized (this) {   // 5
+        synchronized (this) {   // 6
+        synchronized (this) {   // 7
+        synchronized (this) {   // 8
+        synchronized (this) {   // 9
+        synchronized (this) {   // 10
+        synchronized (this) {   // 11
+        synchronized (this) {   // 12
+        synchronized (this) {   // 13
+        synchronized (this) {   // 14
+        synchronized (this) {   // 15
+        synchronized (this) {   // 16
+        synchronized (this) {   // 17
+        synchronized (this) {   // 18
+        synchronized (this) {   // 19
+        synchronized (this) {   // 20
+        synchronized (this) {   // 21
+        synchronized (this) {   // 22
+        synchronized (this) {   // 23
+        synchronized (this) {   // 24
+        synchronized (this) {   // 25
+        synchronized (this) {   // 26
+        synchronized (this) {   // 27
+        synchronized (this) {   // 28
+        synchronized (this) {   // 29
+        synchronized (this) {   // 30
+        synchronized (this) {   // 31
+        synchronized (this) {   // 32
+        }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+    }
+
+    /**
+     * Confirms that we can have more than 32 non-nested monitors in one
+     * method.
+     */
+    void notNested() {
+        synchronized (this) {}  // 1
+        synchronized (this) {}  // 2
+        synchronized (this) {}  // 3
+        synchronized (this) {}  // 4
+        synchronized (this) {}  // 5
+        synchronized (this) {}  // 6
+        synchronized (this) {}  // 7
+        synchronized (this) {}  // 8
+        synchronized (this) {}  // 9
+        synchronized (this) {}  // 10
+        synchronized (this) {}  // 11
+        synchronized (this) {}  // 12
+        synchronized (this) {}  // 13
+        synchronized (this) {}  // 14
+        synchronized (this) {}  // 15
+        synchronized (this) {}  // 16
+        synchronized (this) {}  // 17
+        synchronized (this) {}  // 18
+        synchronized (this) {}  // 19
+        synchronized (this) {}  // 20
+        synchronized (this) {}  // 21
+        synchronized (this) {}  // 22
+        synchronized (this) {}  // 23
+        synchronized (this) {}  // 24
+        synchronized (this) {}  // 25
+        synchronized (this) {}  // 26
+        synchronized (this) {}  // 27
+        synchronized (this) {}  // 28
+        synchronized (this) {}  // 29
+        synchronized (this) {}  // 30
+        synchronized (this) {}  // 31
+        synchronized (this) {}  // 32
+        synchronized (this) {}  // 33
+        synchronized (this) {}  // 34
+    }
+
+    /* does nothing but ensure that the compiler doesn't discard an object */
+    private void doNothing(Object obj) {}
+
+    /**
+     * Conditionally uses one of the synchronized objects.
+     */
+    public void twoPath(Object obj1, Object obj2, int x) {
+        Object localObj;
+
+        synchronized (obj1) {
+            synchronized(obj2) {
+                if (x == 0) {
+                    localObj = obj2;
+                } else {
+                    localObj = obj1;
+                }
+            }
+        }
+
+        doNothing(localObj);
+    }
+
+    /**
+     * Lock the monitor two or three times, and make use of the locked or
+     * unlocked object.
+     */
+    public void triplet(Object obj1, Object obj2, int x) {
+        Object localObj;
+
+        synchronized (obj1) {
+            synchronized(obj1) {
+                if (x == 0) {
+                    synchronized(obj1) {
+                        localObj = obj2;
+                    }
+                } else {
+                    localObj = obj1;
+                }
+            }
+        }
+
+        doNothing(localObj);
+    }
+}
diff --git a/tests/088-monitor-verification/src/MyException.java b/tests/088-monitor-verification/src/MyException.java
new file mode 100644
index 0000000..cf65d6d
--- /dev/null
+++ b/tests/088-monitor-verification/src/MyException.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.
+ */
+
+public class MyException extends RuntimeException {
+    public MyException() {
+        super();
+    }
+    public MyException(String msg) {
+        super(msg);
+    }
+}
diff --git a/tests/088-monitor-verification/src/TooDeep.java b/tests/088-monitor-verification/src/TooDeep.java
new file mode 100644
index 0000000..76192e5
--- /dev/null
+++ b/tests/088-monitor-verification/src/TooDeep.java
@@ -0,0 +1,64 @@
+/*
+ * 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 class has a method with too many levels of nested "synchronized"
+ * blocks.  The verifier will reject it.
+ *
+ * (It would be perfectly okay if the verifier *didn't* reject this.
+ * The goal here is just to exercise the failure path.  It also serves
+ * as a check to see if the monitor checks are enabled.)
+ */
+public class TooDeep {
+
+    public static void excessiveNesting() {
+        synchronized (TooDeep.class) {   // 1
+        synchronized (TooDeep.class) {   // 2
+        synchronized (TooDeep.class) {   // 3
+        synchronized (TooDeep.class) {   // 4
+        synchronized (TooDeep.class) {   // 5
+        synchronized (TooDeep.class) {   // 6
+        synchronized (TooDeep.class) {   // 7
+        synchronized (TooDeep.class) {   // 8
+        synchronized (TooDeep.class) {   // 9
+        synchronized (TooDeep.class) {   // 10
+        synchronized (TooDeep.class) {   // 11
+        synchronized (TooDeep.class) {   // 12
+        synchronized (TooDeep.class) {   // 13
+        synchronized (TooDeep.class) {   // 14
+        synchronized (TooDeep.class) {   // 15
+        synchronized (TooDeep.class) {   // 16
+        synchronized (TooDeep.class) {   // 17
+        synchronized (TooDeep.class) {   // 18
+        synchronized (TooDeep.class) {   // 19
+        synchronized (TooDeep.class) {   // 20
+        synchronized (TooDeep.class) {   // 21
+        synchronized (TooDeep.class) {   // 22
+        synchronized (TooDeep.class) {   // 23
+        synchronized (TooDeep.class) {   // 24
+        synchronized (TooDeep.class) {   // 25
+        synchronized (TooDeep.class) {   // 26
+        synchronized (TooDeep.class) {   // 27
+        synchronized (TooDeep.class) {   // 28
+        synchronized (TooDeep.class) {   // 29
+        synchronized (TooDeep.class) {   // 30
+        synchronized (TooDeep.class) {   // 31
+        synchronized (TooDeep.class) {   // 32
+        synchronized (TooDeep.class) {   // 33
+        }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+    }
+}
diff --git a/tests/089-many-methods/build b/tests/089-many-methods/build
new file mode 100644
index 0000000..0bd90c2
--- /dev/null
+++ b/tests/089-many-methods/build
@@ -0,0 +1,49 @@
+#!/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 65500 total static fields, instance fields, and methods
+# to exceed the dex format's limits.
+mkdir src
+awk '
+BEGIN {
+    writeFileField("FillerStatic", "static public int staticInt");
+    writeFileField("FillerField", "public int fieldInt");
+    writeFileMethod("FillerMethod");
+}
+function writeFileField(name, type) {
+    fileName = "src/" name ".java";
+    printf("public class %s {\n", name) > fileName;
+    for (i = 1; i <= 65500; i++) {
+        printf("    %s%d;\n", type, i) > fileName;
+    }
+    printf("}\n") > fileName;
+}
+function writeFileMethod(name) {
+    fileName = "src/" name ".java";
+    printf("public class %s {\n", name) > fileName;
+    for (i = 1; i <= 65500; i++) {
+      printf("    public void meth%d() { }\n", i) > fileName;
+    }
+    printf("}\n") > fileName;
+}'
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+dx -JXmx1024m --dex --no-optimize classes
+
diff --git a/tests/089-many-methods/expected.txt b/tests/089-many-methods/expected.txt
new file mode 100644
index 0000000..fc97b3d
--- /dev/null
+++ b/tests/089-many-methods/expected.txt
@@ -0,0 +1,4 @@
+
+trouble writing output: Too many fields: 131000; max is 65536. By package:
+131000 default
+build exit status: 2
diff --git a/tests/089-many-methods/info.txt b/tests/089-many-methods/info.txt
new file mode 100644
index 0000000..4f73bd6
--- /dev/null
+++ b/tests/089-many-methods/info.txt
@@ -0,0 +1,2 @@
+Test that we print a reasonable message when the application exceeds more
+than 65536 methods.
diff --git a/tests/090-loop-formation/expected.txt b/tests/090-loop-formation/expected.txt
new file mode 100644
index 0000000..b7e0bb3
--- /dev/null
+++ b/tests/090-loop-formation/expected.txt
@@ -0,0 +1,5 @@
+counter1 is 0
+counter2 is 32767
+counter3 is 32767
+counter4 is 0
+counter5 is 65534
diff --git a/tests/090-loop-formation/info.txt b/tests/090-loop-formation/info.txt
new file mode 100644
index 0000000..98d1d4b
--- /dev/null
+++ b/tests/090-loop-formation/info.txt
@@ -0,0 +1,3 @@
+Test loop formation heuristics and code generation. Basically the problem to
+catch here is to make sure that some never-exercised code blocks are included
+in the loop region, and the JIT compiler won't choke on unresolved fields.
diff --git a/tests/090-loop-formation/src/Main.java b/tests/090-loop-formation/src/Main.java
new file mode 100644
index 0000000..7c16667
--- /dev/null
+++ b/tests/090-loop-formation/src/Main.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Create two versions of loops where the unresolved field is on either the
+ * taken or the non-taken path to make sure that the loop detection code bails
+ * on unresolved fields.
+ */
+public class Main {
+    static int counter1;
+    static int counter2;
+    static int counter3;
+    static int counter4;
+    static int counter5;
+
+    public static void main(String[] args) {
+        /* counter1 is not resolved */
+        for (int i = 0; i < 32767; i++) {
+            if (i < 0) {
+                counter1++;
+            } else {
+                counter2++;
+            }
+            counter5++;
+        }
+
+        /* counter4 is not resolved */
+        for (int i = 0; i < 32767; i++) {
+            if (i >= 0) {
+                counter3++;
+            } else {
+                counter4++;
+            }
+            counter5++;
+        }
+
+        System.out.println("counter1 is " + counter1);
+        System.out.println("counter2 is " + counter2);
+        System.out.println("counter3 is " + counter3);
+        System.out.println("counter4 is " + counter4);
+        System.out.println("counter5 is " + counter5);
+    }
+}
diff --git a/tests/091-deep-interface-hierarchy/expected.txt b/tests/091-deep-interface-hierarchy/expected.txt
new file mode 100644
index 0000000..33bcb02
--- /dev/null
+++ b/tests/091-deep-interface-hierarchy/expected.txt
@@ -0,0 +1 @@
+A new instance of Z was created successfully
diff --git a/tests/091-deep-interface-hierarchy/info.txt b/tests/091-deep-interface-hierarchy/info.txt
new file mode 100644
index 0000000..b62cec6
--- /dev/null
+++ b/tests/091-deep-interface-hierarchy/info.txt
@@ -0,0 +1,4 @@
+Test class loading for deep interface hierarchies. The problem was that in a deep interface
+hierarchy super-interfaces were recursively concatenated without looking for duplicates.
+In cases like this can quickly lead to excessive LinearAlloc consumption due to more than linear
+duplication of iftables.
diff --git a/tests/091-deep-interface-hierarchy/src/Main.java b/tests/091-deep-interface-hierarchy/src/Main.java
new file mode 100644
index 0000000..8ab47f3
--- /dev/null
+++ b/tests/091-deep-interface-hierarchy/src/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Create a hierarchy of interfaces to check if that overflows the LinearAlloc
+ * with iftable entries.
+ */
+public class Main {
+    interface A1 {}
+    interface A2 {}
+    interface A3 {}
+    interface A4 {}
+    interface A5 {}
+
+    interface B1 extends A1, A2, A3, A4, A5 {}
+    interface B2 extends A1, A2, A3, A4, A5 {}
+    interface B3 extends A1, A2, A3, A4, A5 {}
+    interface B4 extends A1, A2, A3, A4, A5 {}
+    interface B5 extends A1, A2, A3, A4, A5 {}
+
+    interface C1 extends B1, B2, B3, B4, B5 {}
+    interface C2 extends B1, B2, B3, B4, B5 {}
+    interface C3 extends B1, B2, B3, B4, B5 {}
+    interface C4 extends B1, B2, B3, B4, B5 {}
+    interface C5 extends B1, B2, B3, B4, B5 {}
+
+    interface D1 extends C1, C2, C3, C4, C5 {}
+    interface D2 extends C1, C2, C3, C4, C5 {}
+    interface D3 extends C1, C2, C3, C4, C5 {}
+    interface D4 extends C1, C2, C3, C4, C5 {}
+    interface D5 extends C1, C2, C3, C4, C5 {}
+
+    interface E1 extends D1, D2, D3, D4, D5 {}
+    interface E2 extends D1, D2, D3, D4, D5 {}
+    interface E3 extends D1, D2, D3, D4, D5 {}
+    interface E4 extends D1, D2, D3, D4, D5 {}
+    interface E5 extends D1, D2, D3, D4, D5 {}
+
+    interface F1 extends E1, E2, E3, E4, E5 {}
+    interface F2 extends E1, E2, E3, E4, E5 {}
+    interface F3 extends E1, E2, E3, E4, E5 {}
+    interface F4 extends E1, E2, E3, E4, E5 {}
+    interface F5 extends E1, E2, E3, E4, E5 {}
+
+    interface G1 extends F1, F2, F3, F4, F5 {}
+    interface G2 extends F1, F2, F3, F4, F5 {}
+    interface G3 extends F1, F2, F3, F4, F5 {}
+    interface G4 extends F1, F2, F3, F4, F5 {}
+    interface G5 extends F1, F2, F3, F4, F5 {}
+
+    interface H1 extends G1, G2, G3, G4, G5 {}
+    interface H2 extends G1, G2, G3, G4, G5 {}
+    interface H3 extends G1, G2, G3, G4, G5 {}
+    interface H4 extends G1, G2, G3, G4, G5 {}
+    interface H5 extends G1, G2, G3, G4, G5 {}
+
+    interface Z extends H1, H2, H3, H4, H5 {}
+
+    public static void main(String[] args) {
+        Z instance = new Z() {};
+        System.out.println("A new instance of Z was created successfully");
+    }
+}
diff --git a/tests/092-locale/expected.txt b/tests/092-locale/expected.txt
new file mode 100644
index 0000000..0a955e7
--- /dev/null
+++ b/tests/092-locale/expected.txt
@@ -0,0 +1,12 @@
+USA(GMT): Sunday, January 1, 2012
+USA: first=1, name=Sunday
+France(GMT): Monday, January 2, 2012
+France: first=2, name=lundi
+USA dfs: [AM, PM]
+en_US: USD $2
+jp_JP: JPY ¥0
+Normalizer passed
+loc: en_US
+ iso3=eng
+loc: eng_USA
+ iso3=eng
diff --git a/tests/092-locale/info.txt b/tests/092-locale/info.txt
new file mode 100644
index 0000000..e3c3a98
--- /dev/null
+++ b/tests/092-locale/info.txt
@@ -0,0 +1 @@
+Exercise some locale-specific classes.
diff --git a/tests/092-locale/src/Main.java b/tests/092-locale/src/Main.java
new file mode 100644
index 0000000..8916a29
--- /dev/null
+++ b/tests/092-locale/src/Main.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.text.DateFormat;
+import java.text.DateFormatSymbols;
+import java.text.Normalizer;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Currency;
+import java.util.Date;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.TimeZone;
+
+/**
+ * Exercise some locale-table-driven stuff.
+ */
+public class Main {
+
+    public static void main(String[] args) {
+        try {
+            testCalendar();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+
+        try {
+            testDateFormatSymbols();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+
+        try {
+            testCurrency();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+
+        try {
+            testNormalizer();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+
+        try {
+            testIso3();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    static void testCalendar() {
+        TimeZone tz = TimeZone.getTimeZone("GMT");
+
+        Locale usa = new Locale("en", "US");
+        Calendar usaCal = Calendar.getInstance(tz, usa);
+        usaCal.clear();     // don't want current date/time
+        usaCal.set(2012, Calendar.JANUARY, 1);
+
+        Date when = usaCal.getTime();
+        DateFormat fmt = DateFormat.getDateInstance(DateFormat.FULL, usa);
+        fmt.setTimeZone(tz);    // defaults to local TZ; force GMT
+        System.out.println("USA(" + fmt.getTimeZone().getID() + "): "
+            + fmt.format(when));
+
+        System.out.println("USA: first="
+            + usaCal.getFirstDayOfWeek() + ", name="
+            + usaCal.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, usa));
+
+
+        Locale france = new Locale("fr", "FR");
+        Calendar franceCal = Calendar.getInstance(tz, france);
+        franceCal.clear();
+        franceCal.set(2012, Calendar.JANUARY, 2);
+
+        when = franceCal.getTime();
+        fmt = DateFormat.getDateInstance(DateFormat.FULL, usa);
+        fmt.setTimeZone(tz);    // defaults to local TZ; force GMT
+        System.out.println("France(" + fmt.getTimeZone().getID() + "): "
+            + fmt.format(when));
+
+        System.out.println("France: first="
+            + franceCal.getFirstDayOfWeek() + ", name="
+            + franceCal.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, france));
+    }
+
+    static void testDateFormatSymbols() {
+        Locale usa = new Locale("en", "US");
+        DateFormatSymbols syms = DateFormatSymbols.getInstance(usa);
+        String[] list = syms.getAmPmStrings();
+        System.out.println("USA dfs: " + Arrays.deepToString(list));
+    }
+
+    static void testCurrency() {
+        Locale usa = new Locale("en", "US");
+        Currency dollars = Currency.getInstance(usa);
+
+        System.out.println(usa.toString() + ": " + dollars.toString()
+            + " " + dollars.getSymbol() + dollars.getDefaultFractionDigits());
+
+        Locale japan = new Locale("jp", "JP");
+        Currency yen = Currency.getInstance(japan);
+
+        System.out.println(japan.toString() + ": " + yen.toString()
+            + " " + yen.getSymbol() + yen.getDefaultFractionDigits());
+    }
+
+    static void testNormalizer() {
+        String composed = "Bl\u00c1ah";
+        String decomposed = "Bl\u0041\u0301ah";
+        String res;
+
+        res = Normalizer.normalize(composed, Normalizer.Form.NFD);
+        if (!decomposed.equals(res)) {
+            System.err.println("Bad decompose: '" + composed + "' --> '"
+                + res + "'");
+        }
+
+        res = Normalizer.normalize(decomposed, Normalizer.Form.NFC);
+        if (!composed.equals(res)) {
+            System.err.println("Bad compose: '" + decomposed + "' --> '"
+                + res + "'");
+        }
+
+        System.out.println("Normalizer passed");
+    }
+
+    /*
+     * Test that we can set and get an ISO3 language code.  Support for this
+     * is expected by the Android framework.
+     */
+    static void testIso3() {
+        Locale loc;
+        loc = new Locale("en", "US");
+        System.out.println("loc: " + loc);
+        System.out.println(" iso3=" + loc.getISO3Language());
+
+        loc = new Locale("eng", "USA");
+        System.out.println("loc: " + loc);
+        try {
+            System.out.println(" iso3=" + loc.getISO3Language());
+        } catch (MissingResourceException mre) {
+            System.err.println("couldn't get iso3 language");
+        }
+    }
+}
diff --git a/tests/093-serialization/expected.txt b/tests/093-serialization/expected.txt
new file mode 100644
index 0000000..60c64f8
--- /dev/null
+++ b/tests/093-serialization/expected.txt
@@ -0,0 +1 @@
+one=true two=2 three=three four=4.0 five=5.0 six=6 seven=7 eight=8 nine=9 thing=X
diff --git a/tests/093-serialization/info.txt b/tests/093-serialization/info.txt
new file mode 100644
index 0000000..effe3d8
--- /dev/null
+++ b/tests/093-serialization/info.txt
@@ -0,0 +1 @@
+Tests object serialization.
diff --git a/tests/093-serialization/src/Main.java b/tests/093-serialization/src/Main.java
new file mode 100644
index 0000000..ca3dc9f
--- /dev/null
+++ b/tests/093-serialization/src/Main.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * Exercise serialization.
+ */
+public class Main {
+
+    public static void main(String[] args) {
+        testObjectSerialization();
+    }
+
+    static void testObjectSerialization() {
+        byte[] serialData;
+
+        try {
+            serialData = createStream();
+            checkStream(serialData);
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe);
+        }
+    }
+
+    static byte[] createStream() throws IOException {
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        ObjectOutputStream objStream = new ObjectOutputStream(byteStream);
+
+        Sub sub = new Sub('X');
+        objStream.writeObject(sub);
+        byte[] bytes = byteStream.toByteArray();
+
+        objStream.close();
+        byteStream.close();
+        return bytes;
+    }
+
+    static void checkStream(byte[] input) throws IOException {
+        ByteArrayInputStream byteStream = new ByteArrayInputStream(input);
+        ObjectInputStream objStream = new ObjectInputStream(byteStream);
+
+        Sub sub;
+        try {
+            sub = (Sub) objStream.readObject();
+        } catch (ClassNotFoundException cnfe) {
+            throw new RuntimeException(cnfe);
+        }
+
+        objStream.close();
+        byteStream.close();
+
+        sub.check();
+    }
+}
+
+class Base implements Serializable {
+    private static final long serialVersionUID = 12345;
+
+    Boolean one;
+    Integer two;
+    String three;
+
+    public Base() {
+        one = true;
+        two = Integer.valueOf(2);
+        three = "three";
+    }
+}
+
+class Sub extends Base {
+    private static final long serialVersionUID = 54321;
+
+    Double four;
+    Float five;
+    private Byte six = 26;
+    Character seven = '7';
+    Short eight;
+    long nine;
+    public Character thing;
+
+    public Sub(char thing) {
+        four = 4.0;
+        five = 5.0f;
+        six = 6;
+        eight = 8;
+        nine = 9;
+        this.thing = thing;
+    }
+
+    public void check() {
+        System.out.println("one=" + one + " two=" + two + " three=" + three
+            + " four=" + four + " five=" + five + " six=" + six
+            + " seven=" + seven + " eight=" + eight + " nine=" + nine
+            + " thing=" + thing);
+    }
+}
+
diff --git a/tests/094-pattern/expected.txt b/tests/094-pattern/expected.txt
new file mode 100644
index 0000000..4af0c66
--- /dev/null
+++ b/tests/094-pattern/expected.txt
@@ -0,0 +1,3 @@
+str1 matches: true
+str2 matches: false
+str3 matches: true
diff --git a/tests/094-pattern/info.txt b/tests/094-pattern/info.txt
new file mode 100644
index 0000000..c1ade33
--- /dev/null
+++ b/tests/094-pattern/info.txt
@@ -0,0 +1,4 @@
+A simple test to exercise pattern matching.
+
+The test may throw a StackOverflowError if the stack size is too small.  With
+some regex libs, -Xss65k is the minimum allowable size.
diff --git a/tests/094-pattern/src/Main.java b/tests/094-pattern/src/Main.java
new file mode 100644
index 0000000..4d7e1a3
--- /dev/null
+++ b/tests/094-pattern/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Main {
+    // from android.util.Patterns
+    public static final String GOOD_IRI_CHAR =
+        "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
+
+    public static final String TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL =
+        "(?:"
+        + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+        + "|(?:biz|b[abdefghijmnorstvwyz])"
+        + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
+        + "|d[ejkmoz]"
+        + "|(?:edu|e[cegrstu])"
+        + "|f[ijkmor]"
+        + "|(?:gov|g[abdefghilmnpqrstuwy])"
+        + "|h[kmnrtu]"
+        + "|(?:info|int|i[delmnoqrst])"
+        + "|(?:jobs|j[emop])"
+        + "|k[eghimnprwyz]"
+        + "|l[abcikrstuvy]"
+        + "|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])"
+        + "|(?:name|net|n[acefgilopruz])"
+        + "|(?:org|om)"
+        + "|(?:pro|p[aefghklmnrstwy])"
+        + "|qa"
+        + "|r[eosuw]"
+        + "|s[abcdeghijklmnortuvyz]"
+        + "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
+        + "|u[agksyz]"
+        + "|v[aceginu]"
+        + "|w[fs]"
+        + "|(?:\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae|\u0438\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u0435|\u0440\u0444|\u0441\u0440\u0431|\u05d8\u05e2\u05e1\u05d8|\u0622\u0632\u0645\u0627\u06cc\u0634\u06cc|\u0625\u062e\u062a\u0628\u0627\u0631|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633|\u0633\u0648\u0631\u064a\u0629|\u0641\u0644\u0633\u0637\u064a\u0646|\u0642\u0637\u0631|\u0645\u0635\u0631|\u092a\u0930\u0940\u0915\u094d\u0937\u093e|\u092d\u093e\u0930\u0924|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd|\u0baa\u0bb0\u0bbf\u0b9f\u0bcd\u0b9a\u0bc8|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e44\u0e17\u0e22|\u30c6\u30b9\u30c8|\u4e2d\u56fd|\u4e2d\u570b|\u53f0\u6e7e|\u53f0\u7063|\u65b0\u52a0\u5761|\u6d4b\u8bd5|\u6e2c\u8a66|\u9999\u6e2f|\ud14c\uc2a4\ud2b8|\ud55c\uad6d|xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-3e0b707e|xn\\-\\-45brj9c|xn\\-\\-80akhbyknj4f|xn\\-\\-90a3ac|xn\\-\\-9t4b11yi5a|xn\\-\\-clchc0ea0b2g2a9gcd|xn\\-\\-deba0ad|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-g6w251d|xn\\-\\-gecrj9c|xn\\-\\-h2brj9c|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-j6w193g|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-kprw13d|xn\\-\\-kpry57d|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-s9brj9c|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-yfro4i67o|xn\\-\\-ygbi2ammx|xn\\-\\-zckzah|xxx)"
+        + "|y[et]"
+        + "|z[amw]))";
+
+    public static final String WEB_URL_STR =
+        "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+        + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+        + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+        + "((?:(?:[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,64}\\.)+"   // named host
+        + TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL
+        + "|(?:(?:25[0-5]|2[0-4]" // or ip address
+        + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
+        + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
+        + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+        + "|[1-9][0-9]|[0-9])))"
+        + "(?:\\:\\d{1,5})?)" // plus option port number
+        + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
+        + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+        + "(?:\\b|$)"; // and finally, a word boundary or end of
+                        // input.  This is to stop foo.sure from
+                        // matching as foo.su
+
+    public static final Pattern WEB_URL = Pattern.compile(WEB_URL_STR);
+
+    public static final String testStr1 =
+        "http://www.google.com/blah?client=tablet-android&source=android-home";
+    public static final String testStr2 = "http:///www.google.com/";
+    public static final String testStr3 =
+        "http://www.google.com/search?hl=en&redir_esc=&client=tablet-android-verizon&source=android-browser-type&v=141000000&qsubts=1327020479959&action=devloc&q=cnn";
+
+    public static void main(String[] args) {
+        System.out.println("str1 matches: " + WEB_URL.matcher(testStr1).matches());
+        System.out.println("str2 matches: " + WEB_URL.matcher(testStr2).matches());
+        System.out.println("str3 matches: " + WEB_URL.matcher(testStr3).matches());
+    }
+
+    static String getStringAsHex(String text) {
+        StringBuilder sb = new StringBuilder(text.length() * 4);
+
+        for (int i = 0; i < text.length(); i++) {
+            sb.append(Integer.toHexString((int) text.charAt(i)));
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/tests/095-switch-MAX_INT/expected.txt b/tests/095-switch-MAX_INT/expected.txt
new file mode 100644
index 0000000..12799cc
--- /dev/null
+++ b/tests/095-switch-MAX_INT/expected.txt
@@ -0,0 +1 @@
+good
diff --git a/tests/095-switch-MAX_INT/info.txt b/tests/095-switch-MAX_INT/info.txt
new file mode 100644
index 0000000..bb901db
--- /dev/null
+++ b/tests/095-switch-MAX_INT/info.txt
@@ -0,0 +1 @@
+Bug: http://code.google.com/p/android/issues/detail?id=22344
diff --git a/tests/095-switch-MAX_INT/src/Main.java b/tests/095-switch-MAX_INT/src/Main.java
new file mode 100644
index 0000000..d1171ea
--- /dev/null
+++ b/tests/095-switch-MAX_INT/src/Main.java
@@ -0,0 +1,11 @@
+public class Main {
+  static public void main(String[] args) throws Exception {
+    switch (0x7fffffff) {
+    case 0x7fffffff:
+      System.err.println("good");
+      break;
+    default:
+      throw new AssertionError();
+    }
+  }
+}
diff --git a/tests/096-array-copy-concurrent-gc/expected.txt b/tests/096-array-copy-concurrent-gc/expected.txt
new file mode 100644
index 0000000..23b9dab
--- /dev/null
+++ b/tests/096-array-copy-concurrent-gc/expected.txt
@@ -0,0 +1,3 @@
+Initializing...
+Starting the test
+Test OK
diff --git a/tests/096-array-copy-concurrent-gc/info.txt b/tests/096-array-copy-concurrent-gc/info.txt
new file mode 100644
index 0000000..37dd8be
--- /dev/null
+++ b/tests/096-array-copy-concurrent-gc/info.txt
@@ -0,0 +1,2 @@
+This is a test to verify that System.arraycopy works nice together with
+the concurrent gc.
diff --git a/tests/096-array-copy-concurrent-gc/src/Main.java b/tests/096-array-copy-concurrent-gc/src/Main.java
new file mode 100644
index 0000000..c8e538b
--- /dev/null
+++ b/tests/096-array-copy-concurrent-gc/src/Main.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Running concurrent gc and doing some System.arraycopy
+ * Several threads is created in order to increase the probability
+ * of thread switches at critical points. Without creating several
+ * threads the test case usually passed even when there were bugs.
+ * Size of array and amount of garbage created is based on experimental
+ * numbers and is a tradeoff between time that the test takes when
+ * it succeeds and the probability that the test discovers a problem.
+ */
+public class Main {
+    public static void main(String args[]) {
+        new ObjectCreatorThread(true).start();
+        new ObjectCreatorThread(false).start();
+        new ObjectCreatorThread(false).start();
+    }
+
+    static class ObjectCreatorThread extends Thread {
+        boolean mDoLog;
+        public ObjectCreatorThread(boolean doLog) {
+            mDoLog = doLog;
+        }
+
+        @Override
+        public void run() {
+            new Main().stressArray(mDoLog);
+        }
+    }
+
+    Object [] array = new Object[10000];
+
+    void stressArray(boolean doLog) {
+        // We want many references in the array
+        // We also want elements close to each other to have large
+        // diff in address so lets skip every 2:nd address so it is null
+        if (doLog) {
+            System.out.println("Initializing...");
+        }
+        for (int i = 0; i < array.length; i+=2) {
+            array[i] = new String("Creating some garbage" + i);
+        }
+
+        if (doLog) {
+            System.out.println("Starting the test");
+        }
+
+        for (int j = 0; j < array.length; j++) {
+            Object obj = array[array.length - 1];
+            System.arraycopy(array, 0, array, 1, array.length - 1);
+            array[0] = obj;
+            new String("Creating some garbage" + Math.random());
+            new String("Creating some garbage" + Math.random());
+            new String("Creating some garbage" + Math.random());
+            new String("Creating some garbage" + Math.random());
+        }
+
+        for (int j = 0; j < array.length; j++) {
+            Object obj = array[0];
+            System.arraycopy(array, 1, array, 0, array.length - 1);
+            array[array.length - 1] = obj;
+            new String("Creating some garbage" + Math.random());
+            new String("Creating some garbage" + Math.random());
+            new String("Creating some garbage" + Math.random());
+            new String("Creating some garbage" + Math.random());
+        }
+
+        if (doLog) {
+            System.out.println("Test OK");
+        }
+    }
+}
diff --git a/tests/300-package-override/expected.txt b/tests/300-package-override/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/tests/300-package-override/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/tests/300-package-override/info.txt b/tests/300-package-override/info.txt
new file mode 100644
index 0000000..0ed59eb
--- /dev/null
+++ b/tests/300-package-override/info.txt
@@ -0,0 +1,2 @@
+Tests a dalvik bug where we'd allow subclasses to override package-protected
+methods.
\ No newline at end of file
diff --git a/tests/300-package-override/src/Main.java b/tests/300-package-override/src/Main.java
new file mode 100644
index 0000000..ad7eaaf
--- /dev/null
+++ b/tests/300-package-override/src/Main.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String args[]) throws Exception {
+    p1.BaseClass c = new p2.DerivedClass();
+    c.run();
+  }
+}
diff --git a/tests/300-package-override/src/p1/BaseClass.java b/tests/300-package-override/src/p1/BaseClass.java
new file mode 100644
index 0000000..1c048ac
--- /dev/null
+++ b/tests/300-package-override/src/p1/BaseClass.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p1;
+
+public class BaseClass {
+  public void run() { foo(); }
+  void foo() { System.out.println("passed"); } // It should not be possible to override this.
+}
diff --git a/tests/300-package-override/src/p2/DerivedClass.java b/tests/300-package-override/src/p2/DerivedClass.java
new file mode 100644
index 0000000..860f50c
--- /dev/null
+++ b/tests/300-package-override/src/p2/DerivedClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p2;
+
+public class DerivedClass extends p1.BaseClass {
+  void foo() { System.out.println("DerivedClass overrode package-private method!"); } // This should not override BaseClass.foo.
+}
diff --git a/tests/301-abstract-protected/expected.txt b/tests/301-abstract-protected/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/tests/301-abstract-protected/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/tests/301-abstract-protected/info.txt b/tests/301-abstract-protected/info.txt
new file mode 100644
index 0000000..0751eff
--- /dev/null
+++ b/tests/301-abstract-protected/info.txt
@@ -0,0 +1,3 @@
+Tests a dalvik bug where we'd treat an abstract method as an implementation
+of an interface method; the RI only cares about the visibility of the actual
+implementation in non-abstract subclasses.
diff --git a/tests/301-abstract-protected/src/Main.java b/tests/301-abstract-protected/src/Main.java
new file mode 100644
index 0000000..9b19a9d
--- /dev/null
+++ b/tests/301-abstract-protected/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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[]) throws Exception {
+    System.err.println(new C().m());
+  }
+}
+
+// An arbitrary interface.
+interface I { public String m(); }
+
+// This is I-like, but doesn't actually claim to implement I.
+abstract class Abstract { protected abstract String m(); }
+
+// This claims to implement I, but the inherited m isn't sufficiently visible.
+abstract class AbstractI extends Abstract implements I { }
+
+// This has a concrete m that's sufficiently visible, so all should be good.
+class C extends AbstractI { public String m() { return "passed"; }; }
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/host-run-test-jar b/tests/etc/host-run-test-jar
new file mode 100755
index 0000000..c85c419
--- /dev/null
+++ b/tests/etc/host-run-test-jar
@@ -0,0 +1,159 @@
+#!/bin/sh
+#
+# Run the code in test.jar using the host-mode 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="jit"
+    msg "Using jit 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 "------------------------------"
+
+HOSTBASE="${ANDROID_BUILD_TOP}/out/host"
+BASE="$OUT" # from build environment
+DATA_DIR=/tmp
+DEBUG_OPTS="-Xcheck:jni -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"
+
+if [ ! -d $DATA_DIR/dalvik-cache ]; then
+    mkdir -p $DATA_DIR/dalvik-cache
+    [[ $? -ne 0 ]] && exit
+fi
+
+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="${HOSTBASE}/linux-x86"
+export LD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
+export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
+
+exe="${ANDROID_ROOT}/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..2028217
--- /dev/null
+++ b/tests/etc/push-and-run-test-jar
@@ -0,0 +1,123 @@
+#!/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
+#   --dev         -- development mode (print the vm invocation cmdline)
+#   --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"
+QUIET="n"
+PRECISE="y"
+DEV_MODE="n"
+
+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--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="jit"
+    msg "Using jit 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
+
+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
+
+cmdline="cd /data; dalvikvm $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG \
+    $GC_OPTS -cp test.jar -Xint:${INTERP} -ea Main"
+if [ "$DEV_MODE" = "y" ]; then
+    echo $cmdline "$@"
+fi
+adb shell $cmdline "$@"
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..f66cd76
--- /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--host" ]; then
+        run_args="${run_args} --host"
+        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 --host --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..406e424
--- /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--host" ]; then
+        RUN="${progdir}/etc/host-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."
+        echo "    --jit          Use the jit (the default)."
+        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 "    --host         Use the host-mode 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..22380ce
--- /dev/null
+++ b/tools/dexdeps/README.txt
@@ -0,0 +1,32 @@
+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[]").
+
+  --just-classes
+
+    Indicates that output should only include a list of classes, as
+    opposed to also listing fields and methods.
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..89dff18
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java
@@ -0,0 +1,602 @@
+/*
+ * 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();
+    }
+
+    /**
+     * Verifies the given magic number.
+     */
+    private static boolean verifyMagic(byte[] magic) {
+        return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC) ||
+            Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_API_13);
+    }
+
+    /**
+     * 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 (!verifyMagic(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, 0x36, 0x00 };
+        public static final byte[] DEX_FILE_MAGIC_API_13 = {
+            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..410694a
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Main.java
@@ -0,0 +1,231 @@
+/*
+ * 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[] mInputFileNames;
+    private String mOutputFormat = "xml";
+
+    /**
+     * whether to only emit info about classes used; when {@code false},
+     * info about fields and methods is also emitted
+     */
+    private boolean mJustClasses = false;
+
+    /**
+     * 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);
+            boolean first = true;
+
+            for (String fileName : mInputFileNames) {
+                RandomAccessFile raf = openInputFile(fileName);
+                DexData dexData = new DexData(raf);
+                dexData.load();
+
+                if (first) {
+                    first = false;
+                    Output.generateFirstHeader(fileName, mOutputFormat);
+                } else {
+                    Output.generateHeader(fileName, mOutputFormat);
+                }
+
+                Output.generate(dexData, mOutputFormat, mJustClasses);
+                Output.generateFooter(mOutputFormat);
+                raf.close();
+            }
+        } 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 an 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.
+     *
+     * @param fileName the name of the file to open
+     */
+    RandomAccessFile openInputFile(String fileName) throws IOException {
+        RandomAccessFile raf;
+
+        raf = openInputFileAsZip(fileName);
+        if (raf == null) {
+            File inputFile = new File(fileName);
+            raf = new RandomAccessFile(inputFile, "r");
+        }
+
+        return raf;
+    }
+
+    /**
+     * Tries to open an input file as a Zip archive (jar/apk) with a
+     * "classes.dex" inside.
+     *
+     * @param fileName the name of the file to open
+     * @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(String fileName) throws IOException {
+        ZipFile zipFile;
+
+        /*
+         * Try it as a zip file.
+         */
+        try {
+            zipFile = new ZipFile(fileName);
+        } catch (FileNotFoundException fnfe) {
+            /* not found, no point in retrying as non-zip */
+            System.err.println("Unable to open '" + fileName + "': " +
+                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 '" + fileName + "'");
+            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 if (arg.equals("--just-classes")) {
+                mJustClasses = true;
+            } else {
+                System.err.println("Unknown option '" + arg + "'");
+                throw new UsageException();
+            }
+        }
+
+        // We expect at least one more argument (file name).
+        int fileCount = args.length - idx;
+        if (fileCount == 0) {
+            throw new UsageException();
+        }
+
+        mInputFileNames = new String[fileCount];
+        System.arraycopy(args, idx, mInputFileNames, 0, fileCount);
+    }
+
+    /**
+     * Prints command-line usage info.
+     */
+    void usage() {
+        System.err.print(
+                "DEX dependency scanner v1.2\n" +
+                "Copyright (C) 2009 The Android Open Source Project\n\n" +
+                "Usage: dexdeps [options] <file.{dex,apk,jar}> ...\n" +
+                "Options:\n" +
+                "  --format={xml,brief}\n" +
+                "  --just-classes\n");
+    }
+}
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..dbe3bc2
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Output.java
@@ -0,0 +1,346 @@
+/*
+ * 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.PrintStream;
+
+/**
+ * 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 = "        ";
+
+    private static final PrintStream out = System.out;
+
+    private static void generateHeader0(String fileName, String format) {
+        if (format.equals("brief")) {
+            if (fileName != null) {
+                out.println("File: " + fileName);
+            }
+        } else if (format.equals("xml")) {
+            if (fileName != null) {
+                out.println(IN0 + "<external file=\"" + fileName + "\">");
+            } else {
+                out.println(IN0 + "<external>");
+            }
+        } else {
+            /* should've been trapped in arg handler */
+            throw new RuntimeException("unknown output format");
+        }
+    }
+
+    public static void generateFirstHeader(String fileName, String format) {
+        generateHeader0(fileName, format);
+    }
+
+    public static void generateHeader(String fileName, String format) {
+        out.println();
+        generateHeader0(fileName, format);
+    }
+
+    public static void generateFooter(String format) {
+        if (format.equals("brief")) {
+            // Nothing to do.
+        } else if (format.equals("xml")) {
+            out.println("</external>");
+        } else {
+            /* should've been trapped in arg handler */
+            throw new RuntimeException("unknown output format");
+        }
+    }
+
+    public static void generate(DexData dexData, String format,
+            boolean justClasses) {
+        if (format.equals("brief")) {
+            printBrief(dexData, justClasses);
+        } else if (format.equals("xml")) {
+            printXml(dexData, justClasses);
+        } 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, boolean justClasses) {
+        ClassRef[] externClassRefs = dexData.getExternalReferences();
+
+        printClassRefs(externClassRefs, justClasses);
+
+        if (!justClasses) {
+            printFieldRefs(externClassRefs);
+            printMethodRefs(externClassRefs);
+        }
+    }
+
+    /**
+     * Prints the list of classes in a simple human-readable format.
+     */
+    static void printClassRefs(ClassRef[] classes, boolean justClasses) {
+        if (!justClasses) {
+            out.println("Classes:");
+        }
+
+        for (int i = 0; i < classes.length; i++) {
+            ClassRef ref = classes[i];
+
+            out.println(descriptorToDot(ref.getName()));
+        }
+    }
+
+    /**
+     * Prints the list of fields in a simple human-readable format.
+     */
+    static void printFieldRefs(ClassRef[] classes) {
+        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];
+
+                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) {
+        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];
+
+                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, boolean justClasses) {
+        ClassRef[] externClassRefs = dexData.getExternalReferences();
+
+        /*
+         * 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) {
+                    out.println(IN1 + "</package>");
+                }
+
+                out.println(IN1 +
+                    "<package name=\"" + packageName + "\">");
+
+                prevPackage = packageName;
+            }
+
+            out.println(IN2 + "<class name=\"" + className + "\">");
+            if (!justClasses) {
+                printXmlFields(cref);
+                printXmlMethods(cref);
+            }
+            out.println(IN2 + "</class>");
+        }
+
+        if (prevPackage != null)
+            out.println(IN1 + "</package>");
+    }
+
+    /**
+     * 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];
+
+            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
+                out.println(IN3 + "<constructor name=\"" +
+                    classNameOnly(declClassName) + "\">");
+            } else {
+                out.println(IN3 + "<method name=\"" + mref.getName() +
+                    "\" return=\"" + descriptorToDot(mref.getReturnTypeName()) +
+                    "\">");
+            }
+            String[] args = mref.getArgumentTypeNames();
+            for (int j = 0; j < args.length; j++) {
+                out.println(IN4 + "<parameter type=\"" +
+                    descriptorToDot(args[j]) + "\"/>");
+            }
+            if (constructor) {
+                out.println(IN3 + "</constructor>");
+            } else {
+                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..a4ae02e
--- /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, "%#x        %s      m       ()\n",
+                    pRecord->methodId, pRecord->fullName);
+        } else if (pRecord->signature == NULL) {
+            fprintf(keyFp, "%#x        %s      %s      ()\n",
+                    pRecord->methodId, pRecord->className,
+                    pRecord->methodName);
+        } else {
+            fprintf(keyFp, "%#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..976fe5f
--- /dev/null
+++ b/tools/dmtracedump/TraceDump.c
@@ -0,0 +1,2906 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.
+ * Version 3 encodes the record size and adds an optional extra timestamp field.
+ */
+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
+
+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;
+    short recordSize;
+} DataHeader;
+
+/*
+ * Entry from the thread list.
+ */
+typedef struct ThreadEntry {
+    int         threadId;
+    const char* threadName;
+} 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" */
+} 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;
+} 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;
+    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;
+
+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
+ * 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);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Count the number of lines until the next token.
+ *
+ * Returns -1 if none found before EOF.
+ */
+int countLinesToToken(const char* data, int len)
+{
+    int count = 0;
+    int next;
+
+    while (*data != TOKEN_CHAR) {
+        next = findNextChar(data, len, '\n');
+        if (next < 0)
+            return -1;
+        count++;
+        data += next+1;
+        len -= next+1;
+    }
+
+    return count;
+}
+
+/*
+ * 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;
+
+    /* Reduce our allocation now that we know where the end of the key section is. */
+    pKeys->fileData = (char *)realloc(pKeys->fileData, offset);
+    pKeys->fileLen = offset;
+    /* 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, 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)
+{
+    int bytesToRead;
+
+    pHeader->magic = read4LE(fp);
+    pHeader->version = read2LE(fp);
+    pHeader->offsetToData = read2LE(fp);
+    pHeader->startWhen = read8LE(fp);
+    bytesToRead = pHeader->offsetToData - 16;
+    if (pHeader->version == 1) {
+        pHeader->recordSize = 9;
+    } else if (pHeader->version == 2) {
+        pHeader->recordSize = 10;
+    } else if (pHeader->version == 3) {
+        pHeader->recordSize = read2LE(fp);
+        bytesToRead -= 2;
+    } else {
+        fprintf(stderr, "Unsupported trace file version: %d\n", pHeader->version);
+        return -1;
+    }
+
+    if (fseek(fp, bytesToRead, SEEK_CUR) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Look up a method by it's method ID.
+ *
+ * Returns NULL if no matching method was found.
+ */
+MethodEntry* lookupMethod(DataKeys* pKeys, unsigned int methodId)
+{
+    int hi, lo, mid;
+    unsigned int id;
+
+    lo = 0;
+    hi = pKeys->numMethods - 1;
+
+    while (hi >= lo) {
+        mid = (hi + lo) / 2;
+
+        id = pKeys->methods[mid].methodId;
+        if (id == methodId)           /* match */
+            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, DataHeader* dataHeader,
+        int *threadId, unsigned int *methodVal, uint64_t *elapsedTime)
+{
+    int id;
+    int bytesToRead;
+
+    bytesToRead = dataHeader->recordSize;
+    if (dataHeader->version == 1) {
+        id = getc(dataFp);
+        bytesToRead -= 1;
+    } else {
+        id = read2LE(dataFp);
+        bytesToRead -= 2;
+    }
+    if (id == EOF)
+        return 1;
+    *threadId = id;
+
+    *methodVal = read4LE(dataFp);
+    *elapsedTime = read4LE(dataFp);
+    bytesToRead -= 8;
+
+    while (bytesToRead-- > 0) {
+        getc(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, &dataHeader, &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: %#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=\"#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=\"#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];
+    char *anchor_close = "";
+
+    total = sumThreadTime;
+    anchor_buf[0] = 0;
+    if (gOptions.outputHtml) {
+        anchor_close = "</a>";
+        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 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");
+        }
+    }
+}
+
+/*
+ * Read the key and data files and return the MethodEntries for those files
+ */
+DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime)
+{
+    DataKeys* dataKeys = NULL;
+    MethodEntry **pMethods = NULL;
+    MethodEntry* method;
+    FILE* dataFp = NULL;
+    DataHeader dataHeader;
+    int ii;
+    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;
+
+#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, &dataHeader, &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;
+            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 {
+            fprintf(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;
+        } 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;
+            }
+        }
+        /* 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 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;
+
+        /* Also, add up the time taken by all of the threads */
+        sumThreadTime += pStack->lastEventTime - pStack->threadStartTime;
+
+        for (ii = 0; ii < pStack->top; ++ii) {
+            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);
+        }
+    }
+    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)
+{
+    /* Print the html header, if necessary */
+    if (gOptions.outputHtml) {
+        printf(htmlHeader, gOptions.sortableUrl);
+        outputTableOfContents();
+    }
+
+    printExclusiveProfile(pMethods, numMethods, sumThreadTime);
+    printInclusiveProfile(pMethods, numMethods, sumThreadTime);
+
+    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, "Copyright (C) 2006 The Android Open Source Project\n\n");
+    fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] 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, "  -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:");
+        if (opt == -1)
+            break;
+        switch (opt) {
+            case 'd':
+                gOptions.diffFileName = optarg;
+                break;
+            case 'g':
+                gOptions.graphFileName = 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;
+
+    TraceData data1;
+    DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName,
+                                       &sumThreadTime);
+    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);
+
+        createDiff(d2, sum2, dataKeys, sumThreadTime);
+
+        freeDataKeys(d2);
+    } else {
+        MethodEntry** methods = parseMethodEntries(dataKeys);
+        profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime);
+        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/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..bd8bc4f
--- /dev/null
+++ b/tools/hprof-conv/HprofConv.c
@@ -0,0 +1,724 @@
+/*
+ * 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)
+{
+    const char *magicString;
+    ExpandBuf* pBuf;
+    int result = -1;
+
+    pBuf = ebAlloc();
+    if (pBuf == NULL)
+        goto bail;
+
+    /*
+     * Start with the header.
+     */
+    if (ebReadString(pBuf, in) != 0)
+        goto bail;
+
+    magicString = (const char*)ebGetBuffer(pBuf);
+    if (strcmp(magicString, "JAVA PROFILE 1.0.3") != 0) {
+        if (strcmp(magicString, "JAVA PROFILE 1.0.2") == 0) {
+            fprintf(stderr, "ERROR: HPROF file already in 1.0.2 format.\n");
+        } else {
+            fprintf(stderr, "ERROR: expecting HPROF file format 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/unit-tests/Android.mk b/unit-tests/Android.mk
new file mode 100644
index 0000000..62b666a
--- /dev/null
+++ b/unit-tests/Android.mk
@@ -0,0 +1,50 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+test_module = dalvik-vm-unit-tests
+test_tags = eng tests
+
+test_src_files = \
+    dvmHumanReadableDescriptor_test.cpp \
+    
+test_c_includes = \
+    dalvik \
+    dalvik/vm \
+
+# Build for the device. Run with:
+#   adb shell /data/nativetest/dalvik-vm-unit-tests/dalvik-vm-unit-tests
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += -DANDROID_SMP=1
+LOCAL_C_INCLUDES += $(test_c_includes)
+LOCAL_MODULE := $(test_module)
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_SHARED_LIBRARIES += libcutils libdvm
+include $(BUILD_NATIVE_TEST)
+
+# Build for the host.
+# TODO: BUILD_HOST_NATIVE_TEST doesn't work yet; STL-related compile-time and
+# run-time failures, presumably astl/stlport/genuine host STL confusion.
+#include $(CLEAR_VARS)
+#LOCAL_C_INCLUDES += $(test_c_includes)
+#LOCAL_MODULE := $(test_module)
+#LOCAL_MODULE_TAGS := $(test_tags)
+#LOCAL_SRC_FILES := $(test_src_files)
+#LOCAL_SHARED_LIBRARIES += libdvm libcrypto libssl libicuuc libicui18n
+#LOCAL_WHOLE_STATIC_LIBRARIES += libcutils liblog libdvm
+#include $(BUILD_HOST_NATIVE_TEST)
diff --git a/unit-tests/dvmHumanReadableDescriptor_test.cpp b/unit-tests/dvmHumanReadableDescriptor_test.cpp
new file mode 100644
index 0000000..89ca85c
--- /dev/null
+++ b/unit-tests/dvmHumanReadableDescriptor_test.cpp
@@ -0,0 +1,43 @@
+#include <gtest/gtest.h>
+
+#include "Dalvik.h"
+
+TEST(dvmHumanReadableDescriptor, ArrayReferences) {
+  ASSERT_EQ("java.lang.Class[]", dvmHumanReadableDescriptor("[Ljava/lang/Class;"));
+  ASSERT_EQ("java.lang.Class[][]", dvmHumanReadableDescriptor("[[Ljava/lang/Class;"));
+}
+
+TEST(dvmHumanReadableDescriptor, ScalarReferences) {
+  ASSERT_EQ("java.lang.String", dvmHumanReadableDescriptor("Ljava.lang.String;"));
+  ASSERT_EQ("java.lang.String", dvmHumanReadableDescriptor("Ljava/lang/String;"));
+}
+
+TEST(dvmHumanReadableDescriptor, PrimitiveArrays) {
+  ASSERT_EQ("boolean[]", dvmHumanReadableDescriptor("[Z"));
+  ASSERT_EQ("boolean[][]", dvmHumanReadableDescriptor("[[Z"));
+  ASSERT_EQ("byte[]", dvmHumanReadableDescriptor("[B"));
+  ASSERT_EQ("byte[][]", dvmHumanReadableDescriptor("[[B"));
+  ASSERT_EQ("char[]", dvmHumanReadableDescriptor("[C"));
+  ASSERT_EQ("char[][]", dvmHumanReadableDescriptor("[[C"));
+  ASSERT_EQ("double[]", dvmHumanReadableDescriptor("[D"));
+  ASSERT_EQ("double[][]", dvmHumanReadableDescriptor("[[D"));
+  ASSERT_EQ("float[]", dvmHumanReadableDescriptor("[F"));
+  ASSERT_EQ("float[][]", dvmHumanReadableDescriptor("[[F"));
+  ASSERT_EQ("int[]", dvmHumanReadableDescriptor("[I"));
+  ASSERT_EQ("int[][]", dvmHumanReadableDescriptor("[[I"));
+  ASSERT_EQ("long[]", dvmHumanReadableDescriptor("[J"));
+  ASSERT_EQ("long[][]", dvmHumanReadableDescriptor("[[J"));
+  ASSERT_EQ("short[]", dvmHumanReadableDescriptor("[S"));
+  ASSERT_EQ("short[][]", dvmHumanReadableDescriptor("[[S"));
+}
+
+TEST(dvmHumanReadableDescriptor, PrimitiveScalars) {
+  ASSERT_EQ("boolean", dvmHumanReadableDescriptor("Z"));
+  ASSERT_EQ("byte", dvmHumanReadableDescriptor("B"));
+  ASSERT_EQ("char", dvmHumanReadableDescriptor("C"));
+  ASSERT_EQ("double", dvmHumanReadableDescriptor("D"));
+  ASSERT_EQ("float", dvmHumanReadableDescriptor("F"));
+  ASSERT_EQ("int", dvmHumanReadableDescriptor("I"));
+  ASSERT_EQ("long", dvmHumanReadableDescriptor("J"));
+  ASSERT_EQ("short", dvmHumanReadableDescriptor("S"));
+}
diff --git a/vm/AllocTracker.cpp b/vm/AllocTracker.cpp
new file mode 100644
index 0000000..94984ec
--- /dev/null
+++ b/vm/AllocTracker.cpp
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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()
+{
+    /* prep locks */
+    dvmInitMutex(&gDvm.allocTrackerLock);
+
+    /* initialized when enabled by DDMS */
+    assert(gDvm.allocRecords == NULL);
+
+    return true;
+}
+
+/*
+ * Release anything we're holding on to.
+ */
+void dvmAllocTrackerShutdown()
+{
+    free(gDvm.allocRecords);
+    dvmDestroyMutex(&gDvm.allocTrackerLock);
+}
+
+
+/*
+ * ===========================================================================
+ *      Collection
+ * ===========================================================================
+ */
+
+/*
+ * Enable allocation tracking.  Does nothing if tracking is already enabled.
+ *
+ * Returns "true" on success.
+ */
+bool dvmEnableAllocTracker()
+{
+    bool result = true;
+    dvmLockMutex(&gDvm.allocTrackerLock);
+
+    if (gDvm.allocRecords == NULL) {
+        ALOGI("Enabling alloc tracker (%d entries, %d frames --> %d bytes)",
+            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()
+{
+    dvmLockMutex(&gDvm.allocTrackerLock);
+
+    if (gDvm.allocRecords != NULL) {
+        free(gDvm.allocRecords);
+        gDvm.allocRecords = NULL;
+    }
+
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+}
+
+/*
+ * Get the last few stack frames.
+ */
+static void getStackFrames(Thread* self, AllocRecord* pRec)
+{
+    int stackDepth = 0;
+    void* fp;
+
+    fp = self->interpSave.curFrame;
+
+    while ((fp != NULL) && (stackDepth < kMaxAllocRecordStackDepth)) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*) fp)) {
+            pRec->stackElem[stackDepth].method = method;
+            if (dvmIsNativeMethod(method)) {
+                pRec->stackElem[stackDepth].pc = 0;
+            } else {
+                assert(saveArea->xtra.currentPc >= method->insns &&
+                        saveArea->xtra.currentPc <
+                        method->insns + dvmGetMethodInsnsSize(method));
+                pRec->stackElem[stackDepth].pc =
+                    (int) (saveArea->xtra.currentPc - method->insns);
+            }
+            stackDepth++;
+        }
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+
+    /* clear out the rest (normally there won't be any) */
+    while (stackDepth < kMaxAllocRecordStackDepth) {
+        pRec->stackElem[stackDepth].method = NULL;
+        pRec->stackElem[stackDepth].pc = 0;
+        stackDepth++;
+    }
+}
+
+/*
+ * Add a new allocation to the set.
+ */
+void dvmDoTrackAllocation(ClassObject* clazz, size_t size)
+{
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        ALOGW("alloc tracker: no thread");
+        return;
+    }
+
+    dvmLockMutex(&gDvm.allocTrackerLock);
+    if (gDvm.allocRecords == NULL) {
+        dvmUnlockMutex(&gDvm.allocTrackerLock);
+        return;
+    }
+
+    /* advance and clip */
+    if (++gDvm.allocRecordHead == 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++;
+
+    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()
+{
+    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);
+    }
+
+    ALOGI("class %d/%d, method %d/%d, file %d/%d",
+        dvmPointerSetGetCount(classNames), classCount,
+        dvmPointerSetGetCount(methodNames), methodCount,
+        dvmPointerSetGetCount(fileNames), fileCount);
+
+    return true;
+}
+
+/*
+ * Generate the base info (i.e. everything but the string tables).
+ *
+ * This should be called twice.  On the first call, "ptr" is NULL and
+ * "baseLen" is zero.  The return value is used to allocate a buffer.
+ * On the second call, "ptr" points to a data buffer, and "baseLen"
+ * holds the value from the result of the first call.
+ *
+ * The size of the output data is returned.
+ */
+static size_t generateBaseOutput(u1* ptr, size_t baseLen,
+    const PointerSet* classNames, const PointerSet* methodNames,
+    const PointerSet* fileNames)
+{
+    u1* origPtr = ptr;
+    int count = gDvm.allocRecordCount;
+    int idx = headIndex();
+
+    if (origPtr != NULL) {
+        set1(&ptr[0], kMessageHeaderLen);
+        set1(&ptr[1], kEntryHeaderLen);
+        set1(&ptr[2], kStackFrameLen);
+        set2BE(&ptr[3], count);
+        set4BE(&ptr[5], baseLen);
+        set2BE(&ptr[9], dvmPointerSetGetCount(classNames));
+        set2BE(&ptr[11], dvmPointerSetGetCount(methodNames));
+        set2BE(&ptr[13], dvmPointerSetGetCount(fileNames));
+    }
+    ptr += kMessageHeaderLen;
+
+    while (count--) {
+        AllocRecord* pRec = &gDvm.allocRecords[idx];
+
+        /* compute depth */
+        int  depth;
+        for (depth = 0; depth < kMaxAllocRecordStackDepth; depth++) {
+            if (pRec->stackElem[depth].method == NULL)
+                break;
+        }
+
+        /* output header */
+        if (origPtr != NULL) {
+            set4BE(&ptr[0], pRec->size);
+            set2BE(&ptr[4], pRec->threadId);
+            set2BE(&ptr[6],
+                dvmPointerSetFind(classNames, pRec->clazz->descriptor));
+            set1(&ptr[8], depth);
+        }
+        ptr += kEntryHeaderLen;
+
+        /* convert stack frames */
+        int i;
+        for (i = 0; i < depth; i++) {
+            if (origPtr != NULL) {
+                const Method* method = pRec->stackElem[i].method;
+                int lineNum;
+
+                lineNum = dvmLineNumFromPC(method, pRec->stackElem[i].pc);
+                if (lineNum > 32767)
+                    lineNum = 32767;
+
+                set2BE(&ptr[0], dvmPointerSetFind(classNames,
+                        method->clazz->descriptor));
+                set2BE(&ptr[2], dvmPointerSetFind(methodNames,
+                        method->name));
+                set2BE(&ptr[4], dvmPointerSetFind(fileNames,
+                        getMethodSourceFile(method)));
+                set2BE(&ptr[6], (u2)lineNum);
+            }
+            ptr += kStackFrameLen;
+        }
+
+        idx = (idx + 1) & (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.
+ */
+static int convertUtf8ToUtf16BEUA(u1* utf16Str, const char* utf8Str)
+{
+    u1* origUtf16Str = utf16Str;
+
+    while (*utf8Str != '\0') {
+        u2 utf16 = dexGetUtf16FromUtf8(&utf8Str);       /* advances utf8Str */
+        set2BE(utf16Str, utf16);
+        utf16Str += 2;
+    }
+
+    return (utf16Str - origUtf16Str) / 2;
+}
+
+/*
+ * Output a string table serially.
+ */
+static size_t outputStringTable(PointerSet* strings, u1* ptr)
+{
+    int count = dvmPointerSetGetCount(strings);
+    u1* origPtr = ptr;
+    int i;
+
+    for (i = 0; i < count; i++) {
+        const char* str = (const char*) dvmPointerSetGetEntry(strings, i);
+        int charLen;
+
+        /* copy UTF-8 string to big-endian unaligned UTF-16 */
+        charLen = convertUtf8ToUtf16BEUA(&ptr[4], str);
+        set4BE(&ptr[0], charLen);
+
+        ptr += 4 + charLen * 2;
+    }
+
+    return ptr - origPtr;
+}
+
+/*
+ * Generate a DDM packet with all of the tracked allocation data.
+ *
+ * On success, returns "true" with "*pData" and "*pDataLen" set.
+ */
+bool dvmGenerateTrackedAllocationReport(u1** pData, size_t* pDataLen)
+{
+    bool result = false;
+    u1* buffer = NULL;
+
+    dvmLockMutex(&gDvm.allocTrackerLock);
+
+    /*
+     * Part 1: generate string tables.
+     */
+    PointerSet* classNames = NULL;
+    PointerSet* methodNames = NULL;
+    PointerSet* fileNames = NULL;
+
+    /*
+     * Allocate storage.  Usually there's 60-120 of each thing (sampled
+     * when max=512), but it varies widely and isn't closely bound to
+     * the number of allocations we've captured.  The sets expand quickly
+     * if needed.
+     */
+    classNames = dvmPointerSetAlloc(128);
+    methodNames = dvmPointerSetAlloc(128);
+    fileNames = dvmPointerSetAlloc(128);
+    if (classNames == NULL || methodNames == NULL || fileNames == NULL) {
+        ALOGE("Failed allocating pointer sets");
+        goto bail;
+    }
+
+    if (!populateStringTables(classNames, methodNames, fileNames))
+        goto bail;
+
+    if (false) {
+        printf("Classes:\n");
+        dumpStringTable(classNames);
+        printf("Methods:\n");
+        dumpStringTable(methodNames);
+        printf("Files:\n");
+        dumpStringTable(fileNames);
+    }
+
+    /*
+     * Part 2: compute the size of the output.
+     *
+     * (Could also just write to an expanding buffer.)
+     */
+    size_t baseSize, totalSize;
+    baseSize = generateBaseOutput(NULL, 0, classNames, methodNames, fileNames);
+    assert(baseSize > 0);
+    totalSize = baseSize;
+    totalSize += computeStringTableSize(classNames);
+    totalSize += computeStringTableSize(methodNames);
+    totalSize += computeStringTableSize(fileNames);
+    ALOGI("Generated AT, size is %zd/%zd", baseSize, totalSize);
+
+    /*
+     * Part 3: allocate a buffer and generate the output.
+     */
+    u1* strPtr;
+
+    buffer = (u1*) malloc(totalSize);
+    strPtr = buffer + baseSize;
+    generateBaseOutput(buffer, baseSize, classNames, methodNames, fileNames);
+    strPtr += outputStringTable(classNames, strPtr);
+    strPtr += outputStringTable(methodNames, strPtr);
+    strPtr += outputStringTable(fileNames, strPtr);
+    if (strPtr - buffer != (int)totalSize) {
+        ALOGE("size mismatch (%d vs %zd)", strPtr - buffer, totalSize);
+        dvmAbort();
+    }
+    //dvmPrintHexDump(buffer, totalSize);
+
+    *pData = buffer;
+    *pDataLen = totalSize;
+    buffer = NULL;          // don't free -- caller will own
+    result = true;
+
+bail:
+    dvmPointerSetFree(classNames);
+    dvmPointerSetFree(methodNames);
+    dvmPointerSetFree(fileNames);
+    free(buffer);
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+    //dvmDumpTrackedAllocations(false);
+    return result;
+}
+
+/*
+ * Dump the tracked allocations to the log file.
+ *
+ * If "enable" is set, we try to enable the feature if it's not already
+ * active.
+ */
+void dvmDumpTrackedAllocations(bool enable)
+{
+    if (enable)
+        dvmEnableAllocTracker();
+
+    dvmLockMutex(&gDvm.allocTrackerLock);
+    if (gDvm.allocRecords == NULL) {
+        dvmUnlockMutex(&gDvm.allocTrackerLock);
+        return;
+    }
+
+    /*
+     * "idx" is the head of the list.  We want to start at the end of the
+     * list and move forward to the tail.
+     */
+    int idx = headIndex();
+    int count = gDvm.allocRecordCount;
+
+    ALOGI("Tracked allocations, (head=%d count=%d)",
+        gDvm.allocRecordHead, count);
+    while (count--) {
+        AllocRecord* pRec = &gDvm.allocRecords[idx];
+        ALOGI(" T=%-2d %6d %s",
+            pRec->threadId, pRec->size, pRec->clazz->descriptor);
+
+        if (true) {
+            for (int i = 0; i < kMaxAllocRecordStackDepth; i++) {
+                if (pRec->stackElem[i].method == NULL)
+                    break;
+
+                const Method* method = pRec->stackElem[i].method;
+                if (dvmIsNativeMethod(method)) {
+                    ALOGI("    %s.%s (Native)",
+                        method->clazz->descriptor, method->name);
+                } else {
+                    ALOGI("    %s.%s +%d",
+                        method->clazz->descriptor, method->name,
+                        pRec->stackElem[i].pc);
+                }
+            }
+        }
+
+        /* pause periodically to help logcat catch up */
+        if ((count % 5) == 0)
+            usleep(40000);
+
+        idx = (idx + 1) & (kNumAllocRecords-1);
+    }
+
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+    if (false) {
+        u1* data;
+        size_t dataLen;
+        if (dvmGenerateTrackedAllocationReport(&data, &dataLen))
+            free(data);
+    }
+}
diff --git a/vm/AllocTracker.h b/vm/AllocTracker.h
new file mode 100644
index 0000000..dede397
--- /dev/null
+++ b/vm/AllocTracker.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Allocation tracking and reporting.
+ */
+#ifndef DALVIK_ALLOCTRACKER_H_
+#define DALVIK_ALLOCTRACKER_H_
+
+/* initialization */
+bool dvmAllocTrackerStartup(void);
+void dvmAllocTrackerShutdown(void);
+
+struct AllocRecord;
+
+/*
+ * Enable allocation tracking.  Does nothing if tracking is already enabled.
+ */
+bool dvmEnableAllocTracker(void);
+
+/*
+ * Disable allocation tracking.  Does nothing if tracking is not enabled.
+ */
+void dvmDisableAllocTracker(void);
+
+/*
+ * If allocation tracking is enabled, add a new entry to the set.
+ */
+#define dvmTrackAllocation(_clazz, _size)                                   \
+    {                                                                       \
+        if (gDvm.allocRecords != NULL)                                      \
+            dvmDoTrackAllocation(_clazz, _size);                            \
+    }
+void dvmDoTrackAllocation(ClassObject* clazz, size_t size);
+
+/*
+ * Generate a DDM packet with all of the tracked allocation data.
+ *
+ * On success, returns "true" with "*pData" and "*pDataLen" set.  "*pData"
+ * refers to newly-allocated storage that must be freed by the caller.
+ */
+bool dvmGenerateTrackedAllocationReport(u1** pData, size_t* pDataLen);
+
+/*
+ * Dump the tracked allocations to the log file.  If "enable" is set, this
+ * will enable tracking if it's not already on.
+ */
+void dvmDumpTrackedAllocations(bool enable);
+
+#endif  // DALVIK_ALLOCTRACKER_H_
diff --git a/vm/Android.mk b/vm/Android.mk
new file mode 100644
index 0000000..0af62e8
--- /dev/null
+++ b/vm/Android.mk
@@ -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.
+
+#
+# Android.mk for Dalvik VM.
+#
+# This makefile builds both for host and target, and so the very large
+# swath of common definitions are factored out into a separate file to
+# minimize duplication.
+#
+# If you enable or disable optional features here (or in Dvm.mk),
+# rebuild the VM with:
+#
+#  make clean-libdvm clean-libdvm_assert clean-libdvm_sv clean-libdvm_interp
+#  make -j4 libdvm
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Build for the target (device).
+#
+
+ifeq ($(TARGET_CPU_SMP),true)
+    target_smp_flag := -DANDROID_SMP=1
+else
+    target_smp_flag := -DANDROID_SMP=0
+endif
+host_smp_flag := -DANDROID_SMP=1
+
+# Build the installed version (libdvm.so) first
+WITH_JIT := true
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+# Overwrite default settings
+LOCAL_MODULE := libdvm
+LOCAL_CFLAGS += $(target_smp_flag)
+
+# Define WITH_ADDRESS_SANITIZER to build an ASan-instrumented version of the
+# library in /system/lib/asan/libdvm.so.
+ifneq ($(strip $(WITH_ADDRESS_SANITIZER)),)
+    LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/asan
+    LOCAL_ADDRESS_SANITIZER := true
+    LOCAL_CFLAGS := $(filter-out $(CLANG_CONFIG_UNKNOWN_CFLAGS),$(LOCAL_CFLAGS))
+endif
+
+# TODO: split out the asflags.
+LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Derivation #1
+# Enable assertions and JIT tuning
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+                -DWITH_JIT_TUNING $(target_smp_flag)
+LOCAL_MODULE := libdvm_assert
+include $(BUILD_SHARED_LIBRARY)
+
+ifneq ($(dvm_arch),mips)    # MIPS support for self-verification is incomplete
+
+    # Derivation #2
+    # Enable assertions and JIT self-verification
+    include $(LOCAL_PATH)/ReconfigureDvm.mk
+    LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+                    -DWITH_SELF_VERIFICATION $(target_smp_flag)
+    # TODO: split out the asflags.
+    LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+    LOCAL_MODULE := libdvm_sv
+    include $(BUILD_SHARED_LIBRARY)
+
+endif # dvm_arch!=mips
+
+# Derivation #3
+# Compile out the JIT
+WITH_JIT := false
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+LOCAL_CFLAGS += $(target_smp_flag)
+# TODO: split out the asflags.
+LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+LOCAL_MODULE := libdvm_interp
+include $(BUILD_SHARED_LIBRARY)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+    include $(CLEAR_VARS)
+
+    # Variables used in the included Dvm.mk.
+    dvm_os := $(HOST_OS)
+    dvm_arch := $(HOST_ARCH)
+    # Note: HOST_ARCH_VARIANT isn't defined.
+    dvm_arch_variant := $(HOST_ARCH)
+    WITH_JIT := true
+    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 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)
+    # TODO: split out the asflags.
+    LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := libdvm
+
+    include $(BUILD_HOST_SHARED_LIBRARY)
+
+    # Copy the dalvik shell script to the host's bin directory
+    include $(CLEAR_VARS)
+    LOCAL_IS_HOST_MODULE := true
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE_CLASS := EXECUTABLES
+    LOCAL_MODULE := dalvik
+    include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dalvik | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+endif
diff --git a/vm/Atomic.cpp b/vm/Atomic.cpp
new file mode 100644
index 0000000..ccbf64a
--- /dev/null
+++ b/vm/Atomic.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+
+#include <cutils/atomic.h>
+
+#if defined(__arm__)
+#include <machine/cpu-features.h>
+#endif
+
+/*****************************************************************************/
+
+#if defined(HAVE_MACOSX_IPC)
+#define NEED_MAC_QUASI_ATOMICS 1
+
+#elif defined(__i386__) || defined(__x86_64__)
+#define NEED_PTHREADS_QUASI_ATOMICS 1
+
+#elif defined(__mips__)
+#define NEED_PTHREADS_QUASI_ATOMICS 1
+
+#elif defined(__arm__)
+
+// TODO: Clang can not process our inline assembly at the moment.
+#if defined(__ARM_HAVE_LDREXD) && !defined(__clang__)
+#define NEED_ARM_LDREXD_QUASI_ATOMICS 1
+#else
+#define NEED_PTHREADS_QUASI_ATOMICS 1
+#endif
+
+#else
+#error "Unsupported atomic operations for this platform"
+#endif
+
+/*****************************************************************************/
+
+#if NEED_ARM_LDREXD_QUASI_ATOMICS
+
+static inline int64_t dvmQuasiAtomicSwap64Body(int64_t newvalue,
+                                               volatile int64_t* addr)
+{
+    int64_t prev;
+    int status;
+    do {
+        __asm__ __volatile__ ("@ dvmQuasiAtomicSwap64\n"
+            "ldrexd     %0, %H0, [%3]\n"
+            "strexd     %1, %4, %H4, [%3]"
+            : "=&r" (prev), "=&r" (status), "+m"(*addr)
+            : "r" (addr), "r" (newvalue)
+            : "cc");
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t newvalue, volatile int64_t* addr)
+{
+    return dvmQuasiAtomicSwap64Body(newvalue, addr);
+}
+
+int64_t dvmQuasiAtomicSwap64Sync(int64_t newvalue, volatile int64_t* addr)
+{
+    int64_t prev;
+    ANDROID_MEMBAR_STORE();
+    prev = dvmQuasiAtomicSwap64Body(newvalue, addr);
+    ANDROID_MEMBAR_FULL();
+    return prev;
+}
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    int64_t prev;
+    int status;
+    do {
+        __asm__ __volatile__ ("@ dvmQuasiAtomicCas64\n"
+            "ldrexd     %0, %H0, [%3]\n"
+            "mov        %1, #0\n"
+            "teq        %0, %4\n"
+            "teqeq      %H0, %H4\n"
+            "strexdeq   %1, %5, %H5, [%3]"
+            : "=&r" (prev), "=&r" (status), "+m"(*addr)
+            : "r" (addr), "Ir" (oldvalue), "r" (newvalue)
+            : "cc");
+    } while (__builtin_expect(status != 0, 0));
+    return prev != oldvalue;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    int64_t value;
+    __asm__ __volatile__ ("@ dvmQuasiAtomicRead64\n"
+        "ldrexd     %0, %H0, [%1]"
+        : "=&r" (value)
+        : "r" (addr));
+    return value;
+}
+#endif
+
+/*****************************************************************************/
+
+#if NEED_MAC_QUASI_ATOMICS
+
+#include <libkern/OSAtomic.h>
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
+            (int64_t*)addr) == 0;
+}
+
+
+static inline int64_t dvmQuasiAtomicSwap64Body(int64_t value,
+                                               volatile int64_t* addr)
+{
+    int64_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (dvmQuasiAtomicCas64(oldValue, value, addr));
+    return oldValue;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+    return dvmQuasiAtomicSwap64Body(value, addr);
+}
+
+int64_t dvmQuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr)
+{
+    int64_t oldValue;
+    ANDROID_MEMBAR_STORE();
+    oldValue = dvmQuasiAtomicSwap64Body(value, addr);
+    /* TUNING: barriers can be avoided on some architectures */
+    ANDROID_MEMBAR_FULL();
+    return oldValue;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    return OSAtomicAdd64Barrier(0, addr);
+}
+#endif
+
+/*****************************************************************************/
+
+#if NEED_PTHREADS_QUASI_ATOMICS
+
+// In the absence of a better implementation, we implement the 64-bit atomic
+// operations through mutex locking.
+
+// another twist is that we use a small array of mutexes to dispatch
+// the contention locks from different memory addresses
+
+#include <pthread.h>
+
+static const size_t kSwapLockCount = 32;
+static pthread_mutex_t* gSwapLocks[kSwapLockCount];
+
+void dvmQuasiAtomicsStartup() {
+    for (size_t i = 0; i < kSwapLockCount; ++i) {
+        pthread_mutex_t* m = new pthread_mutex_t;
+        dvmInitMutex(m);
+        gSwapLocks[i] = m;
+    }
+}
+
+void dvmQuasiAtomicsShutdown() {
+    for (size_t i = 0; i < kSwapLockCount; ++i) {
+        pthread_mutex_t* m = gSwapLocks[i];
+        gSwapLocks[i] = NULL;
+        if (m != NULL) {
+            dvmDestroyMutex(m);
+        }
+        delete m;
+    }
+}
+
+static inline pthread_mutex_t* GetSwapLock(const volatile int64_t* addr) {
+    return gSwapLocks[((unsigned)(void*)(addr) >> 3U) % kSwapLockCount];
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+    int64_t oldValue;
+    pthread_mutex_t* lock = GetSwapLock(addr);
+
+    pthread_mutex_lock(lock);
+
+    oldValue = *addr;
+    *addr    = value;
+
+    pthread_mutex_unlock(lock);
+    return oldValue;
+}
+
+/* Same as dvmQuasiAtomicSwap64 - mutex handles barrier */
+int64_t dvmQuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr)
+{
+    return dvmQuasiAtomicSwap64(value, addr);
+}
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    int result;
+    pthread_mutex_t* lock = GetSwapLock(addr);
+
+    pthread_mutex_lock(lock);
+
+    if (*addr == oldvalue) {
+        *addr  = newvalue;
+        result = 0;
+    } else {
+        result = 1;
+    }
+    pthread_mutex_unlock(lock);
+    return result;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    int64_t result;
+    pthread_mutex_t* lock = GetSwapLock(addr);
+
+    pthread_mutex_lock(lock);
+    result = *addr;
+    pthread_mutex_unlock(lock);
+    return result;
+}
+
+#else
+
+// The other implementations don't need any special setup.
+void dvmQuasiAtomicsStartup() {}
+void dvmQuasiAtomicsShutdown() {}
+
+#endif /*NEED_PTHREADS_QUASI_ATOMICS*/
diff --git a/vm/Atomic.h b/vm/Atomic.h
new file mode 100644
index 0000000..becbeeb
--- /dev/null
+++ b/vm/Atomic.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Atomic operations
+ */
+#ifndef DALVIK_ATOMIC_H_
+#define DALVIK_ATOMIC_H_
+
+#include <cutils/atomic.h>          /* use common Android atomic ops */
+#include <cutils/atomic-inline.h>   /* and some uncommon ones */
+
+void dvmQuasiAtomicsStartup();
+void dvmQuasiAtomicsShutdown();
+
+/*
+ * NOTE: Two "quasiatomic" operations on the exact same memory address
+ * are guaranteed to operate atomically with respect to each other,
+ * but no guarantees are made about quasiatomic operations mixed with
+ * non-quasiatomic operations on the same address, nor about
+ * quasiatomic operations that are performed on partially-overlapping
+ * memory.
+ *
+ * Only the "Sync" versions of these provide a memory barrier.
+ */
+
+/*
+ * Swap the 64-bit value at "addr" with "value".  Returns the previous
+ * value.
+ */
+extern "C" int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr);
+
+/*
+ * Swap the 64-bit value at "addr" with "value".  Returns the previous
+ * value.  Provides memory barriers.
+ */
+extern "C" int64_t dvmQuasiAtomicSwap64Sync(int64_t value,
+                                            volatile int64_t* addr);
+
+/*
+ * Read the 64-bit value at "addr".
+ */
+extern "C" int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr);
+
+/*
+ * If the value at "addr" is equal to "oldvalue", replace it with "newvalue"
+ * and return 0.  Otherwise, don't swap, and return nonzero.
+ */
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr);
+
+#endif  // DALVIK_ATOMIC_H_
diff --git a/vm/AtomicCache.cpp b/vm/AtomicCache.cpp
new file mode 100644
index 0000000..6fbbc12
--- /dev/null
+++ b/vm/AtomicCache.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Mutex-free cache.  Each entry has two 32-bit keys, one 32-bit value,
+ * and a 32-bit version.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * I think modern C mandates that the results of a boolean expression are
+ * 0 or 1.  If not, or we suddenly turn into C++ and bool != int, use this.
+ */
+#define BOOL_TO_INT(x)  (x)
+//#define BOOL_TO_INT(x)  ((x) ? 1 : 0)
+
+#define CPU_CACHE_WIDTH         32
+#define CPU_CACHE_WIDTH_1       (CPU_CACHE_WIDTH-1)
+
+#define ATOMIC_LOCK_FLAG        (1 << 31)
+
+/*
+ * Allocate cache.
+ */
+AtomicCache* dvmAllocAtomicCache(int numEntries)
+{
+    AtomicCache* newCache;
+
+    newCache = (AtomicCache*) calloc(1, sizeof(AtomicCache));
+    if (newCache == NULL)
+        return NULL;
+
+    newCache->numEntries = numEntries;
+
+    newCache->entryAlloc = calloc(1,
+        sizeof(AtomicCacheEntry) * numEntries + CPU_CACHE_WIDTH);
+    if (newCache->entryAlloc == NULL) {
+        free(newCache);
+        return NULL;
+    }
+
+    /*
+     * Adjust storage to align on a 32-byte boundary.  Each entry is 16 bytes
+     * wide.  This ensures that each cache entry sits on a single CPU cache
+     * line.
+     */
+    assert(sizeof(AtomicCacheEntry) == 16);
+    newCache->entries = (AtomicCacheEntry*)
+        (((int) newCache->entryAlloc + CPU_CACHE_WIDTH_1) & ~CPU_CACHE_WIDTH_1);
+
+    return newCache;
+}
+
+/*
+ * Free cache.
+ */
+void dvmFreeAtomicCache(AtomicCache* cache)
+{
+    if (cache != NULL) {
+        free(cache->entryAlloc);
+        free(cache);
+    }
+}
+
+
+/*
+ * Update a cache entry.
+ *
+ * In the event of a collision with another thread, the update may be skipped.
+ *
+ * We only need "pCache" for stats.
+ */
+void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
+    u4 firstVersion
+#if CALC_CACHE_STATS > 0
+    , AtomicCache* pCache
+#endif
+    )
+{
+    /*
+     * The fields don't match, so we want to update them.  There is a risk
+     * that another thread is also trying to update them, so we grab an
+     * ownership flag to lock out other threads.
+     *
+     * If the lock flag was already set in "firstVersion", somebody else
+     * was in mid-update, and we don't want to continue here.  (This means
+     * that using "firstVersion" as the "before" argument to the CAS would
+     * succeed when it shouldn't and vice-versa -- we could also just pass
+     * in (firstVersion & ~ATOMIC_LOCK_FLAG) as the first argument.)
+     *
+     * NOTE: we don't deal with the situation where we overflow the version
+     * counter and trample the ATOMIC_LOCK_FLAG (at 2^31).  Probably not
+     * a real concern.
+     */
+    if ((firstVersion & ATOMIC_LOCK_FLAG) != 0 ||
+        android_atomic_release_cas(
+                firstVersion, firstVersion | ATOMIC_LOCK_FLAG,
+                (volatile s4*) &pEntry->version) != 0)
+    {
+        /*
+         * We couldn't get the write lock.  Return without updating the table.
+         */
+#if CALC_CACHE_STATS > 0
+        pCache->fail++;
+#endif
+        return;
+    }
+
+    /* must be even-valued on entry */
+    assert((firstVersion & 0x01) == 0);
+
+#if CALC_CACHE_STATS > 0
+    /* for stats, assume a key value of zero indicates an empty entry */
+    if (pEntry->key1 == 0)
+        pCache->fills++;
+    else
+        pCache->misses++;
+#endif
+
+    /*
+     * We have the write lock, but somebody could be reading this entry
+     * while we work.  We use memory barriers to ensure that the state
+     * is always consistent when the version number is even.
+     */
+    u4 newVersion = (firstVersion | ATOMIC_LOCK_FLAG) + 1;
+    assert((newVersion & 0x01) == 1);
+
+    pEntry->version = newVersion;
+
+    android_atomic_release_store(key1, (int32_t*) &pEntry->key1);
+    pEntry->key2 = key2;
+    pEntry->value = value;
+
+    newVersion++;
+    android_atomic_release_store(newVersion, (int32_t*) &pEntry->version);
+
+    /*
+     * Clear the lock flag.  Nobody else should have been able to modify
+     * pEntry->version, so if this fails the world is broken.
+     */
+    assert(newVersion == ((firstVersion + 2) | ATOMIC_LOCK_FLAG));
+    if (android_atomic_release_cas(
+            newVersion, newVersion & ~ATOMIC_LOCK_FLAG,
+            (volatile s4*) &pEntry->version) != 0)
+    {
+        //ALOGE("unable to reset the instanceof cache ownership");
+        dvmAbort();
+    }
+}
+
+
+/*
+ * Dump the "instanceof" cache stats.
+ */
+void dvmDumpAtomicCacheStats(const AtomicCache* pCache)
+{
+    if (pCache == NULL)
+        return;
+    dvmFprintf(stdout,
+        "Cache stats: trv=%d fai=%d hit=%d mis=%d fil=%d %d%% (size=%d)\n",
+        pCache->trivial, pCache->fail, pCache->hits,
+        pCache->misses, pCache->fills,
+        (pCache->hits == 0) ? 0 :
+            pCache->hits * 100 /
+                (pCache->fail + pCache->hits + pCache->misses + pCache->fills),
+        pCache->numEntries);
+}
diff --git a/vm/AtomicCache.h b/vm/AtomicCache.h
new file mode 100644
index 0000000..42ba6be
--- /dev/null
+++ b/vm/AtomicCache.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Mutex-free cache for key1+key2=value.
+ */
+#ifndef DALVIK_ATOMICCACHE_H_
+#define DALVIK_ATOMICCACHE_H_
+
+/*
+ * If set to "1", gather some stats on our caching success rate.
+ */
+#define CALC_CACHE_STATS 0
+
+
+/*
+ * One entry in the cache.  We store two keys (e.g. the classes that are
+ * arguments to "instanceof") and one result (e.g. a boolean value).
+ *
+ * Must be exactly 16 bytes.
+ */
+struct AtomicCacheEntry {
+    u4          key1;
+    u4          key2;
+    u4          value;
+    volatile u4 version;    /* version and lock flag */
+};
+
+/*
+ * One cache.
+ *
+ * Thought: we might be able to save a few cycles by storing the cache
+ * struct and "entries" separately, avoiding an indirection.  (We already
+ * handle "numEntries" separately in ATOMIC_CACHE_LOOKUP.)
+ */
+struct AtomicCache {
+    AtomicCacheEntry*   entries;        /* array of entries */
+    int         numEntries;             /* #of entries, must be power of 2 */
+
+    void*       entryAlloc;             /* memory allocated for entries */
+
+    /* cache stats; note we don't guarantee atomic increments for these */
+    int         trivial;                /* cache access not required */
+    int         fail;                   /* contention failure */
+    int         hits;                   /* found entry in cache */
+    int         misses;                 /* entry was for other keys */
+    int         fills;                  /* entry was empty */
+};
+
+/*
+ * Do a cache lookup.  We need to be able to read and write entries
+ * atomically.  There are a couple of ways to do this:
+ *  (1) Have a global lock.  A mutex is too heavy, so instead we would use
+ *      an atomic flag.  If the flag is set, we could sit and spin,
+ *      but if we're a high-priority thread that may cause a lockup.
+ *      Better to just ignore the cache and do the full computation.
+ *  (2) Have a "version" that gets incremented atomically when a write
+ *      begins and again when it completes.  Compare the version before
+ *      and after doing reads.  So long as we have memory barriers in the
+ *      right place the compiler and CPU will do the right thing, allowing
+ *      us to skip atomic ops in the common read case.  The table updates
+ *      are expensive, requiring two writes with barriers.  We also need
+ *      some sort of lock to ensure that nobody else tries to start an
+ *      update while we're in the middle of one.
+ *
+ * We expect a 95+% hit rate for the things we use this for, so #2 is
+ * much better than #1.
+ *
+ * _cache is an AtomicCache*
+ * _cacheSize is _cache->cacheSize (can save a cycle avoiding the lookup)
+ * _key1, _key2 are the keys
+ *
+ * Define a function ATOMIC_CACHE_CALC that returns a 32-bit value.  This
+ * will be invoked when we need to compute the value.
+ *
+ * Returns the value.
+ */
+#if CALC_CACHE_STATS > 0
+# define CACHE_XARG(_value) ,_value
+#else
+# define CACHE_XARG(_value)
+#endif
+#define ATOMIC_CACHE_LOOKUP(_cache, _cacheSize, _key1, _key2) ({            \
+    AtomicCacheEntry* pEntry;                                               \
+    int hash;                                                               \
+    u4 firstVersion, secondVersion;                                         \
+    u4 value;                                                               \
+                                                                            \
+    /* simple hash function */                                              \
+    hash = (((u4)(_key1) >> 2) ^ (u4)(_key2)) & ((_cacheSize)-1);           \
+    pEntry = (_cache)->entries + hash;                                      \
+                                                                            \
+    firstVersion = android_atomic_acquire_load((int32_t*)&pEntry->version); \
+                                                                            \
+    if (pEntry->key1 == (u4)(_key1) && pEntry->key2 == (u4)(_key2)) {       \
+        /*                                                                  \
+         * The fields match.  Get the value, then read the version a        \
+         * second time to verify that we didn't catch a partial update.     \
+         * We're also hosed if "firstVersion" was odd, indicating that      \
+         * an update was in progress before we got here (unlikely).         \
+         */                                                                 \
+        value = android_atomic_acquire_load((int32_t*) &pEntry->value);     \
+        secondVersion = pEntry->version;                                    \
+                                                                            \
+        if ((firstVersion & 0x01) != 0 || firstVersion != secondVersion) {  \
+            /*                                                              \
+             * We clashed with another thread.  Instead of sitting and      \
+             * spinning, which might not complete if we're a high priority  \
+             * thread, just do the regular computation.                     \
+             */                                                             \
+            if (CALC_CACHE_STATS)                                           \
+                (_cache)->fail++;                                           \
+            value = (u4) ATOMIC_CACHE_CALC;                                 \
+        } else {                                                            \
+            /* all good */                                                  \
+            if (CALC_CACHE_STATS)                                           \
+                (_cache)->hits++;                                           \
+        }                                                                   \
+    } else {                                                                \
+        /*                                                                  \
+         * Compute the result and update the cache.  We really want this    \
+         * to happen in a different method -- it makes the ARM frame        \
+         * setup for this method simpler, which gives us a ~10% speed       \
+         * boost.                                                           \
+         */                                                                 \
+        value = (u4) ATOMIC_CACHE_CALC;                                     \
+        if (value == 0 && ATOMIC_CACHE_NULL_ALLOWED) { \
+            dvmUpdateAtomicCache((u4) (_key1), (u4) (_key2), value, pEntry, \
+                        firstVersion CACHE_XARG(_cache) ); \
+        } \
+    }                                                                       \
+    value;                                                                  \
+})
+
+/*
+ * Allocate a cache.
+ */
+AtomicCache* dvmAllocAtomicCache(int numEntries);
+
+/*
+ * Free a cache.
+ */
+void dvmFreeAtomicCache(AtomicCache* cache);
+
+/*
+ * Update a cache entry.
+ *
+ * Making the last argument optional, instead of merely unused, saves us
+ * a few percent in the ATOMIC_CACHE_LOOKUP time.
+ */
+void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
+    u4 firstVersion
+#if CALC_CACHE_STATS > 0
+    , AtomicCache* pCache
+#endif
+    );
+
+/*
+ * Debugging.
+ */
+void dvmDumpAtomicCacheStats(const AtomicCache* pCache);
+
+#endif  // DALVIK_ATOMICCACHE_H_
diff --git a/vm/BitVector.cpp b/vm/BitVector.cpp
new file mode 100644
index 0000000..8b1d141
--- /dev/null
+++ b/vm/BitVector.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of an expandable bit vector.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <strings.h>
+
+#define kBitVectorGrowth    4   /* increase by 4 u4s when limit hit */
+
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ */
+BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable)
+{
+    BitVector* bv;
+    unsigned int count;
+
+    assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
+
+    bv = (BitVector*) malloc(sizeof(BitVector));
+
+    count = (startBits + 31) >> 5;
+
+    bv->storageSize = count;
+    bv->expandable = expandable;
+    bv->storage = (u4*) calloc(count, sizeof(u4));
+    return bv;
+}
+
+/*
+ * Free a BitVector.
+ */
+void dvmFreeBitVector(BitVector* pBits)
+{
+    if (pBits == NULL)
+        return;
+
+    free(pBits->storage);
+    free(pBits);
+}
+
+/*
+ * "Allocate" the first-available bit in the bitmap.
+ *
+ * This is not synchronized.  The caller is expected to hold some sort of
+ * lock that prevents multiple threads from executing simultaneously in
+ * dvmAllocBit/dvmFreeBit.
+ */
+int dvmAllocBit(BitVector* pBits)
+{
+    unsigned int word, bit;
+
+retry:
+    for (word = 0; word < pBits->storageSize; word++) {
+        if (pBits->storage[word] != 0xffffffff) {
+            /*
+             * There are unallocated bits in this word.  Return the first.
+             */
+            bit = ffs(~(pBits->storage[word])) -1;
+            assert(bit < 32);
+            pBits->storage[word] |= 1 << bit;
+            return (word << 5) | bit;
+        }
+    }
+
+    /*
+     * Ran out of space, allocate more if we're allowed to.
+     */
+    if (!pBits->expandable)
+        return -1;
+
+    pBits->storage = (u4*)realloc(pBits->storage,
+                    (pBits->storageSize + kBitVectorGrowth) * sizeof(u4));
+    memset(&pBits->storage[pBits->storageSize], 0x00,
+        kBitVectorGrowth * sizeof(u4));
+    pBits->storageSize += kBitVectorGrowth;
+    goto retry;
+}
+
+/*
+ * Mark the specified bit as "set".
+ */
+void dvmSetBit(BitVector* pBits, unsigned int num)
+{
+    if (num >= pBits->storageSize * sizeof(u4) * 8) {
+        if (!pBits->expandable) {
+            ALOGE("Attempt to set bit outside valid range (%d, limit is %d)",
+                num, pBits->storageSize * sizeof(u4) * 8);
+            dvmAbort();
+        }
+
+        /* Round up to word boundaries for "num+1" bits */
+        unsigned int newSize = (num + 1 + 31) >> 5;
+        assert(newSize > pBits->storageSize);
+        pBits->storage = (u4*)realloc(pBits->storage, newSize * sizeof(u4));
+        if (pBits->storage == NULL) {
+            ALOGE("BitVector expansion to %d failed", newSize * sizeof(u4));
+            dvmAbort();
+        }
+        memset(&pBits->storage[pBits->storageSize], 0x00,
+            (newSize - pBits->storageSize) * sizeof(u4));
+        pBits->storageSize = newSize;
+    }
+
+    pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+}
+
+/*
+ * Mark the specified bit as "clear".
+ */
+void dvmClearBit(BitVector* pBits, unsigned int num)
+{
+    assert(num < pBits->storageSize * sizeof(u4) * 8);
+
+    pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+}
+
+/*
+ * Mark all bits bit as "clear".
+ */
+void dvmClearAllBits(BitVector* pBits)
+{
+    unsigned int count = pBits->storageSize;
+    memset(pBits->storage, 0, count * sizeof(u4));
+}
+
+/*
+ * Mark specified number of bits as "set". Cannot set all bits like ClearAll
+ * since there might be unused bits - setting those to one will confuse the
+ * iterator.
+ */
+void dvmSetInitialBits(BitVector* pBits, unsigned int numBits)
+{
+    unsigned int idx;
+    assert(((numBits + 31) >> 5) <= pBits->storageSize);
+    for (idx = 0; idx < (numBits >> 5); idx++) {
+        pBits->storage[idx] = -1;
+    }
+    unsigned int remNumBits = numBits & 0x1f;
+    if (remNumBits) {
+        pBits->storage[idx] = (1 << remNumBits) - 1;
+    }
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool dvmIsBitSet(const BitVector* pBits, unsigned int num)
+{
+    assert(num < pBits->storageSize * sizeof(u4) * 8);
+
+    unsigned int val = pBits->storage[num >> 5] & (1 << (num & 0x1f));
+    return (val != 0);
+}
+
+/*
+ * Count the number of bits that are set.
+ */
+int dvmCountSetBits(const BitVector* pBits)
+{
+    unsigned int word;
+    unsigned int count = 0;
+
+    for (word = 0; word < pBits->storageSize; word++) {
+        u4 val = pBits->storage[word];
+
+        if (val != 0) {
+            if (val == 0xffffffff) {
+                count += 32;
+            } else {
+                /* count the number of '1' bits */
+                while (val != 0) {
+                    val &= val - 1;
+                    count++;
+                }
+            }
+        }
+    }
+
+    return count;
+}
+
+/*
+ * If the vector sizes don't match, log an error and abort.
+ */
+static void checkSizes(const BitVector* bv1, const BitVector* bv2)
+{
+    if (bv1->storageSize != bv2->storageSize) {
+        ALOGE("Mismatched vector sizes (%d, %d)",
+            bv1->storageSize, bv2->storageSize);
+        dvmAbort();
+    }
+}
+
+/*
+ * Copy a whole vector to the other. Only do that when the both vectors have
+ * the same size.
+ */
+void dvmCopyBitVector(BitVector *dest, const BitVector *src)
+{
+    /* if dest is expandable and < src, we could expand dest to match */
+    checkSizes(dest, src);
+
+    memcpy(dest->storage, src->storage, sizeof(u4) * dest->storageSize);
+}
+
+/*
+ * Intersect two bit vectors and store the result to the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+                            const BitVector *src2)
+{
+    if (dest->storageSize != src1->storageSize ||
+        dest->storageSize != src2->storageSize ||
+        dest->expandable != src1->expandable ||
+        dest->expandable != src2->expandable)
+        return false;
+
+    unsigned int idx;
+    for (idx = 0; idx < dest->storageSize; idx++) {
+        dest->storage[idx] = src1->storage[idx] & src2->storage[idx];
+    }
+    return true;
+}
+
+/*
+ * Unify two bit vectors and store the result to the dest vector.
+ */
+bool dvmUnifyBitVectors(BitVector *dest, const BitVector *src1,
+                        const BitVector *src2)
+{
+    if (dest->storageSize != src1->storageSize ||
+        dest->storageSize != src2->storageSize ||
+        dest->expandable != src1->expandable ||
+        dest->expandable != src2->expandable)
+        return false;
+
+    unsigned int idx;
+    for (idx = 0; idx < dest->storageSize; idx++) {
+        dest->storage[idx] = src1->storage[idx] | src2->storage[idx];
+    }
+    return true;
+}
+
+/*
+ * Compare two bit vectors and return true if difference is seen.
+ */
+bool dvmCompareBitVectors(const BitVector *src1, const BitVector *src2)
+{
+    if (src1->storageSize != src2->storageSize ||
+        src1->expandable != src2->expandable)
+        return true;
+
+    unsigned int idx;
+    for (idx = 0; idx < src1->storageSize; idx++) {
+        if (src1->storage[idx] != src2->storage[idx]) return true;
+    }
+    return false;
+}
+
+/* Initialize the iterator structure */
+void dvmBitVectorIteratorInit(BitVector* pBits, BitVectorIterator* iterator)
+{
+    iterator->pBits = pBits;
+    iterator->bitSize = pBits->storageSize * sizeof(u4) * 8;
+    iterator->idx = 0;
+}
+
+/* Return the next position set to 1. -1 means end-of-element reached */
+int dvmBitVectorIteratorNext(BitVectorIterator* iterator)
+{
+    const BitVector* pBits = iterator->pBits;
+    u4 bitIndex = iterator->idx;
+
+    assert(iterator->bitSize == pBits->storageSize * sizeof(u4) * 8);
+    if (bitIndex >= iterator->bitSize) return -1;
+
+    for (; bitIndex < iterator->bitSize; bitIndex++) {
+        unsigned int wordIndex = bitIndex >> 5;
+        unsigned int mask = 1 << (bitIndex & 0x1f);
+        if (pBits->storage[wordIndex] & mask) {
+            iterator->idx = bitIndex+1;
+            return bitIndex;
+        }
+    }
+    /* No more set bits */
+    return -1;
+}
+
+
+/*
+ * Merge the contents of "src" into "dst", checking to see if this causes
+ * any changes to occur.  This is a logical OR.
+ *
+ * Returns "true" if the contents of the destination vector were modified.
+ */
+bool dvmCheckMergeBitVectors(BitVector* dst, const BitVector* src)
+{
+    bool changed = false;
+
+    checkSizes(dst, src);
+
+    unsigned int idx;
+    for (idx = 0; idx < dst->storageSize; idx++) {
+        u4 merged = src->storage[idx] | dst->storage[idx];
+        if (dst->storage[idx] != merged) {
+            dst->storage[idx] = merged;
+            changed = true;
+        }
+    }
+
+    return changed;
+}
diff --git a/vm/BitVector.h b/vm/BitVector.h
new file mode 100644
index 0000000..2ac0ddf
--- /dev/null
+++ b/vm/BitVector.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#ifndef DALVIK_BITVECTOR_H_
+#define DALVIK_BITVECTOR_H_
+
+/*
+ * Expanding bitmap, used for tracking resources.  Bits are numbered starting
+ * from zero.
+ *
+ * All operations on a BitVector are unsynchronized.
+ */
+struct BitVector {
+    bool    expandable;     /* expand bitmap if we run out? */
+    u4      storageSize;    /* current size, in 32-bit words */
+    u4*     storage;
+};
+
+/* Handy iterator to walk through the bit positions set to 1 */
+struct BitVectorIterator {
+    BitVector *pBits;
+    u4 idx;
+    u4 bitSize;
+};
+
+/* allocate a bit vector with enough space to hold "startBits" bits */
+BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable);
+void dvmFreeBitVector(BitVector* pBits);
+
+/*
+ * dvmAllocBit always allocates the first possible bit.  If we run out of
+ * space in the bitmap, and it's not marked expandable, dvmAllocBit
+ * returns -1.
+ *
+ * dvmSetBit sets the specified bit, expanding the vector if necessary
+ * (and possible).  Attempting to set a bit past the limit of a non-expandable
+ * bit vector will cause a fatal error.
+ *
+ * dvmSetInitialBits sets all bits in [0..numBits-1]. Won't expand the vector.
+ *
+ * dvmIsBitSet returns "true" if the bit is set.
+ */
+int dvmAllocBit(BitVector* pBits);
+void dvmSetBit(BitVector* pBits, unsigned int num);
+void dvmClearBit(BitVector* pBits, unsigned int num);
+void dvmClearAllBits(BitVector* pBits);
+void dvmSetInitialBits(BitVector* pBits, unsigned int numBits);
+bool dvmIsBitSet(const BitVector* pBits, unsigned int num);
+
+/* count the number of bits that have been set */
+int dvmCountSetBits(const BitVector* pBits);
+
+/* copy one vector to another of equal size */
+void dvmCopyBitVector(BitVector *dest, const BitVector *src);
+
+/*
+ * Intersect two bit vectors and store the result to the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+                            const BitVector *src2);
+
+/*
+ * Unify two bit vectors and store the result to the dest vector.
+ */
+bool dvmUnifyBitVectors(BitVector *dest, const BitVector *src1,
+                        const BitVector *src2);
+
+/*
+ * Merge the contents of "src" into "dst", checking to see if this causes
+ * any changes to occur.
+ *
+ * Returns "true" if the contents of the destination vector were modified.
+ */
+bool dvmCheckMergeBitVectors(BitVector* dst, const BitVector* src);
+
+/*
+ * Compare two bit vectors and return true if difference is seen.
+ */
+bool dvmCompareBitVectors(const BitVector *src1, const BitVector *src2);
+
+/* Initialize the iterator structure */
+void dvmBitVectorIteratorInit(BitVector* pBits, BitVectorIterator* iterator);
+
+/* Return the next position set to 1. -1 means end-of-vector reached */
+int dvmBitVectorIteratorNext(BitVectorIterator* iterator);
+
+#endif  // DALVIK_BITVECTOR_H_
diff --git a/vm/Bits.h b/vm/Bits.h
new file mode 100644
index 0000000..04d4fd1
--- /dev/null
+++ b/vm/Bits.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Some handy functions for manipulating bits and bytes.
+ *
+ * These get inlined, so prefer small size over maximum speed.
+ */
+#ifndef DALVIK_BITS_H_
+#define DALVIK_BITS_H_
+
+#include "Common.h"
+#include "Inlines.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Get 1 byte.  (Included to make the code more legible.)
+ */
+INLINE u1 get1(unsigned const char* pSrc)
+{
+    return *pSrc;
+}
+
+/*
+ * Get 2 big-endian bytes.
+ */
+INLINE u2 get2BE(unsigned char const* pSrc)
+{
+    return (pSrc[0] << 8) | pSrc[1];
+}
+
+/*
+ * Get 4 big-endian bytes.
+ */
+INLINE u4 get4BE(unsigned char const* pSrc)
+{
+    return (pSrc[0] << 24) | (pSrc[1] << 16) | (pSrc[2] << 8) | pSrc[3];
+}
+
+/*
+ * Get 8 big-endian bytes.
+ */
+INLINE u8 get8BE(unsigned char const* pSrc)
+{
+    u4 low, high;
+
+    high = pSrc[0];
+    high = (high << 8) | pSrc[1];
+    high = (high << 8) | pSrc[2];
+    high = (high << 8) | pSrc[3];
+    low = pSrc[4];
+    low = (low << 8) | pSrc[5];
+    low = (low << 8) | pSrc[6];
+    low = (low << 8) | pSrc[7];
+
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+INLINE u2 get2LE(unsigned char const* pSrc)
+{
+    return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+INLINE u4 get4LE(unsigned char const* pSrc)
+{
+    u4 result;
+
+    result = pSrc[0];
+    result |= pSrc[1] << 8;
+    result |= pSrc[2] << 16;
+    result |= pSrc[3] << 24;
+
+    return result;
+}
+
+/*
+ * Get 8 little-endian bytes.
+ */
+INLINE u8 get8LE(unsigned char const* pSrc)
+{
+    u4 low, high;
+
+    low = pSrc[0];
+    low |= pSrc[1] << 8;
+    low |= pSrc[2] << 16;
+    low |= pSrc[3] << 24;
+    high = pSrc[4];
+    high |= pSrc[5] << 8;
+    high |= pSrc[6] << 16;
+    high |= pSrc[7] << 24;
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Grab 1 byte and advance the data pointer.
+ */
+INLINE u1 read1(unsigned const char** ppSrc)
+{
+    return *(*ppSrc)++;
+}
+
+/*
+ * Grab 2 big-endian bytes and advance the data pointer.
+ */
+INLINE u2 read2BE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+
+    *ppSrc = pSrc + 2;
+    return pSrc[0] << 8 | pSrc[1];
+}
+
+/*
+ * Grab 4 big-endian bytes and advance the data pointer.
+ */
+INLINE u4 read4BE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 result;
+
+    result = pSrc[0] << 24;
+    result |= pSrc[1] << 16;
+    result |= pSrc[2] << 8;
+    result |= pSrc[3];
+
+    *ppSrc = pSrc + 4;
+    return result;
+}
+
+/*
+ * Get 8 big-endian bytes and advance the data pointer.
+ */
+INLINE u8 read8BE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 low, high;
+
+    high = pSrc[0];
+    high = (high << 8) | pSrc[1];
+    high = (high << 8) | pSrc[2];
+    high = (high << 8) | pSrc[3];
+    low = pSrc[4];
+    low = (low << 8) | pSrc[5];
+    low = (low << 8) | pSrc[6];
+    low = (low << 8) | pSrc[7];
+
+    *ppSrc = pSrc + 8;
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Grab 2 little-endian bytes and advance the data pointer.
+ */
+INLINE u2 read2LE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    *ppSrc = pSrc + 2;
+    return pSrc[0] | pSrc[1] << 8;
+}
+
+/*
+ * Grab 4 little-endian bytes and advance the data pointer.
+ */
+INLINE u4 read4LE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 result;
+
+    result = pSrc[0];
+    result |= pSrc[1] << 8;
+    result |= pSrc[2] << 16;
+    result |= pSrc[3] << 24;
+
+    *ppSrc = pSrc + 4;
+    return result;
+}
+
+/*
+ * Get 8 little-endian bytes and advance the data pointer.
+ */
+INLINE u8 read8LE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 low, high;
+
+    low = pSrc[0];
+    low |= pSrc[1] << 8;
+    low |= pSrc[2] << 16;
+    low |= pSrc[3] << 24;
+    high = pSrc[4];
+    high |= pSrc[5] << 8;
+    high |= pSrc[6] << 16;
+    high |= pSrc[7] << 24;
+
+    *ppSrc = pSrc + 8;
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Skip over a UTF-8 string (preceded by a 4-byte length).
+ */
+INLINE void skipUtf8String(unsigned char const** ppSrc)
+{
+    u4 length = read4BE(ppSrc);
+
+    (*ppSrc) += length;
+}
+
+/*
+ * Read a UTF-8 string into a fixed-size buffer, and null-terminate it.
+ *
+ * Returns the length of the original string.
+ */
+INLINE int readUtf8String(unsigned char const** ppSrc, char* buf, size_t bufLen)
+{
+    u4 length = read4BE(ppSrc);
+    size_t copyLen = (length < bufLen) ? length : bufLen-1;
+
+    memcpy(buf, *ppSrc, copyLen);
+    buf[copyLen] = '\0';
+
+    (*ppSrc) += length;
+    return length;
+}
+
+/*
+ * Read a UTF-8 string into newly-allocated storage, and null-terminate it.
+ *
+ * Returns the string and its length.  (The latter is probably unnecessary
+ * for the way we're using UTF8.)
+ */
+INLINE char* readNewUtf8String(unsigned char const** ppSrc, size_t* pLength)
+{
+    u4 length = read4BE(ppSrc);
+    char* buf;
+
+    buf = (char*) malloc(length+1);
+
+    memcpy(buf, *ppSrc, length);
+    buf[length] = '\0';
+
+    (*ppSrc) += length;
+
+    *pLength = length;
+    return buf;
+}
+
+
+/*
+ * Set 1 byte.  (Included to make code more consistent/legible.)
+ */
+INLINE void set1(u1* buf, u1 val)
+{
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 2 big-endian bytes.
+ */
+INLINE void set2BE(u1* buf, u2 val)
+{
+    *buf++ = (u1)(val >> 8);
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 4 big-endian bytes.
+ */
+INLINE void set4BE(u1* buf, u4 val)
+{
+    *buf++ = (u1)(val >> 24);
+    *buf++ = (u1)(val >> 16);
+    *buf++ = (u1)(val >> 8);
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 8 big-endian bytes.
+ */
+INLINE void set8BE(u1* buf, u8 val)
+{
+    *buf++ = (u1)(val >> 56);
+    *buf++ = (u1)(val >> 48);
+    *buf++ = (u1)(val >> 40);
+    *buf++ = (u1)(val >> 32);
+    *buf++ = (u1)(val >> 24);
+    *buf++ = (u1)(val >> 16);
+    *buf++ = (u1)(val >> 8);
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 2 little-endian bytes.
+ */
+INLINE void set2LE(u1* buf, u2 val)
+{
+    *buf++ = (u1)(val);
+    *buf = (u1)(val >> 8);
+}
+
+/*
+ * Set 4 little-endian bytes.
+ */
+INLINE void set4LE(u1* buf, u4 val)
+{
+    *buf++ = (u1)(val);
+    *buf++ = (u1)(val >> 8);
+    *buf++ = (u1)(val >> 16);
+    *buf = (u1)(val >> 24);
+}
+
+/*
+ * Set 8 little-endian bytes.
+ */
+INLINE void set8LE(u1* buf, u8 val)
+{
+    *buf++ = (u1)(val);
+    *buf++ = (u1)(val >> 8);
+    *buf++ = (u1)(val >> 16);
+    *buf++ = (u1)(val >> 24);
+    *buf++ = (u1)(val >> 32);
+    *buf++ = (u1)(val >> 40);
+    *buf++ = (u1)(val >> 48);
+    *buf = (u1)(val >> 56);
+}
+
+/*
+ * Stuff a UTF-8 string into the buffer.
+ */
+INLINE void setUtf8String(u1* buf, const u1* str)
+{
+    u4 strLen = strlen((const char*)str);
+
+    set4BE(buf, strLen);
+    memcpy(buf + sizeof(u4), str, strLen);
+}
+
+#endif  // DALVIK_BITS_H_
diff --git a/vm/CheckJni.cpp b/vm/CheckJni.cpp
new file mode 100644
index 0000000..8886ea2
--- /dev/null
+++ b/vm/CheckJni.cpp
@@ -0,0 +1,2351 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Support for -Xcheck:jni (the "careful" version of the JNI interfaces).
+ *
+ * We want to verify types, make sure class and field IDs are valid, and
+ * ensure that JNI's semantic expectations are being met.  JNI seems to
+ * be relatively lax when it comes to requirements for permission checks,
+ * e.g. access to private methods is generally allowed from anywhere.
+ */
+
+#include "Dalvik.h"
+#include "JniInternal.h"
+
+#include <sys/mman.h>
+#include <zlib.h>
+
+/*
+ * Abort if we are configured to bail out on JNI warnings.
+ */
+static void abortMaybe() {
+    if (!gDvmJni.warnOnly) {
+        dvmDumpThread(dvmThreadSelf(), false);
+        dvmAbort();
+    }
+}
+
+/*
+ * ===========================================================================
+ *      JNI call bridge wrapper
+ * ===========================================================================
+ */
+
+/*
+ * Check the result of a native method call that returns an object reference.
+ *
+ * The primary goal here is to verify that native code is returning the
+ * correct type of object.  If it's declared to return a String but actually
+ * returns a byte array, things will fail in strange ways later on.
+ *
+ * This can be a fairly expensive operation, since we have to look up the
+ * return type class by name in method->clazz' class loader.  We take a
+ * shortcut here and allow the call to succeed if the descriptor strings
+ * match.  This will allow some false-positives when a class is redefined
+ * by a class loader, but that's rare enough that it doesn't seem worth
+ * testing for.
+ *
+ * At this point, pResult->l has already been converted to an object pointer.
+ */
+static void checkCallResultCommon(const u4* args, const JValue* pResult,
+        const Method* method, Thread* self)
+{
+    assert(pResult->l != NULL);
+    const Object* resultObj = (const Object*) pResult->l;
+
+    if (resultObj == kInvalidIndirectRefObject) {
+        ALOGW("JNI WARNING: invalid reference returned from native code");
+        const Method* method = dvmGetCurrentJNIMethod();
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGW("             in %s.%s:%s", method->clazz->descriptor, method->name, desc);
+        free(desc);
+        abortMaybe();
+        return;
+    }
+
+    ClassObject* objClazz = resultObj->clazz;
+
+    /*
+     * Make sure that pResult->l is an instance of the type this
+     * method was expected to return.
+     */
+    const char* declType = dexProtoGetReturnType(&method->prototype);
+    const char* objType = objClazz->descriptor;
+    if (strcmp(declType, objType) == 0) {
+        /* names match; ignore class loader issues and allow it */
+        ALOGV("Check %s.%s: %s io %s (FAST-OK)",
+            method->clazz->descriptor, method->name, objType, declType);
+    } else {
+        /*
+         * Names didn't match.  We need to resolve declType in the context
+         * of method->clazz->classLoader, and compare the class objects
+         * for equality.
+         *
+         * Since we're returning an instance of declType, it's safe to
+         * assume that it has been loaded and initialized (or, for the case
+         * of an array, generated).  However, the current class loader may
+         * not be listed as an initiating loader, so we can't just look for
+         * it in the loaded-classes list.
+         */
+        ClassObject* declClazz = dvmFindClassNoInit(declType, method->clazz->classLoader);
+        if (declClazz == NULL) {
+            ALOGW("JNI WARNING: method declared to return '%s' returned '%s'",
+                declType, objType);
+            ALOGW("             failed in %s.%s ('%s' not found)",
+                method->clazz->descriptor, method->name, declType);
+            abortMaybe();
+            return;
+        }
+        if (!dvmInstanceof(objClazz, declClazz)) {
+            ALOGW("JNI WARNING: method declared to return '%s' returned '%s'",
+                declType, objType);
+            ALOGW("             failed in %s.%s",
+                method->clazz->descriptor, method->name);
+            abortMaybe();
+            return;
+        } else {
+            ALOGV("Check %s.%s: %s io %s (SLOW-OK)",
+                method->clazz->descriptor, method->name, objType, declType);
+        }
+    }
+}
+
+/*
+ * Determine if we need to check the return type coming out of the call.
+ *
+ * (We don't simply do this at the top of checkCallResultCommon() because
+ * this is on the critical path for native method calls.)
+ */
+static inline bool callNeedsCheck(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    return (method->shorty[0] == 'L' && !dvmCheckException(self) && pResult->l != NULL);
+}
+
+/*
+ * Check a call into native code.
+ */
+void dvmCheckCallJNIMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    dvmCallJNIMethod(args, pResult, method, self);
+    if (callNeedsCheck(args, pResult, method, self)) {
+        checkCallResultCommon(args, pResult, method, self);
+    }
+}
+
+/*
+ * ===========================================================================
+ *      JNI function helpers
+ * ===========================================================================
+ */
+
+static inline const JNINativeInterface* baseEnv(JNIEnv* env) {
+    return ((JNIEnvExt*) env)->baseFuncTable;
+}
+
+static inline const JNIInvokeInterface* baseVm(JavaVM* vm) {
+    return ((JavaVMExt*) vm)->baseFuncTable;
+}
+
+class ScopedCheckJniThreadState {
+public:
+    explicit ScopedCheckJniThreadState(JNIEnv* env) {
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+    }
+
+    ~ScopedCheckJniThreadState() {
+        dvmChangeStatus(NULL, THREAD_NATIVE);
+    }
+
+private:
+    // Disallow copy and assignment.
+    ScopedCheckJniThreadState(const ScopedCheckJniThreadState&);
+    void operator=(const ScopedCheckJniThreadState&);
+};
+
+/*
+ * Flags passed into ScopedCheck.
+ */
+#define kFlag_Default       0x0000
+
+#define kFlag_CritBad       0x0000      /* calling while in critical is bad */
+#define kFlag_CritOkay      0x0001      /* ...okay */
+#define kFlag_CritGet       0x0002      /* this is a critical "get" */
+#define kFlag_CritRelease   0x0003      /* this is a critical "release" */
+#define kFlag_CritMask      0x0003      /* bit mask to get "crit" value */
+
+#define kFlag_ExcepBad      0x0000      /* raised exceptions are bad */
+#define kFlag_ExcepOkay     0x0004      /* ...okay */
+
+#define kFlag_Release       0x0010      /* are we in a non-critical release function? */
+#define kFlag_NullableUtf   0x0020      /* are our UTF parameters nullable? */
+
+#define kFlag_Invocation    0x8000      /* Part of the invocation interface (JavaVM*) */
+
+static const char* indirectRefKindName(IndirectRef iref)
+{
+    return indirectRefKindToString(indirectRefKind(iref));
+}
+
+class ScopedCheck {
+public:
+    // For JNIEnv* functions.
+    explicit ScopedCheck(JNIEnv* env, int flags, const char* functionName) {
+        init(env, flags, functionName, true);
+        checkThread(flags);
+    }
+
+    // For JavaVM* functions.
+    explicit ScopedCheck(bool hasMethod, const char* functionName) {
+        init(NULL, kFlag_Invocation, functionName, hasMethod);
+    }
+
+    /*
+     * In some circumstances the VM will screen class names, but it doesn't
+     * for class lookup.  When things get bounced through a class loader, they
+     * can actually get normalized a couple of times; as a result, passing in
+     * a class name like "java.lang.Thread" instead of "java/lang/Thread" will
+     * work in some circumstances.
+     *
+     * This is incorrect and could cause strange behavior or compatibility
+     * problems, so we want to screen that out here.
+     *
+     * We expect "fully-qualified" class names, like "java/lang/Thread" or
+     * "[Ljava/lang/Object;".
+     */
+    void checkClassName(const char* className) {
+        if (!dexIsValidClassName(className, false)) {
+            ALOGW("JNI WARNING: illegal class name '%s' (%s)", className, mFunctionName);
+            ALOGW("             (should be formed like 'dalvik/system/DexFile')");
+            ALOGW("             or '[Ldalvik/system/DexFile;' or '[[B')");
+            abortMaybe();
+        }
+    }
+
+    void checkFieldTypeForGet(jfieldID fid, const char* expectedSignature, bool isStatic) {
+        if (fid == NULL) {
+            ALOGW("JNI WARNING: null jfieldID");
+            showLocation();
+            abortMaybe();
+        }
+
+        bool printWarn = false;
+        Field* field = (Field*) fid;
+        const char* actualSignature = field->signature;
+        if (*expectedSignature == 'L') {
+            // 'actualSignature' has the exact type.
+            // We just know we're expecting some kind of reference.
+            if (*actualSignature != 'L' && *actualSignature != '[') {
+                printWarn = true;
+            }
+        } else if (*actualSignature != *expectedSignature) {
+            printWarn = true;
+        }
+
+        if (!printWarn && isStatic && !dvmIsStaticField(field)) {
+            if (isStatic) {
+                ALOGW("JNI WARNING: accessing non-static field %s as static", field->name);
+            } else {
+                ALOGW("JNI WARNING: accessing static field %s as non-static", field->name);
+            }
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            ALOGW("JNI WARNING: %s for field '%s' of expected type %s, got %s",
+                    mFunctionName, field->name, expectedSignature, actualSignature);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that the field is of the appropriate type.  If the field has an
+     * object type, "jobj" is the object we're trying to assign into it.
+     *
+     * Works for both static and instance fields.
+     */
+    void checkFieldTypeForSet(jobject jobj, jfieldID fieldID, PrimitiveType prim, bool isStatic) {
+        if (fieldID == NULL) {
+            ALOGW("JNI WARNING: null jfieldID");
+            showLocation();
+            abortMaybe();
+        }
+
+        bool printWarn = false;
+        Field* field = (Field*) fieldID;
+        if ((field->signature[0] == 'L' || field->signature[0] == '[') && jobj != NULL) {
+            ScopedCheckJniThreadState ts(mEnv);
+            Object* obj = dvmDecodeIndirectRef(self(), jobj);
+            /*
+             * If jobj is a weak global ref whose referent has been cleared,
+             * obj will be NULL.  Otherwise, obj should always be non-NULL
+             * and valid.
+             */
+            if (obj != NULL && !dvmIsHeapAddress(obj)) {
+                ALOGW("JNI WARNING: field operation on invalid %s reference (%p)",
+                        indirectRefKindName(jobj), jobj);
+                printWarn = true;
+            } else {
+                ClassObject* fieldClass = dvmFindLoadedClass(field->signature);
+                ClassObject* objClass = obj->clazz;
+
+                assert(fieldClass != NULL);
+                assert(objClass != NULL);
+
+                if (!dvmInstanceof(objClass, fieldClass)) {
+                    ALOGW("JNI WARNING: set field '%s' expected type %s, got %s",
+                            field->name, field->signature, objClass->descriptor);
+                    printWarn = true;
+                }
+            }
+        } else if (dexGetPrimitiveTypeFromDescriptorChar(field->signature[0]) != prim) {
+            ALOGW("JNI WARNING: %s for field '%s' expected type %s, got %s",
+                    mFunctionName, field->name, field->signature, primitiveTypeToName(prim));
+            printWarn = true;
+        } else if (isStatic && !dvmIsStaticField(field)) {
+            if (isStatic) {
+                ALOGW("JNI WARNING: accessing non-static field %s as static", field->name);
+            } else {
+                ALOGW("JNI WARNING: accessing static field %s as non-static", field->name);
+            }
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that this instance field ID is valid for this object.
+     *
+     * Assumes "jobj" has already been validated.
+     */
+    void checkInstanceFieldID(jobject jobj, jfieldID fieldID) {
+        ScopedCheckJniThreadState ts(mEnv);
+
+        Object* obj = dvmDecodeIndirectRef(self(), jobj);
+        if (!dvmIsHeapAddress(obj)) {
+            ALOGW("JNI ERROR: field operation on invalid reference (%p)", jobj);
+            dvmAbort();
+        }
+
+        /*
+         * Check this class and all of its superclasses for a matching field.
+         * Don't need to scan interfaces.
+         */
+        ClassObject* clazz = obj->clazz;
+        while (clazz != NULL) {
+            if ((InstField*) fieldID >= clazz->ifields &&
+                    (InstField*) fieldID < clazz->ifields + clazz->ifieldCount) {
+                return;
+            }
+
+            clazz = clazz->super;
+        }
+
+        ALOGW("JNI WARNING: instance fieldID %p not valid for class %s",
+                fieldID, obj->clazz->descriptor);
+        showLocation();
+        abortMaybe();
+    }
+
+    /*
+     * Verify that the pointer value is non-NULL.
+     */
+    void checkNonNull(const void* ptr) {
+        if (ptr == NULL) {
+            ALOGW("JNI WARNING: invalid null pointer (%s)", mFunctionName);
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that the method's return type matches the type of call.
+     * 'expectedType' will be "L" for all objects, including arrays.
+     */
+    void checkSig(jmethodID methodID, const char* expectedType, bool isStatic) {
+        const Method* method = (const Method*) methodID;
+        bool printWarn = false;
+
+        if (*expectedType != method->shorty[0]) {
+            ALOGW("JNI WARNING: expected return type '%s'", expectedType);
+            printWarn = true;
+        } else if (isStatic && !dvmIsStaticMethod(method)) {
+            if (isStatic) {
+                ALOGW("JNI WARNING: calling non-static method with static call");
+            } else {
+                ALOGW("JNI WARNING: calling static method with non-static call");
+            }
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            ALOGW("             calling %s.%s %s", method->clazz->descriptor, method->name, desc);
+            free(desc);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that this static field ID is valid for this class.
+     *
+     * Assumes "jclazz" has already been validated.
+     */
+    void checkStaticFieldID(jclass jclazz, jfieldID fieldID) {
+        ScopedCheckJniThreadState ts(mEnv);
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(self(), jclazz);
+        StaticField* base = &clazz->sfields[0];
+        int fieldCount = clazz->sfieldCount;
+        if ((StaticField*) fieldID < base || (StaticField*) fieldID >= base + fieldCount) {
+            ALOGW("JNI WARNING: static fieldID %p not valid for class %s",
+                    fieldID, clazz->descriptor);
+            ALOGW("             base=%p count=%d", base, fieldCount);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "methodID" is appropriate for "clazz".
+     *
+     * A mismatch isn't dangerous, because the jmethodID defines the class.  In
+     * fact, jclazz is unused in the implementation.  It's best if we don't
+     * allow bad code in the system though.
+     *
+     * Instances of "jclazz" must be instances of the method's declaring class.
+     */
+    void checkStaticMethod(jclass jclazz, jmethodID methodID) {
+        ScopedCheckJniThreadState ts(mEnv);
+
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(self(), jclazz);
+        const Method* method = (const Method*) methodID;
+
+        if (!dvmInstanceof(clazz, method->clazz)) {
+            ALOGW("JNI WARNING: can't call static %s.%s on class %s",
+                    method->clazz->descriptor, method->name, clazz->descriptor);
+            showLocation();
+            // no abort?
+        }
+    }
+
+    /*
+     * Verify that "methodID" is appropriate for "jobj".
+     *
+     * Make sure the object is an instance of the method's declaring class.
+     * (Note the methodID might point to a declaration in an interface; this
+     * will be handled automatically by the instanceof check.)
+     */
+    void checkVirtualMethod(jobject jobj, jmethodID methodID) {
+        ScopedCheckJniThreadState ts(mEnv);
+
+        Object* obj = dvmDecodeIndirectRef(self(), jobj);
+        const Method* method = (const Method*) methodID;
+
+        if (!dvmInstanceof(obj->clazz, method->clazz)) {
+            ALOGW("JNI WARNING: can't call %s.%s on instance of %s",
+                    method->clazz->descriptor, method->name, obj->clazz->descriptor);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /**
+     * The format string is a sequence of the following characters,
+     * and must be followed by arguments of the corresponding types
+     * in the same order.
+     *
+     * Java primitive types:
+     * B - jbyte
+     * C - jchar
+     * D - jdouble
+     * F - jfloat
+     * I - jint
+     * J - jlong
+     * S - jshort
+     * Z - jboolean (shown as true and false)
+     * V - void
+     *
+     * Java reference types:
+     * L - jobject
+     * a - jarray
+     * c - jclass
+     * s - jstring
+     *
+     * JNI types:
+     * b - jboolean (shown as JNI_TRUE and JNI_FALSE)
+     * f - jfieldID
+     * m - jmethodID
+     * p - void*
+     * r - jint (for release mode arguments)
+     * u - const char* (modified UTF-8)
+     * z - jsize (for lengths; use i if negative values are okay)
+     * v - JavaVM*
+     * E - JNIEnv*
+     * . - no argument; just print "..." (used for varargs JNI calls)
+     *
+     * Use the kFlag_NullableUtf flag where 'u' field(s) are nullable.
+     */
+    void check(bool entry, const char* fmt0, ...) {
+        va_list ap;
+
+        bool shouldTrace = false;
+        const Method* method = NULL;
+        if ((gDvm.jniTrace || gDvmJni.logThirdPartyJni) && mHasMethod) {
+            // We need to guard some of the invocation interface's calls: a bad caller might
+            // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
+            if ((mFlags & kFlag_Invocation) == 0 || dvmThreadSelf() != NULL) {
+                method = dvmGetCurrentJNIMethod();
+            }
+        }
+        if (method != NULL) {
+            // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
+            // when a native method that matches the Xjnitrace argument calls a JNI function
+            // such as NewByteArray.
+            if (gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace) != NULL) {
+                shouldTrace = true;
+            }
+            // If -Xjniopts:logThirdPartyJni is on, we want to log any JNI function calls
+            // made by a third-party native method.
+            if (gDvmJni.logThirdPartyJni) {
+                shouldTrace |= method->shouldTrace;
+            }
+        }
+
+        if (shouldTrace) {
+            va_start(ap, fmt0);
+            std::string msg;
+            for (const char* fmt = fmt0; *fmt;) {
+                char ch = *fmt++;
+                if (ch == 'B') { // jbyte
+                    jbyte b = va_arg(ap, int);
+                    if (b >= 0 && b < 10) {
+                        StringAppendF(&msg, "%d", b);
+                    } else {
+                        StringAppendF(&msg, "%#x (%d)", b, b);
+                    }
+                } else if (ch == 'C') { // jchar
+                    jchar c = va_arg(ap, int);
+                    if (c < 0x7f && c >= ' ') {
+                        StringAppendF(&msg, "U+%x ('%c')", c, c);
+                    } else {
+                        StringAppendF(&msg, "U+%x", c);
+                    }
+                } else if (ch == 'F' || ch == 'D') { // jfloat, jdouble
+                    StringAppendF(&msg, "%g", va_arg(ap, double));
+                } else if (ch == 'I' || ch == 'S') { // jint, jshort
+                    StringAppendF(&msg, "%d", va_arg(ap, int));
+                } else if (ch == 'J') { // jlong
+                    StringAppendF(&msg, "%lld", va_arg(ap, jlong));
+                } else if (ch == 'Z') { // jboolean
+                    StringAppendF(&msg, "%s", va_arg(ap, int) ? "true" : "false");
+                } else if (ch == 'V') { // void
+                    msg += "void";
+                } else if (ch == 'v') { // JavaVM*
+                    JavaVM* vm = va_arg(ap, JavaVM*);
+                    StringAppendF(&msg, "(JavaVM*)%p", vm);
+                } else if (ch == 'E') { // JNIEnv*
+                    JNIEnv* env = va_arg(ap, JNIEnv*);
+                    StringAppendF(&msg, "(JNIEnv*)%p", env);
+                } else if (ch == 'L' || ch == 'a' || ch == 's') { // jobject, jarray, jstring
+                    // For logging purposes, these are identical.
+                    jobject o = va_arg(ap, jobject);
+                    if (o == NULL) {
+                        msg += "NULL";
+                    } else {
+                        StringAppendF(&msg, "%p", o);
+                    }
+                } else if (ch == 'b') { // jboolean (JNI-style)
+                    jboolean b = va_arg(ap, int);
+                    msg += (b ? "JNI_TRUE" : "JNI_FALSE");
+                } else if (ch == 'c') { // jclass
+                    jclass jc = va_arg(ap, jclass);
+                    Object* c = dvmDecodeIndirectRef(self(), jc);
+                    if (c == NULL) {
+                        msg += "NULL";
+                    } else if (c == kInvalidIndirectRefObject || !dvmIsHeapAddress(c)) {
+                        StringAppendF(&msg, "%p(INVALID)", jc);
+                    } else {
+                        std::string className(dvmHumanReadableType(c));
+                        StringAppendF(&msg, "%s", className.c_str());
+                        if (!entry) {
+                            StringAppendF(&msg, " (%p)", jc);
+                        }
+                    }
+                } else if (ch == 'f') { // jfieldID
+                    jfieldID fid = va_arg(ap, jfieldID);
+                    std::string name(dvmHumanReadableField((Field*) fid));
+                    StringAppendF(&msg, "%s", name.c_str());
+                    if (!entry) {
+                        StringAppendF(&msg, " (%p)", fid);
+                    }
+                } else if (ch == 'z') { // non-negative jsize
+                    // You might expect jsize to be size_t, but it's not; it's the same as jint.
+                    // We only treat this specially so we can do the non-negative check.
+                    // TODO: maybe this wasn't worth it?
+                    jint i = va_arg(ap, jint);
+                    StringAppendF(&msg, "%d", i);
+                } else if (ch == 'm') { // jmethodID
+                    jmethodID mid = va_arg(ap, jmethodID);
+                    std::string name(dvmHumanReadableMethod((Method*) mid, true));
+                    StringAppendF(&msg, "%s", name.c_str());
+                    if (!entry) {
+                        StringAppendF(&msg, " (%p)", mid);
+                    }
+                } else if (ch == 'p') { // void* ("pointer")
+                    void* p = va_arg(ap, void*);
+                    if (p == NULL) {
+                        msg += "NULL";
+                    } else {
+                        StringAppendF(&msg, "(void*) %p", p);
+                    }
+                } else if (ch == 'r') { // jint (release mode)
+                    jint releaseMode = va_arg(ap, jint);
+                    if (releaseMode == 0) {
+                        msg += "0";
+                    } else if (releaseMode == JNI_ABORT) {
+                        msg += "JNI_ABORT";
+                    } else if (releaseMode == JNI_COMMIT) {
+                        msg += "JNI_COMMIT";
+                    } else {
+                        StringAppendF(&msg, "invalid release mode %d", releaseMode);
+                    }
+                } else if (ch == 'u') { // const char* (modified UTF-8)
+                    const char* utf = va_arg(ap, const char*);
+                    if (utf == NULL) {
+                        msg += "NULL";
+                    } else {
+                        StringAppendF(&msg, "\"%s\"", utf);
+                    }
+                } else if (ch == '.') {
+                    msg += "...";
+                } else {
+                    ALOGE("unknown trace format specifier %c", ch);
+                    dvmAbort();
+                }
+                if (*fmt) {
+                    StringAppendF(&msg, ", ");
+                }
+            }
+            va_end(ap);
+
+            if (entry) {
+                if (mHasMethod) {
+                    std::string methodName(dvmHumanReadableMethod(method, false));
+                    ALOGI("JNI: %s -> %s(%s)", methodName.c_str(), mFunctionName, msg.c_str());
+                    mIndent = methodName.size() + 1;
+                } else {
+                    ALOGI("JNI: -> %s(%s)", mFunctionName, msg.c_str());
+                    mIndent = 0;
+                }
+            } else {
+                ALOGI("JNI: %*s<- %s returned %s", mIndent, "", mFunctionName, msg.c_str());
+            }
+        }
+
+        // We always do the thorough checks on entry, and never on exit...
+        if (entry) {
+            va_start(ap, fmt0);
+            for (const char* fmt = fmt0; *fmt; ++fmt) {
+                char ch = *fmt;
+                if (ch == 'a') {
+                    checkArray(va_arg(ap, jarray));
+                } else if (ch == 'c') {
+                    checkClass(va_arg(ap, jclass));
+                } else if (ch == 'L') {
+                    checkObject(va_arg(ap, jobject));
+                } else if (ch == 'r') {
+                    checkReleaseMode(va_arg(ap, jint));
+                } else if (ch == 's') {
+                    checkString(va_arg(ap, jstring));
+                } else if (ch == 'u') {
+                    if ((mFlags & kFlag_Release) != 0) {
+                        checkNonNull(va_arg(ap, const char*));
+                    } else {
+                        bool nullable = ((mFlags & kFlag_NullableUtf) != 0);
+                        checkUtfString(va_arg(ap, const char*), nullable);
+                    }
+                } else if (ch == 'z') {
+                    checkLengthPositive(va_arg(ap, jsize));
+                } else if (strchr("BCISZbfmpEv", ch) != NULL) {
+                    va_arg(ap, int); // Skip this argument.
+                } else if (ch == 'D' || ch == 'F') {
+                    va_arg(ap, double); // Skip this argument.
+                } else if (ch == 'J') {
+                    va_arg(ap, long); // Skip this argument.
+                } else if (ch == '.') {
+                } else {
+                    ALOGE("unknown check format specifier %c", ch);
+                    dvmAbort();
+                }
+            }
+            va_end(ap);
+        }
+    }
+
+    // Only safe after checkThread returns.
+    Thread* self() {
+        return ((JNIEnvExt*) mEnv)->self;
+    }
+
+private:
+    JNIEnv* mEnv;
+    const char* mFunctionName;
+    int mFlags;
+    bool mHasMethod;
+    size_t mIndent;
+
+    void init(JNIEnv* env, int flags, const char* functionName, bool hasMethod) {
+        mEnv = env;
+        mFlags = flags;
+
+        // Use +6 to drop the leading "Check_"...
+        mFunctionName = functionName + 6;
+
+        // Set "hasMethod" to true if we have a valid thread with a method pointer.
+        // We won't have one before attaching a thread, after detaching a thread, or
+        // after destroying the VM.
+        mHasMethod = hasMethod;
+    }
+
+    /*
+     * Verify that "array" is non-NULL and points to an Array object.
+     *
+     * Since we're dealing with objects, switch to "running" mode.
+     */
+    void checkArray(jarray jarr) {
+        if (jarr == NULL) {
+            ALOGW("JNI WARNING: received null array");
+            showLocation();
+            abortMaybe();
+            return;
+        }
+
+        ScopedCheckJniThreadState ts(mEnv);
+        bool printWarn = false;
+
+        Object* obj = dvmDecodeIndirectRef(self(), jarr);
+        if (!dvmIsHeapAddress(obj)) {
+            ALOGW("JNI WARNING: jarray is an invalid %s reference (%p)",
+            indirectRefKindName(jarr), jarr);
+            printWarn = true;
+        } else if (obj->clazz->descriptor[0] != '[') {
+            ALOGW("JNI WARNING: jarray arg has wrong type (expected array, got %s)",
+            obj->clazz->descriptor);
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    void checkClass(jclass c) {
+        checkInstance(c, gDvm.classJavaLangClass, "jclass");
+    }
+
+    void checkLengthPositive(jsize length) {
+        if (length < 0) {
+            ALOGW("JNI WARNING: negative jsize (%s)", mFunctionName);
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "jobj" is a valid object, and that it's an object that JNI
+     * is allowed to know about.  We allow NULL references.
+     *
+     * Switches to "running" mode before performing checks.
+     */
+    void checkObject(jobject jobj) {
+        if (jobj == NULL) {
+            return;
+        }
+
+        ScopedCheckJniThreadState ts(mEnv);
+
+        bool printWarn = false;
+        if (dvmGetJNIRefType(self(), jobj) == JNIInvalidRefType) {
+            ALOGW("JNI WARNING: %p is not a valid JNI reference", jobj);
+            printWarn = true;
+        } else {
+            Object* obj = dvmDecodeIndirectRef(self(), jobj);
+            if (obj == kInvalidIndirectRefObject) {
+                ALOGW("JNI WARNING: native code passing in invalid reference %p", jobj);
+                printWarn = true;
+            } else if (obj != NULL && !dvmIsHeapAddress(obj)) {
+                // TODO: when we remove workAroundAppJniBugs, this should be impossible.
+                ALOGW("JNI WARNING: native code passing in reference to invalid object %p %p",
+                        jobj, obj);
+                printWarn = true;
+            }
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that the "mode" argument passed to a primitive array Release
+     * function is one of the valid values.
+     */
+    void checkReleaseMode(jint mode) {
+        if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
+            ALOGW("JNI WARNING: bad value for mode (%d) (%s)", mode, mFunctionName);
+            abortMaybe();
+        }
+    }
+
+    void checkString(jstring s) {
+        checkInstance(s, gDvm.classJavaLangString, "jstring");
+    }
+
+    void checkThread(int flags) {
+        // Get the *correct* JNIEnv by going through our TLS pointer.
+        JNIEnvExt* threadEnv = dvmGetJNIEnvForThread();
+
+        /*
+         * Verify that the current thread is (a) attached and (b) associated with
+         * this particular instance of JNIEnv.
+         */
+        bool printWarn = false;
+        if (threadEnv == NULL) {
+            ALOGE("JNI ERROR: non-VM thread making JNI calls");
+            // don't set printWarn -- it'll try to call showLocation()
+            dvmAbort();
+        } else if ((JNIEnvExt*) mEnv != threadEnv) {
+            if (dvmThreadSelf()->threadId != threadEnv->envThreadId) {
+                ALOGE("JNI: threadEnv != thread->env?");
+                dvmAbort();
+            }
+
+            ALOGW("JNI WARNING: threadid=%d using env from threadid=%d",
+                    threadEnv->envThreadId, ((JNIEnvExt*) mEnv)->envThreadId);
+            printWarn = true;
+
+            // If we're keeping broken code limping along, we need to suppress the abort...
+            if (gDvmJni.workAroundAppJniBugs) {
+                printWarn = false;
+            }
+
+            /* this is a bad idea -- need to throw as we exit, or abort func */
+            //dvmThrowRuntimeException("invalid use of JNI env ptr");
+        } else if (((JNIEnvExt*) mEnv)->self != dvmThreadSelf()) {
+            /* correct JNIEnv*; make sure the "self" pointer is correct */
+            ALOGE("JNI ERROR: env->self != thread-self (%p vs. %p)",
+                    ((JNIEnvExt*) mEnv)->self, dvmThreadSelf());
+            dvmAbort();
+        }
+
+        /*
+         * Verify that, if this thread previously made a critical "get" call, we
+         * do the corresponding "release" call before we try anything else.
+         */
+        switch (flags & kFlag_CritMask) {
+        case kFlag_CritOkay:    // okay to call this method
+            break;
+        case kFlag_CritBad:     // not okay to call
+            if (threadEnv->critical) {
+                ALOGW("JNI WARNING: threadid=%d using JNI after critical get",
+                        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) {
+                ALOGW("JNI WARNING: threadid=%d called too many crit releases",
+                        threadEnv->envThreadId);
+                printWarn = true;
+            }
+            break;
+        default:
+            assert(false);
+        }
+
+        /*
+         * Verify that, if an exception has been raised, the native code doesn't
+         * make any JNI calls other than the Exception* methods.
+         */
+        bool printException = false;
+        if ((flags & kFlag_ExcepOkay) == 0 && dvmCheckException(dvmThreadSelf())) {
+            ALOGW("JNI WARNING: JNI method called with exception pending");
+            printWarn = true;
+            printException = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+        }
+        if (printException) {
+            ALOGW("Pending exception is:");
+            dvmLogExceptionStackTrace();
+        }
+        if (printWarn) {
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "bytes" points to valid "modified UTF-8" data.
+     */
+    void checkUtfString(const char* bytes, bool nullable) {
+        if (bytes == NULL) {
+            if (!nullable) {
+                ALOGW("JNI WARNING: non-nullable const char* was NULL");
+                showLocation();
+                abortMaybe();
+            }
+            return;
+        }
+
+        const char* errorKind = NULL;
+        u1 utf8 = checkUtfBytes(bytes, &errorKind);
+        if (errorKind != NULL) {
+            ALOGW("JNI WARNING: input is not valid Modified UTF-8: illegal %s byte %#x", errorKind, utf8);
+            ALOGW("             string: '%s'", bytes);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "jobj" is a valid non-NULL object reference, and points to
+     * an instance of expectedClass.
+     *
+     * Because we're looking at an object on the GC heap, we have to switch
+     * to "running" mode before doing the checks.
+     */
+    void checkInstance(jobject jobj, ClassObject* expectedClass, const char* argName) {
+        if (jobj == NULL) {
+            ALOGW("JNI WARNING: received null %s", argName);
+            showLocation();
+            abortMaybe();
+            return;
+        }
+
+        ScopedCheckJniThreadState ts(mEnv);
+        bool printWarn = false;
+
+        Object* obj = dvmDecodeIndirectRef(self(), jobj);
+        if (!dvmIsHeapAddress(obj)) {
+            ALOGW("JNI WARNING: %s is an invalid %s reference (%p)",
+                    argName, indirectRefKindName(jobj), jobj);
+            printWarn = true;
+        } else if (obj->clazz != expectedClass) {
+            ALOGW("JNI WARNING: %s arg has wrong type (expected %s, got %s)",
+                    argName, expectedClass->descriptor, obj->clazz->descriptor);
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    static u1 checkUtfBytes(const char* bytes, const char** errorKind) {
+        while (*bytes != '\0') {
+            u1 utf8 = *(bytes++);
+            // Switch on the high four bits.
+            switch (utf8 >> 4) {
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+                // Bit pattern 0xxx. No need for any extra bytes.
+                break;
+            case 0x08:
+            case 0x09:
+            case 0x0a:
+            case 0x0b:
+            case 0x0f:
+                /*
+                 * Bit pattern 10xx or 1111, which are illegal start bytes.
+                 * Note: 1111 is valid for normal UTF-8, but not the
+                 * modified UTF-8 used here.
+                 */
+                *errorKind = "start";
+                return utf8;
+            case 0x0e:
+                // Bit pattern 1110, so there are two additional bytes.
+                utf8 = *(bytes++);
+                if ((utf8 & 0xc0) != 0x80) {
+                    *errorKind = "continuation";
+                    return utf8;
+                }
+                // Fall through to take care of the final byte.
+            case 0x0c:
+            case 0x0d:
+                // Bit pattern 110x, so there is one additional byte.
+                utf8 = *(bytes++);
+                if ((utf8 & 0xc0) != 0x80) {
+                    *errorKind = "continuation";
+                    return utf8;
+                }
+                break;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Returns a human-readable name for the given primitive type.
+     */
+    static const char* primitiveTypeToName(PrimitiveType primType) {
+        switch (primType) {
+        case PRIM_VOID:    return "void";
+        case PRIM_BOOLEAN: return "boolean";
+        case PRIM_BYTE:    return "byte";
+        case PRIM_SHORT:   return "short";
+        case PRIM_CHAR:    return "char";
+        case PRIM_INT:     return "int";
+        case PRIM_LONG:    return "long";
+        case PRIM_FLOAT:   return "float";
+        case PRIM_DOUBLE:  return "double";
+        case PRIM_NOT:     return "Object/array";
+        default:           return "???";
+        }
+    }
+
+    void showLocation() {
+        const Method* method = dvmGetCurrentJNIMethod();
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGW("             in %s.%s:%s (%s)", method->clazz->descriptor, method->name, desc, mFunctionName);
+        free(desc);
+    }
+
+    // Disallow copy and assignment.
+    ScopedCheck(const ScopedCheck&);
+    void operator=(const ScopedCheck&);
+};
+
+/*
+ * ===========================================================================
+ *      Guarded arrays
+ * ===========================================================================
+ */
+
+#define kGuardLen       512         /* must be multiple of 2 */
+#define kGuardPattern   0xd5e3      /* uncommon values; d5e3d5e3 invalid addr */
+#define kGuardMagic     0xffd5aa96
+
+/* this gets tucked in at the start of the buffer; struct size must be even */
+struct GuardedCopy {
+    u4          magic;
+    uLong       adler;
+    size_t      originalLen;
+    const void* originalPtr;
+
+    /* find the GuardedCopy given the pointer into the "live" data */
+    static inline const GuardedCopy* fromData(const void* dataBuf) {
+        return reinterpret_cast<const GuardedCopy*>(actualBuffer(dataBuf));
+    }
+
+    /*
+     * Create an over-sized buffer to hold the contents of "buf".  Copy it in,
+     * filling in the area around it with guard data.
+     *
+     * We use a 16-bit pattern to make a rogue memset less likely to elude us.
+     */
+    static void* create(const void* buf, size_t len, bool modOkay) {
+        size_t newLen = actualLength(len);
+        u1* newBuf = debugAlloc(newLen);
+
+        /* fill it in with a pattern */
+        u2* pat = (u2*) newBuf;
+        for (size_t i = 0; i < newLen / 2; i++) {
+            *pat++ = kGuardPattern;
+        }
+
+        /* copy the data in; note "len" could be zero */
+        memcpy(newBuf + kGuardLen / 2, buf, len);
+
+        /* if modification is not expected, grab a checksum */
+        uLong adler = 0;
+        if (!modOkay) {
+            adler = adler32(0L, Z_NULL, 0);
+            adler = adler32(adler, (const Bytef*)buf, len);
+            *(uLong*)newBuf = adler;
+        }
+
+        GuardedCopy* pExtra = reinterpret_cast<GuardedCopy*>(newBuf);
+        pExtra->magic = kGuardMagic;
+        pExtra->adler = adler;
+        pExtra->originalPtr = buf;
+        pExtra->originalLen = len;
+
+        return newBuf + kGuardLen / 2;
+    }
+
+    /*
+     * Free up the guard buffer, scrub it, and return the original pointer.
+     */
+    static void* destroy(void* dataBuf) {
+        const GuardedCopy* pExtra = GuardedCopy::fromData(dataBuf);
+        void* originalPtr = (void*) pExtra->originalPtr;
+        size_t len = pExtra->originalLen;
+        debugFree(dataBuf, len);
+        return originalPtr;
+    }
+
+    /*
+     * Verify the guard area and, if "modOkay" is false, that the data itself
+     * has not been altered.
+     *
+     * The caller has already checked that "dataBuf" is non-NULL.
+     */
+    static bool check(const void* dataBuf, bool modOkay) {
+        static const u4 kMagicCmp = kGuardMagic;
+        const u1* fullBuf = actualBuffer(dataBuf);
+        const GuardedCopy* pExtra = GuardedCopy::fromData(dataBuf);
+
+        /*
+         * Before we do anything with "pExtra", check the magic number.  We
+         * do the check with memcmp rather than "==" in case the pointer is
+         * unaligned.  If it points to completely bogus memory we're going
+         * to crash, but there's no easy way around that.
+         */
+        if (memcmp(&pExtra->magic, &kMagicCmp, 4) != 0) {
+            u1 buf[4];
+            memcpy(buf, &pExtra->magic, 4);
+            ALOGE("JNI: guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
+                    buf[3], buf[2], buf[1], buf[0], dataBuf); /* assume little endian */
+            return false;
+        }
+
+        size_t len = pExtra->originalLen;
+
+        /* check bottom half of guard; skip over optional checksum storage */
+        const u2* pat = (u2*) fullBuf;
+        for (size_t i = sizeof(GuardedCopy) / 2; i < (kGuardLen / 2 - sizeof(GuardedCopy)) / 2; i++) {
+            if (pat[i] != kGuardPattern) {
+                ALOGE("JNI: guard pattern(1) disturbed at %p + %d", fullBuf, i*2);
+                return false;
+            }
+        }
+
+        int offset = kGuardLen / 2 + len;
+        if (offset & 0x01) {
+            /* odd byte; expected value depends on endian-ness of host */
+            const u2 patSample = kGuardPattern;
+            if (fullBuf[offset] != ((const u1*) &patSample)[1]) {
+                ALOGE("JNI: guard pattern disturbed in odd byte after %p (+%d) 0x%02x 0x%02x",
+                        fullBuf, offset, fullBuf[offset], ((const u1*) &patSample)[1]);
+                return false;
+            }
+            offset++;
+        }
+
+        /* check top half of guard */
+        pat = (u2*) (fullBuf + offset);
+        for (size_t i = 0; i < kGuardLen / 4; i++) {
+            if (pat[i] != kGuardPattern) {
+                ALOGE("JNI: guard pattern(2) disturbed at %p + %d", fullBuf, offset + i*2);
+                return false;
+            }
+        }
+
+        /*
+         * If modification is not expected, verify checksum.  Strictly speaking
+         * this is wrong: if we told the client that we made a copy, there's no
+         * reason they can't alter the buffer.
+         */
+        if (!modOkay) {
+            uLong adler = adler32(0L, Z_NULL, 0);
+            adler = adler32(adler, (const Bytef*)dataBuf, len);
+            if (pExtra->adler != adler) {
+                ALOGE("JNI: buffer modified (0x%08lx vs 0x%08lx) at addr %p",
+                        pExtra->adler, adler, dataBuf);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+private:
+    static u1* debugAlloc(size_t len) {
+        void* result = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+        if (result == MAP_FAILED) {
+            ALOGE("GuardedCopy::create mmap(%d) failed: %s", len, strerror(errno));
+            dvmAbort();
+        }
+        return reinterpret_cast<u1*>(result);
+    }
+
+    static void debugFree(void* dataBuf, size_t len) {
+        u1* fullBuf = actualBuffer(dataBuf);
+        size_t totalByteCount = actualLength(len);
+        // TODO: we could mprotect instead, and keep the allocation around for a while.
+        // This would be even more expensive, but it might catch more errors.
+        // if (mprotect(fullBuf, totalByteCount, PROT_NONE) != 0) {
+        //     ALOGW("mprotect(PROT_NONE) failed: %s", strerror(errno));
+        // }
+        if (munmap(fullBuf, totalByteCount) != 0) {
+            ALOGW("munmap failed: %s", strerror(errno));
+            dvmAbort();
+        }
+    }
+
+    static const u1* actualBuffer(const void* dataBuf) {
+        return reinterpret_cast<const u1*>(dataBuf) - kGuardLen / 2;
+    }
+
+    static u1* actualBuffer(void* dataBuf) {
+        return reinterpret_cast<u1*>(dataBuf) - kGuardLen / 2;
+    }
+
+    // Underlying length of a user allocation of 'length' bytes.
+    static size_t actualLength(size_t length) {
+        return (length + kGuardLen + 1) & ~0x01;
+    }
+};
+
+/*
+ * Return the width, in bytes, of a primitive type.
+ */
+static int dvmPrimitiveTypeWidth(PrimitiveType primType) {
+    switch (primType) {
+        case PRIM_BOOLEAN: return 1;
+        case PRIM_BYTE:    return 1;
+        case PRIM_SHORT:   return 2;
+        case PRIM_CHAR:    return 2;
+        case PRIM_INT:     return 4;
+        case PRIM_LONG:    return 8;
+        case PRIM_FLOAT:   return 4;
+        case PRIM_DOUBLE:  return 8;
+        case PRIM_VOID:
+        default: {
+            assert(false);
+            return -1;
+        }
+    }
+}
+
+/*
+ * Create a guarded copy of a primitive array.  Modifications to the copied
+ * data are allowed.  Returns a pointer to the copied data.
+ */
+static void* createGuardedPACopy(JNIEnv* env, const jarray jarr, jboolean* isCopy) {
+    ScopedCheckJniThreadState ts(env);
+
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(dvmThreadSelf(), jarr);
+    PrimitiveType primType = arrObj->clazz->elementClass->primitiveType;
+    int len = arrObj->length * dvmPrimitiveTypeWidth(primType);
+    void* result = GuardedCopy::create(arrObj->contents, len, true);
+    if (isCopy != NULL) {
+        *isCopy = JNI_TRUE;
+    }
+    return result;
+}
+
+/*
+ * Perform the array "release" operation, which may or may not copy data
+ * back into the VM, and may or may not release the underlying storage.
+ */
+static void* releaseGuardedPACopy(JNIEnv* env, jarray jarr, void* dataBuf, int mode) {
+    ScopedCheckJniThreadState ts(env);
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(dvmThreadSelf(), jarr);
+
+    if (!GuardedCopy::check(dataBuf, true)) {
+        ALOGE("JNI: failed guarded copy check in releaseGuardedPACopy");
+        abortMaybe();
+        return NULL;
+    }
+
+    if (mode != JNI_ABORT) {
+        size_t len = GuardedCopy::fromData(dataBuf)->originalLen;
+        memcpy(arrObj->contents, dataBuf, len);
+    }
+
+    u1* result = NULL;
+    if (mode != JNI_COMMIT) {
+        result = (u1*) GuardedCopy::destroy(dataBuf);
+    } else {
+        result = (u1*) (void*) GuardedCopy::fromData(dataBuf)->originalPtr;
+    }
+
+    /* pointer is to the array contents; back up to the array object */
+    result -= OFFSETOF_MEMBER(ArrayObject, contents);
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI functions
+ * ===========================================================================
+ */
+
+#define CHECK_JNI_ENTRY(flags, types, args...) \
+    ScopedCheck sc(env, flags, __FUNCTION__); \
+    sc.check(true, types, ##args)
+
+#define CHECK_JNI_EXIT(type, exp) ({ \
+    typeof (exp) _rc = (exp); \
+    sc.check(false, type, _rc); \
+    _rc; })
+#define CHECK_JNI_EXIT_VOID() \
+    sc.check(false, "V")
+
+static jint Check_GetVersion(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_Default, "E", env);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetVersion(env));
+}
+
+static jclass Check_DefineClass(JNIEnv* env, const char* name, jobject loader,
+    const jbyte* buf, jsize bufLen)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EuLpz", env, name, loader, buf, bufLen);
+    sc.checkClassName(name);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->DefineClass(env, name, loader, buf, bufLen));
+}
+
+static jclass Check_FindClass(JNIEnv* env, const char* name) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Eu", env, name);
+    sc.checkClassName(name);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->FindClass(env, name));
+}
+
+static jclass Check_GetSuperclass(JNIEnv* env, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, clazz);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->GetSuperclass(env, clazz));
+}
+
+static jboolean Check_IsAssignableFrom(JNIEnv* env, jclass clazz1, jclass clazz2) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecc", env, clazz1, clazz2);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->IsAssignableFrom(env, clazz1, clazz2));
+}
+
+static jmethodID Check_FromReflectedMethod(JNIEnv* env, jobject method) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, method);
+    // TODO: check that 'field' is a java.lang.reflect.Method.
+    return CHECK_JNI_EXIT("m", baseEnv(env)->FromReflectedMethod(env, method));
+}
+
+static jfieldID Check_FromReflectedField(JNIEnv* env, jobject field) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, field);
+    // TODO: check that 'field' is a java.lang.reflect.Field.
+    return CHECK_JNI_EXIT("f", baseEnv(env)->FromReflectedField(env, field));
+}
+
+static jobject Check_ToReflectedMethod(JNIEnv* env, jclass cls,
+        jmethodID methodID, jboolean isStatic)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecmb", env, cls, methodID, isStatic);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedMethod(env, cls, methodID, isStatic));
+}
+
+static jobject Check_ToReflectedField(JNIEnv* env, jclass cls,
+        jfieldID fieldID, jboolean isStatic)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecfb", env, cls, fieldID, isStatic);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedField(env, cls, fieldID, isStatic));
+}
+
+static jint Check_Throw(JNIEnv* env, jthrowable obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    // TODO: check that 'obj' is a java.lang.Throwable.
+    return CHECK_JNI_EXIT("I", baseEnv(env)->Throw(env, obj));
+}
+
+static jint Check_ThrowNew(JNIEnv* env, jclass clazz, const char* message) {
+    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Ecu", env, clazz, message);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->ThrowNew(env, clazz, message));
+}
+
+static jthrowable Check_ExceptionOccurred(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->ExceptionOccurred(env));
+}
+
+static void Check_ExceptionDescribe(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
+    baseEnv(env)->ExceptionDescribe(env);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void Check_ExceptionClear(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
+    baseEnv(env)->ExceptionClear(env);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void Check_FatalError(JNIEnv* env, const char* msg) {
+    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, msg);
+    baseEnv(env)->FatalError(env, msg);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jint Check_PushLocalFrame(JNIEnv* env, jint capacity) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EI", env, capacity);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->PushLocalFrame(env, capacity));
+}
+
+static jobject Check_PopLocalFrame(JNIEnv* env, jobject res) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, res);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->PopLocalFrame(env, res));
+}
+
+static jobject Check_NewGlobalRef(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewGlobalRef(env, obj));
+}
+
+static void Check_DeleteGlobalRef(JNIEnv* env, jobject globalRef) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, globalRef);
+    if (globalRef != NULL && dvmGetJNIRefType(sc.self(), globalRef) != JNIGlobalRefType) {
+        ALOGW("JNI WARNING: DeleteGlobalRef on non-global %p (type=%d)",
+             globalRef, dvmGetJNIRefType(sc.self(), globalRef));
+        abortMaybe();
+    } else {
+        baseEnv(env)->DeleteGlobalRef(env, globalRef);
+        CHECK_JNI_EXIT_VOID();
+    }
+}
+
+static jobject Check_NewLocalRef(JNIEnv* env, jobject ref) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, ref);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewLocalRef(env, ref));
+}
+
+static void Check_DeleteLocalRef(JNIEnv* env, jobject localRef) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, localRef);
+    if (localRef != NULL && dvmGetJNIRefType(sc.self(), localRef) != JNILocalRefType) {
+        ALOGW("JNI WARNING: DeleteLocalRef on non-local %p (type=%d)",
+             localRef, dvmGetJNIRefType(sc.self(), localRef));
+        abortMaybe();
+    } else {
+        baseEnv(env)->DeleteLocalRef(env, localRef);
+        CHECK_JNI_EXIT_VOID();
+    }
+}
+
+static jint Check_EnsureLocalCapacity(JNIEnv *env, jint capacity) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EI", env, capacity);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->EnsureLocalCapacity(env, capacity));
+}
+
+static jboolean Check_IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
+    CHECK_JNI_ENTRY(kFlag_Default, "ELL", env, ref1, ref2);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->IsSameObject(env, ref1, ref2));
+}
+
+static jobject Check_AllocObject(JNIEnv* env, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, clazz);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->AllocObject(env, clazz));
+}
+
+static jobject Check_NewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID);
+    va_list args;
+    va_start(args, methodID);
+    jobject result = baseEnv(env)->NewObjectV(env, clazz, methodID, args);
+    va_end(args);
+    return CHECK_JNI_EXIT("L", result);
+}
+
+static jobject Check_NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectV(env, clazz, methodID, args));
+}
+
+static jobject Check_NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectA(env, clazz, methodID, args));
+}
+
+static jclass Check_GetObjectClass(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->GetObjectClass(env, obj));
+}
+
+static jboolean Check_IsInstanceOf(JNIEnv* env, jobject obj, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "ELc", env, obj, clazz);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->IsInstanceOf(env, obj, clazz));
+}
+
+static jmethodID Check_GetMethodID(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("m", baseEnv(env)->GetMethodID(env, clazz, name, sig));
+}
+
+static jfieldID Check_GetFieldID(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("f", baseEnv(env)->GetFieldID(env, clazz, name, sig));
+}
+
+static jmethodID Check_GetStaticMethodID(JNIEnv* env, jclass clazz,
+        const char* name, const char* sig)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("m", baseEnv(env)->GetStaticMethodID(env, clazz, name, sig));
+}
+
+static jfieldID Check_GetStaticFieldID(JNIEnv* env, jclass clazz,
+        const char* name, const char* sig)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("f", baseEnv(env)->GetStaticFieldID(env, clazz, name, sig));
+}
+
+#define FIELD_ACCESSORS(_ctype, _jname, _ftype, _type) \
+    static _ctype Check_GetStatic##_jname##Field(JNIEnv* env, jclass clazz, jfieldID fieldID) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecf", env, clazz, fieldID); \
+        sc.checkStaticFieldID(clazz, fieldID); \
+        sc.checkFieldTypeForGet(fieldID, _type, true); \
+        return CHECK_JNI_EXIT(_type, baseEnv(env)->GetStatic##_jname##Field(env, clazz, fieldID)); \
+    } \
+    static _ctype Check_Get##_jname##Field(JNIEnv* env, jobject obj, jfieldID fieldID) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELf", env, obj, fieldID); \
+        sc.checkInstanceFieldID(obj, fieldID); \
+        sc.checkFieldTypeForGet(fieldID, _type, false); \
+        return CHECK_JNI_EXIT(_type, baseEnv(env)->Get##_jname##Field(env, obj, fieldID)); \
+    } \
+    static void Check_SetStatic##_jname##Field(JNIEnv* env, jclass clazz, jfieldID fieldID, _ctype value) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecf" _type, env, clazz, fieldID, value); \
+        sc.checkStaticFieldID(clazz, fieldID); \
+        /* "value" arg only used when type == ref */ \
+        sc.checkFieldTypeForSet((jobject)(u4)value, fieldID, _ftype, true); \
+        baseEnv(env)->SetStatic##_jname##Field(env, clazz, fieldID, value); \
+        CHECK_JNI_EXIT_VOID(); \
+    } \
+    static void Check_Set##_jname##Field(JNIEnv* env, jobject obj, jfieldID fieldID, _ctype value) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELf" _type, env, obj, fieldID, value); \
+        sc.checkInstanceFieldID(obj, fieldID); \
+        /* "value" arg only used when type == ref */ \
+        sc.checkFieldTypeForSet((jobject)(u4) value, fieldID, _ftype, false); \
+        baseEnv(env)->Set##_jname##Field(env, obj, fieldID, value); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+FIELD_ACCESSORS(jobject, Object, PRIM_NOT, "L");
+FIELD_ACCESSORS(jboolean, Boolean, PRIM_BOOLEAN, "Z");
+FIELD_ACCESSORS(jbyte, Byte, PRIM_BYTE, "B");
+FIELD_ACCESSORS(jchar, Char, PRIM_CHAR, "C");
+FIELD_ACCESSORS(jshort, Short, PRIM_SHORT, "S");
+FIELD_ACCESSORS(jint, Int, PRIM_INT, "I");
+FIELD_ACCESSORS(jlong, Long, PRIM_LONG, "J");
+FIELD_ACCESSORS(jfloat, Float, PRIM_FLOAT, "F");
+FIELD_ACCESSORS(jdouble, Double, PRIM_DOUBLE, "D");
+
+#define CALL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \
+    /* Virtual... */ \
+    static _ctype Check_Call##_jname##Method(JNIEnv* env, jobject obj, \
+        jmethodID methodID, ...) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        va_list args; \
+        va_start(args, methodID); \
+        _retasgn baseEnv(env)->Call##_jname##MethodV(env, obj, methodID, args); \
+        va_end(args); \
+        _retok; \
+    } \
+    static _ctype Check_Call##_jname##MethodV(JNIEnv* env, jobject obj, \
+        jmethodID methodID, va_list args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->Call##_jname##MethodV(env, obj, methodID, args); \
+        _retok; \
+    } \
+    static _ctype Check_Call##_jname##MethodA(JNIEnv* env, jobject obj, \
+        jmethodID methodID, jvalue* args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->Call##_jname##MethodA(env, obj, methodID, args); \
+        _retok; \
+    } \
+    /* Non-virtual... */ \
+    static _ctype Check_CallNonvirtual##_jname##Method(JNIEnv* env, \
+        jobject obj, jclass clazz, jmethodID methodID, ...) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        va_list args; \
+        va_start(args, methodID); \
+        _retasgn baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, clazz, methodID, args); \
+        va_end(args); \
+        _retok; \
+    } \
+    static _ctype Check_CallNonvirtual##_jname##MethodV(JNIEnv* env, \
+        jobject obj, jclass clazz, jmethodID methodID, va_list args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, clazz, methodID, args); \
+        _retok; \
+    } \
+    static _ctype Check_CallNonvirtual##_jname##MethodA(JNIEnv* env, \
+        jobject obj, jclass clazz, jmethodID methodID, jvalue* args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallNonvirtual##_jname##MethodA(env, obj, clazz, methodID, args); \
+        _retok; \
+    } \
+    /* Static... */ \
+    static _ctype Check_CallStatic##_jname##Method(JNIEnv* env, \
+        jclass clazz, jmethodID methodID, ...) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, true); \
+        sc.checkStaticMethod(clazz, methodID); \
+        _retdecl; \
+        va_list args; \
+        va_start(args, methodID); \
+        _retasgn baseEnv(env)->CallStatic##_jname##MethodV(env, clazz, methodID, args); \
+        va_end(args); \
+        _retok; \
+    } \
+    static _ctype Check_CallStatic##_jname##MethodV(JNIEnv* env, \
+        jclass clazz, jmethodID methodID, va_list args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, true); \
+        sc.checkStaticMethod(clazz, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallStatic##_jname##MethodV(env, clazz, methodID, args); \
+        _retok; \
+    } \
+    static _ctype Check_CallStatic##_jname##MethodA(JNIEnv* env, \
+        jclass clazz, jmethodID methodID, jvalue* args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, true); \
+        sc.checkStaticMethod(clazz, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallStatic##_jname##MethodA(env, clazz, methodID, args); \
+        _retok; \
+    }
+
+#define NON_VOID_RETURN(_retsig, _ctype) return CHECK_JNI_EXIT(_retsig, (_ctype) result)
+#define VOID_RETURN CHECK_JNI_EXIT_VOID()
+
+CALL(jobject, Object, Object* result, result=(Object*), NON_VOID_RETURN("L", jobject), "L");
+CALL(jboolean, Boolean, jboolean result, result=, NON_VOID_RETURN("Z", jboolean), "Z");
+CALL(jbyte, Byte, jbyte result, result=, NON_VOID_RETURN("B", jbyte), "B");
+CALL(jchar, Char, jchar result, result=, NON_VOID_RETURN("C", jchar), "C");
+CALL(jshort, Short, jshort result, result=, NON_VOID_RETURN("S", jshort), "S");
+CALL(jint, Int, jint result, result=, NON_VOID_RETURN("I", jint), "I");
+CALL(jlong, Long, jlong result, result=, NON_VOID_RETURN("J", jlong), "J");
+CALL(jfloat, Float, jfloat result, result=, NON_VOID_RETURN("F", jfloat), "F");
+CALL(jdouble, Double, jdouble result, result=, NON_VOID_RETURN("D", jdouble), "D");
+CALL(void, Void, , , VOID_RETURN, "V");
+
+static jstring Check_NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Epz", env, unicodeChars, len);
+    return CHECK_JNI_EXIT("s", baseEnv(env)->NewString(env, unicodeChars, len));
+}
+
+static jsize Check_GetStringLength(JNIEnv* env, jstring string) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringLength(env, string));
+}
+
+static const jchar* Check_GetStringChars(JNIEnv* env, jstring string, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, string, isCopy);
+    const jchar* result = baseEnv(env)->GetStringChars(env, string, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        ScopedCheckJniThreadState ts(env);
+        StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(dvmThreadSelf(), string);
+        int byteCount = strObj->length() * 2;
+        result = (const jchar*) GuardedCopy::create(result, byteCount, false);
+        if (isCopy != NULL) {
+            *isCopy = JNI_TRUE;
+        }
+    }
+    return CHECK_JNI_EXIT("p", result);
+}
+
+static void Check_ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Esp", env, string, chars);
+    sc.checkNonNull(chars);
+    if (gDvmJni.forceCopy) {
+        if (!GuardedCopy::check(chars, false)) {
+            ALOGE("JNI: failed guarded copy check in ReleaseStringChars");
+            abortMaybe();
+            return;
+        }
+        chars = (const jchar*) GuardedCopy::destroy((jchar*)chars);
+    }
+    baseEnv(env)->ReleaseStringChars(env, string, chars);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jstring Check_NewStringUTF(JNIEnv* env, const char* bytes) {
+    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, bytes); // TODO: show pointer and truncate string.
+    return CHECK_JNI_EXIT("s", baseEnv(env)->NewStringUTF(env, bytes));
+}
+
+static jsize Check_GetStringUTFLength(JNIEnv* env, jstring string) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringUTFLength(env, string));
+}
+
+static const char* Check_GetStringUTFChars(JNIEnv* env, jstring string, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, string, isCopy);
+    const char* result = baseEnv(env)->GetStringUTFChars(env, string, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        result = (const char*) GuardedCopy::create(result, strlen(result) + 1, false);
+        if (isCopy != NULL) {
+            *isCopy = JNI_TRUE;
+        }
+    }
+    return CHECK_JNI_EXIT("u", result); // TODO: show pointer and truncate string.
+}
+
+static void Check_ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_Release, "Esu", env, string, utf); // TODO: show pointer and truncate string.
+    if (gDvmJni.forceCopy) {
+        if (!GuardedCopy::check(utf, false)) {
+            ALOGE("JNI: failed guarded copy check in ReleaseStringUTFChars");
+            abortMaybe();
+            return;
+        }
+        utf = (const char*) GuardedCopy::destroy((char*)utf);
+    }
+    baseEnv(env)->ReleaseStringUTFChars(env, string, utf);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jsize Check_GetArrayLength(JNIEnv* env, jarray array) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Ea", env, array);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetArrayLength(env, array));
+}
+
+static jobjectArray Check_NewObjectArray(JNIEnv* env, jsize length,
+        jclass elementClass, jobject initialElement)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EzcL", env, length, elementClass, initialElement);
+    return CHECK_JNI_EXIT("a", baseEnv(env)->NewObjectArray(env, length, elementClass, initialElement));
+}
+
+static jobject Check_GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EaI", env, array, index);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->GetObjectArrayElement(env, array, index));
+}
+
+static void Check_SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EaIL", env, array, index, value);
+    baseEnv(env)->SetObjectArrayElement(env, array, index, value);
+    CHECK_JNI_EXIT_VOID();
+}
+
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname) \
+    static _artype Check_New##_jname##Array(JNIEnv* env, jsize length) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ez", env, length); \
+        return CHECK_JNI_EXIT("a", baseEnv(env)->New##_jname##Array(env, length)); \
+    }
+NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean);
+NEW_PRIMITIVE_ARRAY(jbyteArray, Byte);
+NEW_PRIMITIVE_ARRAY(jcharArray, Char);
+NEW_PRIMITIVE_ARRAY(jshortArray, Short);
+NEW_PRIMITIVE_ARRAY(jintArray, Int);
+NEW_PRIMITIVE_ARRAY(jlongArray, Long);
+NEW_PRIMITIVE_ARRAY(jfloatArray, Float);
+NEW_PRIMITIVE_ARRAY(jdoubleArray, Double);
+
+
+/*
+ * Hack to allow forcecopy to work with jniGetNonMovableArrayElements.
+ * The code deliberately uses an invalid sequence of operations, so we
+ * need to pass it through unmodified.  Review that code before making
+ * any changes here.
+ */
+#define kNoCopyMagic    0xd5aab57f
+
+#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+    static _ctype* Check_Get##_jname##ArrayElements(JNIEnv* env, \
+        _ctype##Array array, jboolean* isCopy) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Eap", env, array, isCopy); \
+        u4 noCopy = 0; \
+        if (gDvmJni.forceCopy && isCopy != NULL) { \
+            /* capture this before the base call tramples on it */ \
+            noCopy = *(u4*) isCopy; \
+        } \
+        _ctype* result = baseEnv(env)->Get##_jname##ArrayElements(env, array, isCopy); \
+        if (gDvmJni.forceCopy && result != NULL) { \
+            if (noCopy == kNoCopyMagic) { \
+                ALOGV("FC: not copying %p %x", array, noCopy); \
+            } else { \
+                result = (_ctype*) createGuardedPACopy(env, array, isCopy); \
+            } \
+        } \
+        return CHECK_JNI_EXIT("p", result); \
+    }
+
+#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+    static void Check_Release##_jname##ArrayElements(JNIEnv* env, \
+        _ctype##Array array, _ctype* elems, jint mode) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Eapr", env, array, elems, mode); \
+        sc.checkNonNull(elems); \
+        if (gDvmJni.forceCopy) { \
+            if ((uintptr_t)elems == kNoCopyMagic) { \
+                ALOGV("FC: not freeing %p", array); \
+                elems = NULL;   /* base JNI call doesn't currently need */ \
+            } else { \
+                elems = (_ctype*) releaseGuardedPACopy(env, array, elems, mode); \
+            } \
+        } \
+        baseEnv(env)->Release##_jname##ArrayElements(env, array, elems, mode); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Check_Get##_jname##ArrayRegion(JNIEnv* env, \
+            _ctype##Array array, jsize start, jsize len, _ctype* buf) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
+        baseEnv(env)->Get##_jname##ArrayRegion(env, array, start, len, buf); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Check_Set##_jname##ArrayRegion(JNIEnv* env, \
+            _ctype##Array array, jsize start, jsize len, const _ctype* buf) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
+        baseEnv(env)->Set##_jname##ArrayRegion(env, array, start, len, buf); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname, _typechar) \
+    GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+    RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+    GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
+    SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
+
+/* TODO: verify primitive array type matches call type */
+PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, 'Z');
+PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, 'B');
+PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, 'C');
+PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, 'S');
+PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, 'I');
+PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, 'J');
+PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, 'F');
+PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D');
+
+static jint Check_RegisterNatives(JNIEnv* env, jclass clazz, const JNINativeMethod* methods,
+        jint nMethods)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EcpI", env, clazz, methods, nMethods);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->RegisterNatives(env, clazz, methods, nMethods));
+}
+
+static jint Check_UnregisterNatives(JNIEnv* env, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, clazz);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->UnregisterNatives(env, clazz));
+}
+
+static jint Check_MonitorEnter(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorEnter(env, obj));
+}
+
+static jint Check_MonitorExit(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, obj);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorExit(env, obj));
+}
+
+static jint Check_GetJavaVM(JNIEnv *env, JavaVM **vm) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, vm);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetJavaVM(env, vm));
+}
+
+static void Check_GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, jchar* buf) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
+    baseEnv(env)->GetStringRegion(env, str, start, len, buf);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void Check_GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, jsize len, char* buf) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
+    baseEnv(env)->GetStringUTFRegion(env, str, start, len, buf);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void* Check_GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritGet, "Eap", env, array, isCopy);
+    void* result = baseEnv(env)->GetPrimitiveArrayCritical(env, array, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        result = createGuardedPACopy(env, array, isCopy);
+    }
+    return CHECK_JNI_EXIT("p", result);
+}
+
+static void Check_ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode)
+{
+    CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Eapr", env, array, carray, mode);
+    sc.checkNonNull(carray);
+    if (gDvmJni.forceCopy) {
+        carray = releaseGuardedPACopy(env, array, carray, mode);
+    }
+    baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static const jchar* Check_GetStringCritical(JNIEnv* env, jstring string, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritGet, "Esp", env, string, isCopy);
+    const jchar* result = baseEnv(env)->GetStringCritical(env, string, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        ScopedCheckJniThreadState ts(env);
+        StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(dvmThreadSelf(), string);
+        int byteCount = strObj->length() * 2;
+        result = (const jchar*) GuardedCopy::create(result, byteCount, false);
+        if (isCopy != NULL) {
+            *isCopy = JNI_TRUE;
+        }
+    }
+    return CHECK_JNI_EXIT("p", result);
+}
+
+static void Check_ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* carray) {
+    CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Esp", env, string, carray);
+    sc.checkNonNull(carray);
+    if (gDvmJni.forceCopy) {
+        if (!GuardedCopy::check(carray, false)) {
+            ALOGE("JNI: failed guarded copy check in ReleaseStringCritical");
+            abortMaybe();
+            return;
+        }
+        carray = (const jchar*) GuardedCopy::destroy((jchar*)carray);
+    }
+    baseEnv(env)->ReleaseStringCritical(env, string, carray);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jweak Check_NewWeakGlobalRef(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewWeakGlobalRef(env, obj));
+}
+
+static void Check_DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, obj);
+    baseEnv(env)->DeleteWeakGlobalRef(env, obj);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jboolean Check_ExceptionCheck(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay | kFlag_ExcepOkay, "E", env);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->ExceptionCheck(env));
+}
+
+static jobjectRefType Check_GetObjectRefType(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    // TODO: proper decoding of jobjectRefType!
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetObjectRefType(env, obj));
+}
+
+static jobject Check_NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EpJ", env, address, capacity);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity));
+}
+
+static void* Check_GetDirectBufferAddress(JNIEnv* env, jobject buf) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
+    // TODO: check that 'buf' is a java.nio.Buffer.
+    return CHECK_JNI_EXIT("p", baseEnv(env)->GetDirectBufferAddress(env, buf));
+}
+
+static jlong Check_GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
+    // TODO: check that 'buf' is a java.nio.Buffer.
+    return CHECK_JNI_EXIT("J", baseEnv(env)->GetDirectBufferCapacity(env, buf));
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI invocation functions
+ * ===========================================================================
+ */
+
+static jint Check_DestroyJavaVM(JavaVM* vm) {
+    ScopedCheck sc(false, __FUNCTION__);
+    sc.check(true, "v", vm);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->DestroyJavaVM(vm));
+}
+
+static jint Check_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    ScopedCheck sc(false, __FUNCTION__);
+    sc.check(true, "vpp", vm, p_env, thr_args);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->AttachCurrentThread(vm, p_env, thr_args));
+}
+
+static jint Check_AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    ScopedCheck sc(false, __FUNCTION__);
+    sc.check(true, "vpp", vm, p_env, thr_args);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args));
+}
+
+static jint Check_DetachCurrentThread(JavaVM* vm) {
+    ScopedCheck sc(true, __FUNCTION__);
+    sc.check(true, "v", vm);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->DetachCurrentThread(vm));
+}
+
+static jint Check_GetEnv(JavaVM* vm, void** env, jint version) {
+    ScopedCheck sc(true, __FUNCTION__);
+    sc.check(true, "v", vm);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->GetEnv(vm, env, version));
+}
+
+
+/*
+ * ===========================================================================
+ *      Function tables
+ * ===========================================================================
+ */
+
+static const struct JNINativeInterface gCheckNativeInterface = {
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+
+    Check_GetVersion,
+
+    Check_DefineClass,
+    Check_FindClass,
+
+    Check_FromReflectedMethod,
+    Check_FromReflectedField,
+    Check_ToReflectedMethod,
+
+    Check_GetSuperclass,
+    Check_IsAssignableFrom,
+
+    Check_ToReflectedField,
+
+    Check_Throw,
+    Check_ThrowNew,
+    Check_ExceptionOccurred,
+    Check_ExceptionDescribe,
+    Check_ExceptionClear,
+    Check_FatalError,
+
+    Check_PushLocalFrame,
+    Check_PopLocalFrame,
+
+    Check_NewGlobalRef,
+    Check_DeleteGlobalRef,
+    Check_DeleteLocalRef,
+    Check_IsSameObject,
+    Check_NewLocalRef,
+    Check_EnsureLocalCapacity,
+
+    Check_AllocObject,
+    Check_NewObject,
+    Check_NewObjectV,
+    Check_NewObjectA,
+
+    Check_GetObjectClass,
+    Check_IsInstanceOf,
+
+    Check_GetMethodID,
+
+    Check_CallObjectMethod,
+    Check_CallObjectMethodV,
+    Check_CallObjectMethodA,
+    Check_CallBooleanMethod,
+    Check_CallBooleanMethodV,
+    Check_CallBooleanMethodA,
+    Check_CallByteMethod,
+    Check_CallByteMethodV,
+    Check_CallByteMethodA,
+    Check_CallCharMethod,
+    Check_CallCharMethodV,
+    Check_CallCharMethodA,
+    Check_CallShortMethod,
+    Check_CallShortMethodV,
+    Check_CallShortMethodA,
+    Check_CallIntMethod,
+    Check_CallIntMethodV,
+    Check_CallIntMethodA,
+    Check_CallLongMethod,
+    Check_CallLongMethodV,
+    Check_CallLongMethodA,
+    Check_CallFloatMethod,
+    Check_CallFloatMethodV,
+    Check_CallFloatMethodA,
+    Check_CallDoubleMethod,
+    Check_CallDoubleMethodV,
+    Check_CallDoubleMethodA,
+    Check_CallVoidMethod,
+    Check_CallVoidMethodV,
+    Check_CallVoidMethodA,
+
+    Check_CallNonvirtualObjectMethod,
+    Check_CallNonvirtualObjectMethodV,
+    Check_CallNonvirtualObjectMethodA,
+    Check_CallNonvirtualBooleanMethod,
+    Check_CallNonvirtualBooleanMethodV,
+    Check_CallNonvirtualBooleanMethodA,
+    Check_CallNonvirtualByteMethod,
+    Check_CallNonvirtualByteMethodV,
+    Check_CallNonvirtualByteMethodA,
+    Check_CallNonvirtualCharMethod,
+    Check_CallNonvirtualCharMethodV,
+    Check_CallNonvirtualCharMethodA,
+    Check_CallNonvirtualShortMethod,
+    Check_CallNonvirtualShortMethodV,
+    Check_CallNonvirtualShortMethodA,
+    Check_CallNonvirtualIntMethod,
+    Check_CallNonvirtualIntMethodV,
+    Check_CallNonvirtualIntMethodA,
+    Check_CallNonvirtualLongMethod,
+    Check_CallNonvirtualLongMethodV,
+    Check_CallNonvirtualLongMethodA,
+    Check_CallNonvirtualFloatMethod,
+    Check_CallNonvirtualFloatMethodV,
+    Check_CallNonvirtualFloatMethodA,
+    Check_CallNonvirtualDoubleMethod,
+    Check_CallNonvirtualDoubleMethodV,
+    Check_CallNonvirtualDoubleMethodA,
+    Check_CallNonvirtualVoidMethod,
+    Check_CallNonvirtualVoidMethodV,
+    Check_CallNonvirtualVoidMethodA,
+
+    Check_GetFieldID,
+
+    Check_GetObjectField,
+    Check_GetBooleanField,
+    Check_GetByteField,
+    Check_GetCharField,
+    Check_GetShortField,
+    Check_GetIntField,
+    Check_GetLongField,
+    Check_GetFloatField,
+    Check_GetDoubleField,
+    Check_SetObjectField,
+    Check_SetBooleanField,
+    Check_SetByteField,
+    Check_SetCharField,
+    Check_SetShortField,
+    Check_SetIntField,
+    Check_SetLongField,
+    Check_SetFloatField,
+    Check_SetDoubleField,
+
+    Check_GetStaticMethodID,
+
+    Check_CallStaticObjectMethod,
+    Check_CallStaticObjectMethodV,
+    Check_CallStaticObjectMethodA,
+    Check_CallStaticBooleanMethod,
+    Check_CallStaticBooleanMethodV,
+    Check_CallStaticBooleanMethodA,
+    Check_CallStaticByteMethod,
+    Check_CallStaticByteMethodV,
+    Check_CallStaticByteMethodA,
+    Check_CallStaticCharMethod,
+    Check_CallStaticCharMethodV,
+    Check_CallStaticCharMethodA,
+    Check_CallStaticShortMethod,
+    Check_CallStaticShortMethodV,
+    Check_CallStaticShortMethodA,
+    Check_CallStaticIntMethod,
+    Check_CallStaticIntMethodV,
+    Check_CallStaticIntMethodA,
+    Check_CallStaticLongMethod,
+    Check_CallStaticLongMethodV,
+    Check_CallStaticLongMethodA,
+    Check_CallStaticFloatMethod,
+    Check_CallStaticFloatMethodV,
+    Check_CallStaticFloatMethodA,
+    Check_CallStaticDoubleMethod,
+    Check_CallStaticDoubleMethodV,
+    Check_CallStaticDoubleMethodA,
+    Check_CallStaticVoidMethod,
+    Check_CallStaticVoidMethodV,
+    Check_CallStaticVoidMethodA,
+
+    Check_GetStaticFieldID,
+
+    Check_GetStaticObjectField,
+    Check_GetStaticBooleanField,
+    Check_GetStaticByteField,
+    Check_GetStaticCharField,
+    Check_GetStaticShortField,
+    Check_GetStaticIntField,
+    Check_GetStaticLongField,
+    Check_GetStaticFloatField,
+    Check_GetStaticDoubleField,
+
+    Check_SetStaticObjectField,
+    Check_SetStaticBooleanField,
+    Check_SetStaticByteField,
+    Check_SetStaticCharField,
+    Check_SetStaticShortField,
+    Check_SetStaticIntField,
+    Check_SetStaticLongField,
+    Check_SetStaticFloatField,
+    Check_SetStaticDoubleField,
+
+    Check_NewString,
+
+    Check_GetStringLength,
+    Check_GetStringChars,
+    Check_ReleaseStringChars,
+
+    Check_NewStringUTF,
+    Check_GetStringUTFLength,
+    Check_GetStringUTFChars,
+    Check_ReleaseStringUTFChars,
+
+    Check_GetArrayLength,
+    Check_NewObjectArray,
+    Check_GetObjectArrayElement,
+    Check_SetObjectArrayElement,
+
+    Check_NewBooleanArray,
+    Check_NewByteArray,
+    Check_NewCharArray,
+    Check_NewShortArray,
+    Check_NewIntArray,
+    Check_NewLongArray,
+    Check_NewFloatArray,
+    Check_NewDoubleArray,
+
+    Check_GetBooleanArrayElements,
+    Check_GetByteArrayElements,
+    Check_GetCharArrayElements,
+    Check_GetShortArrayElements,
+    Check_GetIntArrayElements,
+    Check_GetLongArrayElements,
+    Check_GetFloatArrayElements,
+    Check_GetDoubleArrayElements,
+
+    Check_ReleaseBooleanArrayElements,
+    Check_ReleaseByteArrayElements,
+    Check_ReleaseCharArrayElements,
+    Check_ReleaseShortArrayElements,
+    Check_ReleaseIntArrayElements,
+    Check_ReleaseLongArrayElements,
+    Check_ReleaseFloatArrayElements,
+    Check_ReleaseDoubleArrayElements,
+
+    Check_GetBooleanArrayRegion,
+    Check_GetByteArrayRegion,
+    Check_GetCharArrayRegion,
+    Check_GetShortArrayRegion,
+    Check_GetIntArrayRegion,
+    Check_GetLongArrayRegion,
+    Check_GetFloatArrayRegion,
+    Check_GetDoubleArrayRegion,
+    Check_SetBooleanArrayRegion,
+    Check_SetByteArrayRegion,
+    Check_SetCharArrayRegion,
+    Check_SetShortArrayRegion,
+    Check_SetIntArrayRegion,
+    Check_SetLongArrayRegion,
+    Check_SetFloatArrayRegion,
+    Check_SetDoubleArrayRegion,
+
+    Check_RegisterNatives,
+    Check_UnregisterNatives,
+
+    Check_MonitorEnter,
+    Check_MonitorExit,
+
+    Check_GetJavaVM,
+
+    Check_GetStringRegion,
+    Check_GetStringUTFRegion,
+
+    Check_GetPrimitiveArrayCritical,
+    Check_ReleasePrimitiveArrayCritical,
+
+    Check_GetStringCritical,
+    Check_ReleaseStringCritical,
+
+    Check_NewWeakGlobalRef,
+    Check_DeleteWeakGlobalRef,
+
+    Check_ExceptionCheck,
+
+    Check_NewDirectByteBuffer,
+    Check_GetDirectBufferAddress,
+    Check_GetDirectBufferCapacity,
+
+    Check_GetObjectRefType
+};
+
+static const struct JNIInvokeInterface gCheckInvokeInterface = {
+    NULL,
+    NULL,
+    NULL,
+
+    Check_DestroyJavaVM,
+    Check_AttachCurrentThread,
+    Check_DetachCurrentThread,
+
+    Check_GetEnv,
+
+    Check_AttachCurrentThreadAsDaemon,
+};
+
+/*
+ * Replace the normal table with the checked table.
+ */
+void dvmUseCheckedJniEnv(JNIEnvExt* pEnv) {
+    assert(pEnv->funcTable != &gCheckNativeInterface);
+    pEnv->baseFuncTable = pEnv->funcTable;
+    pEnv->funcTable = &gCheckNativeInterface;
+}
+
+/*
+ * Replace the normal table with the checked table.
+ */
+void dvmUseCheckedJniVm(JavaVMExt* pVm) {
+    assert(pVm->funcTable != &gCheckInvokeInterface);
+    pVm->baseFuncTable = pVm->funcTable;
+    pVm->funcTable = &gCheckInvokeInterface;
+}
diff --git a/vm/Common.h b/vm/Common.h
new file mode 100644
index 0000000..af31b97
--- /dev/null
+++ b/vm/Common.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_H_
+#define DALVIK_COMMON_H_
+
+#ifndef LOG_TAG
+# define LOG_TAG "dalvikvm"
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include "cutils/log.h"
+
+#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 !defined(NDEBUG) && defined(WITH_DALVIK_ASSERT)
+# undef assert
+# define assert(x) \
+    ((x) ? ((void)0) : (ALOGE("ASSERT FAILED (%s:%d): %s", \
+        __FILE__, __LINE__, #x), *(int*)39=39, (void)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))
+
+#define ALIGN_UP(x, n) (((size_t)(x) + (n) - 1) & ~((n) - 1))
+#define ALIGN_DOWN(x, n) ((size_t)(x) & -(n))
+#define ALIGN_UP_TO_PAGE_SIZE(p) ALIGN_UP(p, SYSTEM_PAGE_SIZE)
+#define ALIGN_DOWN_TO_PAGE_SIZE(p) ALIGN_DOWN(p, SYSTEM_PAGE_SIZE)
+
+#define CLZ(x) __builtin_clz(x)
+
+/*
+ * If "very verbose" logging is enabled, make it equivalent to ALOGV.
+ * 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      ALOGV
+# define IF_LOGVV() IF_ALOGV()
+#else
+# define LOGVV(...) ((void)0)
+# define IF_LOGVV() if (false)
+#endif
+
+
+/*
+ * These match the definitions in the VM specification.
+ */
+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;
+
+/*
+ * 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.
+ */
+struct Object;
+
+union JValue {
+#if defined(HAVE_LITTLE_ENDIAN)
+    u1      z;
+    s1      b;
+    u2      c;
+    s2      s;
+    s4      i;
+    s8      j;
+    float   f;
+    double  d;
+    Object* l;
+#endif
+#if defined(HAVE_BIG_ENDIAN)
+    struct {
+        u1    _z[3];
+        u1    z;
+    };
+    struct {
+        s1    _b[3];
+        s1    b;
+    };
+    struct {
+        u2    _c;
+        u2    c;
+    };
+    struct {
+        s2    _s;
+        s2    s;
+    };
+    s4      i;
+    s8      j;
+    float   f;
+    double  d;
+    void*   l;
+#endif
+};
+
+#define OFFSETOF_MEMBER(t, f)         \
+  (reinterpret_cast<char*>(           \
+     &reinterpret_cast<t*>(16)->f) -  \
+   reinterpret_cast<char*>(16))
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+#endif  // DALVIK_COMMON_H_
diff --git a/vm/Dalvik.h b/vm/Dalvik.h
new file mode 100644
index 0000000..eecbf8d
--- /dev/null
+++ b/vm/Dalvik.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.
+ */
+
+/*
+ * All-inclusive internal header file.  Include this to get everything useful.
+ */
+#ifndef DALVIK_DALVIK_H_
+#define DALVIK_DALVIK_H_
+
+#include "Common.h"
+#include "Inlines.h"
+#include "Misc.h"
+#include "Bits.h"
+#include "BitVector.h"
+#include "libdex/SysUtil.h"
+#include "libdex/DexDebugInfo.h"
+#include "libdex/DexFile.h"
+#include "libdex/DexProto.h"
+#include "libdex/DexUtf.h"
+#include "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/WriteBarrier.h"
+#include "oo/AccessCheck.h"
+#include "JarFile.h"
+#include "jdwp/Jdwp.h"
+#include "SignalCatcher.h"
+#include "StdioConverter.h"
+#include "JniInternal.h"
+#include "LinearAlloc.h"
+#include "analysis/DexVerify.h"
+#include "analysis/DexPrepare.h"
+#include "analysis/RegisterMap.h"
+#include "Init.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/InstrUtils.h"
+#include "AllocTracker.h"
+#include "PointerSet.h"
+#if defined(WITH_JIT)
+#include "compiler/Compiler.h"
+#endif
+#include "Globals.h"
+#include "reflect/Reflect.h"
+#include "oo/TypeCheck.h"
+#include "Atomic.h"
+#include "interp/Interp.h"
+#include "InlineNative.h"
+#include "oo/ObjectInlines.h"
+
+#endif  // DALVIK_DALVIK_H_
diff --git a/vm/DalvikVersion.h b/vm/DalvikVersion.h
new file mode 100644
index 0000000..e71c839
--- /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_H_
+#define DALVIK_VERSION_H_
+
+/*
+ * The version we show to tourists.
+ */
+#define DALVIK_MAJOR_VERSION    1
+#define DALVIK_MINOR_VERSION    6
+#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         27
+
+#endif  // DALVIK_VERSION_H_
diff --git a/vm/Ddm.cpp b/vm/Ddm.cpp
new file mode 100644
index 0000000..d441ec4
--- /dev/null
+++ b/vm/Ddm.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handle Dalvik Debug Monitor requests and events.
+ *
+ * Remember that all DDM traffic is big-endian since it travels over the
+ * JDWP connection.
+ */
+#include "Dalvik.h"
+
+#include <fcntl.h>
+#include <errno.h>
+
+/*
+ * "buf" contains a full JDWP packet, possibly with multiple chunks.  We
+ * need to process each, accumulate the replies, and ship the whole thing
+ * back.
+ *
+ * Returns "true" if we have a reply.  The reply buffer is newly allocated,
+ * and includes the chunk type/length, followed by the data.
+ *
+ * TODO: we currently assume that the request and reply include a single
+ * chunk.  If this becomes inconvenient we will need to adapt.
+ */
+bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen)
+{
+    Thread* self = dvmThreadSelf();
+    const int kChunkHdrLen = 8;
+    ArrayObject* dataArray = NULL;
+    Object* chunk = NULL;
+    bool result = false;
+
+    assert(dataLen >= 0);
+
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyDalvikDdmcChunk)) {
+        if (!dvmInitClass(gDvm.classOrgApacheHarmonyDalvikDdmcChunk)) {
+            dvmLogExceptionStackTrace();
+            dvmClearException(self);
+            goto bail;
+        }
+    }
+
+    /*
+     * The chunk handlers are written in the Java programming language, so
+     * we need to convert the buffer to a byte array.
+     */
+    dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT);
+    if (dataArray == NULL) {
+        ALOGW("array alloc failed (%d)", dataLen);
+        dvmClearException(self);
+        goto bail;
+    }
+    memcpy(dataArray->contents, buf, dataLen);
+
+    /*
+     * Run through and find all chunks.  [Currently just find the first.]
+     */
+    unsigned int offset, length, type;
+    type = get4BE((u1*)dataArray->contents + 0);
+    length = get4BE((u1*)dataArray->contents + 4);
+    offset = kChunkHdrLen;
+    if (offset+length > (unsigned int) dataLen) {
+        ALOGW("WARNING: bad chunk found (len=%u pktLen=%d)", length, dataLen);
+        goto bail;
+    }
+
+    /*
+     * Call the handler.
+     */
+    JValue callRes;
+    dvmCallMethod(self, gDvm.methDalvikDdmcServer_dispatch, NULL, &callRes,
+        type, dataArray, offset, length);
+    if (dvmCheckException(self)) {
+        ALOGI("Exception thrown by dispatcher for 0x%08x", type);
+        dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        goto bail;
+    }
+
+    ArrayObject* replyData;
+    chunk = (Object*) callRes.l;
+    if (chunk == NULL)
+        goto bail;
+
+    /* not strictly necessary -- we don't alloc from managed heap here */
+    dvmAddTrackedAlloc(chunk, self);
+
+    /*
+     * Pull the pieces out of the chunk.  We copy the results into a
+     * newly-allocated buffer that the caller can free.  We don't want to
+     * continue using the Chunk object because nothing has a reference to it.
+     *
+     * We could avoid this by returning type/data/offset/length and having
+     * the caller be aware of the object lifetime issues, but that
+     * integrates the JDWP code more tightly into the VM, and doesn't work
+     * if we have responses for multiple chunks.
+     *
+     * So we're pretty much stuck with copying data around multiple times.
+     */
+    type = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_type);
+    replyData =
+        (ArrayObject*) dvmGetFieldObject(chunk, gDvm.offDalvikDdmcChunk_data);
+    offset = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_offset);
+    length = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_length);
+
+    ALOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d",
+        type, replyData, offset, length);
+
+    if (length == 0 || replyData == NULL)
+        goto bail;
+    if (offset + length > replyData->length) {
+        ALOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d",
+            offset, length, replyData->length);
+        goto bail;
+    }
+
+    u1* reply;
+    reply = (u1*) malloc(length + kChunkHdrLen);
+    if (reply == NULL) {
+        ALOGW("malloc %d failed", length+kChunkHdrLen);
+        goto bail;
+    }
+    set4BE(reply + 0, type);
+    set4BE(reply + 4, length);
+    memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length);
+
+    *pReplyBuf = reply;
+    *pReplyLen = length + kChunkHdrLen;
+    result = true;
+
+    ALOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d",
+        (char*) reply, reply, length);
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) dataArray, self);
+    dvmReleaseTrackedAlloc(chunk, self);
+    return result;
+}
+
+/* defined in org.apache.harmony.dalvik.ddmc.DdmServer */
+#define CONNECTED       1
+#define DISCONNECTED    2
+
+/*
+ * Broadcast an event to all handlers.
+ */
+static void broadcast(int event)
+{
+    Thread* self = dvmThreadSelf();
+
+    if (self->status != THREAD_RUNNING) {
+        ALOGE("ERROR: DDM broadcast with thread status=%d", self->status);
+        /* try anyway? */
+    }
+
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer)) {
+        if (!dvmInitClass(gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer)) {
+            dvmLogExceptionStackTrace();
+            dvmClearException(self);
+            return;
+        }
+    }
+
+    JValue unused;
+    dvmCallMethod(self, gDvm.methDalvikDdmcServer_broadcast, NULL, &unused,
+        event);
+    if (dvmCheckException(self)) {
+        ALOGI("Exception thrown by broadcast(%d)", event);
+        dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        return;
+    }
+}
+
+/*
+ * First DDM packet has arrived over JDWP.  Notify the press.
+ *
+ * We can do some initialization here too.
+ */
+void dvmDdmConnected()
+{
+    // TODO: any init
+
+    ALOGV("Broadcasting DDM connect");
+    broadcast(CONNECTED);
+}
+
+/*
+ * JDWP connection has dropped.
+ *
+ * Do some cleanup.
+ */
+void dvmDdmDisconnected()
+{
+    ALOGV("Broadcasting DDM disconnect");
+    broadcast(DISCONNECTED);
+
+    gDvm.ddmThreadNotification = false;
+}
+
+
+/*
+ * Turn thread notification on or off.
+ */
+void dvmDdmSetThreadNotification(bool enable)
+{
+    /*
+     * We lock the thread list to avoid sending duplicate events or missing
+     * a thread change.  We should be okay holding this lock while sending
+     * the messages out.  (We have to hold it while accessing a live thread.)
+     */
+    dvmLockThreadList(NULL);
+    gDvm.ddmThreadNotification = enable;
+
+    if (enable) {
+        Thread* thread;
+        for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+            //ALOGW("notify %d", thread->threadId);
+            dvmDdmSendThreadNotification(thread, true);
+        }
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Send a notification when a thread starts or stops.
+ *
+ * Because we broadcast the full set of threads when the notifications are
+ * first enabled, it's possible for "thread" to be actively executing.
+ */
+void dvmDdmSendThreadNotification(Thread* thread, bool started)
+{
+    if (!gDvm.ddmThreadNotification) {
+        return;
+    }
+
+    StringObject* nameObj = NULL;
+    Object* threadObj = thread->threadObj;
+
+    if (threadObj != NULL) {
+        nameObj = (StringObject*)
+            dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
+    }
+
+    int type, len;
+    u1 buf[256];
+
+    if (started) {
+        const u2* chars;
+        u2* outChars;
+        size_t stringLen;
+
+        type = CHUNK_TYPE("THCR");
+
+        if (nameObj != NULL) {
+            stringLen = nameObj->length();
+            chars = nameObj->chars();
+        } else {
+            stringLen = 0;
+            chars = NULL;
+        }
+
+        /* leave room for the two integer fields */
+        if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2) {
+            stringLen = (sizeof(buf) - sizeof(u4)*2) / 2;
+        }
+        len = stringLen*2 + sizeof(u4)*2;
+
+        set4BE(&buf[0x00], thread->threadId);
+        set4BE(&buf[0x04], stringLen);
+
+        /* copy the UTF-16 string, transforming to big-endian */
+        outChars = (u2*)(void*)&buf[0x08];
+        while (stringLen--) {
+            set2BE((u1*) (outChars++), *chars++);
+        }
+    } else {
+        type = CHUNK_TYPE("THDE");
+
+        len = 4;
+
+        set4BE(&buf[0x00], thread->threadId);
+    }
+
+    dvmDbgDdmSendChunk(type, len, buf);
+}
+
+/*
+ * Send a notification when a thread's name changes.
+ */
+void dvmDdmSendThreadNameChange(int threadId, StringObject* newName)
+{
+    if (!gDvm.ddmThreadNotification) {
+        return;
+    }
+
+    size_t stringLen = newName->length();
+    const u2* chars = newName->chars();
+
+    /*
+     * Output format:
+     *  (4b) thread ID
+     *  (4b) stringLen
+     *  (xb) string chars
+     */
+    int bufLen = 4 + 4 + (stringLen * 2);
+    u1 buf[bufLen];
+
+    set4BE(&buf[0x00], threadId);
+    set4BE(&buf[0x04], stringLen);
+    u2* outChars = (u2*)(void*)&buf[0x08];
+    while (stringLen--) {
+        set2BE((u1*) (outChars++), *chars++);
+    }
+
+    dvmDbgDdmSendChunk(CHUNK_TYPE("THNM"), bufLen, buf);
+}
+
+/*
+ * Generate the contents of a THST chunk.  The data encompasses all known
+ * threads.
+ *
+ * Response has:
+ *  (1b) header len
+ *  (1b) bytes per entry
+ *  (2b) thread count
+ * Then, for each thread:
+ *  (4b) threadId
+ *  (1b) thread status
+ *  (4b) tid
+ *  (4b) utime
+ *  (4b) stime
+ *  (1b) is daemon?
+ *
+ * The length fields exist in anticipation of adding additional fields
+ * without wanting to break ddms or bump the full protocol version.  I don't
+ * think it warrants full versioning.  They might be extraneous and could
+ * be removed from a future version.
+ *
+ * Returns a new byte[] with the data inside, or NULL on failure.  The
+ * caller must call dvmReleaseTrackedAlloc() on the array.
+ */
+ArrayObject* dvmDdmGenerateThreadStats()
+{
+    const int kHeaderLen = 4;
+    const int kBytesPerEntry = 18;
+
+    dvmLockThreadList(NULL);
+
+    Thread* thread;
+    int threadCount = 0;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next)
+        threadCount++;
+
+    /*
+     * Create a temporary buffer.  We can't perform heap allocation with
+     * the thread list lock held (could cause a GC).  The output is small
+     * enough to sit on the stack.
+     */
+    int bufLen = kHeaderLen + threadCount * kBytesPerEntry;
+    u1 tmpBuf[bufLen];
+    u1* buf = tmpBuf;
+
+    set1(buf+0, kHeaderLen);
+    set1(buf+1, kBytesPerEntry);
+    set2BE(buf+2, (u2) threadCount);
+    buf += kHeaderLen;
+
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        bool isDaemon = false;
+
+        ProcStatData procStatData;
+        if (!dvmGetThreadStats(&procStatData, thread->systemTid)) {
+            /* failed; show zero */
+            memset(&procStatData, 0, sizeof(procStatData));
+        }
+
+        Object* threadObj = thread->threadObj;
+        if (threadObj != NULL) {
+            isDaemon = dvmGetFieldBoolean(threadObj,
+                            gDvm.offJavaLangThread_daemon);
+        }
+
+        set4BE(buf+0, thread->threadId);
+        set1(buf+4, thread->status);
+        set4BE(buf+5, thread->systemTid);
+        set4BE(buf+9, procStatData.utime);
+        set4BE(buf+13, procStatData.stime);
+        set1(buf+17, isDaemon);
+
+        buf += kBytesPerEntry;
+    }
+    dvmUnlockThreadList();
+
+
+    /*
+     * Create a byte array to hold the data.
+     */
+    ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', bufLen, ALLOC_DEFAULT);
+    if (arrayObj != NULL)
+        memcpy(arrayObj->contents, tmpBuf, bufLen);
+    return arrayObj;
+}
+
+
+/*
+ * Find the specified thread and return its stack trace as an array of
+ * StackTraceElement objects.
+ */
+ArrayObject* dvmDdmGetStackTraceById(u4 threadId)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    int* traceBuf;
+
+    dvmLockThreadList(self);
+
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadId == threadId)
+            break;
+    }
+    if (thread == NULL) {
+        ALOGI("dvmDdmGetStackTraceById: threadid=%d not found", threadId);
+        dvmUnlockThreadList();
+        return NULL;
+    }
+
+    /*
+     * Suspend the thread, pull out the stack trace, then resume the thread
+     * and release the thread list lock.  If we're being asked to examine
+     * our own stack trace, skip the suspend/resume.
+     */
+    size_t stackDepth;
+    if (thread != self)
+        dvmSuspendThread(thread);
+    traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth);
+    if (thread != self)
+        dvmResumeThread(thread);
+    dvmUnlockThreadList();
+
+    /*
+     * Convert the raw buffer into an array of StackTraceElement.
+     */
+    ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
+    free(traceBuf);
+    return trace;
+}
+
+/*
+ * Gather up the allocation data and copy it into a byte[].
+ *
+ * Returns NULL on failure with an exception raised.
+ */
+ArrayObject* dvmDdmGetRecentAllocations()
+{
+    u1* data;
+    size_t len;
+
+    if (!dvmGenerateTrackedAllocationReport(&data, &len)) {
+        /* assume OOM */
+        dvmThrowOutOfMemoryError("recent alloc native");
+        return NULL;
+    }
+
+    ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', len, ALLOC_DEFAULT);
+    if (arrayObj != NULL)
+        memcpy(arrayObj->contents, data, len);
+    return arrayObj;
+}
diff --git a/vm/Ddm.h b/vm/Ddm.h
new file mode 100644
index 0000000..f8c22ee
--- /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_H_
+#define DALVIK_DDM_H_
+
+/*
+ * Handle a packet full of DDM goodness.
+ *
+ * Returns "true" if we have anything to say in return; in which case,
+ * "*pReplyBuf" and "*pReplyLen" will also be set.
+ */
+bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen);
+
+/*
+ * Deal with the DDM server connecting and disconnecting.
+ */
+void dvmDdmConnected(void);
+void dvmDdmDisconnected(void);
+
+/*
+ * Turn thread notification on or off.
+ */
+void dvmDdmSetThreadNotification(bool enable);
+
+/*
+ * If thread start/stop notification is enabled, call this when threads
+ * are created or die.
+ */
+void dvmDdmSendThreadNotification(Thread* thread, bool started);
+
+/*
+ * If thread start/stop notification is enabled, call this when the
+ * thread name changes.
+ */
+void dvmDdmSendThreadNameChange(int threadId, StringObject* newName);
+
+/*
+ * Generate a byte[] full of thread stats for a THST packet.
+ */
+ArrayObject* dvmDdmGenerateThreadStats(void);
+
+/*
+ * Let the heap know that the HPIF when value has changed.
+ *
+ * @return true iff the when value is supported by the VM.
+ */
+bool dvmDdmHandleHpifChunk(int when);
+
+/*
+ * Let the heap know that the HPSG or NHSG what/when values have changed.
+ *
+ * @param native false for an HPSG chunk, true for an NHSG chunk
+ *
+ * @return true iff the what/when values are supported by the VM.
+ */
+bool dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native);
+
+/*
+ * Get an array of StackTraceElement objects for the specified thread.
+ */
+ArrayObject* dvmDdmGetStackTraceById(u4 threadId);
+
+/*
+ * Gather up recent allocation data and return it in a byte[].
+ *
+ * Returns NULL on failure with an exception raised.
+ */
+ArrayObject* dvmDdmGetRecentAllocations(void);
+
+#endif  // DALVIK_DDM_H_
diff --git a/vm/Debugger.cpp b/vm/Debugger.cpp
new file mode 100644
index 0000000..5c44f93
--- /dev/null
+++ b/vm/Debugger.cpp
@@ -0,0 +1,2967 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Link between JDWP and the VM.  The code here only runs as a result of
+ * requests from the debugger, so speed is not essential.  Maintaining
+ * isolation of the JDWP code should make it easier to maintain and reuse.
+ *
+ * Collecting all debugger-related pieces here will also allow us to #ifdef
+ * the JDWP code out of release builds.
+ */
+#include "Dalvik.h"
+
+/*
+Notes on garbage collection and object registration
+
+JDWP does not allow the debugger to assume that objects passed to it
+will not be garbage collected.  It specifies explicit commands (e.g.
+ObjectReference.DisableCollection) to allow the debugger to manage
+object lifetime.  It does, however, require that the VM not re-use an
+object ID unless an explicit "dispose" call has been made, and if the
+VM asks for a now-collected object we must return INVALID_OBJECT.
+
+JDWP also requires that, while the VM is suspended, no garbage collection
+occur.  The JDWP docs suggest that this is obvious, because no threads
+can be running.  Unfortunately it's not entirely clear how to deal
+with situations where the debugger itself allocates strings or executes
+code as part of displaying variables.  The easiest way to enforce this,
+short of disabling GC whenever the debugger is connected, is to ensure
+that the debugger thread can't cause a GC: it has to expand the heap or
+fail to allocate.  (Might want to make that "is debugger thread AND all
+other threads are suspended" to avoid unnecessary heap expansion by a
+poorly-timed JDWP request.)
+
+We use an "object registry" so that we can separate our internal
+representation from what we show the debugger.  This allows us to
+return a registry table index instead of a pointer or handle.
+
+There are various approaches we can take to achieve correct behavior:
+
+(1) Disable garbage collection entirely while the debugger is attached.
+This is very easy, but doesn't allow extended debugging sessions on
+small devices.
+
+(2) Keep a list of all object references requested by or sent to the
+debugger, and include the list in the GC root set.  This ensures that
+objects the debugger might care about don't go away.  This is straightforward,
+but it can cause us to hold on to large objects and prevent finalizers from
+being executed.
+
+(3) Keep a list of what amount to weak object references.  This way we
+don't interfere with the GC, and can support JDWP requests like
+"ObjectReference.IsCollected".
+
+The current implementation is #2.  The set should be reasonably small and
+performance isn't critical, so a simple expanding array can be used.
+
+
+Notes on threads:
+
+The VM has a Thread struct associated with every active thread.  The
+ThreadId we pass to the debugger is the ObjectId for the java/lang/Thread
+object, so to retrieve the VM's Thread struct we have to scan through the
+list looking for a match.
+
+When a thread goes away, we lock the list and free the struct.  To
+avoid having the thread list updated or Thread structs freed out from
+under us, we want to acquire and hold the thread list lock while we're
+performing operations on Threads.  Exceptions to this rule are noted in
+a couple of places.
+
+We can speed this up a bit by adding a Thread struct pointer to the
+java/lang/Thread object, and ensuring that both are discarded at the
+same time.
+*/
+
+#define THREAD_GROUP_ALL ((ObjectId) 0x12345)   // magic, internal-only value
+
+#define kSlot0Sub   1000    // Eclipse workaround
+
+/*
+ * System init.  We don't allocate the registry until first use.
+ * Make sure we do this before initializing JDWP.
+ */
+bool dvmDebuggerStartup()
+{
+    if (!dvmBreakpointStartup())
+        return false;
+
+    gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
+    return (gDvm.dbgRegistry != NULL);
+}
+
+/*
+ * Free registry storage.
+ */
+void dvmDebuggerShutdown()
+{
+    dvmHashTableFree(gDvm.dbgRegistry);
+    gDvm.dbgRegistry = NULL;
+    dvmBreakpointShutdown();
+}
+
+
+/*
+ * Pass these through to the VM functions.  Allows extended checking
+ * (e.g. "errorcheck" mutexes).  If nothing else we can assert() success.
+ */
+void dvmDbgInitMutex(pthread_mutex_t* pMutex)
+{
+    dvmInitMutex(pMutex);
+}
+void dvmDbgLockMutex(pthread_mutex_t* pMutex)
+{
+    dvmLockMutex(pMutex);
+}
+void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
+{
+    dvmUnlockMutex(pMutex);
+}
+void dvmDbgInitCond(pthread_cond_t* pCond)
+{
+    pthread_cond_init(pCond, NULL);
+}
+void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
+    assert(cc == 0);
+}
+void dvmDbgCondSignal(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
+    assert(cc == 0);
+}
+void dvmDbgCondBroadcast(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
+    assert(cc == 0);
+}
+
+
+/* keep track of type, in case we need to distinguish them someday */
+enum RegistryType {
+    kObjectId = 0xc1, kRefTypeId
+};
+
+/*
+ * Hash function for object IDs.  Since objects are at least 8 bytes, and
+ * could someday be allocated on 16-byte boundaries, we don't want to use
+ * the low 4 bits in our hash.
+ */
+static inline u4 registryHash(u4 val)
+{
+    return val >> 4;
+}
+
+/*
+ * (This is a dvmHashTableLookup() callback.)
+ */
+static int registryCompare(const void* obj1, const void* obj2)
+{
+    return (int) obj1 - (int) obj2;
+}
+
+
+/*
+ * Determine if an id is already in the list.
+ *
+ * If the list doesn't yet exist, this creates it.
+ *
+ * Lock the registry before calling here.
+ */
+#ifndef NDEBUG
+static bool lookupId(ObjectId id)
+{
+    void* found;
+
+    found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
+                (void*)(u4) id, registryCompare, false);
+    if (found == NULL)
+        return false;
+    assert(found == (void*)(u4) id);
+    return true;
+}
+#endif
+
+/*
+ * Register an object, if it hasn't already been.
+ *
+ * This is used for both ObjectId and RefTypeId.  In theory we don't have
+ * to register RefTypeIds unless we're worried about classes unloading.
+ *
+ * Null references must be represented as zero, or the debugger will get
+ * very confused.
+ */
+static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
+{
+    ObjectId id;
+
+    if (obj == NULL)
+        return 0;
+
+    assert((u4) obj != 0xcccccccc);
+    assert((u4) obj > 0x100);
+
+    id = (ObjectId)(u4)obj | ((u8) type) << 32;
+    if (!reg)
+        return id;
+
+    dvmHashTableLock(gDvm.dbgRegistry);
+    if (!gDvm.debuggerConnected) {
+        /* debugger has detached while we were doing stuff? */
+        ALOGI("ignoring registerObject request in thread=%d",
+            dvmThreadSelf()->threadId);
+        //dvmAbort();
+        goto bail;
+    }
+
+    dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
+                (void*)(u4) id, registryCompare, true);
+
+bail:
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+    return id;
+}
+
+/*
+ * Verify that an object has been registered.  If it hasn't, the debugger
+ * is asking for something we didn't send it, which means something
+ * somewhere is broken.
+ *
+ * If speed is an issue we can encode the registry index in the high
+ * four bytes.  We could also just hard-wire this to "true".
+ *
+ * Note this actually takes both ObjectId and RefTypeId.
+ */
+#ifndef NDEBUG
+static bool objectIsRegistered(ObjectId id, RegistryType type)
+{
+    UNUSED_PARAMETER(type);
+
+    if (id == 0)        // null reference?
+        return true;
+
+    dvmHashTableLock(gDvm.dbgRegistry);
+    bool result = lookupId(id);
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+    return result;
+}
+#endif
+
+/*
+ * Convert to/from a RefTypeId.
+ *
+ * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
+ */
+static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
+{
+    return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
+}
+#if 0
+static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
+{
+    return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
+}
+#endif
+static ClassObject* refTypeIdToClassObject(RefTypeId id)
+{
+    assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
+    return (ClassObject*)(u4) id;
+}
+
+/*
+ * Convert to/from an ObjectId.
+ */
+static ObjectId objectToObjectId(const Object* obj)
+{
+    return registerObject(obj, kObjectId, true);
+}
+static ObjectId objectToObjectIdNoReg(const Object* obj)
+{
+    return registerObject(obj, kObjectId, false);
+}
+static Object* objectIdToObject(ObjectId id)
+{
+    assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
+    return (Object*)(u4) id;
+}
+
+/*
+ * Register an object ID that might not have been registered previously.
+ *
+ * Normally this wouldn't happen -- the conversion to an ObjectId would
+ * have added the object to the registry -- but in some cases (e.g.
+ * throwing exceptions) we really want to do the registration late.
+ */
+void dvmDbgRegisterObjectId(ObjectId id)
+{
+    Object* obj = (Object*)(u4) id;
+    ALOGV("+++ registering %p (%s)", obj, obj->clazz->descriptor);
+    registerObject(obj, kObjectId, true);
+}
+
+/*
+ * Convert to/from a MethodId.
+ *
+ * These IDs are only guaranteed unique within a class, so they could be
+ * an enumeration index.  For now we just use the Method*.
+ */
+static MethodId methodToMethodId(const Method* meth)
+{
+    return (MethodId)(u4) meth;
+}
+static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
+{
+    // TODO? verify "id" is actually a method in "refTypeId"
+    return (Method*)(u4) id;
+}
+
+/*
+ * Convert to/from a FieldId.
+ *
+ * These IDs are only guaranteed unique within a class, so they could be
+ * an enumeration index.  For now we just use the Field*.
+ */
+static FieldId fieldToFieldId(const Field* field)
+{
+    return (FieldId)(u4) field;
+}
+static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
+{
+    // TODO? verify "id" is actually a field in "refTypeId"
+    return (Field*)(u4) id;
+}
+
+/*
+ * Convert to/from a FrameId.
+ *
+ * We just return a pointer to the stack frame.
+ */
+static FrameId frameToFrameId(const void* frame)
+{
+    return (FrameId)(u4) frame;
+}
+static u4* frameIdToFrame(FrameId id)
+{
+    return (u4*)(u4) id;
+}
+
+
+/*
+ * Get the invocation request state.
+ */
+DebugInvokeReq* dvmDbgGetInvokeReq()
+{
+    return &dvmThreadSelf()->invokeReq;
+}
+
+/*
+ * Enable the object registry, but don't enable debugging features yet.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgConnected()
+{
+    assert(!gDvm.debuggerConnected);
+
+    ALOGV("JDWP has attached");
+    assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
+    gDvm.debuggerConnected = true;
+}
+
+/*
+ * Enable all debugging features, including scans for breakpoints.
+ *
+ * This is a no-op if we're already active.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgActive()
+{
+    if (gDvm.debuggerActive)
+        return;
+
+    ALOGI("Debugger is active");
+    dvmInitBreakpoints();
+    gDvm.debuggerActive = true;
+    dvmEnableAllSubMode(kSubModeDebuggerActive);
+#if defined(WITH_JIT)
+    dvmCompilerUpdateGlobalState();
+#endif
+}
+
+/*
+ * Disable debugging features.
+ *
+ * Set "debuggerConnected" to false, which disables use of the object
+ * registry.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgDisconnected()
+{
+    assert(gDvm.debuggerConnected);
+
+    gDvm.debuggerActive = false;
+    dvmDisableAllSubMode(kSubModeDebuggerActive);
+#if defined(WITH_JIT)
+    dvmCompilerUpdateGlobalState();
+#endif
+
+    dvmHashTableLock(gDvm.dbgRegistry);
+    gDvm.debuggerConnected = false;
+
+    ALOGD("Debugger has detached; object registry had %d entries",
+        dvmHashTableNumEntries(gDvm.dbgRegistry));
+    //int i;
+    //for (i = 0; i < gDvm.dbgRegistryNext; i++)
+    //    LOGVV("%4d: 0x%llx", i, gDvm.dbgRegistryTable[i]);
+
+    dvmHashTableClear(gDvm.dbgRegistry);
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+}
+
+/*
+ * Returns "true" if a debugger is connected.
+ *
+ * Does not return "true" if it's just a DDM server.
+ */
+bool dvmDbgIsDebuggerConnected()
+{
+    return gDvm.debuggerActive;
+}
+
+/*
+ * Get time since last debugger activity.  Used when figuring out if the
+ * debugger has finished configuring us.
+ */
+s8 dvmDbgLastDebuggerActivity()
+{
+    return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
+}
+
+/*
+ * JDWP thread is running, don't allow GC.
+ */
+int dvmDbgThreadRunning()
+{
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_RUNNING);
+    return static_cast<int>(oldStatus);
+}
+
+/*
+ * JDWP thread is idle, allow GC.
+ */
+int dvmDbgThreadWaiting()
+{
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+    return static_cast<int>(oldStatus);
+}
+
+/*
+ * Restore state returned by Running/Waiting calls.
+ */
+int dvmDbgThreadContinuing(int status)
+{
+    ThreadStatus newStatus = static_cast<ThreadStatus>(status);
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, newStatus);
+    return static_cast<int>(oldStatus);
+}
+
+/*
+ * The debugger wants us to exit.
+ */
+void dvmDbgExit(int status)
+{
+    // TODO? invoke System.exit() to perform exit processing; ends up
+    // in System.exitInternal(), which can call JNI exit hook
+    ALOGI("GC lifetime allocation: %d bytes", gDvm.allocProf.allocCount);
+    if (CALC_CACHE_STATS) {
+        dvmDumpAtomicCacheStats(gDvm.instanceofCache);
+        dvmDumpBootClassPath();
+    }
+    exit(status);
+}
+
+
+/*
+ * ===========================================================================
+ *      Class, Object, Array
+ * ===========================================================================
+ */
+
+/*
+ * Get the class's type descriptor from a reference type ID.
+ */
+const char* dvmDbgGetClassDescriptor(RefTypeId id)
+{
+    ClassObject* clazz;
+
+    clazz = refTypeIdToClassObject(id);
+    return clazz->descriptor;
+}
+
+/*
+ * Convert a RefTypeId to an ObjectId.
+ */
+ObjectId dvmDbgGetClassObject(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return objectToObjectId((Object*) clazz);
+}
+
+/*
+ * Return the superclass of a class (will be NULL for java/lang/Object).
+ */
+RefTypeId dvmDbgGetSuperclass(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return classObjectToRefTypeId(clazz->super);
+}
+
+/*
+ * Return a class's defining class loader.
+ */
+RefTypeId dvmDbgGetClassLoader(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return objectToObjectId(clazz->classLoader);
+}
+
+/*
+ * Return a class's access flags.
+ */
+u4 dvmDbgGetAccessFlags(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return clazz->accessFlags & JAVA_FLAGS_MASK;
+}
+
+/*
+ * Is this class an interface?
+ */
+bool dvmDbgIsInterface(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return dvmIsInterfaceClass(clazz);
+}
+
+/*
+ * dvmHashForeach callback
+ */
+static int copyRefType(void* vclazz, void* varg)
+{
+    RefTypeId** pRefType = (RefTypeId**)varg;
+    **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
+    (*pRefType)++;
+    return 0;
+}
+
+/*
+ * Get the complete list of reference classes (i.e. all classes except
+ * the primitive types).
+ *
+ * Returns a newly-allocated buffer full of RefTypeId values.
+ */
+void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
+{
+    RefTypeId* pRefType;
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
+    pRefType = *pClassRefBuf =
+        (RefTypeId*)malloc(sizeof(RefTypeId) * *pNumClasses);
+
+    if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
+        ALOGW("Warning: problem getting class list");
+        /* not really expecting this to happen */
+    } else {
+        assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
+    }
+
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the list of reference classes "visible" to the specified class
+ * loader.  A class is visible to a class loader if the ClassLoader object
+ * is the defining loader or is listed as an initiating loader.
+ *
+ * Returns a newly-allocated buffer full of RefTypeId values.
+ */
+void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
+    RefTypeId** pClassRefBuf)
+{
+    Object* classLoader;
+    int numClasses = 0, maxClasses;
+
+    classLoader = objectIdToObject(classLoaderId);
+    // I don't think classLoader can be NULL, but the spec doesn't say
+
+    LOGVV("GetVisibleList: comparing to %p", classLoader);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+
+    /* over-allocate the return buffer */
+    maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
+    *pClassRefBuf = (RefTypeId*)malloc(sizeof(RefTypeId) * maxClasses);
+
+    /*
+     * Run through the list, looking for matches.
+     */
+    HashIter iter;
+    for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
+
+        if (clazz->classLoader == classLoader ||
+            dvmLoaderInInitiatingList(clazz, classLoader))
+        {
+            LOGVV("  match '%s'", clazz->descriptor);
+            (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
+        }
+    }
+    *pNumClasses = numClasses;
+
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the "JNI signature" for a class, e.g. "Ljava/lang/String;".
+ *
+ * Our class descriptors are in the correct format, so we just return that.
+ */
+static const char* jniSignature(ClassObject* clazz)
+{
+    return clazz->descriptor;
+}
+
+/*
+ * Get information about a class.
+ *
+ * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
+ * the class.
+ */
+void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
+    const char** pSignature)
+{
+    ClassObject* clazz = refTypeIdToClassObject(classId);
+
+    if (clazz->descriptor[0] == '[') {
+        /* generated array class */
+        *pStatus = CS_VERIFIED | CS_PREPARED;
+        *pTypeTag = TT_ARRAY;
+    } else {
+        if (clazz->status == CLASS_ERROR)
+            *pStatus = CS_ERROR;
+        else
+            *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
+        if (dvmIsInterfaceClass(clazz))
+            *pTypeTag = TT_INTERFACE;
+        else
+            *pTypeTag = TT_CLASS;
+    }
+    if (pSignature != NULL)
+        *pSignature = jniSignature(clazz);
+}
+
+/*
+ * Search the list of loaded classes for a match.
+ */
+bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
+        RefTypeId* pRefTypeId)
+{
+    ClassObject* clazz;
+
+    clazz = dvmFindLoadedClass(classDescriptor);
+    if (clazz != NULL) {
+        *pRefTypeId = classObjectToRefTypeId(clazz);
+        return true;
+    } else
+        return false;
+}
+
+
+/*
+ * Get an object's class and "type tag".
+ */
+void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
+    RefTypeId* pRefTypeId)
+{
+    Object* obj = objectIdToObject(objectId);
+
+    if (dvmIsArrayClass(obj->clazz))
+        *pRefTypeTag = TT_ARRAY;
+    else if (dvmIsInterfaceClass(obj->clazz))
+        *pRefTypeTag = TT_INTERFACE;
+    else
+        *pRefTypeTag = TT_CLASS;
+    *pRefTypeId = classObjectToRefTypeId(obj->clazz);
+}
+
+/*
+ * Get a class object's "type tag".
+ */
+u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
+{
+    ClassObject* clazz = refTypeIdToClassObject(refTypeId);
+
+    if (dvmIsArrayClass(clazz))
+        return TT_ARRAY;
+    else if (dvmIsInterfaceClass(clazz))
+        return TT_INTERFACE;
+    else
+        return TT_CLASS;
+}
+
+/*
+ * Get a class' signature.
+ */
+const char* dvmDbgGetSignature(RefTypeId refTypeId)
+{
+    ClassObject* clazz;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    return jniSignature(clazz);
+}
+
+/*
+ * Get class' source file.
+ *
+ * Returns a newly-allocated string.
+ */
+const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
+{
+    ClassObject* clazz;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    return clazz->sourceFile;
+}
+
+/*
+ * Get an object's type name.  (For log message display only.)
+ */
+const char* dvmDbgGetObjectTypeName(ObjectId objectId)
+{
+    if (objectId == 0)
+        return "(null)";
+
+    Object* obj = objectIdToObject(objectId);
+    return jniSignature(obj->clazz);
+}
+
+/*
+ * Determine whether or not a tag represents a primitive type.
+ */
+static bool isTagPrimitive(u1 tag)
+{
+    switch (tag) {
+    case JT_BYTE:
+    case JT_CHAR:
+    case JT_FLOAT:
+    case JT_DOUBLE:
+    case JT_INT:
+    case JT_LONG:
+    case JT_SHORT:
+    case JT_VOID:
+    case JT_BOOLEAN:
+        return true;
+    case JT_ARRAY:
+    case JT_OBJECT:
+    case JT_STRING:
+    case JT_CLASS_OBJECT:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+        return false;
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        return false;
+    }
+}
+
+/*
+ * Determine the best tag type given an object's class.
+ */
+static u1 tagFromClass(ClassObject* clazz)
+{
+    if (dvmIsArrayClass(clazz))
+        return JT_ARRAY;
+
+    if (clazz == gDvm.classJavaLangString) {
+        return JT_STRING;
+    } else if (dvmIsTheClassClass(clazz)) {
+        return JT_CLASS_OBJECT;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
+        return JT_THREAD;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
+        return JT_THREAD_GROUP;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
+        return JT_CLASS_LOADER;
+    } else {
+        return JT_OBJECT;
+    }
+}
+
+/*
+ * Return a basic tag value based solely on a type descriptor.
+ *
+ * The ASCII value maps directly to the JDWP tag constants, so we don't
+ * need to do much here.  This does not return the fancier tags like
+ * JT_THREAD.
+ */
+static u1 basicTagFromDescriptor(const char* descriptor)
+{
+    return descriptor[0];
+}
+
+/*
+ * Objects declared to hold Object might actually hold a more specific
+ * type.  The debugger may take a special interest in these (e.g. it
+ * wants to display the contents of Strings), so we want to return an
+ * appropriate tag.
+ *
+ * Null objects are tagged JT_OBJECT.
+ */
+static u1 tagFromObject(const Object* obj)
+{
+    if (obj == NULL)
+        return JT_OBJECT;
+    return tagFromClass(obj->clazz);
+}
+
+/*
+ * Determine the tag for an object.
+ *
+ * "objectId" may be 0 (i.e. NULL reference).
+ */
+u1 dvmDbgGetObjectTag(ObjectId objectId)
+{
+    return tagFromObject(objectIdToObject(objectId));
+}
+
+/*
+ * Get the widths of the specified JDWP.Tag value.
+ */
+int dvmDbgGetTagWidth(int tag)
+{
+    switch (tag) {
+    case JT_VOID:
+        return 0;
+    case JT_BYTE:
+    case JT_BOOLEAN:
+        return 1;
+    case JT_CHAR:
+    case JT_SHORT:
+        return 2;
+    case JT_FLOAT:
+    case JT_INT:
+        return 4;
+    case JT_ARRAY:
+    case JT_OBJECT:
+    case JT_STRING:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+    case JT_CLASS_OBJECT:
+        return sizeof(ObjectId);
+    case JT_DOUBLE:
+    case JT_LONG:
+        return 8;
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        return -1;
+    }
+}
+
+
+/*
+ * Return the length of the specified array.
+ */
+int dvmDbgGetArrayLength(ObjectId arrayId)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+    assert(dvmIsArray(arrayObj));
+    return arrayObj->length;
+}
+
+/*
+ * Return a tag indicating the general type of elements in the array.
+ */
+u1 dvmDbgGetArrayElementTag(ObjectId arrayId)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+
+    ClassObject* arrayClass = arrayObj->clazz;
+    u1 tag = basicTagFromDescriptor(arrayClass->descriptor + 1);
+    if (!isTagPrimitive(tag)) {
+        /* try to refine it */
+        tag = tagFromClass(arrayClass->elementClass);
+    }
+
+    return tag;
+}
+
+/*
+ * Copy a series of values with the specified width, changing the byte
+ * ordering to big-endian.
+ */
+static void copyValuesToBE(u1* out, const u1* in, int count, int width)
+{
+    int i;
+
+    switch (width) {
+    case 1:
+        memcpy(out, in, count);
+        break;
+    case 2:
+        for (i = 0; i < count; i++)
+            *(((u2*) out)+i) = get2BE(in + i*2);
+        break;
+    case 4:
+        for (i = 0; i < count; i++)
+            *(((u4*) out)+i) = get4BE(in + i*4);
+        break;
+    case 8:
+        for (i = 0; i < count; i++)
+            *(((u8*) out)+i) = get8BE(in + i*8);
+        break;
+    default:
+        assert(false);
+    }
+}
+
+/*
+ * Copy a series of values with the specified width, changing the
+ * byte order from big-endian.
+ */
+static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
+{
+    int i;
+
+    switch (width) {
+    case 1:
+        memcpy(out, in, count);
+        break;
+    case 2:
+        for (i = 0; i < count; i++)
+            set2BE(out + i*2, *((u2*)in + i));
+        break;
+    case 4:
+        for (i = 0; i < count; i++)
+            set4BE(out + i*4, *((u4*)in + i));
+        break;
+    case 8:
+        for (i = 0; i < count; i++)
+            set8BE(out + i*8, *((u8*)in + i));
+        break;
+    default:
+        assert(false);
+    }
+}
+
+/*
+ * Output a piece of an array to the reply buffer.
+ *
+ * Returns "false" if something looks fishy.
+ */
+bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
+    ExpandBuf* pReply)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+    const u1* data = (const u1*)arrayObj->contents;
+    u1 tag;
+
+    assert(dvmIsArray(arrayObj));
+
+    if (firstIndex + count > (int)arrayObj->length) {
+        ALOGW("Request for index=%d + count=%d excceds length=%d",
+            firstIndex, count, arrayObj->length);
+        return false;
+    }
+
+    tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
+
+    if (isTagPrimitive(tag)) {
+        int width = dvmDbgGetTagWidth(tag);
+        u1* outBuf;
+
+        outBuf = expandBufAddSpace(pReply, count * width);
+
+        copyValuesToBE(outBuf, data + firstIndex*width, count, width);
+    } else {
+        Object** pObjects;
+        int i;
+
+        pObjects = (Object**) data;
+        pObjects += firstIndex;
+
+        ALOGV("    --> copying %d object IDs", count);
+        //assert(tag == JT_OBJECT);     // could be object or "refined" type
+
+        for (i = 0; i < count; i++, pObjects++) {
+            u1 thisTag;
+            if (*pObjects != NULL)
+                thisTag = tagFromObject(*pObjects);
+            else
+                thisTag = tag;
+            expandBufAdd1(pReply, thisTag);
+            expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Set a range of elements in an array from the data in "buf".
+ */
+bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
+    const u1* buf)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+    u1* data = (u1*)arrayObj->contents;
+    u1 tag;
+
+    assert(dvmIsArray(arrayObj));
+
+    if (firstIndex + count > (int)arrayObj->length) {
+        ALOGW("Attempt to set index=%d + count=%d excceds length=%d",
+            firstIndex, count, arrayObj->length);
+        return false;
+    }
+
+    tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
+
+    if (isTagPrimitive(tag)) {
+        int width = dvmDbgGetTagWidth(tag);
+
+        ALOGV("    --> setting %d '%c' width=%d", count, tag, width);
+
+        copyValuesFromBE(data + firstIndex*width, buf, count, width);
+    } else {
+        Object** pObjects;
+        int i;
+
+        pObjects = (Object**) data;
+        pObjects += firstIndex;
+
+        ALOGV("    --> setting %d objects", count);
+
+        /* should do array type check here */
+        for (i = 0; i < count; i++) {
+            ObjectId id = dvmReadObjectId(&buf);
+            *pObjects++ = objectIdToObject(id);
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Create a new string.
+ *
+ * The only place the reference will be held in the VM is in our registry.
+ */
+ObjectId dvmDbgCreateString(const char* str)
+{
+    StringObject* strObj;
+
+    strObj = dvmCreateStringFromCstr(str);
+    dvmReleaseTrackedAlloc((Object*) strObj, NULL);
+    return objectToObjectId((Object*) strObj);
+}
+
+/*
+ * Allocate a new object of the specified type.
+ *
+ * Add it to the registry to prevent it from being GCed.
+ */
+ObjectId dvmDbgCreateObject(RefTypeId classId)
+{
+    ClassObject* clazz = refTypeIdToClassObject(classId);
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+    return objectToObjectId(newObj);
+}
+
+/*
+ * Allocate a new array object of the specified type and length.  The
+ * type is the array type, not the element type.
+ *
+ * Add it to the registry to prevent it from being GCed.
+ */
+ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
+{
+    ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
+    Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+    return objectToObjectId(newObj);
+}
+
+/*
+ * Determine if "instClassId" is an instance of "classId".
+ */
+bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
+{
+    ClassObject* instClazz = refTypeIdToClassObject(instClassId);
+    ClassObject* clazz = refTypeIdToClassObject(classId);
+
+    return dvmInstanceof(instClazz, clazz);
+}
+
+
+/*
+ * ===========================================================================
+ *      Method and Field
+ * ===========================================================================
+ */
+
+/*
+ * Get the method name from a MethodId.
+ */
+const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
+{
+    Method* meth;
+
+    meth = methodIdToMethod(refTypeId, id);
+    return meth->name;
+}
+
+/*
+ * Augment the access flags for synthetic methods and fields by setting
+ * the (as described by the spec) "0xf0000000 bit".  Also, strip out any
+ * flags not specified by the Java programming language.
+ */
+static u4 augmentedAccessFlags(u4 accessFlags)
+{
+    accessFlags &= JAVA_FLAGS_MASK;
+
+    if ((accessFlags & ACC_SYNTHETIC) != 0) {
+        return accessFlags | 0xf0000000;
+    } else {
+        return accessFlags;
+    }
+}
+
+/*
+ * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
+ * output all fields declared by the class.  Inherited fields are
+ * not included.
+ */
+void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply)
+{
+    ClassObject* clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    u4 declared = clazz->sfieldCount + clazz->ifieldCount;
+    expandBufAdd4BE(pReply, declared);
+
+    for (int i = 0; i < clazz->sfieldCount; i++) {
+        Field* field = &clazz->sfields[i];
+        expandBufAddFieldId(pReply, fieldToFieldId(field));
+        expandBufAddUtf8String(pReply, (const u1*) field->name);
+        expandBufAddUtf8String(pReply, (const u1*) field->signature);
+        if (withGeneric) {
+            static const u1 genericSignature[1] = "";
+            expandBufAddUtf8String(pReply, genericSignature);
+        }
+        expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
+    }
+    for (int i = 0; i < clazz->ifieldCount; i++) {
+        Field* field = &clazz->ifields[i];
+        expandBufAddFieldId(pReply, fieldToFieldId(field));
+        expandBufAddUtf8String(pReply, (const u1*) field->name);
+        expandBufAddUtf8String(pReply, (const u1*) field->signature);
+        if (withGeneric) {
+            static const u1 genericSignature[1] = "";
+            expandBufAddUtf8String(pReply, genericSignature);
+        }
+        expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
+    }
+}
+
+/*
+ * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
+ * output all methods declared by the class.  Inherited methods are
+ * not included.
+ */
+void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply)
+{
+    DexStringCache stringCache;
+    static const u1 genericSignature[1] = "";
+    ClassObject* clazz;
+    Method* meth;
+    u4 declared;
+    int i;
+
+    dexStringCacheInit(&stringCache);
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    declared = clazz->directMethodCount + clazz->virtualMethodCount;
+    expandBufAdd4BE(pReply, declared);
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        meth = &clazz->directMethods[i];
+
+        expandBufAddMethodId(pReply, methodToMethodId(meth));
+        expandBufAddUtf8String(pReply, (const u1*) meth->name);
+
+        expandBufAddUtf8String(pReply,
+            (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
+                    &stringCache));
+
+        if (withGeneric)
+            expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        meth = &clazz->virtualMethods[i];
+
+        expandBufAddMethodId(pReply, methodToMethodId(meth));
+        expandBufAddUtf8String(pReply, (const u1*) meth->name);
+
+        expandBufAddUtf8String(pReply,
+            (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
+                    &stringCache));
+
+        if (withGeneric)
+            expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
+    }
+
+    dexStringCacheRelease(&stringCache);
+}
+
+/*
+ * Output all interfaces directly implemented by the class.
+ */
+void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
+{
+    ClassObject* clazz;
+    int i, count;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    count = clazz->interfaceCount;
+    expandBufAdd4BE(pReply, count);
+    for (i = 0; i < count; i++) {
+        ClassObject* iface = clazz->interfaces[i];
+        expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
+    }
+}
+
+struct DebugCallbackContext {
+    int numItems;
+    ExpandBuf* pReply;
+    // used by locals table
+    bool withGeneric;
+};
+
+static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
+{
+    DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
+
+    expandBufAdd8BE(pContext->pReply, address);
+    expandBufAdd4BE(pContext->pReply, lineNum);
+    pContext->numItems++;
+
+    return 0;
+}
+
+/*
+ * For Method.LineTable: output the line table.
+ *
+ * Note we operate in Dalvik's 16-bit units rather than bytes.
+ */
+void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
+    ExpandBuf* pReply)
+{
+    Method* method;
+    u8 start, end;
+    DebugCallbackContext context;
+
+    memset (&context, 0, sizeof(DebugCallbackContext));
+
+    method = methodIdToMethod(refTypeId, methodId);
+    if (dvmIsNativeMethod(method)) {
+        start = (u8) -1;
+        end = (u8) -1;
+    } else {
+        start = 0;
+        end = dvmGetMethodInsnsSize(method);
+    }
+
+    expandBufAdd8BE(pReply, start);
+    expandBufAdd8BE(pReply, end);
+
+    // Add numLines later
+    size_t numLinesOffset = expandBufGetLength(pReply);
+    expandBufAdd4BE(pReply, 0);
+
+    context.pReply = pReply;
+
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+        dvmGetMethodCode(method),
+        method->clazz->descriptor,
+        method->prototype.protoIdx,
+        method->accessFlags,
+        lineTablePositionsCb, NULL, &context);
+
+    set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
+}
+
+/*
+ * Eclipse appears to expect that the "this" reference is in slot zero.
+ * If it's not, the "variables" display will show two copies of "this",
+ * possibly because it gets "this" from SF.ThisObject and then displays
+ * all locals with nonzero slot numbers.
+ *
+ * So, we remap the item in slot 0 to 1000, and remap "this" to zero.  On
+ * SF.GetValues / SF.SetValues we map them back.
+ */
+static int tweakSlot(int slot, const char* name)
+{
+    int newSlot = slot;
+
+    if (strcmp(name, "this") == 0)      // only remap "this" ptr
+        newSlot = 0;
+    else if (slot == 0)                 // always remap slot 0
+        newSlot = kSlot0Sub;
+
+    ALOGV("untweak: %d to %d", slot, newSlot);
+    return newSlot;
+}
+
+/*
+ * Reverse Eclipse hack.
+ */
+static int untweakSlot(int slot, const void* framePtr)
+{
+    int newSlot = slot;
+
+    if (slot == kSlot0Sub) {
+        newSlot = 0;
+    } else if (slot == 0) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+        const Method* method = saveArea->method;
+        newSlot = method->registersSize - method->insSize;
+    }
+
+    ALOGV("untweak: %d to %d", slot, newSlot);
+    return newSlot;
+}
+
+static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
+        u4 endAddress, const char *name, const char *descriptor,
+        const char *signature)
+{
+    DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
+
+    reg = (u2) tweakSlot(reg, name);
+
+    ALOGV("    %2d: %d(%d) '%s' '%s' slot=%d",
+        pContext->numItems, startAddress, endAddress - startAddress,
+        name, descriptor, reg);
+
+    expandBufAdd8BE(pContext->pReply, startAddress);
+    expandBufAddUtf8String(pContext->pReply, (const u1*)name);
+    expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
+    if (pContext->withGeneric) {
+        expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
+    }
+    expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
+    expandBufAdd4BE(pContext->pReply, reg);
+
+    pContext->numItems++;
+}
+
+/*
+ * For Method.VariableTable[WithGeneric]: output information about local
+ * variables for the specified method.
+ */
+void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
+    bool withGeneric, ExpandBuf* pReply)
+{
+    Method* method;
+    DebugCallbackContext context;
+
+    memset (&context, 0, sizeof(DebugCallbackContext));
+
+    method = methodIdToMethod(refTypeId, methodId);
+
+    expandBufAdd4BE(pReply, method->insSize);
+
+    // Add numLocals later
+    size_t numLocalsOffset = expandBufGetLength(pReply);
+    expandBufAdd4BE(pReply, 0);
+
+    context.pReply = pReply;
+    context.withGeneric = withGeneric;
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+        dvmGetMethodCode(method),
+        method->clazz->descriptor,
+        method->prototype.protoIdx,
+        method->accessFlags,
+        NULL, variableTableCb, &context);
+
+    set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
+}
+
+/*
+ * Get the basic tag for an instance field.
+ */
+u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
+{
+    Object* obj = objectIdToObject(objId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    const Field* field = fieldIdToField(classId, fieldId);
+    return basicTagFromDescriptor(field->signature);
+}
+
+/*
+ * Get the basic tag for a static field.
+ */
+u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
+{
+    const Field* field = fieldIdToField(refTypeId, fieldId);
+    return basicTagFromDescriptor(field->signature);
+}
+
+
+/*
+ * Copy the value of a static field into the output buffer, preceded
+ * by an appropriate tag.  The tag is based on the value held by the
+ * field, not the field's type.
+ */
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
+{
+    Object* obj = objectIdToObject(objectId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
+    u1 tag = basicTagFromDescriptor(ifield->signature);
+
+    if (tag == JT_ARRAY || tag == JT_OBJECT) {
+        Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
+        tag = tagFromObject(objVal);
+        expandBufAdd1(pReply, tag);
+        expandBufAddObjectId(pReply, objectToObjectId(objVal));
+        ALOGV("    --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
+    } else {
+        ALOGV("    --> ifieldId %x --> tag '%c'", fieldId, tag);
+        expandBufAdd1(pReply, tag);
+
+        switch (tag) {
+        case JT_BOOLEAN:
+            expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
+            break;
+        case JT_BYTE:
+            expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
+            break;
+        case JT_SHORT:
+            expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
+            break;
+        case JT_CHAR:
+            expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
+            break;
+        case JT_INT:
+        case JT_FLOAT:
+            expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
+            break;
+        case JT_LONG:
+        case JT_DOUBLE:
+            expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
+            break;
+        default:
+            ALOGE("ERROR: unhandled field type '%s'", ifield->signature);
+            assert(false);
+            break;
+        }
+    }
+}
+
+/*
+ * Set the value of the specified field.
+ */
+void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
+    int width)
+{
+    Object* obj = objectIdToObject(objectId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    InstField* field = (InstField*) fieldIdToField(classId, fieldId);
+
+    switch (field->signature[0]) {
+    case JT_BOOLEAN:
+        assert(width == 1);
+        dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
+        break;
+    case JT_BYTE:
+        assert(width == 1);
+        dvmSetFieldInt(obj, field->byteOffset, value);
+        break;
+    case JT_SHORT:
+    case JT_CHAR:
+        assert(width == 2);
+        dvmSetFieldInt(obj, field->byteOffset, value);
+        break;
+    case JT_INT:
+    case JT_FLOAT:
+        assert(width == 4);
+        dvmSetFieldInt(obj, field->byteOffset, value);
+        break;
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(width == sizeof(ObjectId));
+        dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(width == 8);
+        dvmSetFieldLong(obj, field->byteOffset, value);
+        break;
+    default:
+        ALOGE("ERROR: unhandled class type '%s'", field->signature);
+        assert(false);
+        break;
+    }
+}
+
+/*
+ * Copy the value of a static field into the output buffer, preceded
+ * by an appropriate tag.  The tag is based on the value held by the
+ * field, not the field's type.
+ */
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    ExpandBuf* pReply)
+{
+    StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
+    u1 tag = basicTagFromDescriptor(sfield->signature);
+
+    if (tag == JT_ARRAY || tag == JT_OBJECT) {
+        Object* objVal = dvmGetStaticFieldObject(sfield);
+        tag = tagFromObject(objVal);
+        expandBufAdd1(pReply, tag);
+        expandBufAddObjectId(pReply, objectToObjectId(objVal));
+        ALOGV("    --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
+    } else {
+        JValue value;
+
+        ALOGV("    --> sfieldId %x --> tag '%c'", fieldId, tag);
+        expandBufAdd1(pReply, tag);
+
+        switch (tag) {
+        case JT_BOOLEAN:
+            expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
+            break;
+        case JT_BYTE:
+            expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
+            break;
+        case JT_SHORT:
+            expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
+            break;
+        case JT_CHAR:
+            expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
+            break;
+        case JT_INT:
+            expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
+            break;
+        case JT_FLOAT:
+            value.f = dvmGetStaticFieldFloat(sfield);
+            expandBufAdd4BE(pReply, value.i);
+            break;
+        case JT_LONG:
+            expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
+            break;
+        case JT_DOUBLE:
+            value.d = dvmGetStaticFieldDouble(sfield);
+            expandBufAdd8BE(pReply, value.j);
+            break;
+        default:
+            ALOGE("ERROR: unhandled field type '%s'", sfield->signature);
+            assert(false);
+            break;
+        }
+    }
+}
+
+/*
+ * Set the value of a static field.
+ */
+void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    u8 rawValue, int width)
+{
+    StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
+    Object* objVal;
+    JValue value;
+
+    value.j = rawValue;
+
+    switch (sfield->signature[0]) {
+    case JT_BOOLEAN:
+        assert(width == 1);
+        dvmSetStaticFieldBoolean(sfield, value.z);
+        break;
+    case JT_BYTE:
+        assert(width == 1);
+        dvmSetStaticFieldByte(sfield, value.b);
+        break;
+    case JT_SHORT:
+        assert(width == 2);
+        dvmSetStaticFieldShort(sfield, value.s);
+        break;
+    case JT_CHAR:
+        assert(width == 2);
+        dvmSetStaticFieldChar(sfield, value.c);
+        break;
+    case JT_INT:
+        assert(width == 4);
+        dvmSetStaticFieldInt(sfield, value.i);
+        break;
+    case JT_FLOAT:
+        assert(width == 4);
+        dvmSetStaticFieldFloat(sfield, value.f);
+        break;
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(width == sizeof(ObjectId));
+        objVal = objectIdToObject(rawValue);
+        dvmSetStaticFieldObject(sfield, objVal);
+        break;
+    case JT_LONG:
+        assert(width == 8);
+        dvmSetStaticFieldLong(sfield, value.j);
+        break;
+    case JT_DOUBLE:
+        assert(width == 8);
+        dvmSetStaticFieldDouble(sfield, value.d);
+        break;
+    default:
+        ALOGE("ERROR: unhandled class type '%s'", sfield->signature);
+        assert(false);
+        break;
+    }
+}
+
+/*
+ * Convert a string object to a UTF-8 string.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgStringToUtf8(ObjectId strId)
+{
+    StringObject* strObj = (StringObject*) objectIdToObject(strId);
+
+    return dvmCreateCstrFromString(strObj);
+}
+
+
+/*
+ * ===========================================================================
+ *      Thread and ThreadGroup
+ * ===========================================================================
+ */
+
+/*
+ * Convert a thread object to a Thread ptr.
+ *
+ * This currently requires running through the list of threads and finding
+ * a match.
+ *
+ * IMPORTANT: grab gDvm.threadListLock before calling here.
+ */
+static Thread* threadObjToThread(Object* threadObj)
+{
+    Thread* thread;
+
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadObj == threadObj)
+            break;
+    }
+
+    return thread;
+}
+
+/*
+ * Get the status and suspend state of a thread.
+ */
+bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
+    u4* pSuspendStatus)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result = false;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    switch (thread->status) {
+    case THREAD_ZOMBIE:         *pThreadStatus = TS_ZOMBIE;     break;
+    case THREAD_RUNNING:        *pThreadStatus = TS_RUNNING;    break;
+    case THREAD_TIMED_WAIT:     *pThreadStatus = TS_SLEEPING;   break;
+    case THREAD_MONITOR:        *pThreadStatus = TS_MONITOR;    break;
+    case THREAD_WAIT:           *pThreadStatus = TS_WAIT;       break;
+    case THREAD_INITIALIZING:   *pThreadStatus = TS_ZOMBIE;     break;
+    case THREAD_STARTING:       *pThreadStatus = TS_ZOMBIE;     break;
+    case THREAD_NATIVE:         *pThreadStatus = TS_RUNNING;    break;
+    case THREAD_VMWAIT:         *pThreadStatus = TS_WAIT;       break;
+    case THREAD_SUSPENDED:      *pThreadStatus = TS_RUNNING;    break;
+    default:
+        assert(false);
+        *pThreadStatus = THREAD_ZOMBIE;
+        break;
+    }
+
+    if (dvmIsSuspended(thread))
+        *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
+    else
+        *pSuspendStatus = 0;
+
+    result = true;
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Get the thread's suspend count.
+ */
+u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    u4 result = 0;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    result = thread->suspendCount;
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Determine whether or not a thread exists in the VM's thread list.
+ *
+ * Returns "true" if the thread exists.
+ */
+bool dvmDbgThreadExists(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        result = false;
+    else
+        result = true;
+
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Determine whether or not a thread is suspended.
+ *
+ * Returns "false" if the thread is running or doesn't exist.
+ */
+bool dvmDbgIsSuspended(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result = false;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    result = dvmIsSuspended(thread);
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Return the ObjectId for the "system" thread group.
+ */
+ObjectId dvmDbgGetSystemThreadGroupId()
+{
+    Object* groupObj = dvmGetSystemThreadGroup();
+    return objectToObjectId(groupObj);
+}
+
+/*
+ * Return the ObjectId for the "main" thread group.
+ */
+ObjectId dvmDbgGetMainThreadGroupId()
+{
+    Object* groupObj = dvmGetMainThreadGroup();
+    return objectToObjectId(groupObj);
+}
+
+/*
+ * Get the name of a thread.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetThreadName(ObjectId threadId)
+{
+    Object* threadObj;
+    StringObject* nameStr;
+    char* str;
+    char* result;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+                                                gDvm.offJavaLangThread_name);
+    str = dvmCreateCstrFromString(nameStr);
+    result = (char*) malloc(strlen(str) + 20);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+    Thread* thread = threadObjToThread(threadObj);
+    if (thread != NULL)
+        sprintf(result, "<%d> %s", thread->threadId, str);
+    else
+        sprintf(result, "%s", str);
+    dvmUnlockThreadList();
+
+    free(str);
+    return result;
+}
+
+/*
+ * Get a thread's group.
+ */
+ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
+{
+    Object* threadObj;
+    Object* group;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
+    return objectToObjectId(group);
+}
+
+
+/*
+ * Get the name of a thread group.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
+{
+    Object* threadGroup;
+    StringObject* nameStr;
+
+    threadGroup = objectIdToObject(threadGroupId);
+    assert(threadGroup != NULL);
+
+    nameStr = (StringObject*)
+        dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
+    return dvmCreateCstrFromString(nameStr);
+}
+
+/*
+ * Get the parent of a thread group.
+ *
+ * Returns a newly-allocated string.
+ */
+ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
+{
+    Object* threadGroup;
+    Object* parent;
+
+    threadGroup = objectIdToObject(threadGroupId);
+    assert(threadGroup != NULL);
+
+    parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
+    return objectToObjectId(parent);
+}
+
+/*
+ * Get the list of threads in the thread group.
+ *
+ * We do this by running through the full list of threads and returning
+ * the ones that have the ThreadGroup object as their owner.
+ *
+ * If threadGroupId is set to "kAllThreads", we ignore the group field and
+ * return all threads.
+ *
+ * The caller must free "*ppThreadIds".
+ */
+void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
+    ObjectId** ppThreadIds, u4* pThreadCount)
+{
+    Object* targetThreadGroup = NULL;
+    Thread* thread;
+    int count;
+
+    if (threadGroupId != THREAD_GROUP_ALL) {
+        targetThreadGroup = objectIdToObject(threadGroupId);
+        assert(targetThreadGroup != NULL);
+    }
+
+    dvmLockThreadList(NULL);
+
+    thread = gDvm.threadList;
+    count = 0;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        Object* group;
+
+        /* Skip over the JDWP support thread.  Some debuggers
+         * get bent out of shape when they can't suspend and
+         * query all threads, so it's easier if we just don't
+         * tell them about us.
+         */
+        if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+            continue;
+
+        /* This thread is currently being created, and isn't ready
+         * to be seen by the debugger yet.
+         */
+        if (thread->threadObj == NULL)
+            continue;
+
+        group = dvmGetFieldObject(thread->threadObj,
+                    gDvm.offJavaLangThread_group);
+        if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
+            count++;
+    }
+
+    *pThreadCount = count;
+
+    if (count == 0) {
+        *ppThreadIds = NULL;
+    } else {
+        ObjectId* ptr;
+        ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
+
+        for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+            Object* group;
+
+            /* Skip over the JDWP support thread.  Some debuggers
+             * get bent out of shape when they can't suspend and
+             * query all threads, so it's easier if we just don't
+             * tell them about us.
+             */
+            if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+                continue;
+
+            /* This thread is currently being created, and isn't ready
+             * to be seen by the debugger yet.
+             */
+            if (thread->threadObj == NULL)
+                continue;
+
+            group = dvmGetFieldObject(thread->threadObj,
+                        gDvm.offJavaLangThread_group);
+            if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
+            {
+                *ptr++ = objectToObjectId(thread->threadObj);
+                count--;
+            }
+        }
+
+        assert(count == 0);
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Get all threads.
+ *
+ * The caller must free "*ppThreadIds".
+ */
+void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
+{
+    dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
+}
+
+
+/*
+ * Count up the #of frames on the thread's stack.
+ *
+ * Returns -1 on failure.
+ */
+int dvmDbgGetThreadFrameCount(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    int count = -1;
+
+    threadObj = objectIdToObject(threadId);
+
+    dvmLockThreadList(NULL);
+    thread = threadObjToThread(threadObj);
+    if (thread != NULL) {
+        count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
+    }
+    dvmUnlockThreadList();
+
+    return count;
+}
+
+/*
+ * Get info for frame N from the specified thread's stack.
+ */
+bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
+    JdwpLocation* pLoc)
+{
+    Object* threadObj;
+    Thread* thread;
+    void* framePtr;
+    int count;
+
+    threadObj = objectIdToObject(threadId);
+
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    framePtr = thread->interpSave.curFrame;
+    count = 0;
+    while (framePtr != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*)framePtr)) {
+            if (count == num) {
+                *pFrameId = frameToFrameId(framePtr);
+                if (dvmIsInterfaceClass(method->clazz))
+                    pLoc->typeTag = TT_INTERFACE;
+                else
+                    pLoc->typeTag = TT_CLASS;
+                pLoc->classId = classObjectToRefTypeId(method->clazz);
+                pLoc->methodId = methodToMethodId(method);
+                if (dvmIsNativeMethod(method))
+                    pLoc->idx = (u8)-1;
+                else
+                    pLoc->idx = saveArea->xtra.currentPc - method->insns;
+                dvmUnlockThreadList();
+                return true;
+            }
+
+            count++;
+        }
+
+        framePtr = saveArea->prevFrame;
+    }
+
+bail:
+    dvmUnlockThreadList();
+    return false;
+}
+
+/*
+ * Get the ThreadId for the current thread.
+ */
+ObjectId dvmDbgGetThreadSelfId()
+{
+    Thread* self = dvmThreadSelf();
+    return objectToObjectId(self->threadObj);
+}
+
+/*
+ * Suspend the VM.
+ */
+void dvmDbgSuspendVM(bool isEvent)
+{
+    dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
+}
+
+/*
+ * Resume the VM.
+ */
+void dvmDbgResumeVM()
+{
+    dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
+}
+
+/*
+ * Suspend one thread (not ourselves).
+ */
+void dvmDbgSuspendThread(ObjectId threadId)
+{
+    Object* threadObj = objectIdToObject(threadId);
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL) {
+        /* can happen if our ThreadDeath notify crosses in the mail */
+        ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
+    } else {
+        dvmSuspendThread(thread);
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Resume one thread (not ourselves).
+ */
+void dvmDbgResumeThread(ObjectId threadId)
+{
+    Object* threadObj = objectIdToObject(threadId);
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL) {
+        ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
+    } else {
+        dvmResumeThread(thread);
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Suspend ourselves after sending an event to the debugger.
+ */
+void dvmDbgSuspendSelf()
+{
+    dvmSuspendSelf(true);
+}
+
+/*
+ * Get the "this" object for the specified frame.
+ */
+static Object* getThisObject(const u4* framePtr)
+{
+    const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+    const Method* method = saveArea->method;
+    int argOffset = method->registersSize - method->insSize;
+    Object* thisObj;
+
+    if (method == NULL) {
+        /* this is a "break" frame? */
+        assert(false);
+        return NULL;
+    }
+
+    LOGVV("  Pulling this object for frame at %p", framePtr);
+    LOGVV("    Method='%s' native=%d static=%d this=%p",
+        method->name, dvmIsNativeMethod(method),
+        dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
+
+    /*
+     * No "this" pointer for statics.  No args on the interp stack for
+     * native methods invoked directly from the VM.
+     */
+    if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
+        thisObj = NULL;
+    else
+        thisObj = (Object*) framePtr[argOffset];
+
+    if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
+        ALOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
+            framePtr, method->clazz->descriptor, method->name);
+        thisObj = NULL;
+    }
+
+    return thisObj;
+}
+
+/*
+ * Return the "this" object for the specified frame.  The thread must be
+ * suspended.
+ */
+bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
+{
+    const u4* framePtr = frameIdToFrame(frameId);
+    Object* thisObj;
+
+    UNUSED_PARAMETER(threadId);
+
+    thisObj = getThisObject(framePtr);
+
+    *pThisId = objectToObjectId(thisObj);
+    return true;
+}
+
+/*
+ * Copy the value of a method argument or local variable into the
+ * specified buffer.  The value will be preceeded with the tag.
+ *
+ * The debugger includes the tags in the request.  Object tags may
+ * be updated with a more refined type.
+ */
+void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+    u1 tag, u1* buf, int expectedLen)
+{
+    const u4* framePtr = frameIdToFrame(frameId);
+    Object* objVal;
+    u4 intVal;
+    u8 longVal;
+
+    UNUSED_PARAMETER(threadId);
+
+    slot = untweakSlot(slot, framePtr);     // Eclipse workaround
+
+    switch (tag) {
+    case JT_BOOLEAN:
+        assert(expectedLen == 1);
+        intVal = framePtr[slot];
+        set1(buf+1, intVal != 0);
+        break;
+    case JT_BYTE:
+        assert(expectedLen == 1);
+        intVal = framePtr[slot];
+        set1(buf+1, intVal);
+        break;
+    case JT_SHORT:
+    case JT_CHAR:
+        assert(expectedLen == 2);
+        intVal = framePtr[slot];
+        set2BE(buf+1, intVal);
+        break;
+    case JT_INT:
+    case JT_FLOAT:
+        assert(expectedLen == 4);
+        intVal = framePtr[slot];
+        set4BE(buf+1, intVal);
+        break;
+    case JT_ARRAY:
+        assert(expectedLen == sizeof(ObjectId));
+        {
+            /* convert to "ObjectId" */
+            objVal = (Object*)framePtr[slot];
+            if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
+                ALOGW("JDWP: slot %d expected to hold array, %p invalid",
+                    slot, objVal);
+                dvmAbort();         // DEBUG: make it obvious
+                objVal = NULL;
+                tag = JT_OBJECT;    // JT_ARRAY not expected for NULL ref
+            }
+            dvmSetObjectId(buf+1, objectToObjectId(objVal));
+        }
+        break;
+    case JT_OBJECT:
+        assert(expectedLen == sizeof(ObjectId));
+        {
+            /* convert to "ObjectId" */
+            objVal = (Object*)framePtr[slot];
+
+            if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
+                ALOGW("JDWP: slot %d expected to hold object, %p invalid",
+                    slot, objVal);
+                dvmAbort();         // DEBUG: make it obvious
+                objVal = NULL;
+            }
+            tag = tagFromObject(objVal);
+            dvmSetObjectId(buf+1, objectToObjectId(objVal));
+        }
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(expectedLen == 8);
+        memcpy(&longVal, &framePtr[slot], 8);
+        set8BE(buf+1, longVal);
+        break;
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        break;
+    }
+
+    /* prepend tag, which may have been updated */
+    set1(buf, tag);
+}
+
+/*
+ * Copy a new value into an argument or local variable.
+ */
+void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
+    u8 value, int width)
+{
+    u4* framePtr = frameIdToFrame(frameId);
+
+    UNUSED_PARAMETER(threadId);
+
+    slot = untweakSlot(slot, framePtr);     // Eclipse workaround
+
+    switch (tag) {
+    case JT_BOOLEAN:
+        assert(width == 1);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_BYTE:
+        assert(width == 1);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_SHORT:
+    case JT_CHAR:
+        assert(width == 2);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_INT:
+    case JT_FLOAT:
+        assert(width == 4);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_STRING:
+        /* The debugger calls VirtualMachine.CreateString to create a new
+         * string, then uses this to set the object reference, when you
+         * edit a String object */
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(width == sizeof(ObjectId));
+        framePtr[slot] = (u4) objectIdToObject(value);
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(width == 8);
+        memcpy(&framePtr[slot], &value, 8);
+        break;
+    case JT_VOID:
+    case JT_CLASS_OBJECT:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+        /* not expecting these from debugger; fall through to failure */
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        break;
+    }
+}
+
+
+/*
+ * ===========================================================================
+ *      Debugger notification
+ * ===========================================================================
+ */
+
+/*
+ * Tell JDWP that a breakpoint address has been reached.
+ *
+ * "pcOffset" will be -1 for native methods.
+ * "thisPtr" will be NULL for static methods.
+ */
+void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
+    Object* thisPtr, int eventFlags)
+{
+    JdwpLocation loc;
+
+    if (dvmIsInterfaceClass(method->clazz))
+        loc.typeTag = TT_INTERFACE;
+    else
+        loc.typeTag = TT_CLASS;
+    loc.classId = classObjectToRefTypeId(method->clazz);
+    loc.methodId = methodToMethodId(method);
+    loc.idx = pcOffset;
+
+    /*
+     * Note we use "NoReg" so we don't keep track of references that are
+     * never actually sent to the debugger.  The "thisPtr" is only used to
+     * compare against registered events.
+     */
+
+    if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
+            objectToObjectIdNoReg(thisPtr), eventFlags))
+    {
+        classObjectToRefTypeId(method->clazz);
+        objectToObjectId(thisPtr);
+    }
+}
+
+/*
+ * Tell JDWP that an exception has occurred.
+ */
+void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
+    int catchRelPc, Object* exception)
+{
+    JdwpLocation throwLoc, catchLoc;
+    const Method* throwMeth;
+    const Method* catchMeth;
+
+    throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
+    if (dvmIsInterfaceClass(throwMeth->clazz))
+        throwLoc.typeTag = TT_INTERFACE;
+    else
+        throwLoc.typeTag = TT_CLASS;
+    throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
+    throwLoc.methodId = methodToMethodId(throwMeth);
+    throwLoc.idx = throwRelPc;
+
+    if (catchRelPc < 0) {
+        memset(&catchLoc, 0, sizeof(catchLoc));
+    } else {
+        catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
+        if (dvmIsInterfaceClass(catchMeth->clazz))
+            catchLoc.typeTag = TT_INTERFACE;
+        else
+            catchLoc.typeTag = TT_CLASS;
+        catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
+        catchLoc.methodId = methodToMethodId(catchMeth);
+        catchLoc.idx = catchRelPc;
+    }
+
+    /* need this for InstanceOnly filters */
+    Object* thisObj = getThisObject((u4*)throwFp);
+
+    /*
+     * Hand the event to the JDWP exception handler.  Note we're using the
+     * "NoReg" objectID on the exception, which is not strictly correct --
+     * the exception object WILL be passed up to the debugger if the
+     * debugger is interested in the event.  We do this because the current
+     * implementation of the debugger object registry never throws anything
+     * away, and some people were experiencing a fatal build up of exception
+     * objects when dealing with certain libraries.
+     */
+    dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
+        objectToObjectIdNoReg(exception),
+        classObjectToRefTypeId(exception->clazz), &catchLoc,
+        objectToObjectId(thisObj));
+}
+
+/*
+ * Tell JDWP and/or DDMS that a thread has started.
+ */
+void dvmDbgPostThreadStart(Thread* thread)
+{
+    if (gDvm.debuggerActive) {
+        dvmJdwpPostThreadChange(gDvm.jdwpState,
+            objectToObjectId(thread->threadObj), true);
+    }
+    if (gDvm.ddmThreadNotification)
+        dvmDdmSendThreadNotification(thread, true);
+}
+
+/*
+ * Tell JDWP and/or DDMS that a thread has gone away.
+ */
+void dvmDbgPostThreadDeath(Thread* thread)
+{
+    if (gDvm.debuggerActive) {
+        dvmJdwpPostThreadChange(gDvm.jdwpState,
+            objectToObjectId(thread->threadObj), false);
+    }
+    if (gDvm.ddmThreadNotification)
+        dvmDdmSendThreadNotification(thread, false);
+}
+
+/*
+ * Tell JDWP that a new class has been prepared.
+ */
+void dvmDbgPostClassPrepare(ClassObject* clazz)
+{
+    const char* signature;
+    int tag;
+
+    if (dvmIsInterfaceClass(clazz))
+        tag = TT_INTERFACE;
+    else
+        tag = TT_CLASS;
+
+    // TODO - we currently always send both "verified" and "prepared" since
+    // debuggers seem to like that.  There might be some advantage to honesty,
+    // since the class may not yet be verified.
+    signature = jniSignature(clazz);
+    dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
+        signature, CS_VERIFIED | CS_PREPARED);
+}
+
+/*
+ * The JDWP event mechanism has registered an event with a LocationOnly
+ * mod.  Tell the interpreter to call us if we hit the specified
+ * address.
+ */
+bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
+{
+    Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
+    assert(!dvmIsNativeMethod(method));
+    dvmAddBreakAddr(method, pLoc->idx);
+    return true;        /* assume success */
+}
+
+/*
+ * An event with a LocationOnly mod has been removed.
+ */
+void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
+{
+    Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
+    assert(!dvmIsNativeMethod(method));
+    dvmClearBreakAddr(method, pLoc->idx);
+}
+
+/*
+ * The JDWP event mechanism has registered a single-step event.  Tell
+ * the interpreter about it.
+ */
+bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
+    JdwpStepDepth depth)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result = false;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /*
+     * Get a pointer to the Thread struct for this ID.  The pointer will
+     * be used strictly for comparisons against the current thread pointer
+     * after the setup is complete, so we can safely release the lock.
+     */
+    dvmLockThreadList(NULL);
+    thread = threadObjToThread(threadObj);
+
+    if (thread == NULL) {
+        ALOGE("Thread for single-step not found");
+        goto bail;
+    }
+    if (!dvmIsSuspended(thread)) {
+        ALOGE("Thread for single-step not suspended");
+        assert(!"non-susp step");      // I want to know if this can happen
+        goto bail;
+    }
+
+    assert(dvmIsSuspended(thread));
+    if (!dvmAddSingleStep(thread, size, depth))
+        goto bail;
+
+    result = true;
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * A single-step event has been removed.
+ */
+void dvmDbgUnconfigureStep(ObjectId threadId)
+{
+    UNUSED_PARAMETER(threadId);
+
+    /* right now it's global, so don't need to find Thread */
+    dvmClearSingleStep(NULL);
+}
+
+/*
+ * Invoke a method in a thread that has been stopped on a breakpoint or
+ * other debugger event.  (This function is called from the JDWP thread.)
+ *
+ * Note that access control is not enforced, per spec.
+ */
+JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
+    RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
+    u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
+{
+    Object* threadObj = objectIdToObject(threadId);
+
+    dvmLockThreadList(NULL);
+
+    Thread* targetThread = threadObjToThread(threadObj);
+    if (targetThread == NULL) {
+        dvmUnlockThreadList();
+        return ERR_INVALID_THREAD;       /* thread does not exist */
+    }
+    if (!targetThread->invokeReq.ready) {
+        dvmUnlockThreadList();
+        return ERR_INVALID_THREAD;       /* thread not stopped by event */
+    }
+
+    /*
+     * We currently have a bug where we don't successfully resume the
+     * target thread if the suspend count is too deep.  We're expected to
+     * require one "resume" for each "suspend", but when asked to execute
+     * a method we have to resume fully and then re-suspend it back to the
+     * same level.  (The easiest way to cause this is to type "suspend"
+     * multiple times in jdb.)
+     *
+     * It's unclear what this means when the event specifies "resume all"
+     * and some threads are suspended more deeply than others.  This is
+     * a rare problem, so for now we just prevent it from hanging forever
+     * by rejecting the method invocation request.  Without this, we will
+     * be stuck waiting on a suspended thread.
+     */
+    if (targetThread->suspendCount > 1) {
+        ALOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
+             "for method exec",
+            dvmThreadSelf()->threadId, targetThread->threadId,
+            targetThread->suspendCount);
+        dvmUnlockThreadList();
+        return ERR_THREAD_SUSPENDED;     /* probably not expected here */
+    }
+
+    /*
+     * TODO: ought to screen the various IDs, and verify that the argument
+     * list is valid.
+     */
+
+    targetThread->invokeReq.obj = objectIdToObject(objectId);
+    targetThread->invokeReq.thread = threadObj;
+    targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
+    targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
+    targetThread->invokeReq.numArgs = numArgs;
+    targetThread->invokeReq.argArray = argArray;
+    targetThread->invokeReq.options = options;
+    targetThread->invokeReq.invokeNeeded = true;
+
+    /*
+     * This is a bit risky -- if the thread goes away we're sitting high
+     * and dry -- but we must release this before the dvmResumeAllThreads
+     * call, and it's unwise to hold it during dvmWaitForSuspend.
+     */
+    dvmUnlockThreadList();
+
+    /*
+     * We change our (JDWP thread) status, which should be THREAD_RUNNING,
+     * so the VM can suspend for a GC if the invoke request causes us to
+     * run out of memory.  It's also a good idea to change it before locking
+     * the invokeReq mutex, although that should never be held for long.
+     */
+    Thread* self = dvmThreadSelf();
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+
+    ALOGV("    Transferring control to event thread");
+    dvmLockMutex(&targetThread->invokeReq.lock);
+
+    if ((options & INVOKE_SINGLE_THREADED) == 0) {
+        ALOGV("      Resuming all threads");
+        dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+    } else {
+        ALOGV("      Resuming event thread only");
+        dvmResumeThread(targetThread);
+    }
+
+    /*
+     * Wait for the request to finish executing.
+     */
+    while (targetThread->invokeReq.invokeNeeded) {
+        pthread_cond_wait(&targetThread->invokeReq.cv,
+                          &targetThread->invokeReq.lock);
+    }
+    dvmUnlockMutex(&targetThread->invokeReq.lock);
+    ALOGV("    Control has returned from event thread");
+
+    /* wait for thread to re-suspend itself */
+    dvmWaitForSuspend(targetThread);
+
+    /*
+     * Done waiting, switch back to RUNNING.
+     */
+    dvmChangeStatus(self, oldStatus);
+
+    /*
+     * Suspend the threads.  We waited for the target thread to suspend
+     * itself, so all we need to do is suspend the others.
+     *
+     * The suspendAllThreads() call will double-suspend the event thread,
+     * so we want to resume the target thread once to keep the books straight.
+     */
+    if ((options & INVOKE_SINGLE_THREADED) == 0) {
+        ALOGV("      Suspending all threads");
+        dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+        ALOGV("      Resuming event thread to balance the count");
+        dvmResumeThread(targetThread);
+    }
+
+    /*
+     * Set up the result.
+     */
+    *pResultTag = targetThread->invokeReq.resultTag;
+    if (isTagPrimitive(targetThread->invokeReq.resultTag))
+        *pResultValue = targetThread->invokeReq.resultValue.j;
+    else {
+        Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
+        *pResultValue = objectToObjectId(tmpObj);
+    }
+    *pExceptObj = targetThread->invokeReq.exceptObj;
+    return targetThread->invokeReq.err;
+}
+
+/*
+ * Return a basic tag value for the return type.
+ */
+static u1 getReturnTypeBasicTag(const Method* method)
+{
+    const char* descriptor = dexProtoGetReturnType(&method->prototype);
+    return basicTagFromDescriptor(descriptor);
+}
+
+/*
+ * Execute the method described by "*pReq".
+ *
+ * We're currently in VMWAIT, because we're stopped on a breakpoint.  We
+ * want to switch to RUNNING while we execute.
+ */
+void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
+{
+    Thread* self = dvmThreadSelf();
+    const Method* meth;
+    Object* oldExcept;
+    ThreadStatus oldStatus;
+
+    /*
+     * We can be called while an exception is pending in the VM.  We need
+     * to preserve that across the method invocation.
+     */
+    oldExcept = dvmGetException(self);
+    if (oldExcept != NULL) {
+        dvmAddTrackedAlloc(oldExcept, self);
+        dvmClearException(self);
+    }
+
+    oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Translate the method through the vtable, unless we're calling a
+     * direct method or the debugger wants to suppress it.
+     */
+    if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
+        dvmIsDirectMethod(pReq->method))
+    {
+        meth = pReq->method;
+    } else {
+        meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
+    }
+    assert(meth != NULL);
+
+    assert(sizeof(jvalue) == sizeof(u8));
+
+    IF_ALOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        ALOGV("JDWP invoking method %p/%p %s.%s:%s",
+            pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
+        free(desc);
+    }
+
+    dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
+        (jvalue*)pReq->argArray);
+    pReq->exceptObj = objectToObjectId(dvmGetException(self));
+    pReq->resultTag = getReturnTypeBasicTag(meth);
+    if (pReq->exceptObj != 0) {
+        Object* exc = dvmGetException(self);
+        ALOGD("  JDWP invocation returning with exceptObj=%p (%s)",
+            exc, exc->clazz->descriptor);
+        //dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        /*
+         * Nothing should try to use this, but it looks like something is.
+         * Make it null to be safe.
+         */
+        pReq->resultValue.j = 0; /*0xadadadad;*/
+    } else if (pReq->resultTag == JT_OBJECT) {
+        /* if no exception thrown, examine object result more closely */
+        u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
+        if (newTag != pReq->resultTag) {
+            LOGVV("  JDWP promoted result from %d to %d",
+                pReq->resultTag, newTag);
+            pReq->resultTag = newTag;
+        }
+
+        /*
+         * Register the object.  We don't actually need an ObjectId yet,
+         * but we do need to be sure that the GC won't move or discard the
+         * object when we switch out of RUNNING.  The ObjectId conversion
+         * will add the object to the "do not touch" list.
+         *
+         * We can't use the "tracked allocation" mechanism here because
+         * the object is going to be handed off to a different thread.
+         */
+        objectToObjectId((Object*)pReq->resultValue.l);
+    }
+
+    if (oldExcept != NULL) {
+        dvmSetException(self, oldExcept);
+        dvmReleaseTrackedAlloc(oldExcept, self);
+    }
+    dvmChangeStatus(self, oldStatus);
+}
+
+// for dvmAddressSetForLine
+struct AddressSetContext {
+    bool lastAddressValid;
+    u4 lastAddress;
+    u4 lineNum;
+    AddressSet *pSet;
+};
+
+// for dvmAddressSetForLine
+static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
+{
+    AddressSetContext *pContext = (AddressSetContext *)cnxt;
+
+    if (lineNum == pContext->lineNum) {
+        if (!pContext->lastAddressValid) {
+            // Everything from this address until the next line change is ours
+            pContext->lastAddress = address;
+            pContext->lastAddressValid = true;
+        }
+        // else, If we're already in a valid range for this lineNum,
+        // just keep going (shouldn't really happen)
+    } else if (pContext->lastAddressValid) { // and the line number is new
+        u4 i;
+        // Add everything from the last entry up until here to the set
+        for (i = pContext->lastAddress; i < address; i++) {
+            dvmAddressSetSet(pContext->pSet, i);
+        }
+
+        pContext->lastAddressValid = false;
+    }
+
+    // there may be multiple entries for a line
+    return 0;
+}
+/*
+ * Build up a set of bytecode addresses associated with a line number
+ */
+const AddressSet *dvmAddressSetForLine(const Method* method, int line)
+{
+    AddressSet *result;
+    const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
+    u4 insnsSize = dvmGetMethodInsnsSize(method);
+    AddressSetContext context;
+
+    result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
+    result->setSize = insnsSize;
+
+    memset(&context, 0, sizeof(context));
+    context.pSet = result;
+    context.lineNum = line;
+    context.lastAddressValid = false;
+
+    dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
+        method->clazz->descriptor,
+        method->prototype.protoIdx,
+        method->accessFlags,
+        addressSetCb, NULL, &context);
+
+    // If the line number was the last in the position table...
+    if (context.lastAddressValid) {
+        u4 i;
+        for (i = context.lastAddress; i < insnsSize; i++) {
+            dvmAddressSetSet(result, i);
+        }
+    }
+
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      Dalvik Debug Monitor support
+ * ===========================================================================
+ */
+
+/*
+ * We have received a DDM packet over JDWP.  Hand it off to the VM.
+ */
+bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen)
+{
+    return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
+}
+
+/*
+ * First DDM packet has arrived over JDWP.  Notify the press.
+ */
+void dvmDbgDdmConnected()
+{
+    dvmDdmConnected();
+}
+
+/*
+ * JDWP connection has dropped.
+ */
+void dvmDbgDdmDisconnected()
+{
+    dvmDdmDisconnected();
+}
+
+/*
+ * Send up a JDWP event packet with a DDM chunk in it.
+ */
+void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
+{
+    assert(buf != NULL);
+    struct iovec vec[1] = { {(void*)buf, len} };
+    dvmDbgDdmSendChunkV(type, vec, 1);
+}
+
+/*
+ * Send up a JDWP event packet with a DDM chunk in it.  The chunk is
+ * concatenated from multiple source buffers.
+ */
+void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
+{
+    if (gDvm.jdwpState == NULL) {
+        ALOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
+            type);
+        return;
+    }
+
+    dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
+}
diff --git a/vm/Debugger.h b/vm/Debugger.h
new file mode 100644
index 0000000..760463e
--- /dev/null
+++ b/vm/Debugger.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik-specific side of debugger support.  (The JDWP code is intended to
+ * be relatively generic.)
+ */
+#ifndef DALVIK_DEBUGGER_H_
+#define DALVIK_DEBUGGER_H_
+
+#include <pthread.h>
+#include "Common.h"
+#include "Misc.h"
+#include "jdwp/Jdwp.h"
+
+/* fwd decl */
+struct Object;
+struct ClassObject;
+struct Method;
+struct Thread;
+
+/*
+ * Used by StepControl to track a set of addresses associated with
+ * a single line.
+ */
+struct AddressSet {
+    u4 setSize;
+    u1 set[1];
+};
+
+INLINE void dvmAddressSetSet(AddressSet *pSet, u4 toSet)
+{
+    if (toSet < pSet->setSize) {
+        pSet->set[toSet/8] |= 1 << (toSet % 8);
+    }
+}
+
+INLINE bool dvmAddressSetGet(const AddressSet *pSet, u4 toGet)
+{
+    if (toGet < pSet->setSize) {
+        return (pSet->set[toGet/8] & (1 << (toGet % 8))) != 0;
+    } else {
+        return false;
+    }
+}
+
+/*
+ * Single-step management.
+ */
+struct StepControl {
+    /* request */
+    JdwpStepSize size;
+    JdwpStepDepth depth;
+    Thread* thread;         /* don't deref; for comparison only */
+
+    /* current state */
+    bool active;
+    const Method* method;
+    int line;           /* line #; could be -1 */
+    const AddressSet* pAddressSet;    /* if non-null, address set for line */
+    int frameDepth;
+};
+
+/*
+ * Invoke-during-breakpoint support.
+ */
+struct DebugInvokeReq {
+    /* boolean; only set when we're in the tail end of an event handler */
+    bool ready;
+
+    /* boolean; set if the JDWP thread wants this thread to do work */
+    bool invokeNeeded;
+
+    /* request */
+    Object* obj;        /* not used for ClassType.InvokeMethod */
+    Object* thread;
+    ClassObject* clazz;
+    Method* method;
+    u4 numArgs;
+    u8* argArray;   /* will be NULL if numArgs==0 */
+    u4 options;
+
+    /* result */
+    JdwpError err;
+    u1 resultTag;
+    JValue resultValue;
+    ObjectId exceptObj;
+
+    /* condition variable to wait on while the method executes */
+    pthread_mutex_t lock;
+    pthread_cond_t cv;
+};
+
+/* system init/shutdown */
+bool dvmDebuggerStartup(void);
+void dvmDebuggerShutdown(void);
+
+void dvmDbgInitMutex(pthread_mutex_t* pMutex);
+void dvmDbgLockMutex(pthread_mutex_t* pMutex);
+void dvmDbgUnlockMutex(pthread_mutex_t* pMutex);
+void dvmDbgInitCond(pthread_cond_t* pCond);
+void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex);
+void dvmDbgCondSignal(pthread_cond_t* pCond);
+void dvmDbgCondBroadcast(pthread_cond_t* pCond);
+
+/*
+ * Return the DebugInvokeReq for the current thread.
+ */
+DebugInvokeReq* dvmDbgGetInvokeReq(void);
+
+/*
+ * Enable/disable breakpoints and step modes.  Used to provide a heads-up
+ * when the debugger attaches.
+ */
+void dvmDbgConnected(void);
+void dvmDbgActive(void);
+void dvmDbgDisconnected(void);
+
+/*
+ * Returns "true" if a debugger is connected.  Returns "false" if it's
+ * just DDM.
+ */
+bool dvmDbgIsDebuggerConnected(void);
+
+/*
+ * Time, in milliseconds, since the last debugger activity.  Does not
+ * include DDMS activity.  Returns -1 if there has been no activity.
+ * Returns 0 if we're in the middle of handling a debugger request.
+ */
+s8 dvmDbgLastDebuggerActivity(void);
+
+/*
+ * Block/allow GC depending on what we're doing.  These return the old
+ * status, which can be fed to dvmDbgThreadGoing() to restore the previous
+ * mode.
+ */
+int dvmDbgThreadRunning(void);
+int dvmDbgThreadWaiting(void);
+int dvmDbgThreadContinuing(int status);
+
+/*
+ * The debugger wants the VM to exit.
+ */
+void dvmDbgExit(int status);
+
+/*
+ * Class, Object, Array
+ */
+const char* dvmDbgGetClassDescriptor(RefTypeId id);
+ObjectId dvmDbgGetClassObject(RefTypeId id);
+RefTypeId dvmDbgGetSuperclass(RefTypeId id);
+ObjectId dvmDbgGetClassLoader(RefTypeId id);
+u4 dvmDbgGetAccessFlags(RefTypeId id);
+bool dvmDbgIsInterface(RefTypeId id);
+void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf);
+void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
+        RefTypeId** pClassRefBuf);
+void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
+    const char** pSignature);
+bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
+        RefTypeId* pRefTypeId);
+void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
+    RefTypeId* pRefTypeId);
+u1 dvmDbgGetClassObjectType(RefTypeId refTypeId);
+const char* dvmDbgGetSignature(RefTypeId refTypeId);
+const char* dvmDbgGetSourceFile(RefTypeId refTypeId);
+const char* dvmDbgGetObjectTypeName(ObjectId objectId);
+u1 dvmDbgGetObjectTag(ObjectId objectId);
+int dvmDbgGetTagWidth(int tag);
+
+int dvmDbgGetArrayLength(ObjectId arrayId);
+u1 dvmDbgGetArrayElementTag(ObjectId arrayId);
+bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
+    ExpandBuf* pReply);
+bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
+    const u1* buf);
+
+ObjectId dvmDbgCreateString(const char* str);
+ObjectId dvmDbgCreateObject(RefTypeId classId);
+ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length);
+
+bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId);
+
+/*
+ * Method and Field
+ */
+const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id);
+void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply);
+void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply);
+void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply);
+void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
+    ExpandBuf* pReply);
+void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId id,
+    bool withGeneric, ExpandBuf* pReply);
+
+u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId);
+u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId);
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply);
+void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
+    int width);
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    ExpandBuf* pReply);
+void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    u8 rawValue, int width);
+
+char* dvmDbgStringToUtf8(ObjectId strId);
+
+/*
+ * Thread, ThreadGroup, Frame
+ */
+char* dvmDbgGetThreadName(ObjectId threadId);
+ObjectId dvmDbgGetThreadGroup(ObjectId threadId);
+char* dvmDbgGetThreadGroupName(ObjectId threadGroupId);
+ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId);
+ObjectId dvmDbgGetSystemThreadGroupId(void);
+ObjectId dvmDbgGetMainThreadGroupId(void);
+
+bool dvmDbgGetThreadStatus(ObjectId threadId, u4* threadStatus,
+    u4* suspendStatus);
+u4 dvmDbgGetThreadSuspendCount(ObjectId threadId);
+bool dvmDbgThreadExists(ObjectId threadId);
+bool dvmDbgIsSuspended(ObjectId threadId);
+//void dvmDbgWaitForSuspend(ObjectId threadId);
+void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
+    ObjectId** ppThreadIds, u4* pThreadCount);
+void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount);
+int dvmDbgGetThreadFrameCount(ObjectId threadId);
+bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
+    JdwpLocation* pLoc);
+
+ObjectId dvmDbgGetThreadSelfId(void);
+void dvmDbgSuspendVM(bool isEvent);
+void dvmDbgResumeVM(void);
+void dvmDbgSuspendThread(ObjectId threadId);
+void dvmDbgResumeThread(ObjectId threadId);
+void dvmDbgSuspendSelf(void);
+
+bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId);
+void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+    u1 tag, u1* buf, int expectedLen);
+void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+    u1 tag, u8 value, int width);
+
+
+/*
+ * Debugger notification
+ */
+void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
+    Object* thisPtr, int eventFlags);
+void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
+    int catchRelPc, Object* exception);
+void dvmDbgPostThreadStart(Thread* thread);
+void dvmDbgPostThreadDeath(Thread* thread);
+void dvmDbgPostClassPrepare(ClassObject* clazz);
+
+/* for "eventFlags" */
+enum {
+    DBG_BREAKPOINT      = 0x01,
+    DBG_SINGLE_STEP     = 0x02,
+    DBG_METHOD_ENTRY    = 0x04,
+    DBG_METHOD_EXIT     = 0x08,
+};
+
+bool dvmDbgWatchLocation(const JdwpLocation* pLoc);
+void dvmDbgUnwatchLocation(const JdwpLocation* pLoc);
+bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
+    JdwpStepDepth depth);
+void dvmDbgUnconfigureStep(ObjectId threadId);
+
+JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
+    RefTypeId classId, MethodId methodId, u4 numArgs, u8* argArray,
+    u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj);
+void dvmDbgExecuteMethod(DebugInvokeReq* pReq);
+
+/* Make an AddressSet for a line, for single stepping */
+const AddressSet *dvmAddressSetForLine(const Method* method, int line);
+
+/* perform "late registration" of an object ID */
+void dvmDbgRegisterObjectId(ObjectId id);
+
+/*
+ * DDM support.
+ */
+bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen);
+void dvmDbgDdmConnected(void);
+void dvmDbgDdmDisconnected(void);
+void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf);
+void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt);
+
+#define CHUNK_TYPE(_name) \
+    ((_name)[0] << 24 | (_name)[1] << 16 | (_name)[2] << 8 | (_name)[3])
+
+#endif  // DALVIK_DEBUGGER_H_
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
new file mode 100644
index 0000000..2f53391
--- /dev/null
+++ b/vm/Dvm.mk
@@ -0,0 +1,349 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 += -Wall -Wextra -Wno-unused-parameter
+LOCAL_CFLAGS += -DARCH_VARIANT=\"$(dvm_arch_variant)\"
+
+#
+# Optional features.  These may impact the size or performance of the VM.
+#
+
+# Make a debugging version when building the simulator (if not told
+# otherwise) and when explicitly asked.
+dvm_make_debug_vm := false
+ifneq ($(strip $(DEBUG_DALVIK_VM)),)
+  dvm_make_debug_vm := $(DEBUG_DALVIK_VM)
+endif
+
+ifeq ($(dvm_make_debug_vm),true)
+  #
+  # "Debug" profile:
+  # - debugger enabled
+  # - profiling enabled
+  # - tracked-reference verification enabled
+  # - allocation limits enabled
+  # - GDB helpers enabled
+  # - LOGV
+  # - assert()
+  #
+  LOCAL_CFLAGS += -DWITH_INSTR_CHECKS
+  LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
+  LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS
+  LOCAL_CFLAGS += -DWITH_EXTRA_GC_CHECKS=1
+  #LOCAL_CFLAGS += -DCHECK_MUTEX
+  LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3
+  # add some extra stuff to make it easier to examine with GDB
+  LOCAL_CFLAGS += -DEASY_GDB
+  # overall config may be for a "release" build, so reconfigure these
+  LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
+else  # !dvm_make_debug_vm
+  #
+  # "Performance" profile:
+  # - all development features disabled
+  # - compiler optimizations enabled (redundant for "release" builds)
+  # - (debugging and profiling still enabled)
+  #
+  #LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1
+  # "-O2" is redundant for device (release) but useful for sim (debug)
+  #LOCAL_CFLAGS += -O2 -Winline
+  #LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
+  LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1
+  # if you want to try with assertions on the device, add:
+  #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
+endif  # !dvm_make_debug_vm
+
+# bug hunting: checksum and verify interpreted stack when making JNI calls
+#LOCAL_CFLAGS += -DWITH_JNI_STACK_CHECK
+
+LOCAL_SRC_FILES := \
+	AllocTracker.cpp \
+	Atomic.cpp.arm \
+	AtomicCache.cpp \
+	BitVector.cpp.arm \
+	CheckJni.cpp \
+	Ddm.cpp \
+	Debugger.cpp \
+	DvmDex.cpp \
+	Exception.cpp \
+	Hash.cpp \
+	IndirectRefTable.cpp.arm \
+	Init.cpp \
+	InitRefs.cpp \
+	InlineNative.cpp.arm \
+	Inlines.cpp \
+	Intern.cpp \
+	Jni.cpp \
+	JarFile.cpp \
+	LinearAlloc.cpp \
+	Misc.cpp \
+	Native.cpp \
+	PointerSet.cpp \
+	Profile.cpp \
+	RawDexFile.cpp \
+	ReferenceTable.cpp \
+	SignalCatcher.cpp \
+	StdioConverter.cpp \
+	Sync.cpp \
+	Thread.cpp \
+	UtfString.cpp \
+	alloc/Alloc.cpp \
+	alloc/CardTable.cpp \
+	alloc/HeapBitmap.cpp.arm \
+	alloc/HeapDebug.cpp \
+	alloc/Heap.cpp.arm \
+	alloc/DdmHeap.cpp \
+	alloc/Verify.cpp \
+	alloc/Visit.cpp \
+	analysis/CodeVerify.cpp \
+	analysis/DexPrepare.cpp \
+	analysis/DexVerify.cpp \
+	analysis/Liveness.cpp \
+	analysis/Optimize.cpp \
+	analysis/RegisterMap.cpp \
+	analysis/VerifySubs.cpp \
+	analysis/VfyBasicBlock.cpp \
+	hprof/Hprof.cpp \
+	hprof/HprofClass.cpp \
+	hprof/HprofHeap.cpp \
+	hprof/HprofOutput.cpp \
+	hprof/HprofString.cpp \
+	interp/Interp.cpp.arm \
+	interp/Stack.cpp \
+	jdwp/ExpandBuf.cpp \
+	jdwp/JdwpAdb.cpp \
+	jdwp/JdwpConstants.cpp \
+	jdwp/JdwpEvent.cpp \
+	jdwp/JdwpHandler.cpp \
+	jdwp/JdwpMain.cpp \
+	jdwp/JdwpSocket.cpp \
+	mterp/Mterp.cpp.arm \
+	mterp/out/InterpC-portable.cpp.arm \
+	native/InternalNative.cpp \
+	native/dalvik_bytecode_OpcodeInfo.cpp \
+	native/dalvik_system_DexFile.cpp \
+	native/dalvik_system_VMDebug.cpp \
+	native/dalvik_system_VMRuntime.cpp \
+	native/dalvik_system_VMStack.cpp \
+	native/dalvik_system_Zygote.cpp \
+	native/java_lang_Class.cpp \
+	native/java_lang_Double.cpp \
+	native/java_lang_Float.cpp \
+	native/java_lang_Math.cpp \
+	native/java_lang_Object.cpp \
+	native/java_lang_Runtime.cpp \
+	native/java_lang_String.cpp \
+	native/java_lang_System.cpp \
+	native/java_lang_Throwable.cpp \
+	native/java_lang_VMClassLoader.cpp \
+	native/java_lang_VMThread.cpp \
+	native/java_lang_reflect_AccessibleObject.cpp \
+	native/java_lang_reflect_Array.cpp \
+	native/java_lang_reflect_Constructor.cpp \
+	native/java_lang_reflect_Field.cpp \
+	native/java_lang_reflect_Method.cpp \
+	native/java_lang_reflect_Proxy.cpp \
+	native/java_util_concurrent_atomic_AtomicLong.cpp \
+	native/org_apache_harmony_dalvik_NativeTestTarget.cpp \
+	native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp \
+	native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp \
+	native/sun_misc_Unsafe.cpp \
+	oo/AccessCheck.cpp \
+	oo/Array.cpp \
+	oo/Class.cpp \
+	oo/Object.cpp \
+	oo/Resolve.cpp \
+	oo/TypeCheck.cpp \
+	reflect/Annotation.cpp \
+	reflect/Proxy.cpp \
+	reflect/Reflect.cpp \
+	test/AtomicTest.cpp.arm \
+	test/TestHash.cpp \
+	test/TestIndirectRefTable.cpp
+
+# TODO: this is the wrong test, but what's the right one?
+ifneq ($(filter arm mips,$(dvm_arch)),)
+  LOCAL_SRC_FILES += os/android.cpp
+else
+  LOCAL_SRC_FILES += os/linux.cpp
+endif
+
+WITH_COPYING_GC := $(strip $(WITH_COPYING_GC))
+
+ifeq ($(WITH_COPYING_GC),true)
+  LOCAL_CFLAGS += -DWITH_COPYING_GC
+  LOCAL_SRC_FILES += \
+	alloc/Copying.cpp.arm
+else
+  LOCAL_SRC_FILES += \
+	alloc/DlMalloc.cpp \
+	alloc/HeapSource.cpp \
+	alloc/MarkSweep.cpp.arm
+endif
+
+WITH_JIT := $(strip $(WITH_JIT))
+
+ifeq ($(WITH_JIT),true)
+  LOCAL_CFLAGS += -DWITH_JIT
+  LOCAL_SRC_FILES += \
+	compiler/Compiler.cpp \
+	compiler/Frontend.cpp \
+	compiler/Utility.cpp \
+	compiler/InlineTransformation.cpp \
+	compiler/IntermediateRep.cpp \
+	compiler/Dataflow.cpp \
+	compiler/SSATransformation.cpp \
+	compiler/Loop.cpp \
+	compiler/Ralloc.cpp \
+	interp/Jit.cpp
+endif
+
+LOCAL_C_INCLUDES += \
+	$(JNI_H_INCLUDE) \
+	dalvik \
+	dalvik/vm \
+	external/zlib \
+	libcore/include \
+
+MTERP_ARCH_KNOWN := false
+
+ifeq ($(dvm_arch),arm)
+  #dvm_arch_variant := armv7-a
+  #LOCAL_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp
+  LOCAL_CFLAGS += -Werror
+  MTERP_ARCH_KNOWN := true
+  # Select architecture-specific sources (armv5te, armv7-a, etc.)
+  LOCAL_SRC_FILES += \
+		arch/arm/CallOldABI.S \
+		arch/arm/CallEABI.S \
+		arch/arm/HintsEABI.cpp \
+		mterp/out/InterpC-$(dvm_arch_variant).cpp.arm \
+		mterp/out/InterpAsm-$(dvm_arch_variant).S
+
+  ifeq ($(WITH_JIT),true)
+    LOCAL_SRC_FILES += \
+		compiler/codegen/RallocUtil.cpp \
+		compiler/codegen/arm/$(dvm_arch_variant)/Codegen.cpp \
+		compiler/codegen/arm/$(dvm_arch_variant)/CallingConvention.S \
+		compiler/codegen/arm/Assemble.cpp \
+		compiler/codegen/arm/ArchUtility.cpp \
+		compiler/codegen/arm/LocalOptimizations.cpp \
+		compiler/codegen/arm/GlobalOptimizations.cpp \
+		compiler/codegen/arm/ArmRallocUtil.cpp \
+		compiler/template/out/CompilerTemplateAsm-$(dvm_arch_variant).S
+  endif
+endif
+
+ifeq ($(dvm_arch),mips)
+  MTERP_ARCH_KNOWN := true
+  LOCAL_C_INCLUDES += external/libffi/$(TARGET_OS)-$(TARGET_ARCH)
+  LOCAL_SHARED_LIBRARIES += libffi
+  LOCAL_SRC_FILES += \
+		arch/mips/CallO32.S \
+		arch/mips/HintsO32.cpp \
+		arch/generic/Call.cpp \
+		mterp/out/InterpC-mips.cpp \
+		mterp/out/InterpAsm-mips.S
+
+  ifeq ($(WITH_JIT),true)
+    dvm_arch_variant := mips
+    LOCAL_SRC_FILES += \
+		compiler/codegen/mips/RallocUtil.cpp \
+		compiler/codegen/mips/$(dvm_arch_variant)/Codegen.cpp \
+		compiler/codegen/mips/$(dvm_arch_variant)/CallingConvention.S \
+		compiler/codegen/mips/Assemble.cpp \
+		compiler/codegen/mips/ArchUtility.cpp \
+		compiler/codegen/mips/LocalOptimizations.cpp \
+		compiler/codegen/mips/GlobalOptimizations.cpp \
+		compiler/template/out/CompilerTemplateAsm-$(dvm_arch_variant).S
+  endif
+endif
+
+ifeq ($(dvm_arch),x86)
+  ifeq ($(dvm_os),linux)
+    MTERP_ARCH_KNOWN := true
+    LOCAL_CFLAGS += -DDVM_JMP_TABLE_MTERP=1 \
+                    -DMTERP_STUB
+    LOCAL_SRC_FILES += \
+		arch/$(dvm_arch_variant)/Call386ABI.S \
+		arch/$(dvm_arch_variant)/Hints386ABI.cpp \
+		mterp/out/InterpC-$(dvm_arch_variant).cpp \
+		mterp/out/InterpAsm-$(dvm_arch_variant).S
+    ifeq ($(WITH_JIT),true)
+      LOCAL_CFLAGS += -DARCH_IA32
+      LOCAL_SRC_FILES += \
+                compiler/codegen/x86/LowerAlu.cpp \
+                compiler/codegen/x86/LowerConst.cpp \
+                compiler/codegen/x86/LowerMove.cpp \
+                compiler/codegen/x86/Lower.cpp \
+                compiler/codegen/x86/LowerHelper.cpp \
+                compiler/codegen/x86/LowerJump.cpp \
+                compiler/codegen/x86/LowerObject.cpp \
+                compiler/codegen/x86/AnalysisO1.cpp \
+                compiler/codegen/x86/BytecodeVisitor.cpp \
+                compiler/codegen/x86/NcgAot.cpp \
+                compiler/codegen/x86/CodegenInterface.cpp \
+                compiler/codegen/x86/LowerInvoke.cpp \
+                compiler/codegen/x86/LowerReturn.cpp \
+                compiler/codegen/x86/NcgHelper.cpp \
+                compiler/codegen/x86/LowerGetPut.cpp
+
+      # need apache harmony x86 encoder/decoder
+      LOCAL_C_INCLUDES += \
+                dalvik/vm/compiler/codegen/x86/libenc
+      LOCAL_SRC_FILES += \
+                compiler/codegen/x86/libenc/enc_base.cpp \
+                compiler/codegen/x86/libenc/dec_base.cpp \
+                compiler/codegen/x86/libenc/enc_wrapper.cpp \
+                compiler/codegen/x86/libenc/enc_tabl.cpp
+
+    endif
+  endif
+endif
+
+ifeq ($(MTERP_ARCH_KNOWN),false)
+  # unknown architecture, try to use FFI
+  LOCAL_C_INCLUDES += external/libffi/$(dvm_os)-$(dvm_arch)
+
+  ifeq ($(dvm_os)-$(dvm_arch),darwin-x86)
+      # OSX includes libffi, so just make the linker aware of it directly.
+      LOCAL_LDLIBS += -lffi
+  else
+      LOCAL_SHARED_LIBRARIES += libffi
+  endif
+
+  LOCAL_SRC_FILES += \
+		arch/generic/Call.cpp \
+		arch/generic/Hints.cpp \
+		mterp/out/InterpC-allstubs.cpp
+
+  # The following symbols are usually defined in the asm file, but
+  # since we don't have an asm file in this case, we instead just
+  # peg them at 0 here, and we add an #ifdef'able define for good
+  # measure, too.
+  LOCAL_CFLAGS += -DdvmAsmInstructionStart=0 -DdvmAsmInstructionEnd=0 \
+	-DdvmAsmSisterStart=0 -DdvmAsmSisterEnd=0 -DDVM_NO_ASM_INTERP=1
+endif
diff --git a/vm/DvmDex.cpp b/vm/DvmDex.cpp
new file mode 100644
index 0000000..28cd64e
--- /dev/null
+++ b/vm/DvmDex.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * VM-specific state associated with a DEX file.
+ */
+#include "Dalvik.h"
+#include <sys/mman.h>
+
+/*
+ * Create auxillary data structures.
+ *
+ * We need a 4-byte pointer for every reference to a class, method, field,
+ * or string constant.  Summed up over all loaded DEX files (including the
+ * whoppers in the boostrap class path), this adds up to be quite a bit
+ * of native memory.
+ *
+ * For more traditional VMs these values could be stuffed into the loaded
+ * class file constant pool area, but we don't have that luxury since our
+ * classes are memory-mapped read-only.
+ *
+ * The DEX optimizer will remove the need for some of these (e.g. we won't
+ * use the entry for virtual methods that are only called through
+ * invoke-virtual-quick), creating the possibility of some space reduction
+ * at dexopt time.
+ */
+
+static DvmDex* allocateAuxStructures(DexFile* pDexFile)
+{
+    DvmDex* pDvmDex;
+    const DexHeader* pHeader;
+    u4 stringSize, classSize, methodSize, fieldSize;
+
+    pHeader = pDexFile->pHeader;
+
+    stringSize = pHeader->stringIdsSize * sizeof(struct StringObject*);
+    classSize  = pHeader->typeIdsSize * sizeof(struct ClassObject*);
+    methodSize = pHeader->methodIdsSize * sizeof(struct Method*);
+    fieldSize  = pHeader->fieldIdsSize * sizeof(struct Field*);
+
+    u4 totalSize = sizeof(DvmDex) +
+                   stringSize + classSize + methodSize + fieldSize;
+
+    u1 *blob = (u1 *)dvmAllocRegion(totalSize,
+                              PROT_READ | PROT_WRITE, "dalvik-aux-structure");
+    if ((void *)blob == MAP_FAILED)
+        return NULL;
+
+    pDvmDex = (DvmDex*)blob;
+    blob += sizeof(DvmDex);
+
+    pDvmDex->pDexFile = pDexFile;
+    pDvmDex->pHeader = pHeader;
+
+    pDvmDex->pResStrings = (struct StringObject**)blob;
+    blob += stringSize;
+    pDvmDex->pResClasses = (struct ClassObject**)blob;
+    blob += classSize;
+    pDvmDex->pResMethods = (struct Method**)blob;
+    blob += methodSize;
+    pDvmDex->pResFields = (struct Field**)blob;
+
+    ALOGV("+++ DEX %p: allocateAux (%d+%d+%d+%d)*4 = %d bytes",
+        pDvmDex, stringSize/4, classSize/4, methodSize/4, fieldSize/4,
+        stringSize + classSize + methodSize + fieldSize);
+
+    pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
+
+    dvmInitMutex(&pDvmDex->modLock);
+
+    return pDvmDex;
+}
+
+/*
+ * Given an open optimized DEX file, map it into read-only shared memory and
+ * parse the contents.
+ *
+ * Returns nonzero on error.
+ */
+int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
+{
+    DvmDex* pDvmDex;
+    DexFile* pDexFile;
+    MemMapping memMap;
+    int parseFlags = kDexParseDefault;
+    int result = -1;
+
+    if (gDvm.verifyDexChecksum)
+        parseFlags |= kDexParseVerifyChecksum;
+
+    if (lseek(fd, 0, SEEK_SET) < 0) {
+        ALOGE("lseek rewind failed");
+        goto bail;
+    }
+
+    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
+        ALOGE("Unable to map file");
+        goto bail;
+    }
+
+    pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
+    if (pDexFile == NULL) {
+        ALOGE("DEX parse failed");
+        sysReleaseShmem(&memMap);
+        goto bail;
+    }
+
+    pDvmDex = allocateAuxStructures(pDexFile);
+    if (pDvmDex == NULL) {
+        dexFileFree(pDexFile);
+        sysReleaseShmem(&memMap);
+        goto bail;
+    }
+
+    /* tuck this into the DexFile so it gets released later */
+    sysCopyMap(&pDvmDex->memMap, &memMap);
+    pDvmDex->isMappedReadOnly = true;
+    *ppDvmDex = pDvmDex;
+    result = 0;
+
+bail:
+    return result;
+}
+
+/*
+ * Create a DexFile structure for a "partial" DEX.  This is one that is in
+ * the process of being optimized.  The optimization header isn't finished
+ * and we won't have any of the auxillary data tables, so we have to do
+ * the initialization slightly differently.
+ *
+ * Returns nonzero on error.
+ */
+int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
+{
+    DvmDex* pDvmDex;
+    DexFile* pDexFile;
+    int parseFlags = kDexParseDefault;
+    int result = -1;
+
+    /* -- file is incomplete, new checksum has not yet been calculated
+    if (gDvm.verifyDexChecksum)
+        parseFlags |= kDexParseVerifyChecksum;
+    */
+
+    pDexFile = dexFileParse((u1*)addr, len, parseFlags);
+    if (pDexFile == NULL) {
+        ALOGE("DEX parse failed");
+        goto bail;
+    }
+    pDvmDex = allocateAuxStructures(pDexFile);
+    if (pDvmDex == NULL) {
+        dexFileFree(pDexFile);
+        goto bail;
+    }
+
+    pDvmDex->isMappedReadOnly = false;
+    *ppDvmDex = pDvmDex;
+    result = 0;
+
+bail:
+    return result;
+}
+
+/*
+ * Free up the DexFile and any associated data structures.
+ *
+ * Note we may be called with a partially-initialized DvmDex.
+ */
+void dvmDexFileFree(DvmDex* pDvmDex)
+{
+    u4 totalSize;
+
+    if (pDvmDex == NULL)
+        return;
+
+    dvmDestroyMutex(&pDvmDex->modLock);
+
+    totalSize  = pDvmDex->pHeader->stringIdsSize * sizeof(struct StringObject*);
+    totalSize += pDvmDex->pHeader->typeIdsSize * sizeof(struct ClassObject*);
+    totalSize += pDvmDex->pHeader->methodIdsSize * sizeof(struct Method*);
+    totalSize += pDvmDex->pHeader->fieldIdsSize * sizeof(struct Field*);
+    totalSize += sizeof(DvmDex);
+
+    dexFileFree(pDvmDex->pDexFile);
+
+    ALOGV("+++ DEX %p: freeing aux structs", pDvmDex);
+    dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
+    sysReleaseShmem(&pDvmDex->memMap);
+    munmap(pDvmDex, totalSize);
+}
+
+
+/*
+ * Change the byte at the specified address to a new value.  If the location
+ * already has the new value, do nothing.
+ *
+ * This requires changing the access permissions to read-write, updating
+ * the value, and then resetting the permissions.
+ *
+ * We need to ensure mutual exclusion at a page granularity to avoid a race
+ * where one threads sets read-write, another thread sets read-only, and
+ * then the first thread does a write.  Since we don't do a lot of updates,
+ * and the window is small, we just use a lock across the entire DvmDex.
+ * We're only trying to make the page state change atomic; it's up to the
+ * caller to ensure that multiple threads aren't stomping on the same
+ * location (e.g. breakpoints and verifier/optimizer changes happening
+ * simultaneously).
+ *
+ * TODO: if we're back to the original state of the page, use
+ * madvise(MADV_DONTNEED) to release the private/dirty copy.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
+{
+    if (*addr == newVal) {
+        ALOGV("+++ byte at %p is already 0x%02x", addr, newVal);
+        return true;
+    }
+
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
+    ALOGV("+++ change byte at %p from 0x%02x to 0x%02x", addr, *addr, newVal);
+    if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RW) failed");
+        /* expected on files mounted from FAT; keep going (may crash) */
+    }
+
+    *addr = newVal;
+
+    if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RO) failed");
+        /* expected on files mounted from FAT; keep going */
+    }
+
+    dvmUnlockMutex(&pDvmDex->modLock);
+
+    return true;
+}
+
+/*
+ * Change the 2-byte value at the specified address to a new value.  If the
+ * location already has the new value, do nothing.
+ *
+ * Otherwise works like dvmDexChangeDex1.
+ */
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
+{
+    if (*addr == newVal) {
+        ALOGV("+++ value at %p is already 0x%04x", addr, newVal);
+        return true;
+    }
+
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
+    ALOGV("+++ change 2byte at %p from 0x%04x to 0x%04x", addr, *addr, newVal);
+    if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RW) failed");
+        /* expected on files mounted from FAT; keep going (may crash) */
+    }
+
+    *addr = newVal;
+
+    if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RO) failed");
+        /* expected on files mounted from FAT; keep going */
+    }
+
+    dvmUnlockMutex(&pDvmDex->modLock);
+
+    return true;
+}
diff --git a/vm/DvmDex.h b/vm/DvmDex.h
new file mode 100644
index 0000000..b3b5960
--- /dev/null
+++ b/vm/DvmDex.h
@@ -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.
+ */
+
+/*
+ * The VM wraps some additional data structures around the DexFile.  These
+ * are defined here.
+ */
+#ifndef DALVIK_DVMDEX_H_
+#define DALVIK_DVMDEX_H_
+
+#include "libdex/DexFile.h"
+
+/* extern */
+struct ClassObject;
+struct HashTable;
+struct InstField;
+struct Method;
+struct StringObject;
+
+
+/*
+ * Some additional VM data structures that are associated with the DEX file.
+ */
+struct DvmDex {
+    /* pointer to the DexFile we're associated with */
+    DexFile*            pDexFile;
+
+    /* clone of pDexFile->pHeader (it's used frequently enough) */
+    const DexHeader*    pHeader;
+
+    /* interned strings; parallel to "stringIds" */
+    struct StringObject** pResStrings;
+
+    /* resolved classes; parallel to "typeIds" */
+    struct ClassObject** pResClasses;
+
+    /* resolved methods; parallel to "methodIds" */
+    struct Method**     pResMethods;
+
+    /* resolved instance fields; parallel to "fieldIds" */
+    /* (this holds both InstField and StaticField) */
+    struct Field**      pResFields;
+
+    /* interface method lookup cache */
+    struct AtomicCache* pInterfaceCache;
+
+    /* shared memory region with file contents */
+    bool                isMappedReadOnly;
+    MemMapping          memMap;
+
+    /* lock ensuring mutual exclusion during updates */
+    pthread_mutex_t     modLock;
+};
+
+
+/*
+ * Given a file descriptor for an open "optimized" DEX file, map it into
+ * memory and parse the contents.
+ *
+ * On success, returns 0 and sets "*ppDvmDex" to a newly-allocated DvmDex.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex);
+
+/*
+ * Open a partial DEX file.  Only useful as part of the optimization process.
+ */
+int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex);
+
+/*
+ * Free a DvmDex structure, along with any associated structures.
+ */
+void dvmDexFileFree(DvmDex* pDvmDex);
+
+
+/*
+ * Change the 1- or 2-byte value at the specified address to a new value.  If
+ * the location already has the new value, do nothing.
+ *
+ * This does not make any synchronization guarantees.  The caller must
+ * ensure exclusivity vs. other callers.
+ *
+ * For the 2-byte call, the pointer should have 16-bit alignment.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal);
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal);
+
+
+/*
+ * Return the requested item if it has been resolved, or NULL if it hasn't.
+ */
+INLINE struct StringObject* dvmDexGetResolvedString(const DvmDex* pDvmDex,
+    u4 stringIdx)
+{
+    assert(stringIdx < pDvmDex->pHeader->stringIdsSize);
+    return pDvmDex->pResStrings[stringIdx];
+}
+INLINE struct ClassObject* dvmDexGetResolvedClass(const DvmDex* pDvmDex,
+    u4 classIdx)
+{
+    assert(classIdx < pDvmDex->pHeader->typeIdsSize);
+    return pDvmDex->pResClasses[classIdx];
+}
+INLINE struct Method* dvmDexGetResolvedMethod(const DvmDex* pDvmDex,
+    u4 methodIdx)
+{
+    assert(methodIdx < pDvmDex->pHeader->methodIdsSize);
+    return pDvmDex->pResMethods[methodIdx];
+}
+INLINE struct Field* dvmDexGetResolvedField(const DvmDex* pDvmDex,
+    u4 fieldIdx)
+{
+    assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize);
+    return pDvmDex->pResFields[fieldIdx];
+}
+
+/*
+ * Update the resolved item table.  Resolution always produces the same
+ * result, so we're not worried about atomicity here.
+ */
+INLINE void dvmDexSetResolvedString(DvmDex* pDvmDex, u4 stringIdx,
+    struct StringObject* str)
+{
+    assert(stringIdx < pDvmDex->pHeader->stringIdsSize);
+    pDvmDex->pResStrings[stringIdx] = str;
+}
+INLINE void dvmDexSetResolvedClass(DvmDex* pDvmDex, u4 classIdx,
+    struct ClassObject* clazz)
+{
+    assert(classIdx < pDvmDex->pHeader->typeIdsSize);
+    pDvmDex->pResClasses[classIdx] = clazz;
+}
+INLINE void dvmDexSetResolvedMethod(DvmDex* pDvmDex, u4 methodIdx,
+    struct Method* method)
+{
+    assert(methodIdx < pDvmDex->pHeader->methodIdsSize);
+    pDvmDex->pResMethods[methodIdx] = method;
+}
+INLINE void dvmDexSetResolvedField(DvmDex* pDvmDex, u4 fieldIdx,
+    struct Field* field)
+{
+    assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize);
+    pDvmDex->pResFields[fieldIdx] = field;
+}
+
+#endif  // DALVIK_DVMDEX_H_
diff --git a/vm/Exception.cpp b/vm/Exception.cpp
new file mode 100644
index 0000000..ca76140
--- /dev/null
+++ b/vm/Exception.cpp
@@ -0,0 +1,1454 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Exception handling.
+ */
+#include "Dalvik.h"
+#include "libdex/DexCatch.h"
+
+#include <stdlib.h>
+
+/*
+Notes on Exception Handling
+
+We have one fairly sticky issue to deal with: creating the exception stack
+trace.  The trouble is that we need the current value of the program
+counter for the method now being executed, but that's only held in a local
+variable or hardware register in the main interpreter loop.
+
+The exception mechanism requires that the current stack trace be associated
+with a Throwable at the time the Throwable is constructed.  The construction
+may or may not be associated with a throw.  We have three situations to
+consider:
+
+ (1) A Throwable is created with a "new Throwable" statement in the
+     application code, for immediate or deferred use with a "throw" statement.
+ (2) The VM throws an exception from within the interpreter core, e.g.
+     after an integer divide-by-zero.
+ (3) The VM throws an exception from somewhere deeper down, e.g. while
+     trying to link a class.
+
+We need to have the current value for the PC, which means that for
+situation (3) the interpreter loop must copy it to an externally-accessible
+location before handling any opcode that could cause the VM to throw
+an exception.  We can't store it globally, because the various threads
+would trample each other.  We can't store it in the Thread structure,
+because it'll get overwritten as soon as the Throwable constructor starts
+executing.  It needs to go on the stack, but our stack frames hold the
+caller's *saved* PC, not the current PC.
+
+Situation #1 doesn't require special handling.  Situation #2 could be dealt
+with by passing the PC into the exception creation function.  The trick
+is to solve situation #3 in a way that adds minimal overhead to common
+operations.  Making it more costly to throw an exception is acceptable.
+
+There are a few ways to deal with this:
+
+ (a) Change "savedPc" to "currentPc" in the stack frame.  All of the
+     stack logic gets offset by one frame.  The current PC is written
+     to the current stack frame when necessary.
+ (b) Write the current PC into the current stack frame, but without
+     replacing "savedPc".  The JNI local refs pointer, which is only
+     used for native code, can be overloaded to save space.
+ (c) In dvmThrowException(), push an extra stack frame on, with the
+     current PC in it.  The current PC is written into the Thread struct
+     when necessary, and copied out when the VM throws.
+ (d) Before doing something that might throw an exception, push a
+     temporary frame on with the saved PC in it.
+
+Solution (a) is the simplest, but breaks Dalvik's goal of mingling native
+and interpreted stacks.
+
+Solution (b) retains the simplicity of (a) without rearranging the stack,
+but now in some cases we're storing the PC twice, which feels wrong.
+
+Solution (c) usually works, because we push the saved PC onto the stack
+before the Throwable construction can overwrite the copy in Thread.  One
+way solution (c) could break is:
+ - Interpreter saves the PC
+ - Execute some bytecode, which runs successfully (and alters the saved PC)
+ - Throw an exception before re-saving the PC (i.e in the same opcode)
+This is a risk for anything that could cause <clinit> to execute, e.g.
+executing a static method or accessing a static field.  Attemping to access
+a field that doesn't exist in a class that does exist might cause this.
+It may be possible to simply bracket the dvmCallMethod*() functions to
+save/restore it.
+
+Solution (d) incurs additional overhead, but may have other benefits (e.g.
+it's easy to find the stack frames that should be removed before storage
+in the Throwable).
+
+Current plan is option (b), because it's simple, fast, and doesn't change
+the way the stack works.
+*/
+
+/* fwd */
+static bool initException(Object* exception, const char* msg, Object* cause,
+    Thread* self);
+
+void dvmThrowExceptionFmtV(ClassObject* exceptionClass,
+    const char* fmt, va_list args)
+{
+    char msgBuf[512];
+
+    vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+    dvmThrowChainedException(exceptionClass, msgBuf, NULL);
+}
+
+void dvmThrowChainedException(ClassObject* excepClass, const char* msg,
+    Object* cause)
+{
+    Thread* self = dvmThreadSelf();
+    Object* exception;
+
+    if (excepClass == NULL) {
+        /*
+         * The exception class was passed in as NULL. This might happen
+         * early on in VM initialization. There's nothing better to do
+         * than just log the message as an error and abort.
+         */
+        ALOGE("Fatal error: %s", msg);
+        dvmAbort();
+    }
+
+    /* make sure the exception is initialized */
+    if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) {
+        ALOGE("ERROR: unable to initialize exception class '%s'",
+            excepClass->descriptor);
+        if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0)
+            dvmAbort();
+        dvmThrowChainedException(gDvm.exInternalError,
+            "failed to init original exception class", cause);
+        return;
+    }
+
+    exception = dvmAllocObject(excepClass, ALLOC_DEFAULT);
+    if (exception == NULL) {
+        /*
+         * We're in a lot of trouble.  We might be in the process of
+         * throwing an out-of-memory exception, in which case the
+         * pre-allocated object will have been thrown when our object alloc
+         * failed.  So long as there's an exception raised, return and
+         * allow the system to try to recover.  If not, something is broken
+         * and we need to bail out.
+         */
+        if (dvmCheckException(self))
+            goto bail;
+        ALOGE("FATAL: unable to allocate exception '%s' '%s'",
+            excepClass->descriptor, msg != NULL ? msg : "(no msg)");
+        dvmAbort();
+    }
+
+    /*
+     * Init the exception.
+     */
+    if (gDvm.optimizing) {
+        /* need the exception object, but can't invoke interpreted code */
+        ALOGV("Skipping init of exception %s '%s'",
+            excepClass->descriptor, msg);
+    } else {
+        assert(excepClass == exception->clazz);
+        if (!initException(exception, msg, cause, self)) {
+            /*
+             * Whoops.  If we can't initialize the exception, we can't use
+             * it.  If there's an exception already set, the constructor
+             * probably threw an OutOfMemoryError.
+             */
+            if (!dvmCheckException(self)) {
+                /*
+                 * We're required to throw something, so we just
+                 * throw the pre-constructed internal error.
+                 */
+                self->exception = gDvm.internalErrorObj;
+            }
+            goto bail;
+        }
+    }
+
+    self->exception = exception;
+
+bail:
+    dvmReleaseTrackedAlloc(exception, self);
+}
+
+void dvmThrowChainedExceptionWithClassMessage(
+    ClassObject* exceptionClass, const char* messageDescriptor,
+    Object* cause)
+{
+    char* message = dvmDescriptorToName(messageDescriptor);
+
+    dvmThrowChainedException(exceptionClass, message, cause);
+    free(message);
+}
+
+/*
+ * Find and return an exception constructor method that can take the
+ * indicated parameters, or return NULL if no such constructor exists.
+ */
+static Method* findExceptionInitMethod(ClassObject* excepClass,
+    bool hasMessage, bool hasCause)
+{
+    if (hasMessage) {
+        Method* result;
+
+        if (hasCause) {
+            result = dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>",
+                    "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+        } else {
+            result = dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>", "(Ljava/lang/String;)V");
+        }
+
+        if (result != NULL) {
+            return result;
+        }
+
+        if (hasCause) {
+            return dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>",
+                    "(Ljava/lang/Object;Ljava/lang/Throwable;)V");
+        } else {
+            return dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>", "(Ljava/lang/Object;)V");
+        }
+    } else if (hasCause) {
+        return dvmFindDirectMethodByDescriptor(
+                excepClass, "<init>", "(Ljava/lang/Throwable;)V");
+    } else {
+        return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
+    }
+}
+
+/*
+ * Initialize an exception with an appropriate constructor.
+ *
+ * "exception" is the exception object to initialize.
+ * Either or both of "msg" and "cause" may be null.
+ * "self" is dvmThreadSelf(), passed in so we don't have to look it up again.
+ *
+ * If the process of initializing the exception causes another
+ * exception (e.g., OutOfMemoryError) to be thrown, return an error
+ * and leave self->exception intact.
+ */
+static bool initException(Object* exception, const char* msg, Object* cause,
+    Thread* self)
+{
+    enum {
+        kInitUnknown,
+        kInitNoarg,
+        kInitMsg,
+        kInitMsgThrow,
+        kInitThrow
+    } initKind = kInitUnknown;
+    Method* initMethod = NULL;
+    ClassObject* excepClass = exception->clazz;
+    StringObject* msgStr = NULL;
+    bool result = false;
+    bool needInitCause = false;
+
+    assert(self != NULL);
+    assert(self->exception == NULL);
+
+    /* if we have a message, create a String */
+    if (msg == NULL)
+        msgStr = NULL;
+    else {
+        msgStr = dvmCreateStringFromCstr(msg);
+        if (msgStr == NULL) {
+            ALOGW("Could not allocate message string \"%s\" while "
+                    "throwing internal exception (%s)",
+                    msg, excepClass->descriptor);
+            goto bail;
+        }
+    }
+
+    if (cause != NULL) {
+        if (!dvmInstanceof(cause->clazz, gDvm.exThrowable)) {
+            ALOGE("Tried to init exception with cause '%s'",
+                cause->clazz->descriptor);
+            dvmAbort();
+        }
+    }
+
+    /*
+     * The Throwable class has four public constructors:
+     *  (1) Throwable()
+     *  (2) Throwable(String message)
+     *  (3) Throwable(String message, Throwable cause)  (added in 1.4)
+     *  (4) Throwable(Throwable cause)                  (added in 1.4)
+     *
+     * The first two are part of the original design, and most exception
+     * classes should support them.  The third prototype was used by
+     * individual exceptions. e.g. ClassNotFoundException added it in 1.2.
+     * The general "cause" mechanism was added in 1.4.  Some classes,
+     * such as IllegalArgumentException, initially supported the first
+     * two, but added the second two in a later release.
+     *
+     * Exceptions may be picky about how their "cause" field is initialized.
+     * If you call ClassNotFoundException(String), it may choose to
+     * initialize its "cause" field to null.  Doing so prevents future
+     * calls to Throwable.initCause().
+     *
+     * So, if "cause" is not NULL, we need to look for a constructor that
+     * takes a throwable.  If we can't find one, we fall back on calling
+     * #1/#2 and making a separate call to initCause().  Passing a null ref
+     * for "message" into Throwable(String, Throwable) is allowed, but we
+     * prefer to use the Throwable-only version because it has different
+     * behavior.
+     *
+     * java.lang.TypeNotPresentException is a strange case -- it has #3 but
+     * not #2.  (Some might argue that the constructor is actually not #3,
+     * because it doesn't take the message string as an argument, but it
+     * has the same effect and we can work with it here.)
+     *
+     * java.lang.AssertionError is also a strange case -- it has a
+     * constructor that takes an Object, but not one that takes a String.
+     * There may be other cases like this, as well, so we generally look
+     * for an Object-taking constructor if we can't find one that takes
+     * a String.
+     */
+    if (cause == NULL) {
+        if (msgStr == NULL) {
+            initMethod = findExceptionInitMethod(excepClass, false, false);
+            initKind = kInitNoarg;
+        } else {
+            initMethod = findExceptionInitMethod(excepClass, true, false);
+            if (initMethod != NULL) {
+                initKind = kInitMsg;
+            } else {
+                /* no #2, try #3 */
+                initMethod = findExceptionInitMethod(excepClass, true, true);
+                if (initMethod != NULL) {
+                    initKind = kInitMsgThrow;
+                }
+            }
+        }
+    } else {
+        if (msgStr == NULL) {
+            initMethod = findExceptionInitMethod(excepClass, false, true);
+            if (initMethod != NULL) {
+                initKind = kInitThrow;
+            } else {
+                initMethod = findExceptionInitMethod(excepClass, false, false);
+                initKind = kInitNoarg;
+                needInitCause = true;
+            }
+        } else {
+            initMethod = findExceptionInitMethod(excepClass, true, true);
+            if (initMethod != NULL) {
+                initKind = kInitMsgThrow;
+            } else {
+                initMethod = findExceptionInitMethod(excepClass, true, false);
+                initKind = kInitMsg;
+                needInitCause = true;
+            }
+        }
+    }
+
+    if (initMethod == NULL) {
+        /*
+         * We can't find the desired constructor.  This can happen if a
+         * subclass of java/lang/Throwable doesn't define an expected
+         * constructor, e.g. it doesn't provide one that takes a string
+         * when a message has been provided.
+         */
+        ALOGW("WARNING: exception class '%s' missing constructor "
+            "(msg='%s' kind=%d)",
+            excepClass->descriptor, msg, initKind);
+        assert(strcmp(excepClass->descriptor,
+                      "Ljava/lang/RuntimeException;") != 0);
+        dvmThrowChainedException(gDvm.exRuntimeException,
+            "re-throw on exception class missing constructor", NULL);
+        goto bail;
+    }
+
+    /*
+     * Call the constructor with the appropriate arguments.
+     */
+    JValue unused;
+    switch (initKind) {
+    case kInitNoarg:
+        LOGVV("+++ exc noarg (ic=%d)", needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused);
+        break;
+    case kInitMsg:
+        LOGVV("+++ exc msg (ic=%d)", needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused, msgStr);
+        break;
+    case kInitThrow:
+        LOGVV("+++ exc throw");
+        assert(!needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused, cause);
+        break;
+    case kInitMsgThrow:
+        LOGVV("+++ exc msg+throw");
+        assert(!needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause);
+        break;
+    default:
+        assert(false);
+        goto bail;
+    }
+
+    /*
+     * It's possible the constructor has thrown an exception.  If so, we
+     * return an error and let our caller deal with it.
+     */
+    if (self->exception != NULL) {
+        ALOGW("Exception thrown (%s) while throwing internal exception (%s)",
+            self->exception->clazz->descriptor, exception->clazz->descriptor);
+        goto bail;
+    }
+
+    /*
+     * If this exception was caused by another exception, and we weren't
+     * able to find a cause-setting constructor, set the "cause" field
+     * with an explicit call.
+     */
+    if (needInitCause) {
+        Method* initCause;
+        initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause",
+            "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
+        if (initCause != NULL) {
+            dvmCallMethod(self, initCause, exception, &unused, cause);
+            if (self->exception != NULL) {
+                /* initCause() threw an exception; return an error and
+                 * let the caller deal with it.
+                 */
+                ALOGW("Exception thrown (%s) during initCause() "
+                        "of internal exception (%s)",
+                        self->exception->clazz->descriptor,
+                        exception->clazz->descriptor);
+                goto bail;
+            }
+        } else {
+            ALOGW("WARNING: couldn't find initCause in '%s'",
+                excepClass->descriptor);
+        }
+    }
+
+
+    result = true;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) msgStr, self);     // NULL is ok
+    return result;
+}
+
+
+/*
+ * Clear the pending exception. This is used by the optimization and
+ * verification code, which mostly happens during runs of dexopt.
+ *
+ * This can also be called when the VM is in a "normal" state, e.g. when
+ * verifying classes that couldn't be verified at optimization time.
+ */
+void dvmClearOptException(Thread* self)
+{
+    self->exception = NULL;
+}
+
+/*
+ * Returns "true" if this is a "checked" exception, i.e. it's a subclass
+ * of Throwable (assumed) but not a subclass of RuntimeException or Error.
+ */
+bool dvmIsCheckedException(const Object* exception)
+{
+    if (dvmInstanceof(exception->clazz, gDvm.exError) ||
+        dvmInstanceof(exception->clazz, gDvm.exRuntimeException))
+    {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+/*
+ * Wrap the now-pending exception in a different exception.  This is useful
+ * for reflection stuff that wants to hand a checked exception back from a
+ * method that doesn't declare it.
+ *
+ * If something fails, an (unchecked) exception related to that failure
+ * will be pending instead.
+ */
+void dvmWrapException(const char* newExcepStr)
+{
+    Thread* self = dvmThreadSelf();
+    Object* origExcep;
+    ClassObject* iteClass;
+
+    origExcep = dvmGetException(self);
+    dvmAddTrackedAlloc(origExcep, self);    // don't let the GC free it
+
+    dvmClearException(self);                // clear before class lookup
+    iteClass = dvmFindSystemClass(newExcepStr);
+    if (iteClass != NULL) {
+        Object* iteExcep;
+        Method* initMethod;
+
+        iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT);
+        if (iteExcep != NULL) {
+            initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>",
+                            "(Ljava/lang/Throwable;)V");
+            if (initMethod != NULL) {
+                JValue unused;
+                dvmCallMethod(self, initMethod, iteExcep, &unused,
+                    origExcep);
+
+                /* if <init> succeeded, replace the old exception */
+                if (!dvmCheckException(self))
+                    dvmSetException(self, iteExcep);
+            }
+            dvmReleaseTrackedAlloc(iteExcep, NULL);
+
+            /* if initMethod doesn't exist, or failed... */
+            if (!dvmCheckException(self))
+                dvmSetException(self, origExcep);
+        } else {
+            /* leave OutOfMemoryError pending */
+        }
+    } else {
+        /* leave ClassNotFoundException pending */
+    }
+
+    assert(dvmCheckException(self));
+    dvmReleaseTrackedAlloc(origExcep, self);
+}
+
+/*
+ * Get the "cause" field from an exception.
+ *
+ * The Throwable class initializes the "cause" field to "this" to
+ * differentiate between being initialized to null and never being
+ * initialized.  We check for that here and convert it to NULL.
+ */
+Object* dvmGetExceptionCause(const Object* exception)
+{
+    if (!dvmInstanceof(exception->clazz, gDvm.exThrowable)) {
+        ALOGE("Tried to get cause from object of type '%s'",
+            exception->clazz->descriptor);
+        dvmAbort();
+    }
+    Object* cause =
+        dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
+    if (cause == exception)
+        return NULL;
+    else
+        return cause;
+}
+
+/*
+ * Print the stack trace of the current exception on stderr.  This is called
+ * from the JNI ExceptionDescribe call.
+ *
+ * For consistency we just invoke the Throwable printStackTrace method,
+ * which might be overridden in the exception object.
+ *
+ * Exceptions thrown during the course of printing the stack trace are
+ * ignored.
+ */
+void dvmPrintExceptionStackTrace()
+{
+    Thread* self = dvmThreadSelf();
+    Object* exception;
+    Method* printMethod;
+
+    exception = self->exception;
+    if (exception == NULL)
+        return;
+
+    dvmAddTrackedAlloc(exception, self);
+    self->exception = NULL;
+    printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
+                    "printStackTrace", "()V");
+    if (printMethod != NULL) {
+        JValue unused;
+        dvmCallMethod(self, printMethod, exception, &unused);
+    } else {
+        ALOGW("WARNING: could not find printStackTrace in %s",
+            exception->clazz->descriptor);
+    }
+
+    if (self->exception != NULL) {
+        ALOGW("NOTE: exception thrown while printing stack trace: %s",
+            self->exception->clazz->descriptor);
+    }
+
+    self->exception = exception;
+    dvmReleaseTrackedAlloc(exception, self);
+}
+
+/*
+ * Search the method's list of exceptions for a match.
+ *
+ * Returns the offset of the catch block on success, or -1 on failure.
+ */
+static int findCatchInMethod(Thread* self, const Method* method, int relPc,
+    ClassObject* excepClass)
+{
+    /*
+     * Need to clear the exception before entry.  Otherwise, dvmResolveClass
+     * might think somebody threw an exception while it was loading a class.
+     */
+    assert(!dvmCheckException(self));
+    assert(!dvmIsNativeMethod(method));
+
+    LOGVV("findCatchInMethod %s.%s excep=%s depth=%d",
+        method->clazz->descriptor, method->name, excepClass->descriptor,
+        dvmComputeExactFrameDepth(self->interpSave.curFrame));
+
+    DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexCode* pCode = dvmGetMethodCode(method);
+    DexCatchIterator iterator;
+
+    if (dexFindCatchHandler(&iterator, pCode, relPc)) {
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            if (handler->typeIdx == kDexNoIndex) {
+                /* catch-all */
+                ALOGV("Match on catch-all block at 0x%02x in %s.%s for %s",
+                        relPc, method->clazz->descriptor,
+                        method->name, excepClass->descriptor);
+                return handler->address;
+            }
+
+            ClassObject* throwable =
+                dvmDexGetResolvedClass(pDvmDex, handler->typeIdx);
+            if (throwable == NULL) {
+                /*
+                 * TODO: this behaves badly if we run off the stack
+                 * while trying to throw an exception.  The problem is
+                 * that, if we're in a class loaded by a class loader,
+                 * the call to dvmResolveClass has to ask the class
+                 * loader for help resolving any previously-unresolved
+                 * classes.  If this particular class loader hasn't
+                 * resolved StackOverflowError, it will call into
+                 * interpreted code, and blow up.
+                 *
+                 * We currently replace the previous exception with
+                 * the StackOverflowError, which means they won't be
+                 * catching it *unless* they explicitly catch
+                 * StackOverflowError, in which case we'll be unable
+                 * to resolve the class referred to by the "catch"
+                 * block.
+                 *
+                 * We end up getting a huge pile of warnings if we do
+                 * a simple synthetic test, because this method gets
+                 * called on every stack frame up the tree, and it
+                 * fails every time.
+                 *
+                 * This eventually bails out, effectively becoming an
+                 * uncatchable exception, so other than the flurry of
+                 * warnings it's not really a problem.  Still, we could
+                 * probably handle this better.
+                 */
+                throwable = dvmResolveClass(method->clazz, handler->typeIdx,
+                    true);
+                if (throwable == NULL) {
+                    /*
+                     * We couldn't find the exception they wanted in
+                     * our class files (or, perhaps, the stack blew up
+                     * while we were querying a class loader). Cough
+                     * up a warning, then move on to the next entry.
+                     * Keep the exception status clear.
+                     */
+                    ALOGW("Could not resolve class ref'ed in exception "
+                            "catch list (class index %d, exception %s)",
+                            handler->typeIdx,
+                            (self->exception != NULL) ?
+                            self->exception->clazz->descriptor : "(none)");
+                    dvmClearException(self);
+                    continue;
+                }
+            }
+
+            //ALOGD("ADDR MATCH, check %s instanceof %s",
+            //    excepClass->descriptor, pEntry->excepClass->descriptor);
+
+            if (dvmInstanceof(excepClass, throwable)) {
+                ALOGV("Match on catch block at 0x%02x in %s.%s for %s",
+                        relPc, method->clazz->descriptor,
+                        method->name, excepClass->descriptor);
+                return handler->address;
+            }
+        }
+    }
+
+    ALOGV("No matching catch block at 0x%02x in %s for %s",
+        relPc, method->name, excepClass->descriptor);
+    return -1;
+}
+
+/*
+ * Find a matching "catch" block.  "pc" is the relative PC within the
+ * current method, indicating the offset from the start in 16-bit units.
+ *
+ * Returns the offset to the catch block, or -1 if we run up against a
+ * break frame without finding anything.
+ *
+ * The class resolution stuff we have to do while evaluating the "catch"
+ * blocks could cause an exception.  The caller should clear the exception
+ * before calling here and restore it after.
+ *
+ * Sets *newFrame to the frame pointer of the frame with the catch block.
+ * If "scanOnly" is false, self->interpSave.curFrame is also set to this value.
+ */
+int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+    bool scanOnly, void** newFrame)
+{
+    u4* fp = self->interpSave.curFrame;
+    int catchAddr = -1;
+
+    assert(!dvmCheckException(self));
+
+    while (true) {
+        StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        catchAddr = findCatchInMethod(self, saveArea->method, relPc,
+                        exception->clazz);
+        if (catchAddr >= 0)
+            break;
+
+        /*
+         * Normally we'd check for ACC_SYNCHRONIZED methods and unlock
+         * them as we unroll.  Dalvik uses what amount to generated
+         * "finally" blocks to take care of this for us.
+         */
+
+        /* output method profiling info */
+        if (!scanOnly) {
+            TRACE_METHOD_UNROLL(self, saveArea->method);
+        }
+
+        /*
+         * Move up one frame.  If the next thing up is a break frame,
+         * break out now so we're left unrolled to the last method frame.
+         * We need to point there so we can roll up the JNI local refs
+         * if this was a native method.
+         */
+        assert(saveArea->prevFrame != NULL);
+        if (dvmIsBreakFrame((u4*)saveArea->prevFrame)) {
+            if (!scanOnly)
+                break;      // bail with catchAddr == -1
+
+            /*
+             * We're scanning for the debugger.  It needs to know if this
+             * exception is going to be caught or not, and we need to figure
+             * out if it will be caught *ever* not just between the current
+             * position and the next break frame.  We can't tell what native
+             * code is going to do, so we assume it never catches exceptions.
+             *
+             * Start by finding an interpreted code frame.
+             */
+            fp = saveArea->prevFrame;           // this is the break frame
+            saveArea = SAVEAREA_FROM_FP(fp);
+            fp = saveArea->prevFrame;           // this may be a good one
+            while (fp != NULL) {
+                if (!dvmIsBreakFrame((u4*)fp)) {
+                    saveArea = SAVEAREA_FROM_FP(fp);
+                    if (!dvmIsNativeMethod(saveArea->method))
+                        break;
+                }
+
+                fp = SAVEAREA_FROM_FP(fp)->prevFrame;
+            }
+            if (fp == NULL)
+                break;      // bail with catchAddr == -1
+
+            /*
+             * Now fp points to the "good" frame.  When the interp code
+             * invoked the native code, it saved a copy of its current PC
+             * into xtra.currentPc.  Pull it out of there.
+             */
+            relPc =
+                saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns;
+        } else {
+            fp = saveArea->prevFrame;
+
+            /* savedPc in was-current frame goes with method in now-current */
+            relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns;
+        }
+    }
+
+    if (!scanOnly)
+        self->interpSave.curFrame = fp;
+
+    /*
+     * The class resolution in findCatchInMethod() could cause an exception.
+     * Clear it to be safe.
+     */
+    self->exception = NULL;
+
+    *newFrame = fp;
+    return catchAddr;
+}
+
+/*
+ * We have to carry the exception's stack trace around, but in many cases
+ * it will never be examined.  It makes sense to keep it in a compact,
+ * VM-specific object, rather than an array of Objects with strings.
+ *
+ * Pass in the thread whose stack we're interested in.  If "thread" is
+ * not self, the thread must be suspended.  This implies that the thread
+ * list lock is held, which means we can't allocate objects or we risk
+ * jamming the GC.  So, we allow this function to return different formats.
+ * (This shouldn't be called directly -- see the inline functions in the
+ * header file.)
+ *
+ * If "wantObject" is true, this returns a newly-allocated Object, which is
+ * presently an array of integers, but could become something else in the
+ * future.  If "wantObject" is false, return plain malloc data.
+ *
+ * NOTE: if we support class unloading, we will need to scan the class
+ * object references out of these arrays.
+ */
+void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, size_t* pCount)
+{
+    ArrayObject* stackData = NULL;
+    int* simpleData = NULL;
+    void* fp;
+    void* startFp;
+    size_t stackDepth;
+    int* intPtr;
+
+    if (pCount != NULL)
+        *pCount = 0;
+    fp = thread->interpSave.curFrame;
+
+    assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
+
+    /*
+     * We're looking at a stack frame for code running below a Throwable
+     * constructor.  We want to remove the Throwable methods and the
+     * superclass initializations so the user doesn't see them when they
+     * read the stack dump.
+     *
+     * TODO: this just scrapes off the top layers of Throwable.  Might not do
+     * the right thing if we create an exception object or cause a VM
+     * exception while in a Throwable method.
+     */
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (dvmIsBreakFrame((u4*)fp))
+            break;
+        if (!dvmInstanceof(method->clazz, gDvm.exThrowable))
+            break;
+        //ALOGD("EXCEP: ignoring %s.%s",
+        //         method->clazz->descriptor, method->name);
+        fp = saveArea->prevFrame;
+    }
+    startFp = fp;
+
+    /*
+     * Compute the stack depth.
+     */
+    stackDepth = 0;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+
+        if (!dvmIsBreakFrame((u4*)fp))
+            stackDepth++;
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    //ALOGD("EXCEP: stack depth is %d", stackDepth);
+
+    if (!stackDepth)
+        goto bail;
+
+    /*
+     * We need to store a pointer to the Method and the program counter.
+     * We have 4-byte pointers, so we use '[I'.
+     */
+    if (wantObject) {
+        assert(sizeof(Method*) == 4);
+        stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT);
+        if (stackData == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            goto bail;
+        }
+        intPtr = (int*)(void*)stackData->contents;
+    } else {
+        /* array of ints; first entry is stack depth */
+        assert(sizeof(Method*) == sizeof(int));
+        simpleData = (int*) malloc(sizeof(int) * stackDepth*2);
+        if (simpleData == NULL)
+            goto bail;
+
+        assert(pCount != NULL);
+        intPtr = simpleData;
+    }
+    if (pCount != NULL)
+        *pCount = stackDepth;
+
+    fp = startFp;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*)fp)) {
+            //ALOGD("EXCEP keeping %s.%s", method->clazz->descriptor,
+            //         method->name);
+
+            *intPtr++ = (int) method;
+            if (dvmIsNativeMethod(method)) {
+                *intPtr++ = 0;      /* no saved PC for native methods */
+            } else {
+                assert(saveArea->xtra.currentPc >= method->insns &&
+                        saveArea->xtra.currentPc <
+                        method->insns + dvmGetMethodInsnsSize(method));
+                *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns);
+            }
+
+            stackDepth--;       // for verification
+        }
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    assert(stackDepth == 0);
+
+bail:
+    if (wantObject) {
+        dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf());
+        return stackData;
+    } else {
+        return simpleData;
+    }
+}
+
+
+/*
+ * Given an Object previously created by dvmFillInStackTrace(), use the
+ * contents of the saved stack trace to generate an array of
+ * java/lang/StackTraceElement objects.
+ *
+ * The returned array is not added to the "local refs" list.
+ */
+ArrayObject* dvmGetStackTrace(const Object* ostackData)
+{
+    const ArrayObject* stackData = (const ArrayObject*) ostackData;
+    size_t stackSize = stackData->length / 2;
+    const int* intVals = (const int*)(void*)stackData->contents;
+    return dvmGetStackTraceRaw(intVals, stackSize);
+}
+
+/*
+ * Generate an array of StackTraceElement objects from the raw integer
+ * data encoded by dvmFillInStackTrace().
+ *
+ * "intVals" points to the first {method,pc} pair.
+ *
+ * The returned array is not added to the "local refs" list.
+ */
+ArrayObject* dvmGetStackTraceRaw(const int* intVals, size_t stackDepth)
+{
+    /* allocate a StackTraceElement array */
+    ClassObject* klass = gDvm.classJavaLangStackTraceElementArray;
+    ArrayObject* array = dvmAllocArrayByClass(klass, stackDepth, ALLOC_DEFAULT);
+    if (array != NULL){
+      dvmFillStackTraceElements(intVals, stackDepth, array);
+      dvmReleaseTrackedAlloc((Object*) array, NULL);
+    }
+    return array;
+}
+
+/*
+ * Fills the StackTraceElement array elements from the raw integer
+ * data encoded by dvmFillInStackTrace().
+ *
+ * "intVals" points to the first {method,pc} pair.
+ */
+void dvmFillStackTraceElements(const int* intVals, size_t stackDepth, ArrayObject* steArray)
+{
+    unsigned int i;
+
+    /* init this if we haven't yet */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))
+        dvmInitClass(gDvm.classJavaLangStackTraceElement);
+
+    /*
+     * Allocate and initialize a StackTraceElement for each stack frame.
+     * We use the standard constructor to configure the object.
+     */
+    for (i = 0; i < stackDepth; i++) {
+        Object* ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);
+        if (ste == NULL) {
+            return;
+        }
+
+        Method* meth = (Method*) *intVals++;
+        int pc = *intVals++;
+
+        int lineNumber;
+        if (pc == -1)      // broken top frame?
+            lineNumber = 0;
+        else
+            lineNumber = dvmLineNumFromPC(meth, pc);
+
+        std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor));
+        StringObject* className = dvmCreateStringFromCstr(dotName);
+
+        StringObject* methodName = dvmCreateStringFromCstr(meth->name);
+
+        const char* sourceFile = dvmGetMethodSourceFile(meth);
+        StringObject* fileName = (sourceFile != NULL) ? dvmCreateStringFromCstr(sourceFile) : NULL;
+
+        /*
+         * Invoke:
+         *  public StackTraceElement(String declaringClass, String methodName,
+         *      String fileName, int lineNumber)
+         * (where lineNumber==-2 means "native")
+         */
+        JValue unused;
+        dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init,
+            ste, &unused, className, methodName, fileName, lineNumber);
+
+        dvmReleaseTrackedAlloc(ste, NULL);
+        dvmReleaseTrackedAlloc((Object*) className, NULL);
+        dvmReleaseTrackedAlloc((Object*) methodName, NULL);
+        dvmReleaseTrackedAlloc((Object*) fileName, NULL);
+
+        if (dvmCheckException(dvmThreadSelf())) {
+            return;
+        }
+
+        dvmSetObjectArrayElement(steArray, i, ste);
+    }
+}
+
+/*
+ * Dump the contents of a raw stack trace to the log.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth) {
+    /*
+     * Run through the array of stack frame data.
+     */
+    for (int i = 0; i < stackDepth; i++) {
+        Method* meth = (Method*) *intVals++;
+        int pc = *intVals++;
+
+        std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor));
+        if (dvmIsNativeMethod(meth)) {
+            ALOGI("\tat %s.%s(Native Method)", dotName.c_str(), meth->name);
+        } else {
+            ALOGI("\tat %s.%s(%s:%d)",
+                dotName.c_str(), meth->name, dvmGetMethodSourceFile(meth),
+                dvmLineNumFromPC(meth, pc));
+        }
+    }
+}
+
+/*
+ * Get the message string.  We'd like to just grab the field out of
+ * Throwable, but the getMessage() function can be overridden by the
+ * sub-class.
+ *
+ * Returns the message string object, or NULL if it wasn't set or
+ * we encountered a failure trying to retrieve it.  The string will
+ * be added to the tracked references table.
+ */
+static StringObject* getExceptionMessage(Object* exception)
+{
+    Thread* self = dvmThreadSelf();
+    Method* getMessageMethod;
+    StringObject* messageStr = NULL;
+    Object* pendingException;
+
+    /*
+     * If an exception is pending, clear it while we work and restore
+     * it when we're done.
+     */
+    pendingException = dvmGetException(self);
+    if (pendingException != NULL) {
+        dvmAddTrackedAlloc(pendingException, self);
+        dvmClearException(self);
+    }
+
+    getMessageMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
+            "getMessage", "()Ljava/lang/String;");
+    if (getMessageMethod != NULL) {
+        /* could be in NATIVE mode from CheckJNI, so switch state */
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
+        JValue result;
+
+        dvmCallMethod(self, getMessageMethod, exception, &result);
+        messageStr = (StringObject*) result.l;
+        if (messageStr != NULL)
+            dvmAddTrackedAlloc((Object*) messageStr, self);
+
+        dvmChangeStatus(self, oldStatus);
+    } else {
+        ALOGW("WARNING: could not find getMessage in %s",
+            exception->clazz->descriptor);
+    }
+
+    if (dvmGetException(self) != NULL) {
+        ALOGW("NOTE: exception thrown while retrieving exception message: %s",
+            dvmGetException(self)->clazz->descriptor);
+        /* will be overwritten below */
+    }
+
+    dvmSetException(self, pendingException);
+    if (pendingException != NULL) {
+        dvmReleaseTrackedAlloc(pendingException, self);
+    }
+    return messageStr;
+}
+
+/*
+ * Print the direct stack trace of the given exception to the log.
+ */
+static void logStackTraceOf(Object* exception) {
+    std::string className(dvmHumanReadableDescriptor(exception->clazz->descriptor));
+    StringObject* messageStr = getExceptionMessage(exception);
+    if (messageStr != NULL) {
+        char* cp = dvmCreateCstrFromString(messageStr);
+        dvmReleaseTrackedAlloc((Object*) messageStr, dvmThreadSelf());
+        messageStr = NULL;
+
+        ALOGI("%s: %s", className.c_str(), cp);
+        free(cp);
+    } else {
+        ALOGI("%s:", className.c_str());
+    }
+
+    /*
+     * This relies on the stackState field, which contains the "raw"
+     * form of the stack.  The Throwable class may clear this field
+     * after it generates the "cooked" form, in which case we'll have
+     * nothing to show.
+     */
+    const ArrayObject* stackData = (const ArrayObject*) dvmGetFieldObject(exception,
+                    gDvm.offJavaLangThrowable_stackState);
+    if (stackData == NULL) {
+        ALOGI("  (raw stack trace not found)");
+        return;
+    }
+
+    int stackSize = stackData->length / 2;
+    const int* intVals = (const int*)(void*)stackData->contents;
+
+    dvmLogRawStackTrace(intVals, stackSize);
+}
+
+/*
+ * Print the stack trace of the current thread's exception, as well as
+ * the stack traces of any chained exceptions, to the log. We extract
+ * the stored stack trace and process it internally instead of calling
+ * interpreted code.
+ */
+void dvmLogExceptionStackTrace()
+{
+    Object* exception = dvmThreadSelf()->exception;
+    Object* cause;
+
+    if (exception == NULL) {
+        ALOGW("tried to log a null exception?");
+        return;
+    }
+
+    for (;;) {
+        logStackTraceOf(exception);
+        cause = dvmGetExceptionCause(exception);
+        if (cause == NULL) {
+            break;
+        }
+        ALOGI("Caused by:");
+        exception = cause;
+    }
+}
+
+/*
+ * Helper for a few of the throw functions defined below. This throws
+ * the indicated exception, with a message based on a format in which
+ * "%s" is used exactly twice, first for a received class and second
+ * for the expected class.
+ */
+static void throwTypeError(ClassObject* exceptionClass, const char* fmt,
+    ClassObject* actual, ClassObject* desired)
+{
+    std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor));
+    std::string desiredClassName(dvmHumanReadableDescriptor(desired->descriptor));
+    dvmThrowExceptionFmt(exceptionClass, fmt, actualClassName.c_str(), desiredClassName.c_str());
+}
+
+void dvmThrowAbstractMethodError(const char* msg) {
+    dvmThrowException(gDvm.exAbstractMethodError, msg);
+}
+
+void dvmThrowArithmeticException(const char* msg) {
+    dvmThrowException(gDvm.exArithmeticException, msg);
+}
+
+void dvmThrowArrayIndexOutOfBoundsException(int length, int index)
+{
+    dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
+        "length=%d; index=%d", length, index);
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleElement(ClassObject* objectType,
+        ClassObject* arrayType)
+{
+    throwTypeError(gDvm.exArrayStoreException,
+        "%s cannot be stored in an array of type %s",
+        objectType, arrayType);
+}
+
+void dvmThrowArrayStoreExceptionNotArray(ClassObject* actual, const char* label) {
+    std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor));
+    dvmThrowExceptionFmt(gDvm.exArrayStoreException, "%s of type %s is not an array",
+            label, actualClassName.c_str());
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleArrays(ClassObject* source, ClassObject* destination)
+{
+    throwTypeError(gDvm.exArrayStoreException,
+        "%s and %s are incompatible array types",
+        source, destination);
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleArrayElement(s4 index, ClassObject* objectType,
+        ClassObject* arrayType)
+{
+    std::string objectClassName(dvmHumanReadableDescriptor(objectType->descriptor));
+    std::string arrayClassName(dvmHumanReadableDescriptor(arrayType->descriptor));
+    dvmThrowExceptionFmt(gDvm.exArrayStoreException,
+            "source[%d] of type %s cannot be stored in destination array of type %s",
+            index, objectClassName.c_str(), arrayClassName.c_str());
+}
+
+void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired)
+{
+    throwTypeError(gDvm.exClassCastException,
+        "%s cannot be cast to %s", actual, desired);
+}
+
+void dvmThrowClassCircularityError(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exClassCircularityError,
+            descriptor);
+}
+
+void dvmThrowClassFormatError(const char* msg) {
+    dvmThrowException(gDvm.exClassFormatError, msg);
+}
+
+void dvmThrowClassNotFoundException(const char* name) {
+    dvmThrowChainedClassNotFoundException(name, NULL);
+}
+
+void dvmThrowChainedClassNotFoundException(const char* name, Object* cause) {
+    /*
+     * Note: This exception is thrown in response to a request coming
+     * from client code for the name as given, so it is preferable to
+     * make the exception message be that string, per se, instead of
+     * trying to prettify it.
+     */
+    dvmThrowChainedException(gDvm.exClassNotFoundException, name, cause);
+}
+
+void dvmThrowExceptionInInitializerError()
+{
+    /*
+     * TODO: Should this just use dvmWrapException()?
+     */
+
+    if (gDvm.exExceptionInInitializerError == NULL || gDvm.exError == NULL) {
+        /*
+         * ExceptionInInitializerError isn't itself initialized. This
+         * can happen very early during VM startup if there is a
+         * problem with one of the corest-of-the-core classes, and it
+         * can possibly happen during a dexopt run. Rather than do
+         * anything fancier, we just abort here with a blatant
+         * message.
+         */
+        ALOGE("Fatal error during early class initialization:");
+        dvmLogExceptionStackTrace();
+        dvmAbort();
+    }
+
+    Thread* self = dvmThreadSelf();
+    Object* exception = dvmGetException(self);
+
+    // We only wrap non-Error exceptions; an Error can just be used as-is.
+    if (dvmInstanceof(exception->clazz, gDvm.exError)) {
+        return;
+    }
+
+    dvmAddTrackedAlloc(exception, self);
+    dvmClearException(self);
+
+    dvmThrowChainedException(gDvm.exExceptionInInitializerError,
+            NULL, exception);
+    dvmReleaseTrackedAlloc(exception, self);
+}
+
+void dvmThrowFileNotFoundException(const char* msg) {
+    dvmThrowException(gDvm.exFileNotFoundException, msg);
+}
+
+void dvmThrowIOException(const char* msg) {
+    dvmThrowException(gDvm.exIOException, msg);
+}
+
+void dvmThrowIllegalAccessException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalAccessException, msg);
+}
+
+void dvmThrowIllegalAccessError(const char* msg) {
+    dvmThrowException(gDvm.exIllegalAccessError, msg);
+}
+
+void dvmThrowIllegalArgumentException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalArgumentException, msg);
+}
+
+void dvmThrowIllegalMonitorStateException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalMonitorStateException, msg);
+}
+
+void dvmThrowIllegalStateException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalStateException, msg);
+}
+
+void dvmThrowIllegalThreadStateException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalThreadStateException, msg);
+}
+
+void dvmThrowIncompatibleClassChangeError(const char* msg) {
+    dvmThrowException(gDvm.exIncompatibleClassChangeError, msg);
+}
+
+void dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+        const char* descriptor)
+{
+    dvmThrowExceptionWithClassMessage(
+            gDvm.exIncompatibleClassChangeError, descriptor);
+}
+
+void dvmThrowInstantiationException(ClassObject* clazz, const char* extraDetail) {
+    std::string className(dvmHumanReadableDescriptor(clazz->descriptor));
+    dvmThrowExceptionFmt(gDvm.exInstantiationException,
+            "can't instantiate class %s%s%s", className.c_str(),
+            (extraDetail == NULL) ? "" : "; ",
+            (extraDetail == NULL) ? "" : extraDetail);
+}
+
+void dvmThrowInternalError(const char* msg) {
+    dvmThrowException(gDvm.exInternalError, msg);
+}
+
+void dvmThrowInterruptedException(const char* msg) {
+    dvmThrowException(gDvm.exInterruptedException, msg);
+}
+
+void dvmThrowLinkageError(const char* msg) {
+    dvmThrowException(gDvm.exLinkageError, msg);
+}
+
+void dvmThrowNegativeArraySizeException(s4 size) {
+    dvmThrowExceptionFmt(gDvm.exNegativeArraySizeException, "%d", size);
+}
+
+void dvmThrowNoClassDefFoundError(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exNoClassDefFoundError,
+            descriptor);
+}
+
+void dvmThrowChainedNoClassDefFoundError(const char* descriptor,
+        Object* cause) {
+    dvmThrowChainedExceptionWithClassMessage(
+            gDvm.exNoClassDefFoundError, descriptor, cause);
+}
+
+void dvmThrowNoSuchFieldError(const char* msg) {
+    dvmThrowException(gDvm.exNoSuchFieldError, msg);
+}
+
+void dvmThrowNoSuchFieldException(const char* msg) {
+    dvmThrowException(gDvm.exNoSuchFieldException, msg);
+}
+
+void dvmThrowNoSuchMethodError(const char* msg) {
+    dvmThrowException(gDvm.exNoSuchMethodError, msg);
+}
+
+void dvmThrowNullPointerException(const char* msg) {
+    dvmThrowException(gDvm.exNullPointerException, msg);
+}
+
+void dvmThrowOutOfMemoryError(const char* msg) {
+    dvmThrowException(gDvm.exOutOfMemoryError, msg);
+}
+
+void dvmThrowRuntimeException(const char* msg) {
+    dvmThrowException(gDvm.exRuntimeException, msg);
+}
+
+void dvmThrowStaleDexCacheError(const char* msg) {
+    dvmThrowException(gDvm.exStaleDexCacheError, msg);
+}
+
+void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength,
+        jsize requestIndex) {
+    dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException,
+            "length=%d; index=%d", stringLength, requestIndex);
+}
+
+void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength,
+        jsize requestStart, jsize requestLength) {
+    dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException,
+            "length=%d; regionStart=%d; regionLength=%d",
+            stringLength, requestStart, requestLength);
+}
+
+void dvmThrowTypeNotPresentException(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exTypeNotPresentException,
+            descriptor);
+}
+
+void dvmThrowUnsatisfiedLinkError(const char* msg) {
+    dvmThrowException(gDvm.exUnsatisfiedLinkError, msg);
+}
+
+void dvmThrowUnsatisfiedLinkError(const char* msg, const Method* method) {
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+    char* className = dvmDescriptorToDot(method->clazz->descriptor);
+    dvmThrowExceptionFmt(gDvm.exUnsatisfiedLinkError, "%s: %s.%s:%s",
+        msg, className, method->name, desc);
+    free(className);
+    free(desc);
+}
+
+void dvmThrowUnsupportedOperationException(const char* msg) {
+    dvmThrowException(gDvm.exUnsupportedOperationException, msg);
+}
+
+void dvmThrowVerifyError(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exVerifyError, descriptor);
+}
+
+void dvmThrowVirtualMachineError(const char* msg) {
+    dvmThrowException(gDvm.exVirtualMachineError, msg);
+}
diff --git a/vm/Exception.h b/vm/Exception.h
new file mode 100644
index 0000000..055ed2b
--- /dev/null
+++ b/vm/Exception.h
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Exception handling.
+ */
+#ifndef DALVIK_EXCEPTION_H_
+#define DALVIK_EXCEPTION_H_
+
+/*
+ * Create a Throwable and throw an exception in the current thread (where
+ * "throwing" just means "set the thread's exception pointer").
+ *
+ * "msg" and/or "cause" may be NULL.
+ *
+ * If we have a bad exception hierarchy -- something in Throwable.<init>
+ * is missing -- then every attempt to throw an exception will result
+ * in another exception.  Exceptions are generally allowed to "chain"
+ * to other exceptions, so it's hard to auto-detect this problem.  It can
+ * only happen if the system classes are broken, so it's probably not
+ * worth spending cycles to detect it.
+ *
+ * We do have one case to worry about: if the classpath is completely
+ * wrong, we'll go into a death spin during startup because we can't find
+ * the initial class and then we can't find NoClassDefFoundError.  We have
+ * to handle this case.
+ */
+void dvmThrowChainedException(ClassObject* exceptionClass,
+    const char* msg, Object* cause);
+INLINE void dvmThrowException(ClassObject* exceptionClass,
+    const char* msg)
+{
+    dvmThrowChainedException(exceptionClass, msg, NULL);
+}
+
+/*
+ * Like dvmThrowException, but takes printf-style args for the message.
+ */
+void dvmThrowExceptionFmtV(ClassObject* exceptionClass,
+    const char* fmt, va_list args);
+void dvmThrowExceptionFmt(ClassObject* exceptionClass,
+    const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+INLINE void dvmThrowExceptionFmt(ClassObject* exceptionClass,
+    const char* fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    dvmThrowExceptionFmtV(exceptionClass, fmt, args);
+    va_end(args);
+}
+
+/*
+ * Like dvmThrowChainedException, but take a class object
+ * instead of a name and turn the given message into the
+ * human-readable form for a descriptor.
+ */
+void dvmThrowChainedExceptionWithClassMessage(
+    ClassObject* exceptionClass, const char* messageDescriptor,
+    Object* cause);
+
+/*
+ * Like dvmThrowException, but take a class object instead of a name
+ * and turn the given message into the human-readable form for a descriptor.
+ */
+INLINE void dvmThrowExceptionWithClassMessage(
+    ClassObject* exceptionClass, const char* messageDescriptor)
+{
+    dvmThrowChainedExceptionWithClassMessage(exceptionClass,
+            messageDescriptor, NULL);
+}
+
+/*
+ * Return the exception being thrown in the current thread, or NULL if
+ * no exception is pending.
+ */
+INLINE Object* dvmGetException(Thread* self) {
+    return self->exception;
+}
+
+/*
+ * Set the exception being thrown in the current thread.
+ */
+INLINE void dvmSetException(Thread* self, Object* exception)
+{
+    assert(exception != NULL);
+    self->exception = exception;
+}
+
+/*
+ * Clear the pending exception.
+ *
+ * (We use this rather than "set(null)" because we may need to have special
+ * fixups here for StackOverflowError stuff.  Calling "clear" in the code
+ * makes it obvious.)
+ */
+INLINE void dvmClearException(Thread* self) {
+    self->exception = NULL;
+}
+
+/*
+ * Clear the pending exception.  Used by the optimization and verification
+ * code, which has to run with "initializing" set to avoid going into a
+ * death-spin if the "class not found" exception can't be found.
+ */
+void dvmClearOptException(Thread* self);
+
+/*
+ * Returns "true" if an exception is pending.  Use this if you have a
+ * "self" pointer.
+ */
+INLINE bool dvmCheckException(Thread* self) {
+    return (self->exception != NULL);
+}
+
+/*
+ * Returns "true" if this is a "checked" exception, i.e. it's a subclass
+ * of Throwable (assumed) but not a subclass of RuntimeException or Error.
+ */
+bool dvmIsCheckedException(const Object* exception);
+
+/*
+ * Wrap the now-pending exception in a different exception.
+ *
+ * If something fails, an (unchecked) exception related to that failure
+ * will be pending instead.
+ */
+void dvmWrapException(const char* newExcepStr);
+
+/*
+ * Get the "cause" field from an exception.
+ *
+ * Returns NULL if the field is null or uninitialized.
+ */
+Object* dvmGetExceptionCause(const Object* exception);
+
+/*
+ * Print the exception stack trace on stderr.  Calls the exception's
+ * print function.
+ */
+void dvmPrintExceptionStackTrace(void);
+
+/*
+ * Print the exception stack trace to the log file.  The exception stack
+ * trace is computed within the VM.
+ */
+void dvmLogExceptionStackTrace(void);
+
+/*
+ * Search for a catch block that matches "exception".
+ *
+ * "*newFrame" gets a copy of the new frame pointer.
+ *
+ * If "doUnroll" is set, we unroll "thread"s stack as we go (and update
+ * self->interpSave.curFrame with the same value as in *newFrame).
+ *
+ * Returns the offset to the catch code on success, or -1 if we couldn't
+ * find a catcher.
+ */
+extern "C" int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+    bool doUnroll, void** newFrame);
+
+/*
+ * Support for saving exception stack traces and converting them to
+ * usable form.  Use the "FillIn" function to generate a compact array
+ * that represents the stack frames, then "GetStackTrace" to convert it
+ * to an array of StackTraceElement objects.
+ *
+ * Don't call the "Internal" form of the function directly.
+ */
+void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, size_t* pCount);
+/* return an [I for use by interpreted code */
+INLINE Object* dvmFillInStackTrace(Thread* thread) {
+    return (Object*) dvmFillInStackTraceInternal(thread, true, NULL);
+}
+ArrayObject* dvmGetStackTrace(const Object* stackState);
+/* return an int* and array count; caller must free() the return value */
+INLINE int* dvmFillInStackTraceRaw(Thread* thread, size_t* pCount) {
+    return (int*) dvmFillInStackTraceInternal(thread, false, pCount);
+}
+ArrayObject* dvmGetStackTraceRaw(const int* intVals, size_t stackDepth);
+void dvmFillStackTraceElements(const int* intVals, size_t stackDepth, ArrayObject* steArray);
+
+/*
+ * Print a formatted version of a raw stack trace to the log file.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth);
+
+/**
+ * Throw an AbstractMethodError in the current thread, with the given detail
+ * message.
+ */
+void dvmThrowAbstractMethodError(const char* msg);
+
+/**
+ * Throw an ArithmeticException in the current thread, with the given detail
+ * message.
+ */
+extern "C" void dvmThrowArithmeticException(const char* msg);
+
+/*
+ * Throw an ArrayIndexOutOfBoundsException in the current thread,
+ * using the given array length and index in the detail message.
+ */
+extern "C" void dvmThrowArrayIndexOutOfBoundsException(int length, int index);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given classes'
+ * names in the detail message, indicating that an object of the given type
+ * can't be stored into an array of the given type.
+ */
+extern "C" void dvmThrowArrayStoreExceptionIncompatibleElement(ClassObject* objectType, ClassObject* arrayType);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given
+ * class name and argument label in the detail message, indicating
+ * that it is not an array.
+ */
+void dvmThrowArrayStoreExceptionNotArray(ClassObject* actual, const char* label);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given
+ * classes' names in the detail message, indicating that the arrays
+ * aren't compatible (for copying contents).
+ */
+void dvmThrowArrayStoreExceptionIncompatibleArrays(ClassObject* source, ClassObject* destination);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given
+ * index and classes' names in the detail message, indicating that the
+ * object at the given index and of the given type cannot be stored
+ * into an array of the given type.
+ */
+void dvmThrowArrayStoreExceptionIncompatibleArrayElement(s4 index, ClassObject* objectType,
+        ClassObject* arrayType);
+
+/**
+ * Throw a ClassCastException in the current thread, using the given classes'
+ * names in the detail message.
+ */
+extern "C" void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired);
+
+/**
+ * Throw a ClassCircularityError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowClassCircularityError(const char* descriptor);
+
+/**
+ * Throw a ClassFormatError in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowClassFormatError(const char* msg);
+
+/**
+ * Throw a ClassNotFoundException in the current thread, with the given
+ * class name as the detail message.
+ */
+void dvmThrowClassNotFoundException(const char* name);
+
+/**
+ * Throw a ClassNotFoundException in the current thread, with the given
+ * cause, and the given class name as the detail message.
+ */
+void dvmThrowChainedClassNotFoundException(const char* name, Object* cause);
+
+/*
+ * Throw the VM-spec-mandated error when an exception is thrown during
+ * class initialization. Unlike other helper functions, this automatically
+ * wraps the current thread's pending exception.
+ */
+void dvmThrowExceptionInInitializerError(void);
+
+/**
+ * Throw a FileNotFoundException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowFileNotFoundException(const char* msg);
+
+/**
+ * Throw an IOException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowIOException(const char* msg);
+
+/**
+ * Throw an IllegalAccessError in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowIllegalAccessError(const char* msg);
+
+/**
+ * Throw an IllegalAccessException in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowIllegalAccessException(const char* msg);
+
+/**
+ * Throw an IllegalArgumentException in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowIllegalArgumentException(const char* msg);
+
+/**
+ * Throw an IllegalMonitorStateException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowIllegalMonitorStateException(const char* msg);
+
+/**
+ * Throw an IllegalStateException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowIllegalStateException(const char* msg);
+
+/**
+ * Throw an IllegalThreadStateException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowIllegalThreadStateException(const char* msg);
+
+/**
+ * Throw an IncompatibleClassChangeError in the current thread,
+ * the given detail message.
+ */
+void dvmThrowIncompatibleClassChangeError(const char* msg);
+
+/**
+ * Throw an IncompatibleClassChangeError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+        const char* descriptor);
+
+/**
+ * Throw an InstantiationException in the current thread, with
+ * the human-readable form of the given class as the detail message,
+ * with optional extra detail appended to the message.
+ */
+void dvmThrowInstantiationException(ClassObject* clazz,
+        const char* extraDetail);
+
+/**
+ * Throw an InternalError in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowInternalError(const char* msg);
+
+/**
+ * Throw an InterruptedException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowInterruptedException(const char* msg);
+
+/**
+ * Throw a LinkageError in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowLinkageError(const char* msg);
+
+/**
+ * Throw a NegativeArraySizeException in the current thread, with the
+ * given number as the detail message.
+ */
+extern "C" void dvmThrowNegativeArraySizeException(s4 size);
+
+/**
+ * Throw a NoClassDefFoundError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+extern "C" void dvmThrowNoClassDefFoundError(const char* descriptor);
+
+/**
+ * Throw a NoClassDefFoundError in the current thread, with the given
+ * cause, and the human-readable form of the given descriptor as the
+ * detail message.
+ */
+void dvmThrowChainedNoClassDefFoundError(const char* descriptor,
+        Object* cause);
+
+/**
+ * Throw a NoSuchFieldError in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowNoSuchFieldError(const char* msg);
+
+/**
+ * Throw a NoSuchFieldException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowNoSuchFieldException(const char* msg);
+
+/**
+ * Throw a NoSuchMethodError in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowNoSuchMethodError(const char* msg);
+
+/**
+ * Throw a NullPointerException in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowNullPointerException(const char* msg);
+
+/**
+ * Throw an OutOfMemoryError in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowOutOfMemoryError(const char* msg);
+
+/**
+ * Throw a RuntimeException in the current thread, with the given detail
+ * message.
+ */
+void dvmThrowRuntimeException(const char* msg);
+
+/**
+ * Throw a StaleDexCacheError in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowStaleDexCacheError(const char* msg);
+
+/**
+ * Throw a StringIndexOutOfBoundsException in the current thread, with
+ * a detail message specifying an actual length as well as a requested
+ * index.
+ */
+void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength,
+        jsize requestIndex);
+
+/**
+ * Throw a StringIndexOutOfBoundsException in the current thread, with
+ * a detail message specifying an actual length as well as a requested
+ * region.
+ */
+void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength,
+        jsize requestStart, jsize requestLength);
+
+/**
+ * Throw a TypeNotPresentException in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowTypeNotPresentException(const char* descriptor);
+
+/**
+ * Throw an UnsatisfiedLinkError in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowUnsatisfiedLinkError(const char* msg);
+void dvmThrowUnsatisfiedLinkError(const char* msg, const Method* method);
+
+/**
+ * Throw an UnsupportedOperationException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowUnsupportedOperationException(const char* msg);
+
+/**
+ * Throw a VerifyError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowVerifyError(const char* descriptor);
+
+/**
+ * Throw a VirtualMachineError in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowVirtualMachineError(const char* msg);
+
+#endif  // DALVIK_EXCEPTION_H_
diff --git a/vm/Globals.h b/vm/Globals.h
new file mode 100644
index 0000000..565c92a
--- /dev/null
+++ b/vm/Globals.h
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Variables with library scope.
+ *
+ * Prefer this over scattered static and global variables -- it's easier to
+ * view the state in a debugger, it makes clean shutdown simpler, we can
+ * trivially dump the state into a crash log, and it dodges most naming
+ * collisions that will arise when we are embedded in a larger program.
+ *
+ * If we want multiple VMs per process, this can get stuffed into TLS (or
+ * accessed through a Thread field).  May need to pass it around for some
+ * of the early initialization functions.
+ */
+#ifndef DALVIK_GLOBALS_H_
+#define DALVIK_GLOBALS_H_
+
+#include <string>
+#include <vector>
+
+#include <stdarg.h>
+#include <pthread.h>
+
+/* private structures */
+struct GcHeap;
+struct BreakpointSet;
+struct InlineSub;
+
+/*
+ * One of these for each -ea/-da/-esa/-dsa on the command line.
+ */
+struct AssertionControl {
+    char*   pkgOrClass;         /* package/class string, or NULL for esa/dsa */
+    int     pkgOrClassLen;      /* string length, for quick compare */
+    bool    enable;             /* enable or disable */
+    bool    isPackage;          /* string ended with "..."? */
+};
+
+/*
+ * Register map generation mode.  Only applicable when generateRegisterMaps
+ * is enabled.  (The "disabled" state is not folded into this because
+ * there are callers like dexopt that want to enable/disable without
+ * specifying the configuration details.)
+ *
+ * "TypePrecise" is slower and requires additional storage for the register
+ * maps, but allows type-precise GC.  "LivePrecise" is even slower and
+ * requires additional heap during processing, but allows live-precise GC.
+ */
+enum RegisterMapMode {
+    kRegisterMapModeUnknown = 0,
+    kRegisterMapModeTypePrecise,
+    kRegisterMapModeLivePrecise
+};
+
+/*
+ * Profiler clock source.
+ */
+enum ProfilerClockSource {
+    kProfilerClockSourceThreadCpu,
+    kProfilerClockSourceWall,
+    kProfilerClockSourceDual,
+};
+
+/*
+ * All fields are initialized to zero.
+ *
+ * Storage allocated here must be freed by a subsystem shutdown function.
+ */
+struct DvmGlobals {
+    /*
+     * Some options from the command line or environment.
+     */
+    char*       bootClassPathStr;
+    char*       classPathStr;
+
+    size_t      heapStartingSize;
+    size_t      heapMaximumSize;
+    size_t      heapGrowthLimit;
+    double      heapTargetUtilization;
+    size_t      heapMinFree;
+    size_t      heapMaxFree;
+    size_t      stackSize;
+    size_t      mainThreadStackSize;
+
+    bool        verboseGc;
+    bool        verboseJni;
+    bool        verboseClass;
+    bool        verboseShutdown;
+
+    bool        jdwpAllowed;        // debugging allowed for this process?
+    bool        jdwpConfigured;     // has debugging info been provided?
+    JdwpTransportType jdwpTransport;
+    bool        jdwpServer;
+    char*       jdwpHost;
+    int         jdwpPort;
+    bool        jdwpSuspend;
+
+    ProfilerClockSource profilerClockSource;
+
+    /*
+     * Lock profiling threshold value in milliseconds.  Acquires that
+     * exceed threshold are logged.  Acquires within the threshold are
+     * logged with a probability of $\frac{time}{threshold}$ .  If the
+     * threshold is unset no additional logging occurs.
+     */
+    u4          lockProfThreshold;
+
+    int         (*vfprintfHook)(FILE*, const char*, va_list);
+    void        (*exitHook)(int);
+    void        (*abortHook)(void);
+    bool        (*isSensitiveThreadHook)(void);
+
+    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        generateRegisterMaps;
+    RegisterMapMode     registerMapMode;
+
+    bool        monitorVerification;
+
+    bool        dexOptForSmp;
+
+    /*
+     * GC option flags.
+     */
+    bool        preciseGc;
+    bool        preVerify;
+    bool        postVerify;
+    bool        concurrentMarkSweep;
+    bool        verifyCardTable;
+    bool        disableExplicitGc;
+
+    int         assertionCtrlCount;
+    AssertionControl*   assertionCtrl;
+
+    ExecutionMode   executionMode;
+
+    bool        commonInit; /* whether common stubs are generated */
+    bool        constInit; /* whether global constants are initialized */
+
+    /*
+     * VM init management.
+     */
+    bool        initializing;
+    bool        optimizing;
+
+    /*
+     * java.lang.System properties set from the command line with -D.
+     * This is effectively a set, where later entries override earlier
+     * ones.
+     */
+    std::vector<std::string>* properties;
+
+    /*
+     * Where the VM goes to find system classes.
+     */
+    ClassPathEntry* bootClassPath;
+    /* used by the DEX optimizer to load classes from an unfinished DEX */
+    DvmDex*     bootClassPathOptExtra;
+    bool        optimizingBootstrapClass;
+
+    /*
+     * Loaded classes, hashed by class name.  Each entry is a ClassObject*,
+     * allocated in GC space.
+     */
+    HashTable*  loadedClasses;
+
+    /*
+     * Value for the next class serial number to be assigned.  This is
+     * incremented as we load classes.  Failed loads and races may result
+     * in some numbers being skipped, and the serial number is not
+     * guaranteed to start at 1, so the current value should not be used
+     * as a count of loaded classes.
+     */
+    volatile int classSerialNumber;
+
+    /*
+     * Classes with a low classSerialNumber are probably in the zygote, and
+     * their InitiatingLoaderList is not used, to promote sharing. The list is
+     * kept here instead.
+     */
+    InitiatingLoaderList* initiatingLoaderList;
+
+    /*
+     * Interned strings.
+     */
+
+    /* A mutex that guards access to the interned string tables. */
+    pthread_mutex_t internLock;
+
+    /* Hash table of strings interned by the user. */
+    HashTable*  internedStrings;
+
+    /* Hash table of strings interned by the class loader. */
+    HashTable*  literalStrings;
+
+    /*
+     * Classes constructed directly by the vm.
+     */
+
+    /* the class Class */
+    ClassObject* classJavaLangClass;
+
+    /* synthetic classes representing primitive types */
+    ClassObject* typeVoid;
+    ClassObject* typeBoolean;
+    ClassObject* typeByte;
+    ClassObject* typeShort;
+    ClassObject* typeChar;
+    ClassObject* typeInt;
+    ClassObject* typeLong;
+    ClassObject* typeFloat;
+    ClassObject* typeDouble;
+
+    /* synthetic classes for arrays of primitives */
+    ClassObject* classArrayBoolean;
+    ClassObject* classArrayByte;
+    ClassObject* classArrayShort;
+    ClassObject* classArrayChar;
+    ClassObject* classArrayInt;
+    ClassObject* classArrayLong;
+    ClassObject* classArrayFloat;
+    ClassObject* classArrayDouble;
+
+    /*
+     * Quick lookups for popular classes used internally.
+     */
+    ClassObject* classJavaLangClassArray;
+    ClassObject* classJavaLangClassLoader;
+    ClassObject* classJavaLangObject;
+    ClassObject* classJavaLangObjectArray;
+    ClassObject* classJavaLangString;
+    ClassObject* classJavaLangThread;
+    ClassObject* classJavaLangVMThread;
+    ClassObject* classJavaLangThreadGroup;
+    ClassObject* classJavaLangStackTraceElement;
+    ClassObject* classJavaLangStackTraceElementArray;
+    ClassObject* classJavaLangAnnotationAnnotationArray;
+    ClassObject* classJavaLangAnnotationAnnotationArrayArray;
+    ClassObject* classJavaLangReflectAccessibleObject;
+    ClassObject* classJavaLangReflectConstructor;
+    ClassObject* classJavaLangReflectConstructorArray;
+    ClassObject* classJavaLangReflectField;
+    ClassObject* classJavaLangReflectFieldArray;
+    ClassObject* classJavaLangReflectMethod;
+    ClassObject* classJavaLangReflectMethodArray;
+    ClassObject* classJavaLangReflectProxy;
+    ClassObject* classJavaNioReadWriteDirectByteBuffer;
+    ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationFactory;
+    ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMember;
+    ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMemberArray;
+    ClassObject* classOrgApacheHarmonyDalvikDdmcChunk;
+    ClassObject* classOrgApacheHarmonyDalvikDdmcDdmServer;
+    ClassObject* classJavaLangRefFinalizerReference;
+
+    /*
+     * classes representing exception types. The names here don't include
+     * packages, just to keep the use sites a bit less verbose. All are
+     * in java.lang, except where noted.
+     */
+    ClassObject* exAbstractMethodError;
+    ClassObject* exArithmeticException;
+    ClassObject* exArrayIndexOutOfBoundsException;
+    ClassObject* exArrayStoreException;
+    ClassObject* exClassCastException;
+    ClassObject* exClassCircularityError;
+    ClassObject* exClassFormatError;
+    ClassObject* exClassNotFoundException;
+    ClassObject* exError;
+    ClassObject* exExceptionInInitializerError;
+    ClassObject* exFileNotFoundException; /* in java.io */
+    ClassObject* exIOException;           /* in java.io */
+    ClassObject* exIllegalAccessError;
+    ClassObject* exIllegalAccessException;
+    ClassObject* exIllegalArgumentException;
+    ClassObject* exIllegalMonitorStateException;
+    ClassObject* exIllegalStateException;
+    ClassObject* exIllegalThreadStateException;
+    ClassObject* exIncompatibleClassChangeError;
+    ClassObject* exInstantiationError;
+    ClassObject* exInstantiationException;
+    ClassObject* exInternalError;
+    ClassObject* exInterruptedException;
+    ClassObject* exLinkageError;
+    ClassObject* exNegativeArraySizeException;
+    ClassObject* exNoClassDefFoundError;
+    ClassObject* exNoSuchFieldError;
+    ClassObject* exNoSuchFieldException;
+    ClassObject* exNoSuchMethodError;
+    ClassObject* exNullPointerException;
+    ClassObject* exOutOfMemoryError;
+    ClassObject* exRuntimeException;
+    ClassObject* exStackOverflowError;
+    ClassObject* exStaleDexCacheError;    /* in dalvik.system */
+    ClassObject* exStringIndexOutOfBoundsException;
+    ClassObject* exThrowable;
+    ClassObject* exTypeNotPresentException;
+    ClassObject* exUnsatisfiedLinkError;
+    ClassObject* exUnsupportedOperationException;
+    ClassObject* exVerifyError;
+    ClassObject* exVirtualMachineError;
+
+    /* method offsets - Object */
+    int         voffJavaLangObject_equals;
+    int         voffJavaLangObject_hashCode;
+    int         voffJavaLangObject_toString;
+
+    /* field offsets - String */
+    int         offJavaLangString_value;
+    int         offJavaLangString_count;
+    int         offJavaLangString_offset;
+    int         offJavaLangString_hashCode;
+
+    /* field offsets - Thread */
+    int         offJavaLangThread_vmThread;
+    int         offJavaLangThread_group;
+    int         offJavaLangThread_daemon;
+    int         offJavaLangThread_name;
+    int         offJavaLangThread_priority;
+    int         offJavaLangThread_uncaughtHandler;
+    int         offJavaLangThread_contextClassLoader;
+
+    /* method offsets - Thread */
+    int         voffJavaLangThread_run;
+
+    /* field offsets - ThreadGroup */
+    int         offJavaLangThreadGroup_name;
+    int         offJavaLangThreadGroup_parent;
+
+    /* field offsets - VMThread */
+    int         offJavaLangVMThread_thread;
+    int         offJavaLangVMThread_vmData;
+
+    /* method offsets - ThreadGroup */
+    int         voffJavaLangThreadGroup_removeThread;
+
+    /* field offsets - Throwable */
+    int         offJavaLangThrowable_stackState;
+    int         offJavaLangThrowable_cause;
+
+    /* method offsets - ClassLoader */
+    int         voffJavaLangClassLoader_loadClass;
+
+    /* direct method pointers - ClassLoader */
+    Method*     methJavaLangClassLoader_getSystemClassLoader;
+
+    /* field offsets - java.lang.reflect.* */
+    int         offJavaLangReflectConstructor_slot;
+    int         offJavaLangReflectConstructor_declClass;
+    int         offJavaLangReflectField_slot;
+    int         offJavaLangReflectField_declClass;
+    int         offJavaLangReflectMethod_slot;
+    int         offJavaLangReflectMethod_declClass;
+
+    /* field offsets - java.lang.ref.Reference */
+    int         offJavaLangRefReference_referent;
+    int         offJavaLangRefReference_queue;
+    int         offJavaLangRefReference_queueNext;
+    int         offJavaLangRefReference_pendingNext;
+
+    /* field offsets - java.lang.ref.FinalizerReference */
+    int offJavaLangRefFinalizerReference_zombie;
+
+    /* method pointers - java.lang.ref.ReferenceQueue */
+    Method* methJavaLangRefReferenceQueueAdd;
+
+    /* method pointers - java.lang.ref.FinalizerReference */
+    Method* methJavaLangRefFinalizerReferenceAdd;
+
+    /* constructor method pointers; no vtable involved, so use Method* */
+    Method*     methJavaLangStackTraceElement_init;
+    Method*     methJavaLangReflectConstructor_init;
+    Method*     methJavaLangReflectField_init;
+    Method*     methJavaLangReflectMethod_init;
+    Method*     methOrgApacheHarmonyLangAnnotationAnnotationMember_init;
+
+    /* static method pointers - android.lang.annotation.* */
+    Method*
+        methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation;
+
+    /* direct method pointers - java.lang.reflect.Proxy */
+    Method*     methJavaLangReflectProxy_constructorPrototype;
+
+    /* field offsets - java.lang.reflect.Proxy */
+    int         offJavaLangReflectProxy_h;
+
+    /* field offsets - java.io.FileDescriptor */
+    int         offJavaIoFileDescriptor_descriptor;
+
+    /* direct method pointers - dalvik.system.NativeStart */
+    Method*     methDalvikSystemNativeStart_main;
+    Method*     methDalvikSystemNativeStart_run;
+
+    /* assorted direct buffer helpers */
+    Method*     methJavaNioReadWriteDirectByteBuffer_init;
+    int         offJavaNioBuffer_capacity;
+    int         offJavaNioBuffer_effectiveDirectAddress;
+
+    /* direct method pointers - org.apache.harmony.dalvik.ddmc.DdmServer */
+    Method*     methDalvikDdmcServer_dispatch;
+    Method*     methDalvikDdmcServer_broadcast;
+
+    /* field offsets - org.apache.harmony.dalvik.ddmc.Chunk */
+    int         offDalvikDdmcChunk_type;
+    int         offDalvikDdmcChunk_data;
+    int         offDalvikDdmcChunk_offset;
+    int         offDalvikDdmcChunk_length;
+
+    /*
+     * Thread list.  This always has at least one element in it (main),
+     * and main is always the first entry.
+     *
+     * The threadListLock is used for several things, including the thread
+     * start condition variable.  Generally speaking, you must hold the
+     * threadListLock when:
+     *  - adding/removing items from the list
+     *  - waiting on or signaling threadStartCond
+     *  - examining the Thread struct for another thread (this is to avoid
+     *    one thread freeing the Thread struct while another thread is
+     *    perusing it)
+     */
+    Thread*     threadList;
+    pthread_mutex_t threadListLock;
+
+    pthread_cond_t threadStartCond;
+
+    /*
+     * The thread code grabs this before suspending all threads.  There
+     * are a few things that can cause a "suspend all":
+     *  (1) the GC is starting;
+     *  (2) the debugger has sent a "suspend all" request;
+     *  (3) a thread has hit a breakpoint or exception that the debugger
+     *      has marked as a "suspend all" event;
+     *  (4) the SignalCatcher caught a signal that requires suspension.
+     *  (5) (if implemented) the JIT needs to perform a heavyweight
+     *      rearrangement of the translation cache or JitTable.
+     *
+     * Because we use "safe point" self-suspension, it is never safe to
+     * do a blocking "lock" call on this mutex -- if it has been acquired,
+     * somebody is probably trying to put you to sleep.  The leading '_' is
+     * intended as a reminder that this lock is special.
+     */
+    pthread_mutex_t _threadSuspendLock;
+
+    /*
+     * Guards Thread->suspendCount for all threads, and
+     * provides the lock for the condition variable that all suspended threads
+     * sleep on (threadSuspendCountCond).
+     *
+     * This has to be separate from threadListLock because of the way
+     * threads put themselves to sleep.
+     */
+    pthread_mutex_t threadSuspendCountLock;
+
+    /*
+     * Suspended threads sleep on this.  They should sleep on the condition
+     * variable until their "suspend count" is zero.
+     *
+     * Paired with "threadSuspendCountLock".
+     */
+    pthread_cond_t  threadSuspendCountCond;
+
+    /*
+     * Sum of all threads' suspendCount fields. Guarded by
+     * threadSuspendCountLock.
+     */
+    int  sumThreadSuspendCount;
+
+    /*
+     * MUTEX ORDERING: when locking multiple mutexes, always grab them in
+     * this order to avoid deadlock:
+     *
+     *  (1) _threadSuspendLock      (use lockThreadSuspend())
+     *  (2) threadListLock          (use dvmLockThreadList())
+     *  (3) threadSuspendCountLock  (use lockThreadSuspendCount())
+     */
+
+
+    /*
+     * Thread ID bitmap.  We want threads to have small integer IDs so
+     * we can use them in "thin locks".
+     */
+    BitVector*  threadIdMap;
+
+    /*
+     * Manage exit conditions.  The VM exits when all non-daemon threads
+     * have exited.  If the main thread returns early, we need to sleep
+     * on a condition variable.
+     */
+    int         nonDaemonThreadCount;   /* must hold threadListLock to access */
+    pthread_cond_t  vmExitCond;
+
+    /*
+     * The set of DEX files loaded by custom class loaders.
+     */
+    HashTable*  userDexFiles;
+
+    /*
+     * JNI global reference table.
+     */
+    IndirectRefTable jniGlobalRefTable;
+    IndirectRefTable jniWeakGlobalRefTable;
+    pthread_mutex_t jniGlobalRefLock;
+    pthread_mutex_t jniWeakGlobalRefLock;
+    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 */
+
+    /*
+     * Cache results of "A instanceof B".
+     */
+    AtomicCache* instanceofCache;
+
+    /* inline substitution table, used during optimization */
+    InlineSub*          inlineSubs;
+
+    /*
+     * Bootstrap class loader linear allocator.
+     */
+    LinearAllocHdr* pBootLoaderAlloc;
+
+    /*
+     * Compute some stats on loaded classes.
+     */
+    int         numLoadedClasses;
+    int         numDeclaredMethods;
+    int         numDeclaredInstFields;
+    int         numDeclaredStaticFields;
+
+    /* when using a native debugger, set this to suppress watchdog timers */
+    bool        nativeDebuggerActive;
+
+    /*
+     * JDWP debugger support.
+     *
+     * Note: Each thread will normally determine whether the debugger is active
+     * for it by referring to its subMode flags.  "debuggerActive" here should be
+     * seen as "debugger is making requests of 1 or more threads".
+     */
+    bool        debuggerConnected;      /* debugger or DDMS is connected */
+    bool        debuggerActive;         /* debugger is making requests */
+    JdwpState*  jdwpState;
+
+    /*
+     * Registry of objects known to the debugger.
+     */
+    HashTable*  dbgRegistry;
+
+    /*
+     * Debugger breakpoint table.
+     */
+    BreakpointSet*  breakpointSet;
+
+    /*
+     * Single-step control struct.  We currently only allow one thread to
+     * be single-stepping at a time, which is all that really makes sense,
+     * but it's possible we may need to expand this to be per-thread.
+     */
+    StepControl stepControl;
+
+    /*
+     * DDM features embedded in the VM.
+     */
+    bool        ddmThreadNotification;
+
+    /*
+     * Zygote (partially-started process) support
+     */
+    bool        zygote;
+
+    /*
+     * Used for tracking allocations that we report to DDMS.  When the feature
+     * is enabled (through a DDMS request) the "allocRecords" pointer becomes
+     * non-NULL.
+     */
+    pthread_mutex_t allocTrackerLock;
+    AllocRecord*    allocRecords;
+    int             allocRecordHead;        /* most-recently-added entry */
+    int             allocRecordCount;       /* #of valid entries */
+
+    /*
+     * When a profiler is enabled, this is incremented.  Distinct profilers
+     * include "dmtrace" method tracing, emulator method tracing, and
+     * possibly instruction counting.
+     *
+     * The purpose of this is to have a single value that shows whether any
+     * profiling is going on.  Individual thread will normally check their
+     * thread-private subMode flags to take any profiling action.
+     */
+    volatile int activeProfilers;
+
+    /*
+     * State for method-trace profiling.
+     */
+    MethodTraceState methodTrace;
+    Method*     methodTraceGcMethod;
+    Method*     methodTraceClassPrepMethod;
+
+    /*
+     * State for emulator tracing.
+     */
+    void*       emulatorTracePage;
+    int         emulatorTraceEnableCount;
+
+    /*
+     * Global state for memory allocation profiling.
+     */
+    AllocProfState allocProf;
+
+    /*
+     * Pointers to the original methods for things that have been inlined.
+     * This makes it easy for us to output method entry/exit records for
+     * the method calls we're not actually making.  (Used by method
+     * profiling.)
+     */
+    Method**    inlinedMethods;
+
+    /*
+     * Dalvik instruction counts (kNumPackedOpcodes entries).
+     */
+    int*        executedInstrCounts;
+    int         instructionCountEnableCount;
+
+    /*
+     * Signal catcher thread (for SIGQUIT).
+     */
+    pthread_t   signalCatcherHandle;
+    bool        haltSignalCatcher;
+
+    /*
+     * Stdout/stderr conversion thread.
+     */
+    bool            haltStdioConverter;
+    bool            stdioConverterReady;
+    pthread_t       stdioConverterHandle;
+    pthread_mutex_t stdioConverterLock;
+    pthread_cond_t  stdioConverterCond;
+    int             stdoutPipe[2];
+    int             stderrPipe[2];
+
+    /*
+     * pid of the system_server process. We track it so that when system server
+     * crashes the Zygote process will be killed and restarted.
+     */
+    pid_t systemServerPid;
+
+    int kernelGroupScheduling;
+
+//#define COUNT_PRECISE_METHODS
+#ifdef COUNT_PRECISE_METHODS
+    PointerSet* preciseMethods;
+#endif
+
+    /* some RegisterMap statistics, useful during development */
+    void*       registerMapStats;
+
+#ifdef VERIFIER_STATS
+    VerifierStats verifierStats;
+#endif
+
+    /* String pointed here will be deposited on the stack frame of dvmAbort */
+    const char *lastMessage;
+};
+
+extern struct DvmGlobals gDvm;
+
+#if defined(WITH_JIT)
+
+/* Trace profiling modes.  Ordering matters - off states before on states */
+enum TraceProfilingModes {
+    kTraceProfilingDisabled = 0,      // Not profiling
+    kTraceProfilingPeriodicOff = 1,   // Periodic profiling, off phase
+    kTraceProfilingContinuous = 2,    // Always profiling
+    kTraceProfilingPeriodicOn = 3     // Periodic profiling, on phase
+};
+
+/*
+ * Exiting the compiled code w/o chaining will incur overhead to look up the
+ * target in the code cache which is extra work only when JIT is enabled. So
+ * we want to monitor it closely to make sure we don't have performance bugs.
+ */
+enum NoChainExits {
+    kInlineCacheMiss = 0,
+    kCallsiteInterpreted,
+    kSwitchOverflow,
+    kHeavyweightMonitor,
+    kNoChainExitLast,
+};
+
+/*
+ * JIT-specific global state
+ */
+struct DvmJitGlobals {
+    /*
+     * Guards writes to Dalvik PC (dPC), translated code address (codeAddr) and
+     * chain fields within the JIT hash table.  Note carefully the access
+     * mechanism.
+     * Only writes are guarded, and the guarded fields must be updated in a
+     * specific order using atomic operations.  Further, once a field is
+     * written it cannot be changed without halting all threads.
+     *
+     * The write order is:
+     *    1) codeAddr
+     *    2) dPC
+     *    3) chain [if necessary]
+     *
+     * This mutex also guards both read and write of curJitTableEntries.
+     */
+    pthread_mutex_t tableLock;
+
+    /* The JIT hash table.  Note that for access speed, copies of this pointer
+     * are stored in each thread. */
+    struct JitEntry *pJitEntryTable;
+
+    /* Array of compilation trigger threshold counters */
+    unsigned char *pProfTable;
+
+    /* Trace profiling counters */
+    struct JitTraceProfCounters *pJitTraceProfCounters;
+
+    /* Copy of pProfTable used for temporarily disabling the Jit */
+    unsigned char *pProfTableCopy;
+
+    /* Size of JIT hash table in entries.  Must be a power of 2 */
+    unsigned int jitTableSize;
+
+    /* Mask used in hash function for JitTable.  Should be jitTableSize-1 */
+    unsigned int jitTableMask;
+
+    /* How many entries in the JitEntryTable are in use */
+    unsigned int jitTableEntriesUsed;
+
+    /* Bytes allocated for the code cache */
+    unsigned int codeCacheSize;
+
+    /* Trigger for trace selection */
+    unsigned short threshold;
+
+    /* JIT Compiler Control */
+    bool               haltCompilerThread;
+    bool               blockingMode;
+    bool               methodTraceSupport;
+    bool               genSuspendPoll;
+    Thread*            compilerThread;
+    pthread_t          compilerHandle;
+    pthread_mutex_t    compilerLock;
+    pthread_mutex_t    compilerICPatchLock;
+    pthread_cond_t     compilerQueueActivity;
+    pthread_cond_t     compilerQueueEmpty;
+    volatile int       compilerQueueLength;
+    int                compilerHighWater;
+    int                compilerWorkEnqueueIndex;
+    int                compilerWorkDequeueIndex;
+    int                compilerICPatchIndex;
+
+    /* JIT internal stats */
+    int                compilerMaxQueued;
+    int                translationChains;
+
+    /* Compiled code cache */
+    void* codeCache;
+
+    /*
+     * This is used to store the base address of an in-flight compilation whose
+     * class object pointers have been calculated to populate literal pool.
+     * Once the compiler thread has changed its status to VM_WAIT, we cannot
+     * guarantee whether GC has happened before the code address has been
+     * installed to the JIT table. Because of that, this field can only
+     * been cleared/overwritten by the compiler thread if it is in the
+     * THREAD_RUNNING state or in a safe point.
+     */
+    void *inflightBaseAddr;
+
+    /* Translation cache version (protected by compilerLock */
+    int cacheVersion;
+
+    /* Bytes used by the code templates */
+    unsigned int templateSize;
+
+    /* Bytes already used in the code cache */
+    unsigned int codeCacheByteUsed;
+
+    /* Number of installed compilations in the cache */
+    unsigned int numCompilations;
+
+    /* Flag to indicate that the code cache is full */
+    bool codeCacheFull;
+
+    /* Page size  - 1 */
+    unsigned int pageSizeMask;
+
+    /* Lock to change the protection type of the code cache */
+    pthread_mutex_t    codeCacheProtectionLock;
+
+    /* Number of times that the code cache has been reset */
+    int numCodeCacheReset;
+
+    /* Number of times that the code cache reset request has been delayed */
+    int numCodeCacheResetDelayed;
+
+    /* true/false: compile/reject opcodes specified in the -Xjitop list */
+    bool includeSelectedOp;
+
+    /* true/false: compile/reject methods specified in the -Xjitmethod list */
+    bool includeSelectedMethod;
+
+    /* true/false: compile/reject traces with offset specified in the -Xjitoffset list */
+    bool includeSelectedOffset;
+
+    /* Disable JIT for selected opcodes - one bit for each opcode */
+    char opList[(kNumPackedOpcodes+7)/8];
+
+    /* Disable JIT for selected methods */
+    HashTable *methodTable;
+
+    /* Disable JIT for selected classes */
+    HashTable *classTable;
+
+    /* Disable JIT for selected offsets */
+    unsigned int pcTable[COMPILER_PC_OFFSET_SIZE];
+    int num_entries_pcTable;
+
+    /* Flag to dump all compiled code */
+    bool printMe;
+
+    /* Flag to dump compiled binary code in bytes */
+    bool printBinary;
+
+    /* Per-process debug flag toggled when receiving a SIGUSR2 */
+    bool receivedSIGUSR2;
+
+    /* Trace profiling mode */
+    TraceProfilingModes profileMode;
+
+    /* Periodic trace profiling countdown timer */
+    int profileCountdown;
+
+    /* Vector to disable selected optimizations */
+    int disableOpt;
+
+    /* Table to track the overall and trace statistics of hot methods */
+    HashTable*  methodStatsTable;
+
+    /* Filter method compilation blacklist with call-graph information */
+    bool checkCallGraph;
+
+    /* New translation chain has been set up */
+    volatile bool hasNewChain;
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Spin when error is detected, volatile so GDB can reset it */
+    volatile bool selfVerificationSpin;
+#endif
+
+    /* Framework or stand-alone? */
+    bool runningInAndroidFramework;
+
+    /* Framework callback happened? */
+    bool alreadyEnabledViaFramework;
+
+    /* Framework requests to disable the JIT for good */
+    bool disableJit;
+
+#if defined(SIGNATURE_BREAKPOINT)
+    /* Signature breakpoint */
+    u4 signatureBreakpointSize;         // # of words
+    u4 *signatureBreakpoint;            // Signature content
+#endif
+
+#if defined(WITH_JIT_TUNING)
+    /* Performance tuning counters */
+    int                addrLookupsFound;
+    int                addrLookupsNotFound;
+    int                noChainExit[kNoChainExitLast];
+    int                normalExit;
+    int                puntExit;
+    int                invokeMonomorphic;
+    int                invokePolymorphic;
+    int                invokeNative;
+    int                invokeMonoGetterInlined;
+    int                invokeMonoSetterInlined;
+    int                invokePolyGetterInlined;
+    int                invokePolySetterInlined;
+    int                returnOp;
+    int                icPatchInit;
+    int                icPatchLockFree;
+    int                icPatchQueued;
+    int                icPatchRejected;
+    int                icPatchDropped;
+    int                codeCachePatches;
+    int                numCompilerThreadBlockGC;
+    u8                 jitTime;
+    u8                 compilerThreadBlockGCStart;
+    u8                 compilerThreadBlockGCTime;
+    u8                 maxCompilerThreadBlockGCTime;
+#endif
+
+#if defined(ARCH_IA32)
+    JitOptLevel        optLevel;
+#endif
+
+    /* Place arrays at the end to ease the display in gdb sessions */
+
+    /* Work order queue for compilations */
+    CompilerWorkOrder compilerWorkQueue[COMPILER_WORK_QUEUE_SIZE];
+
+    /* Work order queue for predicted chain patching */
+    ICPatchWorkOrder compilerICPatchQueue[COMPILER_IC_PATCH_QUEUE_SIZE];
+};
+
+extern struct DvmJitGlobals gDvmJit;
+
+#if defined(WITH_JIT_TUNING)
+extern int gDvmICHitCount;
+#endif
+
+#endif
+
+struct DvmJniGlobals {
+    bool useCheckJni;
+    bool warnOnly;
+    bool forceCopy;
+
+    // Provide backwards compatibility for pre-ICS apps on ICS.
+    bool workAroundAppJniBugs;
+
+    // Debugging help for third-party developers. Similar to -Xjnitrace.
+    bool logThirdPartyJni;
+
+    // We only support a single JavaVM per process.
+    JavaVM*     jniVm;
+};
+
+extern struct DvmJniGlobals gDvmJni;
+
+#endif  // DALVIK_GLOBALS_H_
diff --git a/vm/Hash.cpp b/vm/Hash.cpp
new file mode 100644
index 0000000..cd4530a
--- /dev/null
+++ b/vm/Hash.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Hash table.  The dominant calls are add and lookup, with removals
+ * happening very infrequently.  We use probing, and don't worry much
+ * about tombstone removal.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/* table load factor, i.e. how full can it get before we resize */
+//#define LOAD_NUMER  3       // 75%
+//#define LOAD_DENOM  4
+#define LOAD_NUMER  5       // 62.5%
+#define LOAD_DENOM  8
+//#define LOAD_NUMER  1       // 50%
+//#define LOAD_DENOM  2
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements.
+ */
+size_t dvmHashSize(size_t size) {
+    return (size * LOAD_DENOM) / LOAD_NUMER +1;
+}
+
+
+/*
+ * Create and initialize a hash table.
+ */
+HashTable* dvmHashTableCreate(size_t initialSize, HashFreeFunc freeFunc)
+{
+    HashTable* pHashTable;
+
+    assert(initialSize > 0);
+
+    pHashTable = (HashTable*) malloc(sizeof(*pHashTable));
+    if (pHashTable == NULL)
+        return NULL;
+
+    dvmInitMutex(&pHashTable->lock);
+
+    pHashTable->tableSize = dexRoundUpPower2(initialSize);
+    pHashTable->numEntries = pHashTable->numDeadEntries = 0;
+    pHashTable->freeFunc = freeFunc;
+    pHashTable->pEntries =
+        (HashEntry*) calloc(pHashTable->tableSize, sizeof(HashEntry));
+    if (pHashTable->pEntries == NULL) {
+        free(pHashTable);
+        return NULL;
+    }
+
+    return pHashTable;
+}
+
+/*
+ * Clear out all entries.
+ */
+void dvmHashTableClear(HashTable* pHashTable)
+{
+    HashEntry* pEnt;
+    int i;
+
+    pEnt = pHashTable->pEntries;
+    for (i = 0; i < pHashTable->tableSize; i++, pEnt++) {
+        if (pEnt->data == HASH_TOMBSTONE) {
+            // nuke entry
+            pEnt->data = NULL;
+        } else if (pEnt->data != NULL) {
+            // call free func then nuke entry
+            if (pHashTable->freeFunc != NULL)
+                (*pHashTable->freeFunc)(pEnt->data);
+            pEnt->data = NULL;
+        }
+    }
+
+    pHashTable->numEntries = 0;
+    pHashTable->numDeadEntries = 0;
+}
+
+/*
+ * Free the table.
+ */
+void dvmHashTableFree(HashTable* pHashTable)
+{
+    if (pHashTable == NULL)
+        return;
+    dvmHashTableClear(pHashTable);
+    free(pHashTable->pEntries);
+    free(pHashTable);
+}
+
+#ifndef NDEBUG
+/*
+ * Count up the number of tombstone entries in the hash table.
+ */
+static int countTombStones(HashTable* pHashTable)
+{
+    int i, count;
+
+    for (count = i = 0; i < pHashTable->tableSize; i++) {
+        if (pHashTable->pEntries[i].data == HASH_TOMBSTONE)
+            count++;
+    }
+    return count;
+}
+#endif
+
+/*
+ * Resize a hash table.  We do this when adding an entry increased the
+ * size of the table beyond its comfy limit.
+ *
+ * This essentially requires re-inserting all elements into the new storage.
+ *
+ * If multiple threads can access the hash table, the table's lock should
+ * have been grabbed before issuing the "lookup+add" call that led to the
+ * resize, so we don't have a synchronization problem here.
+ */
+static bool resizeHash(HashTable* pHashTable, int newSize)
+{
+    HashEntry* pNewEntries;
+    int i;
+
+    assert(countTombStones(pHashTable) == pHashTable->numDeadEntries);
+    //ALOGI("before: dead=%d", pHashTable->numDeadEntries);
+
+    pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashEntry));
+    if (pNewEntries == NULL)
+        return false;
+
+    for (i = 0; i < pHashTable->tableSize; i++) {
+        void* data = pHashTable->pEntries[i].data;
+        if (data != NULL && data != HASH_TOMBSTONE) {
+            int hashValue = pHashTable->pEntries[i].hashValue;
+            int newIdx;
+
+            /* probe for new spot, wrapping around */
+            newIdx = hashValue & (newSize-1);
+            while (pNewEntries[newIdx].data != NULL)
+                newIdx = (newIdx + 1) & (newSize-1);
+
+            pNewEntries[newIdx].hashValue = hashValue;
+            pNewEntries[newIdx].data = data;
+        }
+    }
+
+    free(pHashTable->pEntries);
+    pHashTable->pEntries = pNewEntries;
+    pHashTable->tableSize = newSize;
+    pHashTable->numDeadEntries = 0;
+
+    assert(countTombStones(pHashTable) == 0);
+    return true;
+}
+
+/*
+ * Look up an entry.
+ *
+ * We probe on collisions, wrapping around the table.
+ */
+void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
+    HashCompareFunc cmpFunc, bool doAdd)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+    void* result = NULL;
+
+    assert(pHashTable->tableSize > 0);
+    assert(item != HASH_TOMBSTONE);
+    assert(item != NULL);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data != HASH_TOMBSTONE &&
+            pEntry->hashValue == itemHash &&
+            (*cmpFunc)(pEntry->data, item) == 0)
+        {
+            /* match */
+            //ALOGD("+++ match on entry %d", pEntry - pHashTable->pEntries);
+            break;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        //ALOGI("+++ look probing %d...", pEntry - pHashTable->pEntries);
+    }
+
+    if (pEntry->data == NULL) {
+        if (doAdd) {
+            pEntry->hashValue = itemHash;
+            pEntry->data = item;
+            pHashTable->numEntries++;
+
+            /*
+             * We've added an entry.  See if this brings us too close to full.
+             */
+            if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM
+                > pHashTable->tableSize * LOAD_NUMER)
+            {
+                if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) {
+                    /* don't really have a way to indicate failure */
+                    ALOGE("Dalvik hash resize failure");
+                    dvmAbort();
+                }
+                /* note "pEntry" is now invalid */
+            } else {
+                //ALOGW("okay %d/%d/%d",
+                //    pHashTable->numEntries, pHashTable->tableSize,
+                //    (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
+            }
+
+            /* full table is bad -- search for nonexistent never halts */
+            assert(pHashTable->numEntries < pHashTable->tableSize);
+            result = item;
+        } else {
+            assert(result == NULL);
+        }
+    } else {
+        result = pEntry->data;
+    }
+
+    return result;
+}
+
+/*
+ * Remove an entry from the table.
+ *
+ * Does NOT invoke the "free" function on the item.
+ */
+bool dvmHashTableRemove(HashTable* pHashTable, u4 itemHash, void* item)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+
+    assert(pHashTable->tableSize > 0);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data == item) {
+            //ALOGI("+++ stepping on entry %d", pEntry - pHashTable->pEntries);
+            pEntry->data = HASH_TOMBSTONE;
+            pHashTable->numEntries--;
+            pHashTable->numDeadEntries++;
+            return true;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        //ALOGI("+++ del probing %d...", pEntry - pHashTable->pEntries);
+    }
+
+    return false;
+}
+
+/*
+ * Scan every entry in the hash table and evaluate it with the specified
+ * indirect function call. If the function returns 1, remove the entry from
+ * the table.
+ *
+ * Does NOT invoke the "free" function on the item.
+ *
+ * Returning values other than 0 or 1 will abort the routine.
+ */
+int dvmHashForeachRemove(HashTable* pHashTable, HashForeachRemoveFunc func)
+{
+    int i, val, tableSize;
+
+    tableSize = pHashTable->tableSize;
+
+    for (i = 0; i < tableSize; i++) {
+        HashEntry* pEnt = &pHashTable->pEntries[i];
+
+        if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
+            val = (*func)(pEnt->data);
+            if (val == 1) {
+                pEnt->data = HASH_TOMBSTONE;
+                pHashTable->numEntries--;
+                pHashTable->numDeadEntries++;
+            }
+            else if (val != 0) {
+                return val;
+            }
+        }
+    }
+    return 0;
+}
+
+
+/*
+ * Execute a function on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg)
+{
+    int i, val, tableSize;
+
+    tableSize = pHashTable->tableSize;
+
+    for (i = 0; i < tableSize; i++) {
+        HashEntry* pEnt = &pHashTable->pEntries[i];
+
+        if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
+            val = (*func)(pEnt->data, arg);
+            if (val != 0)
+                return val;
+        }
+    }
+
+    return 0;
+}
+
+
+/*
+ * Look up an entry, counting the number of times we have to probe.
+ *
+ * Returns -1 if the entry wasn't found.
+ */
+static int countProbes(HashTable* pHashTable, u4 itemHash, const void* item,
+    HashCompareFunc cmpFunc)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+    int count = 0;
+
+    assert(pHashTable->tableSize > 0);
+    assert(item != HASH_TOMBSTONE);
+    assert(item != NULL);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data != HASH_TOMBSTONE &&
+            pEntry->hashValue == itemHash &&
+            (*cmpFunc)(pEntry->data, item) == 0)
+        {
+            /* match */
+            break;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        count++;
+    }
+    if (pEntry->data == NULL)
+        return -1;
+
+    return count;
+}
+
+/*
+ * Evaluate the amount of probing required for the specified hash table.
+ *
+ * We do this by running through all entries in the hash table, computing
+ * the hash value and then doing a lookup.
+ *
+ * The caller should lock the table before calling here.
+ */
+void dvmHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+    HashCompareFunc cmpFunc)
+{
+    int numEntries, minProbe, maxProbe, totalProbe;
+    HashIter iter;
+
+    numEntries = maxProbe = totalProbe = 0;
+    minProbe = 65536*32767;
+
+    for (dvmHashIterBegin(pHashTable, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        const void* data = (const void*)dvmHashIterData(&iter);
+        int count;
+
+        count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc);
+
+        numEntries++;
+
+        if (count < minProbe)
+            minProbe = count;
+        if (count > maxProbe)
+            maxProbe = count;
+        totalProbe += count;
+    }
+
+    ALOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f",
+        minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize,
+        (float) totalProbe / (float) numEntries);
+}
diff --git a/vm/Hash.h b/vm/Hash.h
new file mode 100644
index 0000000..38f710f
--- /dev/null
+++ b/vm/Hash.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * General purpose hash table, used for finding classes, methods, etc.
+ *
+ * When the number of elements reaches a certain percentage of the table's
+ * capacity, the table will be resized.
+ */
+#ifndef DALVIK_HASH_H_
+#define DALVIK_HASH_H_
+
+/* compute the hash of an item with a specific type */
+typedef u4 (*HashCompute)(const void* item);
+
+/*
+ * Compare a hash entry with a "loose" item after their hash values match.
+ * Returns { <0, 0, >0 } depending on ordering of items (same semantics
+ * as strcmp()).
+ */
+typedef int (*HashCompareFunc)(const void* tableItem, const void* looseItem);
+
+/*
+ * This function will be used to free entries in the table.  This can be
+ * NULL if no free is required, free(), or a custom function.
+ */
+typedef void (*HashFreeFunc)(void* ptr);
+
+/*
+ * Used by dvmHashForeach().
+ */
+typedef int (*HashForeachFunc)(void* data, void* arg);
+
+/*
+ * Used by dvmHashForeachRemove().
+ */
+typedef int (*HashForeachRemoveFunc)(void* data);
+
+/*
+ * One entry in the hash table.  "data" values are expected to be (or have
+ * the same characteristics as) valid pointers.  In particular, a NULL
+ * value for "data" indicates an empty slot, and HASH_TOMBSTONE indicates
+ * a no-longer-used slot that must be stepped over during probing.
+ *
+ * Attempting to add a NULL or tombstone value is an error.
+ *
+ * When an entry is released, we will call (HashFreeFunc)(entry->data).
+ */
+struct HashEntry {
+    u4 hashValue;
+    void* data;
+};
+
+#define HASH_TOMBSTONE ((void*) 0xcbcacccd)     // invalid ptr value
+
+/*
+ * Expandable hash table.
+ *
+ * This structure should be considered opaque.
+ */
+struct HashTable {
+    int         tableSize;          /* must be power of 2 */
+    int         numEntries;         /* current #of "live" entries */
+    int         numDeadEntries;     /* current #of tombstone entries */
+    HashEntry*  pEntries;           /* array on heap */
+    HashFreeFunc freeFunc;
+    pthread_mutex_t lock;
+};
+
+/*
+ * Create and initialize a HashTable structure, using "initialSize" as
+ * a basis for the initial capacity of the table.  (The actual initial
+ * table size may be adjusted upward.)  If you know exactly how many
+ * elements the table will hold, pass the result from dvmHashSize() in.)
+ *
+ * Returns "false" if unable to allocate the table.
+ */
+HashTable* dvmHashTableCreate(size_t initialSize, HashFreeFunc freeFunc);
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements.  Use
+ * this when you know ahead of time how many elements the table will hold.
+ * Pass this value into dvmHashTableCreate() to ensure that you can add
+ * all elements without needing to reallocate the table.
+ */
+size_t dvmHashSize(size_t size);
+
+/*
+ * Clear out a hash table, freeing the contents of any used entries.
+ */
+void dvmHashTableClear(HashTable* pHashTable);
+
+/*
+ * Free a hash table.  Performs a "clear" first.
+ */
+void dvmHashTableFree(HashTable* pHashTable);
+
+/*
+ * Exclusive access.  Important when adding items to a table, or when
+ * doing any operations on a table that could be added to by another thread.
+ */
+INLINE void dvmHashTableLock(HashTable* pHashTable) {
+    dvmLockMutex(&pHashTable->lock);
+}
+INLINE void dvmHashTableUnlock(HashTable* pHashTable) {
+    dvmUnlockMutex(&pHashTable->lock);
+}
+
+/*
+ * Get #of entries in hash table.
+ */
+INLINE int dvmHashTableNumEntries(HashTable* pHashTable) {
+    return pHashTable->numEntries;
+}
+
+/*
+ * Get total size of hash table (for memory usage calculations).
+ */
+INLINE int dvmHashTableMemUsage(HashTable* pHashTable) {
+    return sizeof(HashTable) + pHashTable->tableSize * sizeof(HashEntry);
+}
+
+/*
+ * Look up an entry in the table, possibly adding it if it's not there.
+ *
+ * If "item" is not found, and "doAdd" is false, NULL is returned.
+ * Otherwise, a pointer to the found or added item is returned.  (You can
+ * tell the difference by seeing if return value == item.)
+ *
+ * An "add" operation may cause the entire table to be reallocated.  Don't
+ * forget to lock the table before calling this.
+ */
+void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
+    HashCompareFunc cmpFunc, bool doAdd);
+
+/*
+ * Remove an item from the hash table, given its "data" pointer.  Does not
+ * invoke the "free" function; just detaches it from the table.
+ */
+bool dvmHashTableRemove(HashTable* pHashTable, u4 hash, void* item);
+
+/*
+ * Execute "func" on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg);
+
+/*
+ * Execute "func" on every entry in the hash table.
+ *
+ * If "func" returns 1 detach the entry from the hash table. Does not invoke
+ * the "free" function.
+ *
+ * Returning values other than 0 or 1 from "func" will abort the routine.
+ */
+int dvmHashForeachRemove(HashTable* pHashTable, HashForeachRemoveFunc func);
+
+/*
+ * An alternative to dvmHashForeach(), using an iterator.
+ *
+ * Use like this:
+ *   HashIter iter;
+ *   for (dvmHashIterBegin(hashTable, &iter); !dvmHashIterDone(&iter);
+ *       dvmHashIterNext(&iter))
+ *   {
+ *       MyData* data = (MyData*)dvmHashIterData(&iter);
+ *   }
+ */
+struct HashIter {
+    void*       data;
+    HashTable*  pHashTable;
+    int         idx;
+};
+INLINE void dvmHashIterNext(HashIter* pIter) {
+    int i = pIter->idx +1;
+    int lim = pIter->pHashTable->tableSize;
+    for ( ; i < lim; i++) {
+        void* data = pIter->pHashTable->pEntries[i].data;
+        if (data != NULL && data != HASH_TOMBSTONE)
+            break;
+    }
+    pIter->idx = i;
+}
+INLINE void dvmHashIterBegin(HashTable* pHashTable, HashIter* pIter) {
+    pIter->pHashTable = pHashTable;
+    pIter->idx = -1;
+    dvmHashIterNext(pIter);
+}
+INLINE bool dvmHashIterDone(HashIter* pIter) {
+    return (pIter->idx >= pIter->pHashTable->tableSize);
+}
+INLINE void* dvmHashIterData(HashIter* pIter) {
+    assert(pIter->idx >= 0 && pIter->idx < pIter->pHashTable->tableSize);
+    return pIter->pHashTable->pEntries[pIter->idx].data;
+}
+
+
+/*
+ * Evaluate hash table performance by examining the number of times we
+ * have to probe for an entry.
+ *
+ * The caller should lock the table beforehand.
+ */
+typedef u4 (*HashCalcFunc)(const void* item);
+void dvmHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+    HashCompareFunc cmpFunc);
+
+#endif  // DALVIK_HASH_H_
diff --git a/vm/IndirectRefTable.cpp b/vm/IndirectRefTable.cpp
new file mode 100644
index 0000000..69afccd
--- /dev/null
+++ b/vm/IndirectRefTable.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Indirect reference table management.
+ */
+#include "Dalvik.h"
+
+static void abortMaybe() {
+    // If CheckJNI is on, it'll give a more detailed error before aborting.
+    // Otherwise, we want to abort rather than hand back a bad reference.
+    if (!gDvmJni.useCheckJni) {
+        dvmAbort();
+    }
+}
+
+bool IndirectRefTable::init(size_t initialCount,
+        size_t maxCount, IndirectRefKind desiredKind)
+{
+    assert(initialCount > 0);
+    assert(initialCount <= maxCount);
+    assert(desiredKind != kIndirectKindInvalid);
+
+    table_ = (IndirectRefSlot*) malloc(initialCount * sizeof(IndirectRefSlot));
+    if (table_ == NULL) {
+        return false;
+    }
+    memset(table_, 0xd1, initialCount * sizeof(IndirectRefSlot));
+
+    segmentState.all = IRT_FIRST_SEGMENT;
+    alloc_entries_ = initialCount;
+    max_entries_ = maxCount;
+    kind_ = desiredKind;
+
+    return true;
+}
+
+/*
+ * Clears out the contents of a IndirectRefTable, freeing allocated storage.
+ */
+void IndirectRefTable::destroy()
+{
+    free(table_);
+    table_ = NULL;
+    alloc_entries_ = max_entries_ = -1;
+}
+
+IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
+{
+    IRTSegmentState prevState;
+    prevState.all = cookie;
+    size_t topIndex = segmentState.parts.topIndex;
+
+    assert(obj != NULL);
+    assert(dvmIsHeapAddress(obj));
+    assert(table_ != NULL);
+    assert(alloc_entries_ <= max_entries_);
+    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+    /*
+     * We know there's enough room in the table.  Now we just need to find
+     * the right spot.  If there's a hole, find it and fill it; otherwise,
+     * add to the end of the list.
+     */
+    IndirectRef result;
+    IndirectRefSlot* slot;
+    int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
+    if (numHoles > 0) {
+        assert(topIndex > 1);
+        /* find the first hole; likely to be near the end of the list,
+         * we know the item at the topIndex is not a hole */
+        slot = &table_[topIndex - 1];
+        assert(slot->obj != NULL);
+        while ((--slot)->obj != NULL) {
+            assert(slot >= table_ + prevState.parts.topIndex);
+        }
+        segmentState.parts.numHoles--;
+    } else {
+        /* add to the end, grow if needed */
+        if (topIndex == alloc_entries_) {
+            /* reached end of allocated space; did we hit buffer max? */
+            if (topIndex == max_entries_) {
+                ALOGE("JNI ERROR (app bug): %s reference table overflow (max=%d)",
+                        indirectRefKindToString(kind_), max_entries_);
+                return NULL;
+            }
+
+            size_t newSize = alloc_entries_ * 2;
+            if (newSize > max_entries_) {
+                newSize = max_entries_;
+            }
+            assert(newSize > alloc_entries_);
+
+            IndirectRefSlot* newTable =
+                    (IndirectRefSlot*) realloc(table_, newSize * sizeof(IndirectRefSlot));
+            if (table_ == NULL) {
+                ALOGE("JNI ERROR (app bug): unable to expand %s reference table "
+                        "(from %d to %d, max=%d)",
+                        indirectRefKindToString(kind_),
+                        alloc_entries_, newSize, max_entries_);
+                return NULL;
+            }
+
+            memset(newTable + alloc_entries_, 0xd1,
+                   (newSize - alloc_entries_) * sizeof(IndirectRefSlot));
+
+            alloc_entries_ = newSize;
+            table_ = newTable;
+        }
+        slot = &table_[topIndex++];
+        segmentState.parts.topIndex = topIndex;
+    }
+
+    slot->obj = obj;
+    slot->serial = nextSerial(slot->serial);
+    result = toIndirectRef(slot - table_, slot->serial, kind_);
+
+    assert(result != NULL);
+    return result;
+}
+
+/*
+ * Get the referent of an indirect ref from the table.
+ *
+ * Returns kInvalidIndirectRefObject if iref is invalid.
+ */
+Object* IndirectRefTable::get(IndirectRef iref) const {
+    IndirectRefKind kind = indirectRefKind(iref);
+    if (kind != kind_) {
+        if (iref == NULL) {
+            ALOGW("Attempt to look up NULL %s reference", indirectRefKindToString(kind_));
+            return kInvalidIndirectRefObject;
+        }
+        if (kind == kIndirectKindInvalid) {
+            ALOGE("JNI ERROR (app bug): invalid %s reference %p",
+                    indirectRefKindToString(kind_), iref);
+            abortMaybe();
+            return kInvalidIndirectRefObject;
+        }
+        // References of the requested kind cannot appear within this table.
+        return kInvalidIndirectRefObject;
+    }
+
+    u4 topIndex = segmentState.parts.topIndex;
+    u4 index = extractIndex(iref);
+    if (index >= topIndex) {
+        /* bad -- stale reference? */
+        ALOGE("JNI ERROR (app bug): accessed stale %s reference %p (index %d in a table of size %d)",
+                indirectRefKindToString(kind_), iref, index, topIndex);
+        abortMaybe();
+        return kInvalidIndirectRefObject;
+    }
+
+    Object* obj = table_[index].obj;
+    if (obj == NULL) {
+        ALOGI("JNI ERROR (app bug): accessed deleted %s reference %p",
+                indirectRefKindToString(kind_), iref);
+        abortMaybe();
+        return kInvalidIndirectRefObject;
+    }
+
+    u4 serial = extractSerial(iref);
+    if (serial != table_[index].serial) {
+        ALOGE("JNI ERROR (app bug): attempt to use stale %s reference %p",
+                indirectRefKindToString(kind_), iref);
+        abortMaybe();
+        return kInvalidIndirectRefObject;
+    }
+
+    return obj;
+}
+
+static int findObject(const Object* obj, int bottomIndex, int topIndex,
+        const IndirectRefSlot* table) {
+    for (int i = bottomIndex; i < topIndex; ++i) {
+        if (table[i].obj == obj) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+bool IndirectRefTable::contains(const Object* obj) const {
+    return findObject(obj, 0, segmentState.parts.topIndex, table_) >= 0;
+}
+
+/*
+ * Remove "obj" from "pRef".  We extract the table offset bits from "iref"
+ * and zap the corresponding entry, leaving a hole if it's not at the top.
+ *
+ * If the entry is not between the current top index and the bottom index
+ * specified by the cookie, we don't remove anything.  This is the behavior
+ * required by JNI's DeleteLocalRef function.
+ *
+ * Note this is NOT called when a local frame is popped.  This is only used
+ * for explicit single removals.
+ *
+ * Returns "false" if nothing was removed.
+ */
+bool IndirectRefTable::remove(u4 cookie, IndirectRef iref)
+{
+    IRTSegmentState prevState;
+    prevState.all = cookie;
+    u4 topIndex = segmentState.parts.topIndex;
+    u4 bottomIndex = prevState.parts.topIndex;
+
+    assert(table_ != NULL);
+    assert(alloc_entries_ <= max_entries_);
+    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+    IndirectRefKind kind = indirectRefKind(iref);
+    u4 index;
+    if (kind == kind_) {
+        index = extractIndex(iref);
+        if (index < bottomIndex) {
+            /* wrong segment */
+            ALOGV("Attempt to remove index outside index area (%ud vs %ud-%ud)",
+                    index, bottomIndex, topIndex);
+            return false;
+        }
+        if (index >= topIndex) {
+            /* bad -- stale reference? */
+            ALOGD("Attempt to remove invalid index %ud (bottom=%ud top=%ud)",
+                    index, bottomIndex, topIndex);
+            return false;
+        }
+        if (table_[index].obj == NULL) {
+            ALOGD("Attempt to remove cleared %s reference %p",
+                    indirectRefKindToString(kind_), iref);
+            return false;
+        }
+        u4 serial = extractSerial(iref);
+        if (table_[index].serial != serial) {
+            ALOGD("Attempt to remove stale %s reference %p",
+                    indirectRefKindToString(kind_), iref);
+            return false;
+        }
+    } else if (kind == kIndirectKindInvalid && gDvmJni.workAroundAppJniBugs) {
+        // reference looks like a pointer, scan the table to find the index
+        int i = findObject(reinterpret_cast<Object*>(iref), bottomIndex, topIndex, table_);
+        if (i < 0) {
+            ALOGW("trying to work around app JNI bugs, but didn't find %p in table!", iref);
+            return false;
+        }
+        index = i;
+    } else {
+        // References of the requested kind cannot appear within this table.
+        return false;
+    }
+
+    if (index == topIndex - 1) {
+        // Top-most entry.  Scan up and consume holes.
+        int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
+        if (numHoles != 0) {
+            while (--topIndex > bottomIndex && numHoles != 0) {
+                ALOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p",
+                    topIndex-1, cookie, table_[topIndex-1].obj);
+                if (table_[topIndex-1].obj != NULL) {
+                    break;
+                }
+                ALOGV("+++ ate hole at %d", topIndex-1);
+                numHoles--;
+            }
+            segmentState.parts.numHoles = numHoles + prevState.parts.numHoles;
+            segmentState.parts.topIndex = topIndex;
+        } else {
+            segmentState.parts.topIndex = topIndex-1;
+            ALOGV("+++ ate last entry %d", topIndex-1);
+        }
+    } else {
+        /*
+         * Not the top-most entry.  This creates a hole.  We NULL out the
+         * entry to prevent somebody from deleting it twice and screwing up
+         * the hole count.
+         */
+        table_[index].obj = NULL;
+        segmentState.parts.numHoles++;
+        ALOGV("+++ left hole at %d, holes=%d", index, segmentState.parts.numHoles);
+    }
+
+    return true;
+}
+
+const char* indirectRefKindToString(IndirectRefKind kind)
+{
+    switch (kind) {
+    case kIndirectKindInvalid:      return "invalid";
+    case kIndirectKindLocal:        return "local";
+    case kIndirectKindGlobal:       return "global";
+    case kIndirectKindWeakGlobal:   return "weak global";
+    default:                        return "UNKNOWN";
+    }
+}
+
+void IndirectRefTable::dump(const char* descr) const
+{
+    size_t count = capacity();
+    Object** copy = new Object*[count];
+    for (size_t i = 0; i < count; i++) {
+        copy[i] = table_[i].obj;
+    }
+    dvmDumpReferenceTableContents(copy, count, descr);
+    delete[] copy;
+}
diff --git a/vm/IndirectRefTable.h b/vm/IndirectRefTable.h
new file mode 100644
index 0000000..9172ada
--- /dev/null
+++ b/vm/IndirectRefTable.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_INDIRECTREFTABLE_H_
+#define DALVIK_INDIRECTREFTABLE_H_
+
+/*
+ * Maintain a table of indirect references.  Used for local/global JNI
+ * references.
+ *
+ * The table contains object references that are part of the GC root set.
+ * When an object is added we return an IndirectRef that is not a valid
+ * pointer but can be used to find the original value in O(1) time.
+ * Conversions to and from indirect refs are performed on JNI method calls
+ * in and out of the VM, so they need to be very fast.
+ *
+ * To be efficient for JNI local variable storage, we need to provide
+ * operations that allow us to operate on segments of the table, where
+ * segments are pushed and popped as if on a stack.  For example, deletion
+ * of an entry should only succeed if it appears in the current segment,
+ * and we want to be able to strip off the current segment quickly when
+ * a method returns.  Additions to the table must be made in the current
+ * segment even if space is available in an earlier area.
+ *
+ * A new segment is created when we call into native code from interpreted
+ * code, or when we handle the JNI PushLocalFrame function.
+ *
+ * The GC must be able to scan the entire table quickly.
+ *
+ * In summary, these must be very fast:
+ *  - adding or removing a segment
+ *  - adding references to a new segment
+ *  - converting an indirect reference back to an Object
+ * These can be a little slower, but must still be pretty quick:
+ *  - adding references to a "mature" segment
+ *  - removing individual references
+ *  - scanning the entire table straight through
+ *
+ * If there's more than one segment, we don't guarantee that the table
+ * will fill completely before we fail due to lack of space.  We do ensure
+ * that the current segment will pack tightly, which should satisfy JNI
+ * requirements (e.g. EnsureLocalCapacity).
+ *
+ * To make everything fit nicely in 32-bit integers, the maximum size of
+ * the table is capped at 64K.
+ *
+ * None of the table functions are synchronized.
+ */
+
+/*
+ * Indirect reference definition.  This must be interchangeable with JNI's
+ * jobject, and it's convenient to let null be null, so we use void*.
+ *
+ * We need a 16-bit table index and a 2-bit reference type (global, local,
+ * weak global).  Real object pointers will have zeroes in the low 2 or 3
+ * bits (4- or 8-byte alignment), so it's useful to put the ref type
+ * in the low bits and reserve zero as an invalid value.
+ *
+ * The remaining 14 bits can be used to detect stale indirect references.
+ * For example, if objects don't move, we can use a hash of the original
+ * Object* to make sure the entry hasn't been re-used.  (If the Object*
+ * we find there doesn't match because of heap movement, we could do a
+ * secondary check on the preserved hash value; this implies that creating
+ * a global/local ref queries the hash value and forces it to be saved.)
+ *
+ * A more rigorous approach would be to put a serial number in the extra
+ * bits, and keep a copy of the serial number in a parallel table.  This is
+ * easier when objects can move, but requires 2x the memory and additional
+ * memory accesses on add/get.  It will catch additional problems, e.g.:
+ * create iref1 for obj, delete iref1, create iref2 for same obj, lookup
+ * iref1.  A pattern based on object bits will miss this.
+ *
+ * For now, we use a serial number.
+ */
+typedef void* IndirectRef;
+
+/* magic failure value; must not pass dvmIsHeapAddress() */
+#define kInvalidIndirectRefObject reinterpret_cast<Object*>(0xdead4321)
+
+#define kClearedJniWeakGlobal reinterpret_cast<Object*>(0xdead1234)
+
+/*
+ * Indirect reference kind, used as the two low bits of IndirectRef.
+ *
+ * For convenience these match up with enum jobjectRefType from jni.h.
+ */
+enum IndirectRefKind {
+    kIndirectKindInvalid    = 0,
+    kIndirectKindLocal      = 1,
+    kIndirectKindGlobal     = 2,
+    kIndirectKindWeakGlobal = 3
+};
+const char* indirectRefKindToString(IndirectRefKind kind);
+
+/*
+ * Determine what kind of indirect reference this is.
+ */
+INLINE IndirectRefKind indirectRefKind(IndirectRef iref)
+{
+    return (IndirectRefKind)((u4) iref & 0x03);
+}
+
+/*
+ * Information we store for each slot in the reference table.
+ */
+struct IndirectRefSlot {
+    Object* obj;        /* object pointer itself, NULL if the slot is unused */
+    u4      serial;     /* slot serial number */
+};
+
+/* use as initial value for "cookie", and when table has only one segment */
+#define IRT_FIRST_SEGMENT   0
+
+/*
+ * Table definition.
+ *
+ * For the global reference table, the expected common operations are
+ * adding a new entry and removing a recently-added entry (usually the
+ * most-recently-added entry).  For JNI local references, the common
+ * operations are adding a new entry and removing an entire table segment.
+ *
+ * If "alloc_entries_" is not equal to "max_entries_", the table may expand
+ * when entries are added, which means the memory may move.  If you want
+ * to keep pointers into "table" rather than offsets, you must use a
+ * fixed-size table.
+ *
+ * If we delete entries from the middle of the list, we will be left with
+ * "holes".  We track the number of holes so that, when adding new elements,
+ * we can quickly decide to do a trivial append or go slot-hunting.
+ *
+ * When the top-most entry is removed, any holes immediately below it are
+ * also removed.  Thus, deletion of an entry may reduce "topIndex" by more
+ * than one.
+ *
+ * To get the desired behavior for JNI locals, we need to know the bottom
+ * and top of the current "segment".  The top is managed internally, and
+ * the bottom is passed in as a function argument (the VM keeps it in a
+ * slot in the interpreted stack frame).  When we call a native method or
+ * push a local frame, the current top index gets pushed on, and serves
+ * as the new bottom.  When we pop a frame off, the value from the stack
+ * becomes the new top index, and the value stored in the previous frame
+ * becomes the new bottom.
+ *
+ * To avoid having to re-scan the table after a pop, we want to push the
+ * number of holes in the table onto the stack.  Because of our 64K-entry
+ * cap, we can combine the two into a single unsigned 32-bit value.
+ * Instead of a "bottom" argument we take a "cookie", which includes the
+ * bottom index and the count of holes below the bottom.
+ *
+ * We need to minimize method call/return overhead.  If we store the
+ * "cookie" externally, on the interpreted call stack, the VM can handle
+ * pushes and pops with a single 4-byte load and store.  (We could also
+ * store it internally in a public structure, but the local JNI refs are
+ * logically tied to interpreted stack frames anyway.)
+ *
+ * Common alternative implementation: make IndirectRef a pointer to the
+ * actual reference slot.  Instead of getting a table and doing a lookup,
+ * the lookup can be done instantly.  Operations like determining the
+ * type and deleting the reference are more expensive because the table
+ * must be hunted for (i.e. you have to do a pointer comparison to see
+ * which table it's in), you can't move the table when expanding it (so
+ * realloc() is out), and tricks like serial number checking to detect
+ * stale references aren't possible (though we may be able to get similar
+ * benefits with other approaches).
+ *
+ * TODO: consider a "lastDeleteIndex" for quick hole-filling when an
+ * add immediately follows a delete; must invalidate after segment pop
+ * (which could increase the cost/complexity of method call/return).
+ * Might be worth only using it for JNI globals.
+ *
+ * TODO: may want completely different add/remove algorithms for global
+ * and local refs to improve performance.  A large circular buffer might
+ * reduce the amortized cost of adding global references.
+ *
+ * TODO: if we can guarantee that the underlying storage doesn't move,
+ * e.g. by using oversized mmap regions to handle expanding tables, we may
+ * be able to avoid having to synchronize lookups.  Might make sense to
+ * add a "synchronized lookup" call that takes the mutex as an argument,
+ * and either locks or doesn't lock based on internal details.
+ */
+union IRTSegmentState {
+    u4          all;
+    struct {
+        u4      topIndex:16;            /* index of first unused entry */
+        u4      numHoles:16;            /* #of holes in entire table */
+    } parts;
+};
+
+class iref_iterator {
+public:
+    explicit iref_iterator(IndirectRefSlot* table, size_t i, size_t capacity) :
+            table_(table), i_(i), capacity_(capacity) {
+        skipNullsAndTombstones();
+    }
+
+    iref_iterator& operator++() {
+        ++i_;
+        skipNullsAndTombstones();
+        return *this;
+    }
+
+    Object** operator*() {
+        return &table_[i_].obj;
+    }
+
+    bool equals(const iref_iterator& rhs) const {
+        return (i_ == rhs.i_ && table_ == rhs.table_);
+    }
+
+private:
+    void skipNullsAndTombstones() {
+        // We skip NULLs and tombstones. Clients don't want to see implementation details.
+        while (i_ < capacity_ && (table_[i_].obj == NULL
+                || table_[i_].obj == kClearedJniWeakGlobal)) {
+            ++i_;
+        }
+    }
+
+    IndirectRefSlot* table_;
+    size_t i_;
+    size_t capacity_;
+};
+
+bool inline operator!=(const iref_iterator& lhs, const iref_iterator& rhs) {
+    return !lhs.equals(rhs);
+}
+
+struct IndirectRefTable {
+public:
+    typedef iref_iterator iterator;
+
+    /* semi-public - read/write by interpreter in native call handler */
+    IRTSegmentState segmentState;
+
+    /*
+     * private:
+     *
+     * TODO: we can't make these private as long as the interpreter
+     * uses offsetof, since private member data makes us non-POD.
+     */
+    /* bottom of the stack */
+    IndirectRefSlot* table_;
+    /* bit mask, ORed into all irefs */
+    IndirectRefKind kind_;
+    /* #of entries we have space for */
+    size_t          alloc_entries_;
+    /* max #of entries allowed */
+    size_t          max_entries_;
+
+    // TODO: want hole-filling stats (#of holes filled, total entries scanned)
+    //       for performance evaluation.
+
+    /*
+     * Add a new entry.  "obj" must be a valid non-NULL object reference
+     * (though it's okay if it's not fully-formed, e.g. the result from
+     * dvmMalloc doesn't have obj->clazz set).
+     *
+     * Returns NULL if the table is full (max entries reached, or alloc
+     * failed during expansion).
+     */
+    IndirectRef add(u4 cookie, Object* obj);
+
+    /*
+     * Given an IndirectRef in the table, return the Object it refers to.
+     *
+     * Returns kInvalidIndirectRefObject if iref is invalid.
+     */
+    Object* get(IndirectRef iref) const;
+
+    /*
+     * Returns true if the table contains a reference to this object.
+     */
+    bool contains(const Object* obj) const;
+
+    /*
+     * Remove an existing entry.
+     *
+     * If the entry is not between the current top index and the bottom index
+     * specified by the cookie, we don't remove anything.  This is the behavior
+     * required by JNI's DeleteLocalRef function.
+     *
+     * Returns "false" if nothing was removed.
+     */
+    bool remove(u4 cookie, IndirectRef iref);
+
+    /*
+     * Initialize an IndirectRefTable.
+     *
+     * If "initialCount" != "maxCount", the table will expand as required.
+     *
+     * "kind" should be Local or Global.  The Global table may also hold
+     * WeakGlobal refs.
+     *
+     * Returns "false" if table allocation fails.
+     */
+    bool init(size_t initialCount, size_t maxCount, IndirectRefKind kind);
+
+    /*
+     * Clear out the contents, freeing allocated storage.
+     *
+     * You must call dvmInitReferenceTable() before you can re-use this table.
+     *
+     * TODO: this should be a destructor.
+     */
+    void destroy();
+
+    /*
+     * Dump the contents of a reference table to the log file.
+     *
+     * The caller should lock any external sync before calling.
+     *
+     * TODO: we should name the table in a constructor and remove
+     * the argument here.
+     */
+    void dump(const char* descr) const;
+
+    /*
+     * Return the #of entries in the entire table.  This includes holes, and
+     * so may be larger than the actual number of "live" entries.
+     */
+    size_t capacity() const {
+        return segmentState.parts.topIndex;
+    }
+
+    iterator begin() {
+        return iterator(table_, 0, capacity());
+    }
+
+    iterator end() {
+        return iterator(table_, capacity(), capacity());
+    }
+
+private:
+    static inline u4 extractIndex(IndirectRef iref) {
+        u4 uref = (u4) iref;
+        return (uref >> 2) & 0xffff;
+    }
+
+    static inline u4 extractSerial(IndirectRef iref) {
+        u4 uref = (u4) iref;
+        return uref >> 20;
+    }
+
+    static inline u4 nextSerial(u4 serial) {
+        return (serial + 1) & 0xfff;
+    }
+
+    static inline IndirectRef toIndirectRef(u4 index, u4 serial, IndirectRefKind kind) {
+        assert(index < 65536);
+        return reinterpret_cast<IndirectRef>((serial << 20) | (index << 2) | kind);
+    }
+};
+
+#endif  // DALVIK_INDIRECTREFTABLE_H_
diff --git a/vm/Init.cpp b/vm/Init.cpp
new file mode 100644
index 0000000..11d884e
--- /dev/null
+++ b/vm/Init.cpp
@@ -0,0 +1,2095 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.
+ */
+#define __STDC_LIMIT_MACROS
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <linux/fs.h>
+#include <cutils/fs.h>
+#include <unistd.h>
+
+#include "Dalvik.h"
+#include "test/Test.h"
+#include "mterp/Mterp.h"
+#include "Hash.h"
+
+#if defined(WITH_JIT)
+#include "compiler/codegen/Optimizer.h"
+#endif
+
+#define kMinHeapStartSize   (1*1024*1024)
+#define kMinHeapSize        (2*1024*1024)
+#define kMaxHeapSize        (1*1024*1024*1024)
+
+/*
+ * Register VM-agnostic native methods for system classes.
+ */
+extern int jniRegisterSystemMethods(JNIEnv* env);
+
+/* fwd */
+static bool registerSystemNatives(JNIEnv* pEnv);
+static bool initJdwp();
+static bool initZygote();
+
+
+/* global state */
+struct DvmGlobals gDvm;
+struct DvmJniGlobals gDvmJni;
+
+/* JIT-specific global state */
+#if defined(WITH_JIT)
+struct DvmJitGlobals gDvmJit;
+
+#if defined(WITH_JIT_TUNING)
+/*
+ * Track the number of hits in the inline cache for predicted chaining.
+ * Use an ugly global variable here since it is accessed in assembly code.
+ */
+int gDvmICHitCount;
+#endif
+
+#endif
+
+/*
+ * Show usage.
+ *
+ * We follow the tradition of unhyphenated compound words.
+ */
+static void usage(const char* progName)
+{
+    dvmFprintf(stderr, "%s: [options] class [argument ...]\n", progName);
+    dvmFprintf(stderr, "%s: [options] -jar file.jar [argument ...]\n",progName);
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "The following standard options are recognized:\n");
+    dvmFprintf(stderr, "  -classpath classpath\n");
+    dvmFprintf(stderr, "  -Dproperty=value\n");
+    dvmFprintf(stderr, "  -verbose:tag  ('gc', 'jni', or 'class')\n");
+    dvmFprintf(stderr, "  -ea[:<package name>... |:<class name>]\n");
+    dvmFprintf(stderr, "  -da[:<package name>... |:<class name>]\n");
+    dvmFprintf(stderr, "   (-enableassertions, -disableassertions)\n");
+    dvmFprintf(stderr, "  -esa\n");
+    dvmFprintf(stderr, "  -dsa\n");
+    dvmFprintf(stderr,
+                "   (-enablesystemassertions, -disablesystemassertions)\n");
+    dvmFprintf(stderr, "  -showversion\n");
+    dvmFprintf(stderr, "  -help\n");
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "The following extended options are recognized:\n");
+    dvmFprintf(stderr, "  -Xrunjdwp:<options>\n");
+    dvmFprintf(stderr, "  -Xbootclasspath:bootclasspath\n");
+    dvmFprintf(stderr, "  -Xcheck:tag  (e.g. 'jni')\n");
+    dvmFprintf(stderr, "  -XmsN  (min heap, must be multiple of 1K, >= 1MB)\n");
+    dvmFprintf(stderr, "  -XmxN  (max heap, must be multiple of 1K, >= 2MB)\n");
+    dvmFprintf(stderr, "  -XssN  (stack size, >= %dKB, <= %dKB)\n",
+        kMinStackSize / 1024, kMaxStackSize / 1024);
+    dvmFprintf(stderr, "  -Xverify:{none,remote,all}\n");
+    dvmFprintf(stderr, "  -Xrs\n");
+#if defined(WITH_JIT)
+    dvmFprintf(stderr,
+                "  -Xint  (extended to accept ':portable', ':fast' and ':jit')\n");
+#else
+    dvmFprintf(stderr,
+                "  -Xint  (extended to accept ':portable' and ':fast')\n");
+#endif
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "These are unique to Dalvik:\n");
+    dvmFprintf(stderr, "  -Xzygote\n");
+    dvmFprintf(stderr, "  -Xdexopt:{none,verified,all,full}\n");
+    dvmFprintf(stderr, "  -Xnoquithandler\n");
+    dvmFprintf(stderr,
+                "  -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, "  -Xstacktracefile:<filename>\n");
+    dvmFprintf(stderr, "  -Xgc:[no]precise\n");
+    dvmFprintf(stderr, "  -Xgc:[no]preverify\n");
+    dvmFprintf(stderr, "  -Xgc:[no]postverify\n");
+    dvmFprintf(stderr, "  -Xgc:[no]concurrent\n");
+    dvmFprintf(stderr, "  -Xgc:[no]verifycardtable\n");
+    dvmFprintf(stderr, "  -XX:+DisableExplicitGC\n");
+    dvmFprintf(stderr, "  -X[no]genregmap\n");
+    dvmFprintf(stderr, "  -Xverifyopt:[no]checkmon\n");
+    dvmFprintf(stderr, "  -Xcheckdexsum\n");
+#if defined(WITH_JIT)
+    dvmFprintf(stderr, "  -Xincludeselectedop\n");
+    dvmFprintf(stderr, "  -Xjitop:hexopvalue[-endvalue]"
+                       "[,hexopvalue[-endvalue]]*\n");
+    dvmFprintf(stderr, "  -Xincludeselectedmethod\n");
+    dvmFprintf(stderr, "  -Xjitthreshold:decimalvalue\n");
+    dvmFprintf(stderr, "  -Xjitblocking\n");
+    dvmFprintf(stderr, "  -Xjitmethod:signature[,signature]* "
+                       "(eg Ljava/lang/String\\;replace)\n");
+    dvmFprintf(stderr, "  -Xjitclass:classname[,classname]*\n");
+    dvmFprintf(stderr, "  -Xjitoffset:offset[,offset]\n");
+    dvmFprintf(stderr, "  -Xjitconfig:filename\n");
+    dvmFprintf(stderr, "  -Xjitcheckcg\n");
+    dvmFprintf(stderr, "  -Xjitverbose\n");
+    dvmFprintf(stderr, "  -Xjitprofile\n");
+    dvmFprintf(stderr, "  -Xjitdisableopt\n");
+    dvmFprintf(stderr, "  -Xjitsuspendpoll\n");
+#endif
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "Configured with:"
+        " debugger"
+        " profiler"
+        " hprof"
+#ifdef WITH_TRACKREF_CHECKS
+        " trackref_checks"
+#endif
+#ifdef WITH_INSTR_CHECKS
+        " instr_checks"
+#endif
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+        " extra_object_validation"
+#endif
+#ifdef WITH_EXTRA_GC_CHECKS
+        " extra_gc_checks"
+#endif
+#if !defined(NDEBUG) && defined(WITH_DALVIK_ASSERT)
+        " dalvik_assert"
+#endif
+#ifdef WITH_JNI_STACK_CHECK
+        " jni_stack_check"
+#endif
+#ifdef EASY_GDB
+        " easy_gdb"
+#endif
+#ifdef CHECK_MUTEX
+        " check_mutex"
+#endif
+#if defined(WITH_JIT)
+        " jit(" ARCH_VARIANT ")"
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+        " self_verification"
+#endif
+#if ANDROID_SMP != 0
+        " smp"
+#endif
+    );
+#ifdef DVM_SHOW_EXCEPTION
+    dvmFprintf(stderr, " show_exception=%d", DVM_SHOW_EXCEPTION);
+#endif
+    dvmFprintf(stderr, "\n\n");
+}
+
+/*
+ * Show helpful information on JDWP options.
+ */
+static void showJdwpHelp()
+{
+    dvmFprintf(stderr,
+        "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n");
+    dvmFprintf(stderr,
+        "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
+}
+
+/*
+ * Show version and copyright info.
+ */
+static void showVersion()
+{
+    dvmFprintf(stdout, "DalvikVM version %d.%d.%d\n",
+        DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+    dvmFprintf(stdout,
+        "Copyright (C) 2007 The Android Open Source Project\n\n"
+        "This software is built from source code licensed under the "
+        "Apache License,\n"
+        "Version 2.0 (the \"License\"). You may obtain a copy of the "
+        "License at\n\n"
+        "     http://www.apache.org/licenses/LICENSE-2.0\n\n"
+        "See the associated NOTICE file for this software for further "
+        "details.\n");
+}
+
+/*
+ * Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+ * memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
+ * [gG] gigabytes.
+ *
+ * "s" should point just past the "-Xm?" part of the string.
+ * "min" specifies the lowest acceptable value described by "s".
+ * "div" specifies a divisor, e.g. 1024 if the value must be a multiple
+ * of 1024.
+ *
+ * The spec says the -Xmx and -Xms options must be multiples of 1024.  It
+ * doesn't say anything about -Xss.
+ *
+ * Returns 0 (a useless size) if "s" is malformed or specifies a low or
+ * non-evenly-divisible value.
+ */
+static size_t parseMemOption(const char* s, size_t div)
+{
+    /* strtoul accepts a leading [+-], which we don't want,
+     * so make sure our string starts with a decimal digit.
+     */
+    if (isdigit(*s)) {
+        const char* s2;
+        size_t val;
+
+        val = strtoul(s, (char* *)&s2, 10);
+        if (s2 != s) {
+            /* s2 should be pointing just after the number.
+             * If this is the end of the string, the user
+             * has specified a number of bytes.  Otherwise,
+             * there should be exactly one more character
+             * that specifies a multiplier.
+             */
+            if (*s2 != '\0') {
+                char c;
+
+                /* The remainder of the string is either a single multiplier
+                 * character, or nothing to indicate that the value is in
+                 * bytes.
+                 */
+                c = *s2++;
+                if (*s2 == '\0') {
+                    size_t mul;
+
+                    if (c == '\0') {
+                        mul = 1;
+                    } else if (c == 'k' || c == 'K') {
+                        mul = 1024;
+                    } else if (c == 'm' || c == 'M') {
+                        mul = 1024 * 1024;
+                    } else if (c == 'g' || c == 'G') {
+                        mul = 1024 * 1024 * 1024;
+                    } else {
+                        /* Unknown multiplier character.
+                         */
+                        return 0;
+                    }
+
+                    if (val <= SIZE_MAX / mul) {
+                        val *= mul;
+                    } else {
+                        /* Clamp to a multiple of 1024.
+                         */
+                        val = SIZE_MAX & ~(1024-1);
+                    }
+                } else {
+                    /* There's more than one character after the
+                     * numeric part.
+                     */
+                    return 0;
+                }
+            }
+
+            /* The man page says that a -Xm value must be
+             * a multiple of 1024.
+             */
+            if (val % div == 0) {
+                return val;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Handle one of the JDWP name/value pairs.
+ *
+ * JDWP options are:
+ *  help: if specified, show help message and bail
+ *  transport: may be dt_socket or dt_shmem
+ *  address: for dt_socket, "host:port", or just "port" when listening
+ *  server: if "y", wait for debugger to attach; if "n", attach to debugger
+ *  timeout: how long to wait for debugger to connect / listen
+ *
+ * Useful with server=n (these aren't supported yet):
+ *  onthrow=<exception-name>: connect to debugger when exception thrown
+ *  onuncaught=y|n: connect to debugger when uncaught exception thrown
+ *  launch=<command-line>: launch the debugger itself
+ *
+ * The "transport" option is required, as is "address" if server=n.
+ */
+static bool handleJdwpOption(const char* name, const char* value)
+{
+    if (strcmp(name, "transport") == 0) {
+        if (strcmp(value, "dt_socket") == 0) {
+            gDvm.jdwpTransport = kJdwpTransportSocket;
+        } else if (strcmp(value, "dt_android_adb") == 0) {
+            gDvm.jdwpTransport = kJdwpTransportAndroidAdb;
+        } else {
+            ALOGE("JDWP transport '%s' not supported", value);
+            return false;
+        }
+    } else if (strcmp(name, "server") == 0) {
+        if (*value == 'n')
+            gDvm.jdwpServer = false;
+        else if (*value == 'y')
+            gDvm.jdwpServer = true;
+        else {
+            ALOGE("JDWP option 'server' must be 'y' or 'n'");
+            return false;
+        }
+    } else if (strcmp(name, "suspend") == 0) {
+        if (*value == 'n')
+            gDvm.jdwpSuspend = false;
+        else if (*value == 'y')
+            gDvm.jdwpSuspend = true;
+        else {
+            ALOGE("JDWP option 'suspend' must be 'y' or 'n'");
+            return false;
+        }
+    } else if (strcmp(name, "address") == 0) {
+        /* this is either <port> or <host>:<port> */
+        const char* colon = strchr(value, ':');
+        char* end;
+        long port;
+
+        if (colon != NULL) {
+            free(gDvm.jdwpHost);
+            gDvm.jdwpHost = (char*) malloc(colon - value +1);
+            strncpy(gDvm.jdwpHost, value, colon - value +1);
+            gDvm.jdwpHost[colon-value] = '\0';
+            value = colon + 1;
+        }
+        if (*value == '\0') {
+            ALOGE("JDWP address missing port");
+            return false;
+        }
+        port = strtol(value, &end, 10);
+        if (*end != '\0') {
+            ALOGE("JDWP address has junk in port field '%s'", value);
+            return false;
+        }
+        gDvm.jdwpPort = port;
+    } else if (strcmp(name, "launch") == 0 ||
+               strcmp(name, "onthrow") == 0 ||
+               strcmp(name, "oncaught") == 0 ||
+               strcmp(name, "timeout") == 0)
+    {
+        /* valid but unsupported */
+        ALOGI("Ignoring JDWP option '%s'='%s'", name, value);
+    } else {
+        ALOGI("Ignoring unrecognized JDWP option '%s'='%s'", name, value);
+    }
+
+    return true;
+}
+
+/*
+ * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
+ * "transport=dt_socket,address=8000,server=y,suspend=n"
+ */
+static bool parseJdwpOptions(const char* str)
+{
+    char* mangle = strdup(str);
+    char* name = mangle;
+    bool result = false;
+
+    /*
+     * Process all of the name=value pairs.
+     */
+    while (true) {
+        char* value;
+        char* comma;
+
+        value = strchr(name, '=');
+        if (value == NULL) {
+            ALOGE("JDWP opts: garbage at '%s'", name);
+            goto bail;
+        }
+
+        comma = strchr(name, ',');      // use name, not value, for safety
+        if (comma != NULL) {
+            if (comma < value) {
+                ALOGE("JDWP opts: found comma before '=' in '%s'", mangle);
+                goto bail;
+            }
+            *comma = '\0';
+        }
+
+        *value++ = '\0';        // stomp the '='
+
+        if (!handleJdwpOption(name, value))
+            goto bail;
+
+        if (comma == NULL) {
+            /* out of options */
+            break;
+        }
+        name = comma+1;
+    }
+
+    /*
+     * Make sure the combination of arguments makes sense.
+     */
+    if (gDvm.jdwpTransport == kJdwpTransportUnknown) {
+        ALOGE("JDWP opts: must specify transport");
+        goto bail;
+    }
+    if (!gDvm.jdwpServer && (gDvm.jdwpHost == NULL || gDvm.jdwpPort == 0)) {
+        ALOGE("JDWP opts: when server=n, must specify host and port");
+        goto bail;
+    }
+    // transport mandatory
+    // outbound server address
+
+    gDvm.jdwpConfigured = true;
+    result = true;
+
+bail:
+    free(mangle);
+    return result;
+}
+
+/*
+ * Handle one of the four kinds of assertion arguments.
+ *
+ * "pkgOrClass" is the last part of an enable/disable line.  For a package
+ * the arg looks like "-ea:com.google.fubar...", for a class it looks
+ * like "-ea:com.google.fubar.Wahoo".  The string we get starts at the ':'.
+ *
+ * For system assertions (-esa/-dsa), "pkgOrClass" is NULL.
+ *
+ * Multiple instances of these arguments can be specified, e.g. you can
+ * enable assertions for a package and then disable them for one class in
+ * the package.
+ */
+static bool enableAssertions(const char* pkgOrClass, bool enable)
+{
+    AssertionControl* pCtrl = &gDvm.assertionCtrl[gDvm.assertionCtrlCount++];
+    pCtrl->enable = enable;
+
+    if (pkgOrClass == NULL) {
+        /* enable or disable for all system classes */
+        pCtrl->isPackage = false;
+        pCtrl->pkgOrClass = NULL;
+        pCtrl->pkgOrClassLen = 0;
+    } else {
+        if (*pkgOrClass == '\0') {
+            /* global enable/disable for all but system */
+            pCtrl->isPackage = false;
+            pCtrl->pkgOrClass = strdup("");
+            pCtrl->pkgOrClassLen = 0;
+        } else {
+            pCtrl->pkgOrClass = dvmDotToSlash(pkgOrClass+1);    // skip ':'
+            if (pCtrl->pkgOrClass == NULL) {
+                /* can happen if class name includes an illegal '/' */
+                ALOGW("Unable to process assertion arg '%s'", pkgOrClass);
+                return false;
+            }
+
+            int len = strlen(pCtrl->pkgOrClass);
+            if (len >= 3 && strcmp(pCtrl->pkgOrClass + len-3, "///") == 0) {
+                /* mark as package, truncate two of the three slashes */
+                pCtrl->isPackage = true;
+                *(pCtrl->pkgOrClass + len-2) = '\0';
+                pCtrl->pkgOrClassLen = len - 2;
+            } else {
+                /* just a class */
+                pCtrl->isPackage = false;
+                pCtrl->pkgOrClassLen = len;
+            }
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Turn assertions on when requested to do so by the Zygote.
+ *
+ * This is a bit sketchy.  We can't (easily) go back and fiddle with all
+ * of the classes that have already been initialized, so this only
+ * affects classes that have yet to be loaded.  If some or all assertions
+ * have been enabled through some other means, we don't want to mess with
+ * it here, so we do nothing.  Finally, we assume that there's room in
+ * "assertionCtrl" to hold at least one entry; this is guaranteed by the
+ * allocator.
+ *
+ * This must only be called from the main thread during zygote init.
+ */
+void dvmLateEnableAssertions()
+{
+    if (gDvm.assertionCtrl == NULL) {
+        ALOGD("Not late-enabling assertions: no assertionCtrl array");
+        return;
+    } else if (gDvm.assertionCtrlCount != 0) {
+        ALOGD("Not late-enabling assertions: some asserts already configured");
+        return;
+    }
+    ALOGD("Late-enabling assertions");
+
+    /* global enable for all but system */
+    AssertionControl* pCtrl = gDvm.assertionCtrl;
+    pCtrl->pkgOrClass = strdup("");
+    pCtrl->pkgOrClassLen = 0;
+    pCtrl->isPackage = false;
+    pCtrl->enable = true;
+    gDvm.assertionCtrlCount = 1;
+}
+
+
+/*
+ * Release memory associated with the AssertionCtrl array.
+ */
+static void freeAssertionCtrl()
+{
+    int i;
+
+    for (i = 0; i < gDvm.assertionCtrlCount; i++)
+        free(gDvm.assertionCtrl[i].pkgOrClass);
+    free(gDvm.assertionCtrl);
+}
+
+#if defined(WITH_JIT)
+/* Parse -Xjitop to selectively turn on/off certain opcodes for JIT */
+static void processXjitop(const char* opt)
+{
+    if (opt[7] == ':') {
+        const char* startPtr = &opt[8];
+        char* endPtr = NULL;
+
+        do {
+            long startValue, endValue;
+
+            startValue = strtol(startPtr, &endPtr, 16);
+            if (startPtr != endPtr) {
+                /* Just in case value is out of range */
+                startValue %= kNumPackedOpcodes;
+
+                if (*endPtr == '-') {
+                    endValue = strtol(endPtr+1, &endPtr, 16);
+                    endValue %= kNumPackedOpcodes;
+                } else {
+                    endValue = startValue;
+                }
+
+                for (; startValue <= endValue; startValue++) {
+                    ALOGW("Dalvik opcode %x is selected for debugging",
+                         (unsigned int) startValue);
+                    /* Mark the corresponding bit to 1 */
+                    gDvmJit.opList[startValue >> 3] |= 1 << (startValue & 0x7);
+                }
+
+                if (*endPtr == 0) {
+                    break;
+                }
+
+                startPtr = endPtr + 1;
+
+                continue;
+            } else {
+                if (*endPtr != 0) {
+                    dvmFprintf(stderr,
+                        "Warning: Unrecognized opcode value substring "
+                        "%s\n", endPtr);
+                }
+                break;
+            }
+        } while (1);
+    } else {
+        int i;
+        for (i = 0; i < (kNumPackedOpcodes+7)/8; i++) {
+            gDvmJit.opList[i] = 0xff;
+        }
+        dvmFprintf(stderr, "Warning: select all opcodes\n");
+    }
+}
+
+/* Parse -Xjitoffset to selectively turn on/off traces with certain offsets for JIT */
+static void processXjitoffset(const char* opt) {
+    gDvmJit.num_entries_pcTable = 0;
+    char* buf = strdup(opt);
+    char* start, *end;
+    start = buf;
+    int idx = 0;
+    do {
+        end = strchr(start, ',');
+        if (end) {
+            *end = 0;
+        }
+
+        dvmFprintf(stderr, "processXjitoffset start = %s\n", start);
+        char* tmp = strdup(start);
+        gDvmJit.pcTable[idx++] = atoi(tmp);
+        free(tmp);
+        if (idx >= COMPILER_PC_OFFSET_SIZE) {
+            dvmFprintf(stderr, "processXjitoffset: ignore entries beyond %d\n", COMPILER_PC_OFFSET_SIZE);
+            break;
+        }
+        if (end) {
+            start = end + 1;
+        } else {
+            break;
+        }
+    } while (1);
+    gDvmJit.num_entries_pcTable = idx;
+    free(buf);
+}
+
+/* Parse -Xjitmethod to selectively turn on/off certain methods for JIT */
+static void processXjitmethod(const char* opt, bool isMethod) {
+    char* buf = strdup(opt);
+
+    if (isMethod && gDvmJit.methodTable == NULL) {
+        gDvmJit.methodTable = dvmHashTableCreate(8, NULL);
+    }
+    if (!isMethod && gDvmJit.classTable == NULL) {
+        gDvmJit.classTable = dvmHashTableCreate(8, NULL);
+    }
+
+    char* start = buf;
+    char* end;
+    /*
+     * Break comma-separated method signatures and enter them into the hash
+     * table individually.
+     */
+    do {
+        int hashValue;
+
+        end = strchr(start, ',');
+        if (end) {
+            *end = 0;
+        }
+
+        hashValue = dvmComputeUtf8Hash(start);
+        dvmHashTableLookup(isMethod ? gDvmJit.methodTable : gDvmJit.classTable,
+                           hashValue, strdup(start), (HashCompareFunc) strcmp, true);
+
+        if (end) {
+            start = end + 1;
+        } else {
+            break;
+        }
+    } while (1);
+    free(buf);
+}
+
+/* The format of jit_config.list:
+   EXCLUDE or INCLUDE
+   CLASS
+   prefix1 ...
+   METHOD
+   prefix 1 ...
+   OFFSET
+   index ... //each pair is a range, if pcOff falls into a range, JIT
+*/
+static int processXjitconfig(const char* opt) {
+   FILE* fp = fopen(opt, "r");
+   if (fp == NULL) {
+       return -1;
+   }
+
+   char fLine[500];
+   bool startClass = false, startMethod = false, startOffset = false;
+   gDvmJit.num_entries_pcTable = 0;
+   int idx = 0;
+
+   while (fgets(fLine, 500, fp) != NULL) {
+       char* curLine = strtok(fLine, " \t\r\n");
+       /* handles keyword CLASS, METHOD, INCLUDE, EXCLUDE */
+       if (!strncmp(curLine, "CLASS", 5)) {
+           startClass = true;
+           startMethod = false;
+           startOffset = false;
+           continue;
+       }
+       if (!strncmp(curLine, "METHOD", 6)) {
+           startMethod = true;
+           startClass = false;
+           startOffset = false;
+           continue;
+       }
+       if (!strncmp(curLine, "OFFSET", 6)) {
+           startOffset = true;
+           startMethod = false;
+           startClass = false;
+           continue;
+       }
+       if (!strncmp(curLine, "EXCLUDE", 7)) {
+          gDvmJit.includeSelectedMethod = false;
+          continue;
+       }
+       if (!strncmp(curLine, "INCLUDE", 7)) {
+          gDvmJit.includeSelectedMethod = true;
+          continue;
+       }
+       if (!startMethod && !startClass && !startOffset) {
+         continue;
+       }
+
+        int hashValue = dvmComputeUtf8Hash(curLine);
+        if (startMethod) {
+            if (gDvmJit.methodTable == NULL) {
+                gDvmJit.methodTable = dvmHashTableCreate(8, NULL);
+            }
+            dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               strdup(curLine),
+                               (HashCompareFunc) strcmp, true);
+        } else if (startClass) {
+            if (gDvmJit.classTable == NULL) {
+                gDvmJit.classTable = dvmHashTableCreate(8, NULL);
+            }
+            dvmHashTableLookup(gDvmJit.classTable, hashValue,
+                               strdup(curLine),
+                               (HashCompareFunc) strcmp, true);
+        } else if (startOffset) {
+           int tmpInt = atoi(curLine);
+           gDvmJit.pcTable[idx++] = tmpInt;
+           if (idx >= COMPILER_PC_OFFSET_SIZE) {
+               printf("processXjitoffset: ignore entries beyond %d\n", COMPILER_PC_OFFSET_SIZE);
+               break;
+           }
+        }
+   }
+   gDvmJit.num_entries_pcTable = idx;
+   fclose(fp);
+   return 0;
+}
+#endif
+
+/*
+ * Process an argument vector full of options.  Unlike standard C programs,
+ * argv[0] does not contain the name of the program.
+ *
+ * If "ignoreUnrecognized" is set, we ignore options starting with "-X" or "_"
+ * that we don't recognize.  Otherwise, we return with an error as soon as
+ * we see anything we can't identify.
+ *
+ * Returns 0 on success, -1 on failure, and 1 for the special case of
+ * "-version" where we want to stop without showing an error message.
+ */
+static int processOptions(int argc, const char* const argv[],
+    bool ignoreUnrecognized)
+{
+    int i;
+
+    ALOGV("VM options (%d):", argc);
+    for (i = 0; i < argc; i++)
+        ALOGV("  %d: '%s'", i, argv[i]);
+
+    /*
+     * Over-allocate AssertionControl array for convenience.  If allocated,
+     * the array must be able to hold at least one entry, so that the
+     * zygote-time activation can do its business.
+     */
+    assert(gDvm.assertionCtrl == NULL);
+    if (argc > 0) {
+        gDvm.assertionCtrl =
+            (AssertionControl*) malloc(sizeof(AssertionControl) * argc);
+        if (gDvm.assertionCtrl == NULL)
+            return -1;
+        assert(gDvm.assertionCtrlCount == 0);
+    }
+
+    for (i = 0; i < argc; i++) {
+        if (strcmp(argv[i], "-help") == 0) {
+            /* show usage and stop */
+            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) {
+            /* Properties are handled in managed code. We just check syntax. */
+            if (strchr(argv[i], '=') == NULL) {
+                dvmFprintf(stderr, "Bad system property setting: \"%s\"\n",
+                    argv[i]);
+                return -1;
+            }
+            gDvm.properties->push_back(argv[i] + 2);
+
+        } else if (strcmp(argv[i], "-jar") == 0) {
+            // TODO: handle this; name of jar should be in argv[i+1]
+            dvmFprintf(stderr, "-jar not yet handled\n");
+            assert(false);
+
+        } else if (strncmp(argv[i], "-Xms", 4) == 0) {
+            size_t val = parseMemOption(argv[i]+4, 1024);
+            if (val != 0) {
+                if (val >= kMinHeapStartSize && val <= kMaxHeapSize) {
+                    gDvm.heapStartingSize = val;
+                } else {
+                    dvmFprintf(stderr,
+                        "Invalid -Xms '%s', range is %dKB to %dKB\n",
+                        argv[i], kMinHeapStartSize/1024, kMaxHeapSize/1024);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -Xms option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xmx", 4) == 0) {
+            size_t val = parseMemOption(argv[i]+4, 1024);
+            if (val != 0) {
+                if (val >= kMinHeapSize && val <= kMaxHeapSize) {
+                    gDvm.heapMaximumSize = val;
+                } else {
+                    dvmFprintf(stderr,
+                        "Invalid -Xmx '%s', range is %dKB to %dKB\n",
+                        argv[i], kMinHeapSize/1024, kMaxHeapSize/1024);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -Xmx option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-XX:HeapGrowthLimit=", 20) == 0) {
+            size_t val = parseMemOption(argv[i] + 20, 1024);
+            if (val != 0) {
+                gDvm.heapGrowthLimit = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapGrowthLimit option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-XX:HeapMinFree=", 16) == 0) {
+            size_t val = parseMemOption(argv[i] + 16, 1024);
+            if (val != 0) {
+                gDvm.heapMinFree = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapMinFree option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-XX:HeapMaxFree=", 16) == 0) {
+            size_t val = parseMemOption(argv[i] + 16, 1024);
+            if (val != 0) {
+                gDvm.heapMaxFree = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapMaxFree option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-XX:HeapTargetUtilization=", 26) == 0) {
+            const char* start = argv[i] + 26;
+            const char* end = start;
+            double val = strtod(start, const_cast<char**>(&end));
+            // Ensure that we have a value, there was no cruft after it and it
+            // satisfies a sensible range.
+            bool sane_val = (start != end) && (end[0] == '\0') &&
+                (val >= 0.1) && (val <= 0.9);
+            if (sane_val) {
+                gDvm.heapTargetUtilization = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapTargetUtilization option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xss", 4) == 0) {
+            size_t val = parseMemOption(argv[i]+4, 1);
+            if (val != 0) {
+                if (val >= kMinStackSize && val <= kMaxStackSize) {
+                    gDvm.stackSize = val;
+                    if (val > gDvm.mainThreadStackSize) {
+                        gDvm.mainThreadStackSize = val;
+                    }
+                } else {
+                    dvmFprintf(stderr, "Invalid -Xss '%s', range is %d to %d\n",
+                        argv[i], kMinStackSize, kMaxStackSize);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -Xss option '%s'\n", argv[i]);
+                return -1;
+            }
+
+        } else if (strncmp(argv[i], "-XX:mainThreadStackSize=", strlen("-XX:mainThreadStackSize=")) == 0) {
+            size_t val = parseMemOption(argv[i] + strlen("-XX:mainThreadStackSize="), 1);
+            if (val != 0) {
+                if (val >= kMinStackSize && val <= kMaxStackSize) {
+                    gDvm.mainThreadStackSize = val;
+                } else {
+                    dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize '%s', range is %d to %d\n",
+                               argv[i], kMinStackSize, kMaxStackSize);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize option '%s'\n", argv[i]);
+                return -1;
+            }
+
+        } else if (strncmp(argv[i], "-XX:+DisableExplicitGC", 22) == 0) {
+            gDvm.disableExplicitGc = true;
+        } else if (strcmp(argv[i], "-verbose") == 0 ||
+            strcmp(argv[i], "-verbose:class") == 0)
+        {
+            // JNI spec says "-verbose:gc,class" is valid, but cmd line
+            // doesn't work that way; may want to support.
+            gDvm.verboseClass = true;
+        } else if (strcmp(argv[i], "-verbose:jni") == 0) {
+            gDvm.verboseJni = true;
+        } else if (strcmp(argv[i], "-verbose:gc") == 0) {
+            gDvm.verboseGc = true;
+        } else if (strcmp(argv[i], "-verbose:shutdown") == 0) {
+            gDvm.verboseShutdown = true;
+
+        } else if (strncmp(argv[i], "-enableassertions", 17) == 0) {
+            enableAssertions(argv[i] + 17, true);
+        } else if (strncmp(argv[i], "-ea", 3) == 0) {
+            enableAssertions(argv[i] + 3, true);
+        } else if (strncmp(argv[i], "-disableassertions", 18) == 0) {
+            enableAssertions(argv[i] + 18, false);
+        } else if (strncmp(argv[i], "-da", 3) == 0) {
+            enableAssertions(argv[i] + 3, false);
+        } else if (strcmp(argv[i], "-enablesystemassertions") == 0 ||
+                   strcmp(argv[i], "-esa") == 0)
+        {
+            enableAssertions(NULL, true);
+        } else if (strcmp(argv[i], "-disablesystemassertions") == 0 ||
+                   strcmp(argv[i], "-dsa") == 0)
+        {
+            enableAssertions(NULL, false);
+
+        } else if (strncmp(argv[i], "-Xcheck:jni", 11) == 0) {
+            /* nothing to do now -- was handled during JNI init */
+
+        } else if (strcmp(argv[i], "-Xdebug") == 0) {
+            /* accept but ignore */
+
+        } else if (strncmp(argv[i], "-Xrunjdwp:", 10) == 0 ||
+            strncmp(argv[i], "-agentlib:jdwp=", 15) == 0)
+        {
+            const char* tail;
+
+            if (argv[i][1] == 'X')
+                tail = argv[i] + 10;
+            else
+                tail = argv[i] + 15;
+
+            if (strncmp(tail, "help", 4) == 0 || !parseJdwpOptions(tail)) {
+                showJdwpHelp();
+                return 1;
+            }
+        } else if (strcmp(argv[i], "-Xrs") == 0) {
+            gDvm.reduceSignals = true;
+        } else if (strcmp(argv[i], "-Xnoquithandler") == 0) {
+            /* disables SIGQUIT handler thread while still blocking SIGQUIT */
+            /* (useful if we don't want thread but system still signals us) */
+            gDvm.noQuitHandler = true;
+        } else if (strcmp(argv[i], "-Xzygote") == 0) {
+            gDvm.zygote = true;
+#if defined(WITH_JIT)
+            gDvmJit.runningInAndroidFramework = true;
+#endif
+        } else if (strncmp(argv[i], "-Xdexopt:", 9) == 0) {
+            if (strcmp(argv[i] + 9, "none") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_NONE;
+            else if (strcmp(argv[i] + 9, "verified") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
+            else if (strcmp(argv[i] + 9, "all") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_ALL;
+            else if (strcmp(argv[i] + 9, "full") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_FULL;
+            else {
+                dvmFprintf(stderr, "Unrecognized dexopt option '%s'\n",argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xverify:", 9) == 0) {
+            if (strcmp(argv[i] + 9, "none") == 0)
+                gDvm.classVerifyMode = VERIFY_MODE_NONE;
+            else if (strcmp(argv[i] + 9, "remote") == 0)
+                gDvm.classVerifyMode = VERIFY_MODE_REMOTE;
+            else if (strcmp(argv[i] + 9, "all") == 0)
+                gDvm.classVerifyMode = VERIFY_MODE_ALL;
+            else {
+                dvmFprintf(stderr, "Unrecognized verify option '%s'\n",argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xjnigreflimit:", 15) == 0) {
+            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:", 12) == 0) {
+            processXjitmethod(argv[i] + strlen("-Xjitmethod:"), true);
+        } else if (strncmp(argv[i], "-Xjitclass:", 11) == 0) {
+            processXjitmethod(argv[i] + strlen("-Xjitclass:"), false);
+        } else if (strncmp(argv[i], "-Xjitoffset:", 12) == 0) {
+            processXjitoffset(argv[i] + strlen("-Xjitoffset:"));
+        } else if (strncmp(argv[i], "-Xjitconfig:", 12) == 0) {
+            processXjitconfig(argv[i] + strlen("-Xjitconfig:"));
+        } else if (strncmp(argv[i], "-Xjitblocking", 13) == 0) {
+          gDvmJit.blockingMode = true;
+        } else if (strncmp(argv[i], "-Xjitthreshold:", 15) == 0) {
+          gDvmJit.threshold = atoi(argv[i] + 15);
+        } else if (strncmp(argv[i], "-Xincludeselectedop", 19) == 0) {
+          gDvmJit.includeSelectedOp = true;
+        } else if (strncmp(argv[i], "-Xincludeselectedmethod", 23) == 0) {
+          gDvmJit.includeSelectedMethod = true;
+        } else if (strncmp(argv[i], "-Xjitcheckcg", 12) == 0) {
+          gDvmJit.checkCallGraph = true;
+          /* Need to enable blocking mode due to stack crawling */
+          gDvmJit.blockingMode = true;
+        } else if (strncmp(argv[i], "-Xjitdumpbin", 12) == 0) {
+          gDvmJit.printBinary = true;
+        } else if (strncmp(argv[i], "-Xjitverbose", 12) == 0) {
+          gDvmJit.printMe = true;
+        } else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) {
+          gDvmJit.profileMode = kTraceProfilingContinuous;
+        } else if (strncmp(argv[i], "-Xjitdisableopt", 15) == 0) {
+          /* Disable selected optimizations */
+          if (argv[i][15] == ':') {
+              sscanf(argv[i] + 16, "%x", &gDvmJit.disableOpt);
+          /* Disable all optimizations */
+          } else {
+              gDvmJit.disableOpt = -1;
+          }
+        } else if (strncmp(argv[i], "-Xjitsuspendpoll", 16) == 0) {
+          gDvmJit.genSuspendPoll = true;
+#endif
+
+        } else if (strncmp(argv[i], "-Xstacktracefile:", 17) == 0) {
+            gDvm.stackTraceFile = strdup(argv[i]+17);
+
+        } else if (strcmp(argv[i], "-Xgenregmap") == 0) {
+            gDvm.generateRegisterMaps = true;
+        } else if (strcmp(argv[i], "-Xnogenregmap") == 0) {
+            gDvm.generateRegisterMaps = false;
+
+        } else if (strcmp(argv[i], "Xverifyopt:checkmon") == 0) {
+            gDvm.monitorVerification = true;
+        } else if (strcmp(argv[i], "Xverifyopt:nocheckmon") == 0) {
+            gDvm.monitorVerification = false;
+
+        } else if (strncmp(argv[i], "-Xgc:", 5) == 0) {
+            if (strcmp(argv[i] + 5, "precise") == 0)
+                gDvm.preciseGc = true;
+            else if (strcmp(argv[i] + 5, "noprecise") == 0)
+                gDvm.preciseGc = false;
+            else if (strcmp(argv[i] + 5, "preverify") == 0)
+                gDvm.preVerify = true;
+            else if (strcmp(argv[i] + 5, "nopreverify") == 0)
+                gDvm.preVerify = false;
+            else if (strcmp(argv[i] + 5, "postverify") == 0)
+                gDvm.postVerify = true;
+            else if (strcmp(argv[i] + 5, "nopostverify") == 0)
+                gDvm.postVerify = false;
+            else if (strcmp(argv[i] + 5, "concurrent") == 0)
+                gDvm.concurrentMarkSweep = true;
+            else if (strcmp(argv[i] + 5, "noconcurrent") == 0)
+                gDvm.concurrentMarkSweep = false;
+            else if (strcmp(argv[i] + 5, "verifycardtable") == 0)
+                gDvm.verifyCardTable = true;
+            else if (strcmp(argv[i] + 5, "noverifycardtable") == 0)
+                gDvm.verifyCardTable = false;
+            else {
+                dvmFprintf(stderr, "Bad value for -Xgc");
+                return -1;
+            }
+            ALOGV("Precise GC configured %s", gDvm.preciseGc ? "ON" : "OFF");
+
+        } else if (strcmp(argv[i], "-Xcheckdexsum") == 0) {
+            gDvm.verifyDexChecksum = true;
+
+        } else if (strcmp(argv[i], "-Xprofile:threadcpuclock") == 0) {
+            gDvm.profilerClockSource = kProfilerClockSourceThreadCpu;
+        } else if (strcmp(argv[i], "-Xprofile:wallclock") == 0) {
+            gDvm.profilerClockSource = kProfilerClockSourceWall;
+        } else if (strcmp(argv[i], "-Xprofile:dualclock") == 0) {
+            gDvm.profilerClockSource = kProfilerClockSourceDual;
+
+        } else {
+            if (!ignoreUnrecognized) {
+                dvmFprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
+                return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Set defaults for fields altered or modified by arguments.
+ *
+ * Globals are initialized to 0 (a/k/a NULL or false).
+ */
+static void setCommandLineDefaults()
+{
+    const char* envStr = getenv("CLASSPATH");
+    if (envStr != NULL) {
+        gDvm.classPathStr = strdup(envStr);
+    } else {
+        gDvm.classPathStr = strdup(".");
+    }
+    envStr = getenv("BOOTCLASSPATH");
+    if (envStr != NULL) {
+        gDvm.bootClassPathStr = strdup(envStr);
+    } else {
+        gDvm.bootClassPathStr = strdup(".");
+    }
+
+    gDvm.properties = new std::vector<std::string>();
+
+    /* Defaults overridden by -Xms and -Xmx.
+     * TODO: base these on a system or application-specific default
+     */
+    gDvm.heapStartingSize = 2 * 1024 * 1024;  // Spec says 16MB; too big for us.
+    gDvm.heapMaximumSize = 16 * 1024 * 1024;  // Spec says 75% physical mem
+    gDvm.heapGrowthLimit = 0;  // 0 means no growth limit
+    gDvm.stackSize = kDefaultStackSize;
+    gDvm.mainThreadStackSize = kDefaultStackSize;
+    // When the heap is less than the maximum or growth limited size,
+    // fix the free portion of the heap. The utilization is the ratio
+    // of live to free memory, 0.5 implies half the heap is available
+    // to allocate into before a GC occurs. Min free and max free
+    // force the free memory to never be smaller than min free or
+    // larger than max free.
+    gDvm.heapTargetUtilization = 0.5;
+    gDvm.heapMaxFree = 2 * 1024 * 1024;
+    gDvm.heapMinFree = gDvm.heapMaxFree / 4;
+
+    gDvm.concurrentMarkSweep = true;
+
+    /* gDvm.jdwpSuspend = true; */
+
+    /* allowed unless zygote config doesn't allow it */
+    gDvm.jdwpAllowed = true;
+
+    /* default verification and optimization modes */
+    gDvm.classVerifyMode = VERIFY_MODE_ALL;
+    gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
+    gDvm.monitorVerification = false;
+    gDvm.generateRegisterMaps = true;
+    gDvm.registerMapMode = kRegisterMapModeTypePrecise;
+
+    /*
+     * Default execution mode.
+     *
+     * This should probably interact with the mterp code somehow, e.g. if
+     * we know we're using the "desktop" build we should probably be
+     * using "portable" rather than "fast".
+     */
+#if defined(WITH_JIT)
+    gDvm.executionMode = kExecutionModeJit;
+    gDvmJit.num_entries_pcTable = 0;
+    gDvmJit.includeSelectedMethod = false;
+    gDvmJit.includeSelectedOffset = false;
+    gDvmJit.methodTable = NULL;
+    gDvmJit.classTable = NULL;
+
+    gDvm.constInit = false;
+    gDvm.commonInit = false;
+#else
+    gDvm.executionMode = kExecutionModeInterpFast;
+#endif
+
+    /*
+     * SMP support is a compile-time define, but we may want to have
+     * dexopt target a differently-configured device.
+     */
+    gDvm.dexOptForSmp = (ANDROID_SMP != 0);
+
+    /*
+     * Default profiler configuration.
+     */
+    gDvm.profilerClockSource = kProfilerClockSourceDual;
+}
+
+
+/*
+ * Handle a SIGBUS, which frequently occurs because somebody replaced an
+ * optimized DEX file out from under us.
+ */
+static void busCatcher(int signum, siginfo_t* info, void* context)
+{
+    void* addr = info->si_addr;
+
+    ALOGE("Caught a SIGBUS (%d), addr=%p", signum, addr);
+
+    /*
+     * If we return at this point the SIGBUS just keeps happening, so we
+     * remove the signal handler and allow it to kill us.  TODO: restore
+     * the original, which points to a debuggerd stub; if we don't then
+     * debuggerd won't be notified.
+     */
+    signal(SIGBUS, SIG_DFL);
+}
+
+/*
+ * Configure signals.  We need to block SIGQUIT so that the signal only
+ * reaches the dump-stack-trace thread.
+ *
+ * This can be disabled with the "-Xrs" flag.
+ */
+static void blockSignals()
+{
+    sigset_t mask;
+    int cc;
+
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGQUIT);
+    sigaddset(&mask, SIGUSR1);      // used to initiate heap dump
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    sigaddset(&mask, SIGUSR2);      // used to investigate JIT internals
+#endif
+    //sigaddset(&mask, SIGPIPE);
+    cc = sigprocmask(SIG_BLOCK, &mask, NULL);
+    assert(cc == 0);
+
+    if (false) {
+        /* TODO: save the old sigaction in a global */
+        struct sigaction sa;
+        memset(&sa, 0, sizeof(sa));
+        sa.sa_sigaction = busCatcher;
+        sa.sa_flags = SA_SIGINFO;
+        cc = sigaction(SIGBUS, &sa, NULL);
+        assert(cc == 0);
+    }
+}
+
+class ScopedShutdown {
+public:
+    ScopedShutdown() : armed_(true) {
+    }
+
+    ~ScopedShutdown() {
+        if (armed_) {
+            dvmShutdown();
+        }
+    }
+
+    void disarm() {
+        armed_ = false;
+    }
+
+private:
+    bool armed_;
+};
+
+/*
+ * VM initialization.  Pass in any options provided on the command line.
+ * Do not pass in the class name or the options for the class.
+ *
+ * Returns 0 on success.
+ */
+std::string dvmStartup(int argc, const char* const argv[],
+        bool ignoreUnrecognized, JNIEnv* pEnv)
+{
+    ScopedShutdown scopedShutdown;
+
+    assert(gDvm.initializing);
+
+    ALOGV("VM init args (%d):", argc);
+    for (int i = 0; i < argc; i++) {
+        ALOGV("  %d: '%s'", i, argv[i]);
+    }
+    setCommandLineDefaults();
+
+    /*
+     * Process the option flags (if any).
+     */
+    int cc = processOptions(argc, argv, ignoreUnrecognized);
+    if (cc != 0) {
+        if (cc < 0) {
+            dvmFprintf(stderr, "\n");
+            usage("dalvikvm");
+        }
+        return "syntax error";
+    }
+
+#if WITH_EXTRA_GC_CHECKS > 1
+    /* only "portable" interp has the extra goodies */
+    if (gDvm.executionMode != kExecutionModeInterpPortable) {
+        ALOGI("Switching to 'portable' interpreter for GC checks");
+        gDvm.executionMode = kExecutionModeInterpPortable;
+    }
+#endif
+
+    /* Configure group scheduling capabilities */
+    if (!access("/dev/cpuctl/tasks", F_OK)) {
+        ALOGV("Using kernel group scheduling");
+        gDvm.kernelGroupScheduling = 1;
+    } else {
+        ALOGV("Using kernel scheduler policies");
+    }
+
+    /* configure signal handling */
+    if (!gDvm.reduceSignals)
+        blockSignals();
+
+    /* verify system page size */
+    if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
+        return StringPrintf("expected page size %d, got %d",
+                SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
+    }
+
+    /* mterp setup */
+    ALOGV("Using executionMode %d", gDvm.executionMode);
+    dvmCheckAsmConstants();
+
+    /*
+     * Initialize components.
+     */
+    dvmQuasiAtomicsStartup();
+    if (!dvmAllocTrackerStartup()) {
+        return "dvmAllocTrackerStartup failed";
+    }
+    if (!dvmGcStartup()) {
+        return "dvmGcStartup failed";
+    }
+    if (!dvmThreadStartup()) {
+        return "dvmThreadStartup failed";
+    }
+    if (!dvmInlineNativeStartup()) {
+        return "dvmInlineNativeStartup";
+    }
+    if (!dvmRegisterMapStartup()) {
+        return "dvmRegisterMapStartup failed";
+    }
+    if (!dvmInstanceofStartup()) {
+        return "dvmInstanceofStartup failed";
+    }
+    if (!dvmClassStartup()) {
+        return "dvmClassStartup failed";
+    }
+
+    /*
+     * At this point, the system is guaranteed to be sufficiently
+     * initialized that we can look up classes and class members. This
+     * call populates the gDvm instance with all the class and member
+     * references that the VM wants to use directly.
+     */
+    if (!dvmFindRequiredClassesAndMembers()) {
+        return "dvmFindRequiredClassesAndMembers failed";
+    }
+
+    if (!dvmStringInternStartup()) {
+        return "dvmStringInternStartup failed";
+    }
+    if (!dvmNativeStartup()) {
+        return "dvmNativeStartup failed";
+    }
+    if (!dvmInternalNativeStartup()) {
+        return "dvmInternalNativeStartup failed";
+    }
+    if (!dvmJniStartup()) {
+        return "dvmJniStartup failed";
+    }
+    if (!dvmProfilingStartup()) {
+        return "dvmProfilingStartup failed";
+    }
+
+    /*
+     * Create a table of methods for which we will substitute an "inline"
+     * version for performance.
+     */
+    if (!dvmCreateInlineSubsTable()) {
+        return "dvmCreateInlineSubsTable failed";
+    }
+
+    /*
+     * Miscellaneous class library validation.
+     */
+    if (!dvmValidateBoxClasses()) {
+        return "dvmValidateBoxClasses failed";
+    }
+
+    /*
+     * Do the last bits of Thread struct initialization we need to allow
+     * JNI calls to work.
+     */
+    if (!dvmPrepMainForJni(pEnv)) {
+        return "dvmPrepMainForJni failed";
+    }
+
+    /*
+     * Explicitly initialize java.lang.Class.  This doesn't happen
+     * automatically because it's allocated specially (it's an instance
+     * of itself).  Must happen before registration of system natives,
+     * which make some calls that throw assertions if the classes they
+     * operate on aren't initialized.
+     */
+    if (!dvmInitClass(gDvm.classJavaLangClass)) {
+        return "couldn't initialized java.lang.Class";
+    }
+
+    /*
+     * Register the system native methods, which are registered through JNI.
+     */
+    if (!registerSystemNatives(pEnv)) {
+        return "couldn't register system natives";
+    }
+
+    /*
+     * Do some "late" initialization for the memory allocator.  This may
+     * allocate storage and initialize classes.
+     */
+    if (!dvmCreateStockExceptions()) {
+        return "dvmCreateStockExceptions failed";
+    }
+
+    /*
+     * At this point, the VM is in a pretty good state.  Finish prep on
+     * the main thread (specifically, create a java.lang.Thread object to go
+     * along with our Thread struct).  Note we will probably be executing
+     * some interpreted class initializer code in here.
+     */
+    if (!dvmPrepMainThread()) {
+        return "dvmPrepMainThread failed";
+    }
+
+    /*
+     * Make sure we haven't accumulated any tracked references.  The main
+     * thread should be starting with a clean slate.
+     */
+    if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
+    {
+        ALOGW("Warning: tracked references remain post-initialization");
+        dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
+    }
+
+    /* general debugging setup */
+    if (!dvmDebuggerStartup()) {
+        return "dvmDebuggerStartup failed";
+    }
+
+    if (!dvmGcStartupClasses()) {
+        return "dvmGcStartupClasses failed";
+    }
+
+    /*
+     * Init for either zygote mode or non-zygote mode.  The key difference
+     * is that we don't start any additional threads in Zygote mode.
+     */
+    if (gDvm.zygote) {
+        if (!initZygote()) {
+            return "initZygote failed";
+        }
+    } else {
+        if (!dvmInitAfterZygote()) {
+            return "dvmInitAfterZygote failed";
+        }
+    }
+
+
+#ifndef NDEBUG
+    if (!dvmTestHash())
+        ALOGE("dvmTestHash FAILED");
+    if (false /*noisy!*/ && !dvmTestIndirectRefTable())
+        ALOGE("dvmTestIndirectRefTable FAILED");
+#endif
+
+    if (dvmCheckException(dvmThreadSelf())) {
+        dvmLogExceptionStackTrace();
+        return "Exception pending at end of VM initialization";
+    }
+
+    scopedShutdown.disarm();
+    return "";
+}
+
+static void loadJniLibrary(const char* name) {
+    std::string mappedName(StringPrintf(OS_SHARED_LIB_FORMAT_STR, name));
+    char* reason = NULL;
+    if (!dvmLoadNativeCode(mappedName.c_str(), NULL, &reason)) {
+        ALOGE("dvmLoadNativeCode failed for \"%s\": %s", name, reason);
+        dvmAbort();
+    }
+}
+
+/*
+ * Register java.* natives from our class libraries.  We need to do
+ * this after we're ready for JNI registration calls, but before we
+ * do any class initialization.
+ *
+ * If we get this wrong, we will blow up in the ThreadGroup class init if
+ * interpreted code makes any reference to System.  It will likely do this
+ * since it wants to do some java.io.File setup (e.g. for static in/out/err).
+ *
+ * We need to have gDvm.initializing raised here so that JNI FindClass
+ * won't try to use the system/application class loader.
+ */
+static bool registerSystemNatives(JNIEnv* pEnv)
+{
+    // Main thread is always first in list.
+    Thread* self = gDvm.threadList;
+
+    // Must set this before allowing JNI-based method registration.
+    self->status = THREAD_NATIVE;
+
+    // Most JNI libraries can just use System.loadLibrary, but you can't
+    // if you're the library that implements System.loadLibrary!
+    loadJniLibrary("javacore");
+    loadJniLibrary("nativehelper");
+
+    // Back to run mode.
+    self->status = THREAD_RUNNING;
+
+    return true;
+}
+
+
+/*
+ * Do zygote-mode-only initialization.
+ */
+static bool initZygote()
+{
+    /* zygote goes into its own process group */
+    setpgid(0,0);
+
+    // See storage config details at http://source.android.com/tech/storage/
+    // Create private mount namespace shared by all children
+    if (unshare(CLONE_NEWNS) == -1) {
+        SLOGE("Failed to unshare(): %s", strerror(errno));
+        return -1;
+    }
+
+    // Mark rootfs as being a slave so that changes from default
+    // namespace only flow into our children.
+    if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) {
+        SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno));
+        return -1;
+    }
+
+    // Create a staging tmpfs that is shared by our children; they will
+    // bind mount storage into their respective private namespaces, which
+    // are isolated from each other.
+    const char* target_base = getenv("EMULATED_STORAGE_TARGET");
+    if (target_base != NULL) {
+        if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,
+                "uid=0,gid=1028,mode=0050") == -1) {
+            SLOGE("Failed to mount tmpfs to %s: %s", target_base, strerror(errno));
+            return -1;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Do non-zygote-mode initialization.  This is done during VM init for
+ * standard startup, or after a "zygote fork" when creating a new process.
+ */
+bool dvmInitAfterZygote()
+{
+    u8 startHeap, startQuit, startJdwp;
+    u8 endHeap, endQuit, endJdwp;
+
+    startHeap = dvmGetRelativeTimeUsec();
+
+    /*
+     * Post-zygote heap initialization, including starting
+     * the HeapWorker thread.
+     */
+    if (!dvmGcStartupAfterZygote())
+        return false;
+
+    endHeap = dvmGetRelativeTimeUsec();
+    startQuit = dvmGetRelativeTimeUsec();
+
+    /* start signal catcher thread that dumps stacks on SIGQUIT */
+    if (!gDvm.reduceSignals && !gDvm.noQuitHandler) {
+        if (!dvmSignalCatcherStartup())
+            return false;
+    }
+
+    /* start stdout/stderr copier, if requested */
+    if (gDvm.logStdio) {
+        if (!dvmStdioConverterStartup())
+            return false;
+    }
+
+    endQuit = dvmGetRelativeTimeUsec();
+    startJdwp = dvmGetRelativeTimeUsec();
+
+    /*
+     * Start JDWP thread.  If the command-line debugger flags specified
+     * "suspend=y", this will pause the VM.  We probably want this to
+     * come last.
+     */
+    if (!initJdwp()) {
+        ALOGD("JDWP init failed; continuing anyway");
+    }
+
+    endJdwp = dvmGetRelativeTimeUsec();
+
+    ALOGV("thread-start heap=%d quit=%d jdwp=%d total=%d usec",
+        (int)(endHeap-startHeap), (int)(endQuit-startQuit),
+        (int)(endJdwp-startJdwp), (int)(endJdwp-startHeap));
+
+#ifdef WITH_JIT
+    if (gDvm.executionMode == kExecutionModeJit) {
+        if (!dvmCompilerStartup())
+            return false;
+    }
+#endif
+
+    return true;
+}
+
+/*
+ * Prepare for a connection to a JDWP-compliant debugger.
+ *
+ * Note this needs to happen fairly late in the startup process, because
+ * we need to have all of the java.* native methods registered (which in
+ * turn requires JNI to be fully prepped).
+ *
+ * There are several ways to initialize:
+ *   server=n
+ *     We immediately try to connect to host:port.  Bail on failure.  On
+ *     success, send VM_START (suspending the VM if "suspend=y").
+ *   server=y suspend=n
+ *     Passively listen for a debugger to connect.  Return immediately.
+ *   server=y suspend=y
+ *     Wait until debugger connects.  Send VM_START ASAP, suspending the
+ *     VM after the message is sent.
+ *
+ * This gets more complicated with a nonzero value for "timeout".
+ */
+static bool initJdwp()
+{
+    assert(!gDvm.zygote);
+
+    /*
+     * Init JDWP if the debugger is enabled.  This may connect out to a
+     * debugger, passively listen for a debugger, or block waiting for a
+     * debugger.
+     */
+    if (gDvm.jdwpAllowed && gDvm.jdwpConfigured) {
+        JdwpStartupParams params;
+
+        if (gDvm.jdwpHost != NULL) {
+            if (strlen(gDvm.jdwpHost) >= sizeof(params.host)-1) {
+                ALOGE("ERROR: hostname too long: '%s'", gDvm.jdwpHost);
+                return false;
+            }
+            strcpy(params.host, gDvm.jdwpHost);
+        } else {
+            params.host[0] = '\0';
+        }
+        params.transport = gDvm.jdwpTransport;
+        params.server = gDvm.jdwpServer;
+        params.suspend = gDvm.jdwpSuspend;
+        params.port = gDvm.jdwpPort;
+
+        gDvm.jdwpState = dvmJdwpStartup(&params);
+        if (gDvm.jdwpState == NULL) {
+            ALOGW("WARNING: debugger thread failed to initialize");
+            /* TODO: ignore? fail? need to mimic "expected" behavior */
+        }
+    }
+
+    /*
+     * If a debugger has already attached, send the "welcome" message.  This
+     * may cause us to suspend all threads.
+     */
+    if (dvmJdwpIsActive(gDvm.jdwpState)) {
+        //dvmChangeStatus(NULL, THREAD_RUNNING);
+        if (!dvmJdwpPostVMStart(gDvm.jdwpState, gDvm.jdwpSuspend)) {
+            ALOGW("WARNING: failed to post 'start' message to debugger");
+            /* keep going */
+        }
+        //dvmChangeStatus(NULL, THREAD_NATIVE);
+    }
+
+    return true;
+}
+
+/*
+ * An alternative to JNI_CreateJavaVM/dvmStartup that does the first bit
+ * of initialization and then returns with "initializing" still set.  (Used
+ * by DexOpt command-line utility.)
+ *
+ * Attempting to use JNI or internal natives will fail.  It's best
+ * if no bytecode gets executed, which means no <clinit>, which means
+ * no exception-throwing.  (In practice we need to initialize Class and
+ * Object, and probably some exception classes.)
+ *
+ * Returns 0 on success.
+ */
+int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode,
+    DexClassVerifyMode verifyMode, int dexoptFlags)
+{
+    gDvm.initializing = true;
+    gDvm.optimizing = true;
+
+    /* configure signal handling */
+    blockSignals();
+
+    /* set some defaults */
+    setCommandLineDefaults();
+    free(gDvm.bootClassPathStr);
+    gDvm.bootClassPathStr = strdup(bootClassPath);
+
+    /* set opt/verify modes */
+    gDvm.dexOptMode = dexOptMode;
+    gDvm.classVerifyMode = verifyMode;
+    gDvm.generateRegisterMaps = (dexoptFlags & DEXOPT_GEN_REGISTER_MAPS) != 0;
+    if (dexoptFlags & DEXOPT_SMP) {
+        assert((dexoptFlags & DEXOPT_UNIPROCESSOR) == 0);
+        gDvm.dexOptForSmp = true;
+    } else if (dexoptFlags & DEXOPT_UNIPROCESSOR) {
+        gDvm.dexOptForSmp = false;
+    } else {
+        gDvm.dexOptForSmp = (ANDROID_SMP != 0);
+    }
+
+    /*
+     * Initialize the heap, some basic thread control mutexes, and
+     * get the bootclasspath prepped.
+     *
+     * We can't load any classes yet because we may not yet have a source
+     * for things like java.lang.Object and java.lang.Class.
+     */
+    if (!dvmGcStartup())
+        goto fail;
+    if (!dvmThreadStartup())
+        goto fail;
+    if (!dvmInlineNativeStartup())
+        goto fail;
+    if (!dvmRegisterMapStartup())
+        goto fail;
+    if (!dvmInstanceofStartup())
+        goto fail;
+    if (!dvmClassStartup())
+        goto fail;
+
+    /*
+     * We leave gDvm.initializing set to "true" so that, if we're not
+     * able to process the "core" classes, we don't go into a death-spin
+     * trying to throw a "class not found" exception.
+     */
+
+    return 0;
+
+fail:
+    dvmShutdown();
+    return 1;
+}
+
+
+/*
+ * All threads have stopped.  Finish the shutdown procedure.
+ *
+ * We can also be called if startup fails partway through, so be prepared
+ * to deal with partially initialized data.
+ *
+ * Free any storage allocated in gGlobals.
+ *
+ * We can't dlclose() shared libs we've loaded, because it's possible a
+ * thread not associated with the VM is running code in one.
+ *
+ * This is called from the JNI DestroyJavaVM function, which can be
+ * called from any thread.  (In practice, this will usually run in the
+ * same thread that started the VM, a/k/a the main thread, but we don't
+ * want to assume that.)
+ */
+void dvmShutdown()
+{
+    ALOGV("VM shutting down");
+
+    if (CALC_CACHE_STATS)
+        dvmDumpAtomicCacheStats(gDvm.instanceofCache);
+
+    /*
+     * Stop our internal threads.
+     */
+    dvmGcThreadShutdown();
+
+    if (gDvm.jdwpState != NULL)
+        dvmJdwpShutdown(gDvm.jdwpState);
+    free(gDvm.jdwpHost);
+    gDvm.jdwpHost = NULL;
+    free(gDvm.jniTrace);
+    gDvm.jniTrace = NULL;
+    free(gDvm.stackTraceFile);
+    gDvm.stackTraceFile = NULL;
+
+    /* tell signal catcher to shut down if it was started */
+    dvmSignalCatcherShutdown();
+
+    /* shut down stdout/stderr conversion */
+    dvmStdioConverterShutdown();
+
+#ifdef WITH_JIT
+    if (gDvm.executionMode == kExecutionModeJit) {
+        /* shut down the compiler thread */
+        dvmCompilerShutdown();
+    }
+#endif
+
+    /*
+     * Kill any daemon threads that still exist.  Actively-running threads
+     * are likely to crash the process if they continue to execute while
+     * the VM shuts down.
+     */
+    dvmSlayDaemons();
+
+    if (gDvm.verboseShutdown)
+        ALOGD("VM cleaning up");
+
+    dvmDebuggerShutdown();
+    dvmProfilingShutdown();
+    dvmJniShutdown();
+    dvmStringInternShutdown();
+    dvmThreadShutdown();
+    dvmClassShutdown();
+    dvmRegisterMapShutdown();
+    dvmInstanceofShutdown();
+    dvmInlineNativeShutdown();
+    dvmGcShutdown();
+    dvmAllocTrackerShutdown();
+
+    /* these must happen AFTER dvmClassShutdown has walked through class data */
+    dvmNativeShutdown();
+    dvmInternalNativeShutdown();
+
+    dvmFreeInlineSubsTable();
+
+    free(gDvm.bootClassPathStr);
+    free(gDvm.classPathStr);
+    delete gDvm.properties;
+
+    freeAssertionCtrl();
+
+    dvmQuasiAtomicsShutdown();
+
+    /*
+     * We want valgrind to report anything we forget to free as "definitely
+     * lost".  If there's a pointer in the global chunk, it would be reported
+     * as "still reachable".  Erasing the memory fixes this.
+     *
+     * This must be erased to zero if we want to restart the VM within this
+     * process.
+     */
+    memset(&gDvm, 0xcd, sizeof(gDvm));
+}
+
+
+/*
+ * fprintf() wrapper that calls through the JNI-specified vfprintf hook if
+ * one was specified.
+ */
+int dvmFprintf(FILE* fp, const char* format, ...)
+{
+    va_list args;
+    int result;
+
+    va_start(args, format);
+    if (gDvm.vfprintfHook != NULL)
+        result = (*gDvm.vfprintfHook)(fp, format, args);
+    else
+        result = vfprintf(fp, format, args);
+    va_end(args);
+
+    return result;
+}
+
+#ifdef __GLIBC__
+#include <execinfo.h>
+/*
+ * glibc-only stack dump function.  Requires link with "--export-dynamic".
+ *
+ * TODO: move this into libs/cutils and make it work for all platforms.
+ */
+void dvmPrintNativeBackTrace()
+{
+    size_t MAX_STACK_FRAMES = 64;
+    void* stackFrames[MAX_STACK_FRAMES];
+    size_t frameCount = backtrace(stackFrames, MAX_STACK_FRAMES);
+
+    /*
+     * TODO: in practice, we may find that we should use backtrace_symbols_fd
+     * to avoid allocation, rather than use our own custom formatting.
+     */
+    char** strings = backtrace_symbols(stackFrames, frameCount);
+    if (strings == NULL) {
+        ALOGE("backtrace_symbols failed: %s", strerror(errno));
+        return;
+    }
+
+    size_t i;
+    for (i = 0; i < frameCount; ++i) {
+        ALOGW("#%-2d %s", i, strings[i]);
+    }
+    free(strings);
+}
+#else
+void dvmPrintNativeBackTrace() {
+    /* Hopefully, you're on an Android device and debuggerd will do this. */
+}
+#endif
+
+/*
+ * Abort the VM.  We get here on fatal errors.  Try very hard not to use
+ * this; whenever possible, return an error to somebody responsible.
+ */
+void dvmAbort()
+{
+    /*
+     * Leave gDvm.lastMessage on the stack frame which can be decoded in the
+     * tombstone file. This is for situations where we only have tombstone files
+     * but no logs (ie b/5372634).
+     *
+     * For example, in the tombstone file you usually see this:
+     *
+     *   #00  pc 00050ef2  /system/lib/libdvm.so (dvmAbort)
+     *   #01  pc 00077670  /system/lib/libdvm.so (_Z15dvmClassStartupv)
+     *     :
+     *
+     * stack:
+     *     :
+     * #00 beed2658  00000000
+     *     beed265c  7379732f
+     *     beed2660  2f6d6574
+     *     beed2664  6d617266
+     *     beed2668  726f7765
+     *     beed266c  6f632f6b
+     *     beed2670  6a2e6572
+     *     beed2674  00007261
+     *     beed2678  00000000
+     *
+     * The ascii values between beed265c and beed2674 belongs to messageBuffer
+     * and it can be decoded as "/system/framework/core.jar".
+     */
+    const int messageLength = 512;
+    char messageBuffer[messageLength] = {0};
+    int result = 0;
+
+    snprintf(messageBuffer, messageLength, "%s", gDvm.lastMessage);
+
+    /* So that messageBuffer[] looks like useful stuff to the compiler */
+    for (int i = 0; i < messageLength && messageBuffer[i]; i++) {
+        result += messageBuffer[i];
+    }
+
+    ALOGE("VM aborting");
+
+    fflush(NULL);       // flush all open file buffers
+
+    /* JNI-supplied abort hook gets right of first refusal */
+    if (gDvm.abortHook != NULL)
+        (*gDvm.abortHook)();
+
+    /*
+     * On the device, debuggerd will give us a stack trace.
+     * On the host, we have to help ourselves.
+     */
+    dvmPrintNativeBackTrace();
+
+    /*
+     * 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) = result;
+    abort();
+
+    /* notreached */
+}
diff --git a/vm/Init.h b/vm/Init.h
new file mode 100644
index 0000000..1b585fa
--- /dev/null
+++ b/vm/Init.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * VM initialization and shutdown.
+ */
+#ifndef DALVIK_INIT_H_
+#define DALVIK_INIT_H_
+
+/*
+ * Standard VM initialization, usually invoked through JNI.
+ */
+std::string dvmStartup(int argc, const char* const argv[],
+        bool ignoreUnrecognized, JNIEnv* pEnv);
+void dvmShutdown(void);
+bool dvmInitAfterZygote(void);
+
+/*
+ * Enable Java programming language assert statements after the Zygote fork.
+ */
+void dvmLateEnableAssertions(void);
+
+/*
+ * Partial VM initialization; only used as part of "dexopt", which may be
+ * asked to optimize a DEX file holding fundamental classes.
+ */
+int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode,
+    DexClassVerifyMode verifyMode, int dexoptFlags);
+
+/*
+ * Look up the set of classes and members used directly by the VM,
+ * storing references to them into the globals instance. See
+ * Globals.h. This function is exposed so that dex optimization may
+ * call it (while avoiding doing other unnecessary VM initialization).
+ *
+ * The function returns a success flag (true == success).
+ */
+bool dvmFindRequiredClassesAndMembers(void);
+
+/*
+ * Look up required members of the class Reference, and set the global
+ * reference to Reference itself too. This needs to be done separately
+ * from dvmFindRequiredClassesAndMembers(), during the course of
+ * linking the class Reference (which is done specially).
+ */
+bool dvmFindReferenceMembers(ClassObject* classReference);
+
+/*
+ * Replacement for fprintf() when we want to send a message to the console.
+ * This defaults to fprintf(), but will use the JNI fprintf callback if
+ * one was provided.
+ */
+int dvmFprintf(FILE* fp, const char* format, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+
+#endif  // DALVIK_INIT_H_
diff --git a/vm/InitRefs.cpp b/vm/InitRefs.cpp
new file mode 100644
index 0000000..c483604
--- /dev/null
+++ b/vm/InitRefs.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Code to initialize references to classes and members for use by
+ * lower-level VM facilities
+ */
+
+#include "Dalvik.h"
+
+static bool initClassReference(ClassObject** pClass, const char* name) {
+    ClassObject* result;
+
+    assert(*pClass == NULL);
+
+    if (name[0] == '[') {
+        result = dvmFindArrayClass(name, NULL);
+    } else {
+        result = dvmFindSystemClassNoInit(name);
+    }
+
+    if (result == NULL) {
+        ALOGE("Could not find essential class %s", name);
+        return false;
+    }
+
+    *pClass = result;
+    return true;
+}
+
+static bool initClassReferences() {
+    static struct { ClassObject** ref; const char* name; } classes[] = {
+        /*
+         * Note: The class Class gets special treatment during initial
+         * VM startup, so there is no need to list it here.
+         */
+
+        /* The corest of the core classes */
+        { &gDvm.classJavaLangObject, "Ljava/lang/Object;" },
+        { &gDvm.exThrowable,         "Ljava/lang/Throwable;" },
+
+        /* Slightly less core, but still down there, classes */
+        { &gDvm.classJavaLangClassArray,             "[Ljava/lang/Class;" },
+        { &gDvm.classJavaLangClassLoader,            "Ljava/lang/ClassLoader;" },
+        { &gDvm.classJavaLangObjectArray,            "[Ljava/lang/Object;"},
+        { &gDvm.classJavaLangStackTraceElement,      "Ljava/lang/StackTraceElement;" },
+        { &gDvm.classJavaLangStackTraceElementArray, "[Ljava/lang/StackTraceElement;" },
+        { &gDvm.classJavaLangString,                 "Ljava/lang/String;" },
+        { &gDvm.classJavaLangThread,                 "Ljava/lang/Thread;" },
+        { &gDvm.classJavaLangThreadGroup,            "Ljava/lang/ThreadGroup;" },
+        { &gDvm.classJavaLangVMThread,               "Ljava/lang/VMThread;" },
+
+        /* Arrays of primitive types */
+        { &gDvm.classArrayBoolean, "[Z" },
+        { &gDvm.classArrayByte,    "[B" },
+        { &gDvm.classArrayShort,   "[S" },
+        { &gDvm.classArrayChar,    "[C" },
+        { &gDvm.classArrayInt,     "[I" },
+        { &gDvm.classArrayLong,    "[J" },
+        { &gDvm.classArrayFloat,   "[F" },
+        { &gDvm.classArrayDouble,  "[D" },
+
+        /* Exception classes */
+        { &gDvm.exAbstractMethodError,             "Ljava/lang/AbstractMethodError;" },
+        { &gDvm.exArithmeticException,             "Ljava/lang/ArithmeticException;" },
+        { &gDvm.exArrayIndexOutOfBoundsException,  "Ljava/lang/ArrayIndexOutOfBoundsException;" },
+        { &gDvm.exArrayStoreException,             "Ljava/lang/ArrayStoreException;" },
+        { &gDvm.exClassCastException,              "Ljava/lang/ClassCastException;" },
+        { &gDvm.exClassCircularityError,           "Ljava/lang/ClassCircularityError;" },
+        { &gDvm.exClassNotFoundException,          "Ljava/lang/ClassNotFoundException;" },
+        { &gDvm.exClassFormatError,                "Ljava/lang/ClassFormatError;" },
+        { &gDvm.exError,                           "Ljava/lang/Error;" },
+        { &gDvm.exExceptionInInitializerError,     "Ljava/lang/ExceptionInInitializerError;" },
+        { &gDvm.exFileNotFoundException,           "Ljava/io/FileNotFoundException;" },
+        { &gDvm.exIOException,                     "Ljava/io/IOException;" },
+        { &gDvm.exIllegalAccessError,              "Ljava/lang/IllegalAccessError;" },
+        { &gDvm.exIllegalAccessException,          "Ljava/lang/IllegalAccessException;" },
+        { &gDvm.exIllegalArgumentException,        "Ljava/lang/IllegalArgumentException;" },
+        { &gDvm.exIllegalMonitorStateException,    "Ljava/lang/IllegalMonitorStateException;" },
+        { &gDvm.exIllegalStateException,           "Ljava/lang/IllegalStateException;" },
+        { &gDvm.exIllegalThreadStateException,     "Ljava/lang/IllegalThreadStateException;" },
+        { &gDvm.exIncompatibleClassChangeError,    "Ljava/lang/IncompatibleClassChangeError;" },
+        { &gDvm.exInstantiationError,              "Ljava/lang/InstantiationError;" },
+        { &gDvm.exInstantiationException,          "Ljava/lang/InstantiationException;" },
+        { &gDvm.exInternalError,                   "Ljava/lang/InternalError;" },
+        { &gDvm.exInterruptedException,            "Ljava/lang/InterruptedException;" },
+        { &gDvm.exLinkageError,                    "Ljava/lang/LinkageError;" },
+        { &gDvm.exNegativeArraySizeException,      "Ljava/lang/NegativeArraySizeException;" },
+        { &gDvm.exNoClassDefFoundError,            "Ljava/lang/NoClassDefFoundError;" },
+        { &gDvm.exNoSuchFieldError,                "Ljava/lang/NoSuchFieldError;" },
+        { &gDvm.exNoSuchFieldException,            "Ljava/lang/NoSuchFieldException;" },
+        { &gDvm.exNoSuchMethodError,               "Ljava/lang/NoSuchMethodError;" },
+        { &gDvm.exNullPointerException,            "Ljava/lang/NullPointerException;" },
+        { &gDvm.exOutOfMemoryError,                "Ljava/lang/OutOfMemoryError;" },
+        { &gDvm.exRuntimeException,                "Ljava/lang/RuntimeException;" },
+        { &gDvm.exStackOverflowError,              "Ljava/lang/StackOverflowError;" },
+        { &gDvm.exStaleDexCacheError,              "Ldalvik/system/StaleDexCacheError;" },
+        { &gDvm.exStringIndexOutOfBoundsException, "Ljava/lang/StringIndexOutOfBoundsException;" },
+        { &gDvm.exTypeNotPresentException,         "Ljava/lang/TypeNotPresentException;" },
+        { &gDvm.exUnsatisfiedLinkError,            "Ljava/lang/UnsatisfiedLinkError;" },
+        { &gDvm.exUnsupportedOperationException,   "Ljava/lang/UnsupportedOperationException;" },
+        { &gDvm.exVerifyError,                     "Ljava/lang/VerifyError;" },
+        { &gDvm.exVirtualMachineError,             "Ljava/lang/VirtualMachineError;" },
+
+        /* Other classes */
+        { &gDvm.classJavaLangAnnotationAnnotationArray, "[Ljava/lang/annotation/Annotation;" },
+        { &gDvm.classJavaLangAnnotationAnnotationArrayArray,
+          "[[Ljava/lang/annotation/Annotation;" },
+        { &gDvm.classJavaLangReflectAccessibleObject,   "Ljava/lang/reflect/AccessibleObject;" },
+        { &gDvm.classJavaLangReflectConstructor,        "Ljava/lang/reflect/Constructor;" },
+        { &gDvm.classJavaLangReflectConstructorArray,   "[Ljava/lang/reflect/Constructor;" },
+        { &gDvm.classJavaLangReflectField,              "Ljava/lang/reflect/Field;" },
+        { &gDvm.classJavaLangReflectFieldArray,         "[Ljava/lang/reflect/Field;" },
+        { &gDvm.classJavaLangReflectMethod,             "Ljava/lang/reflect/Method;" },
+        { &gDvm.classJavaLangReflectMethodArray,        "[Ljava/lang/reflect/Method;"},
+        { &gDvm.classJavaLangReflectProxy,              "Ljava/lang/reflect/Proxy;" },
+        { &gDvm.classJavaNioReadWriteDirectByteBuffer,  "Ljava/nio/ReadWriteDirectByteBuffer;" },
+        { &gDvm.classOrgApacheHarmonyDalvikDdmcChunk,
+          "Lorg/apache/harmony/dalvik/ddmc/Chunk;" },
+        { &gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer,
+          "Lorg/apache/harmony/dalvik/ddmc/DdmServer;" },
+        { &gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory,
+          "Lorg/apache/harmony/lang/annotation/AnnotationFactory;" },
+        { &gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember,
+          "Lorg/apache/harmony/lang/annotation/AnnotationMember;" },
+        { &gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray,
+          "[Lorg/apache/harmony/lang/annotation/AnnotationMember;" },
+
+        { NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; classes[i].ref != NULL; i++) {
+        if (!initClassReference(classes[i].ref, classes[i].name)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initFieldOffset(ClassObject* clazz, int *pOffset,
+        const char* name, const char* type) {
+    int offset = dvmFindFieldOffset(clazz, name, type);
+    if (offset < 0) {
+        ALOGE("Could not find essential field %s.%s of type %s", clazz->descriptor, name, type);
+        return false;
+    }
+
+    *pOffset = offset;
+    return true;
+}
+
+static bool initFieldOffsets() {
+    struct FieldInfo {
+        int* offset;
+        const char* name;
+        const char* type;
+    };
+
+    static struct FieldInfo infoDdmcChunk[] = {
+        { &gDvm.offDalvikDdmcChunk_type,   "type",   "I" },
+        { &gDvm.offDalvikDdmcChunk_data,   "data",   "[B" },
+        { &gDvm.offDalvikDdmcChunk_offset, "offset", "I" },
+        { &gDvm.offDalvikDdmcChunk_length, "length", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoFileDescriptor[] = {
+        { &gDvm.offJavaIoFileDescriptor_descriptor, "descriptor", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoString[] = {
+        { &gDvm.offJavaLangString_value,    "value",    "[C" },
+        { &gDvm.offJavaLangString_count,    "count",    "I" },
+        { &gDvm.offJavaLangString_offset,   "offset",   "I" },
+        { &gDvm.offJavaLangString_hashCode, "hashCode", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoThread[] = {
+        { &gDvm.offJavaLangThread_vmThread,           "vmThread",           "Ljava/lang/VMThread;" },
+        { &gDvm.offJavaLangThread_group,              "group",              "Ljava/lang/ThreadGroup;" },
+        { &gDvm.offJavaLangThread_daemon,             "daemon",             "Z" },
+        { &gDvm.offJavaLangThread_name,               "name",               "Ljava/lang/String;" },
+        { &gDvm.offJavaLangThread_priority,           "priority",           "I" },
+        { &gDvm.offJavaLangThread_uncaughtHandler,    "uncaughtHandler",    "Ljava/lang/Thread$UncaughtExceptionHandler;" },
+        { &gDvm.offJavaLangThread_contextClassLoader, "contextClassLoader", "Ljava/lang/ClassLoader;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoThreadGroup[] = {
+        { &gDvm.offJavaLangThreadGroup_name,   "name",   "Ljava/lang/String;" },
+        { &gDvm.offJavaLangThreadGroup_parent, "parent", "Ljava/lang/ThreadGroup;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoThrowable[] = {
+        { &gDvm.offJavaLangThrowable_stackState, "stackState", "Ljava/lang/Object;" },
+        { &gDvm.offJavaLangThrowable_cause,      "cause",      "Ljava/lang/Throwable;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoVMThread[] = {
+        { &gDvm.offJavaLangVMThread_thread, "thread", "Ljava/lang/Thread;" },
+        { &gDvm.offJavaLangVMThread_vmData, "vmData", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoFinalizerReference[] = {
+        { &gDvm.offJavaLangRefFinalizerReference_zombie, "zombie", "Ljava/lang/Object;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoConstructor[] = {
+        { &gDvm.offJavaLangReflectConstructor_slot,      "slot",           "I" },
+        { &gDvm.offJavaLangReflectConstructor_declClass, "declaringClass", "Ljava/lang/Class;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoField[] = {
+        { &gDvm.offJavaLangReflectField_slot,      "slot",           "I" },
+        { &gDvm.offJavaLangReflectField_declClass, "declaringClass", "Ljava/lang/Class;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoMethod[] = {
+        { &gDvm.offJavaLangReflectMethod_slot,      "slot",           "I" },
+        { &gDvm.offJavaLangReflectMethod_declClass, "declaringClass", "Ljava/lang/Class;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoProxy[] = {
+        { &gDvm.offJavaLangReflectProxy_h, "h", "Ljava/lang/reflect/InvocationHandler;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoBuffer[] = {
+        { &gDvm.offJavaNioBuffer_capacity,               "capacity",               "I" },
+        { &gDvm.offJavaNioBuffer_effectiveDirectAddress, "effectiveDirectAddress", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct { const char* name; const struct FieldInfo* fields; } classes[] = {
+        { "Lorg/apache/harmony/dalvik/ddmc/Chunk;", infoDdmcChunk },
+        { "Ljava/io/FileDescriptor;",               infoFileDescriptor },
+        { "Ljava/lang/String;",                     infoString },
+        { "Ljava/lang/Thread;",                     infoThread },
+        { "Ljava/lang/ThreadGroup;",                infoThreadGroup },
+        { "Ljava/lang/Throwable;",                  infoThrowable },
+        { "Ljava/lang/VMThread;",                   infoVMThread },
+        { "Ljava/lang/ref/FinalizerReference;", infoFinalizerReference },
+        { "Ljava/lang/reflect/Constructor;",        infoConstructor },
+        { "Ljava/lang/reflect/Field;",              infoField },
+        { "Ljava/lang/reflect/Method;",             infoMethod },
+        { "Ljava/lang/reflect/Proxy;",              infoProxy },
+        { "Ljava/nio/Buffer;",                      infoBuffer },
+        { NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; classes[i].name != NULL; i++) {
+        const char* className = classes[i].name;
+        ClassObject* clazz = dvmFindSystemClassNoInit(className);
+        const struct FieldInfo* fields = classes[i].fields;
+
+        if (clazz == NULL) {
+            ALOGE("Could not find essential class %s for field lookup", className);
+            return false;
+        }
+
+        int j;
+        for (j = 0; fields[j].offset != NULL; j++) {
+            if (!initFieldOffset(clazz, fields[j].offset, fields[j].name, fields[j].type)) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+static bool initDirectMethodReferenceByClass(Method** pMethod, ClassObject* clazz,
+        const char* name, const char* descriptor) {
+    Method* method = dvmFindDirectMethodByDescriptor(clazz, name, descriptor);
+
+    if (method == NULL) {
+        ALOGE("Could not find essential direct method %s.%s with descriptor %s",
+                clazz->descriptor, name, descriptor);
+        return false;
+    }
+
+    *pMethod = method;
+    return true;
+}
+
+static bool initDirectMethodReference(Method** pMethod, const char* className,
+        const char* name, const char* descriptor) {
+    ClassObject* clazz = dvmFindSystemClassNoInit(className);
+
+    if (clazz == NULL) {
+        ALOGE("Could not find essential class %s for direct method lookup", className);
+        return false;
+    }
+
+    return initDirectMethodReferenceByClass(pMethod, clazz, name, descriptor);
+}
+
+static bool initConstructorReferences() {
+    static struct { Method** method; const char* name; const char* descriptor; } constructors[] = {
+        { &gDvm.methJavaLangStackTraceElement_init, "Ljava/lang/StackTraceElement;",
+          "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V" },
+        { &gDvm.methJavaLangReflectConstructor_init, "Ljava/lang/reflect/Constructor;",
+          "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;I)V" },
+        { &gDvm.methJavaLangReflectField_init, "Ljava/lang/reflect/Field;",
+          "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V" },
+        { &gDvm.methJavaLangReflectMethod_init, "Ljava/lang/reflect/Method;",
+          "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;"
+          "Ljava/lang/String;I)V" },
+        { &gDvm.methJavaNioReadWriteDirectByteBuffer_init, "Ljava/nio/ReadWriteDirectByteBuffer;",
+          "(II)V" },
+        { &gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init,
+          "Lorg/apache/harmony/lang/annotation/AnnotationMember;",
+          "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V" },
+        { NULL, NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; constructors[i].method != NULL; i++) {
+        if (!initDirectMethodReference(constructors[i].method, constructors[i].name,
+                "<init>", constructors[i].descriptor)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initDirectMethodReferences() {
+    static struct {
+        Method** method;
+        const char* className;
+        const char* name;
+        const char* descriptor;
+    } methods[] = {
+        { &gDvm.methJavaLangClassLoader_getSystemClassLoader, "Ljava/lang/ClassLoader;",
+          "getSystemClassLoader", "()Ljava/lang/ClassLoader;" },
+        { &gDvm.methJavaLangReflectProxy_constructorPrototype, "Ljava/lang/reflect/Proxy;",
+          "constructorPrototype", "(Ljava/lang/reflect/InvocationHandler;)V" },
+        { &gDvm.methodTraceGcMethod, "Ldalvik/system/VMDebug;", "startGC", "()V" },
+        { &gDvm.methodTraceClassPrepMethod, "Ldalvik/system/VMDebug;", "startClassPrep", "()V" },
+        { &gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation,
+          "Lorg/apache/harmony/lang/annotation/AnnotationFactory;", "createAnnotation",
+          "(Ljava/lang/Class;[Lorg/apache/harmony/lang/annotation/AnnotationMember;)"
+          "Ljava/lang/annotation/Annotation;" },
+        { &gDvm.methDalvikSystemNativeStart_main, "Ldalvik/system/NativeStart;", "main", "([Ljava/lang/String;)V" },
+        { &gDvm.methDalvikSystemNativeStart_run, "Ldalvik/system/NativeStart;", "run", "()V" },
+        { &gDvm.methJavaLangRefFinalizerReferenceAdd,
+          "Ljava/lang/ref/FinalizerReference;", "add", "(Ljava/lang/Object;)V" },
+        { &gDvm.methDalvikDdmcServer_dispatch,
+          "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;" },
+        { &gDvm.methDalvikDdmcServer_broadcast,
+          "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "broadcast", "(I)V" },
+        { &gDvm.methJavaLangRefReferenceQueueAdd,
+          "Ljava/lang/ref/ReferenceQueue;", "add", "(Ljava/lang/ref/Reference;)V" },
+        { NULL, NULL, NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; methods[i].method != NULL; i++) {
+        if (!initDirectMethodReference(methods[i].method, methods[i].className,
+                methods[i].name, methods[i].descriptor)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initVirtualMethodOffset(int* pOffset, const char* className,
+        const char* name, const char* descriptor) {
+    ClassObject* clazz = dvmFindSystemClassNoInit(className);
+
+    if (clazz == NULL) {
+        ALOGE("Could not find essential class %s for virtual method lookup", className);
+        return false;
+    }
+
+    Method* method = dvmFindVirtualMethodByDescriptor(clazz, name, descriptor);
+
+    if (method == NULL) {
+        ALOGE("Could not find essential virtual method %s.%s with descriptor %s",
+                clazz->descriptor, name, descriptor);
+        return false;
+    }
+
+    *pOffset = method->methodIndex;
+    return true;
+}
+
+static bool initVirtualMethodOffsets() {
+    static struct {
+        int* offset;
+        const char* className;
+        const char* name;
+        const char* descriptor;
+    } methods[] = {
+        { &gDvm.voffJavaLangClassLoader_loadClass, "Ljava/lang/ClassLoader;", "loadClass",
+          "(Ljava/lang/String;)Ljava/lang/Class;" },
+        { &gDvm.voffJavaLangObject_equals, "Ljava/lang/Object;", "equals",
+          "(Ljava/lang/Object;)Z" },
+        { &gDvm.voffJavaLangObject_hashCode, "Ljava/lang/Object;", "hashCode", "()I" },
+        { &gDvm.voffJavaLangObject_toString, "Ljava/lang/Object;", "toString",
+          "()Ljava/lang/String;" },
+        { &gDvm.voffJavaLangThread_run, "Ljava/lang/Thread;", "run", "()V" },
+        { &gDvm.voffJavaLangThreadGroup_removeThread, "Ljava/lang/ThreadGroup;",
+          "removeThread", "(Ljava/lang/Thread;)V" },
+        { NULL, NULL, NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; methods[i].offset != NULL; i++) {
+        if (!initVirtualMethodOffset(methods[i].offset, methods[i].className,
+                methods[i].name, methods[i].descriptor)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initFinalizerReference()
+{
+    gDvm.classJavaLangRefFinalizerReference =
+        dvmFindSystemClass("Ljava/lang/ref/FinalizerReference;");
+    return gDvm.classJavaLangRefFinalizerReference != NULL;
+}
+
+static bool verifyStringOffset(const char* name, int actual, int expected) {
+    if (actual != expected) {
+        ALOGE("InitRefs: String.%s offset = %d; expected %d", name, actual, expected);
+        return false;
+    }
+
+    return true;
+}
+
+static bool verifyStringOffsets() {
+    /*
+     * Various parts of the system use predefined constants for the
+     * offsets to a few fields of the class String. This code verifies
+     * that the predefined offsets match what is actually defined by
+     * the class.
+     */
+
+    bool ok = true;
+    ok &= verifyStringOffset("value",    gDvm.offJavaLangString_value,  STRING_FIELDOFF_VALUE);
+    ok &= verifyStringOffset("count",    gDvm.offJavaLangString_count,  STRING_FIELDOFF_COUNT);
+    ok &= verifyStringOffset("offset",   gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
+    ok &= verifyStringOffset("hashCode", gDvm.offJavaLangString_hashCode,
+            STRING_FIELDOFF_HASHCODE);
+
+    return ok;
+}
+
+/* (documented in header) */
+bool dvmFindRequiredClassesAndMembers() {
+    /*
+     * Note: Under normal VM use, this is called by dvmStartup()
+     * in Init.c. For dex optimization, this is called as well, but in
+     * that case, the call is made from DexPrepare.c.
+     */
+
+    return initClassReferences()
+        && initFieldOffsets()
+        && initConstructorReferences()
+        && initDirectMethodReferences()
+        && initVirtualMethodOffsets()
+        && initFinalizerReference()
+        && verifyStringOffsets();
+}
+
+/* (documented in header) */
+bool dvmFindReferenceMembers(ClassObject* classReference) {
+    if (strcmp(classReference->descriptor, "Ljava/lang/ref/Reference;") != 0) {
+        ALOGE("Attempt to set up the wrong class as Reference");
+        return false;
+    }
+    return initFieldOffset(classReference, &gDvm.offJavaLangRefReference_pendingNext,
+                "pendingNext", "Ljava/lang/ref/Reference;")
+        && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queue,
+                "queue", "Ljava/lang/ref/ReferenceQueue;")
+        && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queueNext,
+                "queueNext", "Ljava/lang/ref/Reference;")
+        && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_referent,
+                "referent", "Ljava/lang/Object;");
+}
diff --git a/vm/InlineNative.cpp b/vm/InlineNative.cpp
new file mode 100644
index 0000000..00c1e95
--- /dev/null
+++ b/vm/InlineNative.cpp
@@ -0,0 +1,915 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "C" 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)
+ */
+bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    int count, offset;
+    ArrayObject* chars;
+
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    //ALOGI("String.charAt this=0x%08x index=%d", arg0, arg1);
+    count = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    if ((s4) arg1 < 0 || (s4) arg1 >= count) {
+        dvmThrowStringIndexOutOfBoundsExceptionWithIndex(count, arg1);
+        return false;
+    } else {
+        offset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+        chars = (ArrayObject*)
+            dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+
+        pResult->i = ((const u2*)(void*)chars->contents)[arg1 + offset];
+        return true;
+    }
+}
+
+#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);
+
+    ALOGE("%s expected %d got %d", compareType, expectResult, newResult);
+    ALOGE(" this (o=%d l=%d) '%s'", thisOffset, thisCount, thisStr);
+    ALOGE(" comp (o=%d l=%d) '%s'", 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)
+ */
+bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /*
+     * Null reference check on "this".  Normally this is performed during
+     * the setup of the virtual method call.  We need to do it before
+     * anything else.  While we're at it, check out the other string,
+     * which must also be non-null.
+     */
+    if ((Object*) arg0 == NULL || (Object*) arg1 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    /* quick test for comparison with itself */
+    if (arg0 == arg1) {
+        pResult->i = 0;
+        return true;
+    }
+
+    /*
+     * This would be simpler and faster if we promoted StringObject to
+     * a full representation, lining up the C structure fields with the
+     * actual object fields.
+     */
+    int thisCount, thisOffset, compCount, compOffset;
+    ArrayObject* thisArray;
+    ArrayObject* compArray;
+    const u2* thisChars;
+    const u2* compChars;
+    int minCount, countDiff;
+
+    thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
+    countDiff = thisCount - compCount;
+    minCount = (countDiff < 0) ? thisCount : compCount;
+    thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+    compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
+    thisArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+    compArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
+    thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset;
+    compChars = ((const u2*)(void*)compArray->contents) + compOffset;
+
+#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)
+ */
+bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /*
+     * Null reference check on "this".
+     */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    /* quick test for comparison with itself */
+    if (arg0 == arg1) {
+        pResult->i = true;
+        return true;
+    }
+
+    /*
+     * See if the other object is also a String.
+     *
+     * str.equals(null) is expected to return false, presumably based on
+     * the results of the instanceof test.
+     */
+    if (arg1 == 0 || ((Object*) arg0)->clazz != ((Object*) arg1)->clazz) {
+        pResult->i = false;
+        return true;
+    }
+
+    /*
+     * This would be simpler and faster if we promoted StringObject to
+     * a full representation, lining up the C structure fields with the
+     * actual object fields.
+     */
+    int thisCount, thisOffset, compCount, compOffset;
+    ArrayObject* thisArray;
+    ArrayObject* compArray;
+    const u2* thisChars;
+    const u2* compChars;
+
+    /* quick length check */
+    thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
+    if (thisCount != compCount) {
+        pResult->i = false;
+        return true;
+    }
+
+    /*
+     * You may, at this point, be tempted to pull out the hashCode fields
+     * and compare them.  If both fields have been initialized, and they
+     * are not equal, we can return false immediately.
+     *
+     * However, the hashCode field is often not set.  If it is set,
+     * there's an excellent chance that the String is being used as a key
+     * in a hashed data structure (e.g. HashMap).  That data structure has
+     * already made the comparison and determined that the hashes are equal,
+     * making a check here redundant.
+     *
+     * It's not clear that checking the hashes will be a win in "typical"
+     * use cases.  We err on the side of simplicity and ignore them.
+     */
+
+    thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+    compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
+    thisArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+    compArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
+    thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset;
+    compChars = ((const u2*)(void*)compArray->contents) + compOffset;
+
+#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()
+ */
+bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    //ALOGI("String.length this=0x%08x pResult=%p", arg0, pResult);
+
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    pResult->i = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    return true;
+}
+
+/*
+ * public boolean isEmpty()
+ */
+bool javaLangString_isEmpty(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    //ALOGI("String.isEmpty this=0x%08x pResult=%p", arg0, pResult);
+
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    pResult->i = (dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT) == 0);
+    return true;
+}
+
+/*
+ * Determine the index of the first character matching "ch".  The string
+ * to search is described by "chars", "offset", and "count".
+ *
+ * The character must be <= 0xffff. Supplementary characters are handled in
+ * Java.
+ *
+ * The "start" parameter must be clamped to [0..count].
+ *
+ * Returns -1 if no match is found.
+ */
+static inline int indexOfCommon(Object* strObj, int ch, int start)
+{
+    //if ((ch & 0xffff) != ch)        /* 32-bit code point */
+    //    return -1;
+
+    /* pull out the basic elements */
+    ArrayObject* charArray =
+        (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE);
+    const u2* chars = (const u2*)(void*)charArray->contents;
+    int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
+    int count = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
+    //ALOGI("String.indexOf(0x%08x, 0x%04x, %d) off=%d count=%d",
+    //    (u4) strObj, ch, start, offset, count);
+
+    /* factor out the offset */
+    chars += offset;
+
+    if (start < 0)
+        start = 0;
+    else if (start > count)
+        start = count;
+
+#if 0
+    /* 16-bit loop, simple */
+    while (start < count) {
+        if (chars[start] == ch)
+            return start;
+        start++;
+    }
+#else
+    /* 16-bit loop, slightly better on ARM */
+    const u2* ptr = chars + start;
+    const u2* endPtr = chars + count;
+    while (ptr < endPtr) {
+        if (*ptr++ == ch)
+            return (ptr-1) - chars;
+    }
+#endif
+
+    return -1;
+}
+
+/*
+ * public int indexOf(int c, int start)
+ *
+ * Scan forward through the string for a matching character.
+ * The character must be <= 0xffff; this method does not handle supplementary
+ * characters.
+ */
+bool javaLangString_fastIndexOf_II(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    pResult->i = indexOfCommon((Object*) arg0, arg1, arg2);
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      java.lang.Math
+ * ===========================================================================
+ */
+
+union Convert32 {
+    u4 arg;
+    float ff;
+};
+
+union Convert64 {
+    u4 arg[2];
+    s8 ll;
+    double dd;
+};
+
+/*
+ * public static int abs(int)
+ */
+bool javaLangMath_abs_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    s4 val = (s4) arg0;
+    pResult->i = (val >= 0) ? val : -val;
+    return true;
+}
+
+/*
+ * public static long abs(long)
+ */
+bool javaLangMath_abs_long(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    s8 val = convert.ll;
+    pResult->j = (val >= 0) ? val : -val;
+    return true;
+}
+
+/*
+ * public static float abs(float)
+ */
+bool javaLangMath_abs_float(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert32 convert;
+    /* clear the sign bit; assumes a fairly common fp representation */
+    convert.arg = arg0 & 0x7fffffff;
+    pResult->f = convert.ff;
+    return true;
+}
+
+/*
+ * public static double abs(double)
+ */
+bool javaLangMath_abs_double(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    /* clear the sign bit in the (endian-dependent) high word */
+    convert.ll &= 0x7fffffffffffffffULL;
+    pResult->d = convert.dd;
+    return true;
+}
+
+/*
+ * public static int min(int)
+ */
+bool javaLangMath_min_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    pResult->i = ((s4) arg0 < (s4) arg1) ? arg0 : arg1;
+    return true;
+}
+
+/*
+ * public static int max(int)
+ */
+bool javaLangMath_max_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    pResult->i = ((s4) arg0 > (s4) arg1) ? arg0 : arg1;
+    return true;
+}
+
+/*
+ * public static double sqrt(double)
+ *
+ * With ARM VFP enabled, gcc turns this into an fsqrtd instruction, followed
+ * by an fcmpd of the result against itself.  If it doesn't match (i.e.
+ * it's NaN), the libm sqrt() is invoked.
+ */
+bool javaLangMath_sqrt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = sqrt(convert.dd);
+    return true;
+}
+
+/*
+ * public static double cos(double)
+ */
+bool javaLangMath_cos(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = cos(convert.dd);
+    return true;
+}
+
+/*
+ * public static double sin(double)
+ */
+bool javaLangMath_sin(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = sin(convert.dd);
+    return true;
+}
+
+/*
+ * ===========================================================================
+ *      java.lang.Float
+ * ===========================================================================
+ */
+
+bool javaLangFloat_floatToIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert32 convert;
+    convert.arg = arg0;
+    pResult->i = isnanf(convert.ff) ? 0x7fc00000 : arg0;
+    return true;
+}
+
+bool javaLangFloat_floatToRawIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    pResult->i = arg0;
+    return true;
+}
+
+bool javaLangFloat_intBitsToFloat(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert32 convert;
+    convert.arg = arg0;
+    pResult->f = convert.ff;
+    return true;
+}
+
+/*
+ * ===========================================================================
+ *      java.lang.Double
+ * ===========================================================================
+ */
+
+bool javaLangDouble_doubleToLongBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->j = isnan(convert.dd) ? 0x7ff8000000000000LL : convert.ll;
+    return true;
+}
+
+bool javaLangDouble_doubleToRawLongBits(u4 arg0, u4 arg1, u4 arg2,
+    u4 arg, JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->j = convert.ll;
+    return true;
+}
+
+bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = convert.dd;
+    return true;
+}
+
+/*
+ * ===========================================================================
+ *      Infrastructure
+ * ===========================================================================
+ */
+
+/*
+ * Table of methods.
+ *
+ * The DEX optimizer uses the class/method/signature string fields to decide
+ * which calls it can trample.  The interpreter just uses the function
+ * pointer field.
+ *
+ * IMPORTANT: you must update DALVIK_VM_BUILD in DalvikVersion.h if you make
+ * changes to this table.
+ *
+ * NOTE: If present, the JIT will also need to know about changes
+ * to this table.  Update the NativeInlineOps enum in InlineNative.h and
+ * the dispatch code in compiler/codegen/<target>/Codegen.c.
+ */
+const InlineOperation gDvmInlineOpsTable[] = {
+    { org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod,
+        "Lorg/apache/harmony/dalvik/NativeTestTarget;",
+        "emptyInlineMethod", "()V" },
+
+    { javaLangString_charAt, "Ljava/lang/String;", "charAt", "(I)C" },
+    { javaLangString_compareTo, "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I" },
+    { javaLangString_equals, "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z" },
+    { javaLangString_fastIndexOf_II, "Ljava/lang/String;", "fastIndexOf", "(II)I" },
+    { javaLangString_isEmpty, "Ljava/lang/String;", "isEmpty", "()Z" },
+    { javaLangString_length, "Ljava/lang/String;", "length", "()I" },
+
+    { javaLangMath_abs_int, "Ljava/lang/Math;", "abs", "(I)I" },
+    { javaLangMath_abs_long, "Ljava/lang/Math;", "abs", "(J)J" },
+    { javaLangMath_abs_float, "Ljava/lang/Math;", "abs", "(F)F" },
+    { javaLangMath_abs_double, "Ljava/lang/Math;", "abs", "(D)D" },
+    { javaLangMath_min_int, "Ljava/lang/Math;", "min", "(II)I" },
+    { javaLangMath_max_int, "Ljava/lang/Math;", "max", "(II)I" },
+    { javaLangMath_sqrt, "Ljava/lang/Math;", "sqrt", "(D)D" },
+    { javaLangMath_cos, "Ljava/lang/Math;", "cos", "(D)D" },
+    { javaLangMath_sin, "Ljava/lang/Math;", "sin", "(D)D" },
+
+    { javaLangFloat_floatToIntBits, "Ljava/lang/Float;", "floatToIntBits", "(F)I" },
+    { javaLangFloat_floatToRawIntBits, "Ljava/lang/Float;", "floatToRawIntBits", "(F)I" },
+    { javaLangFloat_intBitsToFloat, "Ljava/lang/Float;", "intBitsToFloat", "(I)F" },
+
+    { javaLangDouble_doubleToLongBits, "Ljava/lang/Double;", "doubleToLongBits", "(D)J" },
+    { javaLangDouble_doubleToRawLongBits, "Ljava/lang/Double;", "doubleToRawLongBits", "(D)J" },
+    { javaLangDouble_longBitsToDouble, "Ljava/lang/Double;", "longBitsToDouble", "(J)D" },
+
+    // These are implemented exactly the same in Math and StrictMath,
+    // so we can make the StrictMath calls fast too. Note that this
+    // isn't true in general!
+    { javaLangMath_abs_int, "Ljava/lang/StrictMath;", "abs", "(I)I" },
+    { javaLangMath_abs_long, "Ljava/lang/StrictMath;", "abs", "(J)J" },
+    { javaLangMath_abs_float, "Ljava/lang/StrictMath;", "abs", "(F)F" },
+    { javaLangMath_abs_double, "Ljava/lang/StrictMath;", "abs", "(D)D" },
+    { javaLangMath_min_int, "Ljava/lang/StrictMath;", "min", "(II)I" },
+    { javaLangMath_max_int, "Ljava/lang/StrictMath;", "max", "(II)I" },
+    { javaLangMath_sqrt, "Ljava/lang/StrictMath;", "sqrt", "(D)D" },
+};
+
+/*
+ * Allocate some tables.
+ */
+bool dvmInlineNativeStartup()
+{
+    gDvm.inlinedMethods =
+        (Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*));
+    if (gDvm.inlinedMethods == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Free generated tables.
+ */
+void dvmInlineNativeShutdown()
+{
+    free(gDvm.inlinedMethods);
+}
+
+
+/*
+ * Get a pointer to the inlineops table.
+ */
+const InlineOperation* dvmGetInlineOpsTable()
+{
+    return gDvmInlineOpsTable;
+}
+
+/*
+ * Get the number of entries in the inlineops table.
+ */
+int dvmGetInlineOpsTableLength()
+{
+    return NELEM(gDvmInlineOpsTable);
+}
+
+Method* dvmFindInlinableMethod(const char* classDescriptor,
+    const char* methodName, const char* methodSignature)
+{
+    /*
+     * Find the class.
+     */
+    ClassObject* clazz = dvmFindClassNoInit(classDescriptor, NULL);
+    if (clazz == NULL) {
+        ALOGE("dvmFindInlinableMethod: can't find class '%s'",
+            classDescriptor);
+        dvmClearException(dvmThreadSelf());
+        return NULL;
+    }
+
+    /*
+     * Method could be virtual or direct.  Try both.  Don't use
+     * the "hier" versions.
+     */
+    Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName,
+        methodSignature);
+    if (method == NULL) {
+        method = dvmFindVirtualMethodByDescriptor(clazz, methodName,
+            methodSignature);
+    }
+    if (method == NULL) {
+        ALOGE("dvmFindInlinableMethod: can't find method %s.%s %s",
+            clazz->descriptor, methodName, methodSignature);
+        return NULL;
+    }
+
+    /*
+     * Check that the method is appropriate for inlining.
+     */
+    if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
+        ALOGE("dvmFindInlinableMethod: can't inline non-final method %s.%s",
+            clazz->descriptor, method->name);
+        return NULL;
+    }
+    if (dvmIsSynchronizedMethod(method) ||
+            dvmIsDeclaredSynchronizedMethod(method)) {
+        ALOGE("dvmFindInlinableMethod: can't inline synchronized method %s.%s",
+            clazz->descriptor, method->name);
+        return NULL;
+    }
+
+    return method;
+}
+
+/*
+ * Populate the methods table on first use.  It's possible the class
+ * hasn't been resolved yet, so we need to do the full "calling the
+ * method for the first time" routine.  (It's probably okay to skip
+ * the access checks.)
+ *
+ * Currently assuming that we're only inlining stuff loaded by the
+ * bootstrap class loader.  This is a safe assumption for many reasons.
+ */
+Method* dvmResolveInlineNative(int opIndex)
+{
+    assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable));
+    Method* method = gDvm.inlinedMethods[opIndex];
+    if (method != NULL) {
+        return method;
+    }
+
+    method = dvmFindInlinableMethod(
+        gDvmInlineOpsTable[opIndex].classDescriptor,
+        gDvmInlineOpsTable[opIndex].methodName,
+        gDvmInlineOpsTable[opIndex].methodSignature);
+
+    if (method == NULL) {
+        /* We already reported the error. */
+        return NULL;
+    }
+
+    gDvm.inlinedMethods[opIndex] = method;
+    IF_ALOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGV("Registered for profile: %s.%s %s",
+            method->clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    return method;
+}
+
+/*
+ * Make an inline call for the "debug" interpreter, used when the debugger
+ * or profiler is active.
+ */
+bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult, int opIndex)
+{
+    Method* method = dvmResolveInlineNative(opIndex);
+    if (method == NULL) {
+        return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3,
+            pResult);
+    }
+
+    Thread* self = dvmThreadSelf();
+    TRACE_METHOD_ENTER(self, method);
+    bool result = (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3,
+        pResult);
+    TRACE_METHOD_EXIT(self, method);
+    return result;
+}
diff --git a/vm/InlineNative.h b/vm/InlineNative.h
new file mode 100644
index 0000000..fe14f8b
--- /dev/null
+++ b/vm/InlineNative.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Inlined native functions.
+ */
+#ifndef DALVIK_INLINENATIVE_H_
+#define DALVIK_INLINENATIVE_H_
+
+/* startup/shutdown */
+bool dvmInlineNativeStartup(void);
+void dvmInlineNativeShutdown(void);
+
+Method* dvmFindInlinableMethod(const char* classDescriptor,
+    const char* methodName, const char* methodSignature);
+
+/*
+ * Basic 4-argument inline operation handler.
+ */
+typedef bool (*InlineOp4Func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult);
+
+/*
+ * Table of inline operations.
+ *
+ * Try to keep this at a power-of-two size, so we don't have to multiply.
+ *
+ * TODO: might be to our advantage to generate a compact jump table on
+ * the heap at runtime (or just declare two static tables, one with full
+ * info and one with just function pointers).  Especially useful if we decide
+ * to support other method call forms, e.g. /range.  We can also just
+ * generate assembly code that knows how many args it needs and has the
+ * target address embedded.
+ */
+struct InlineOperation {
+    InlineOp4Func   func;               /* MUST be first entry */
+    const char*     classDescriptor;
+    const char*     methodName;
+    const char*     methodSignature;
+};
+
+/*
+ * Must be kept in sync w/ gDvmInlineOpsTable in InlineNative.cpp
+ *
+ * You should also add a test to libcore's IntrinsicTest.
+ */
+enum NativeInlineOps {
+    INLINE_EMPTYINLINEMETHOD = 0,
+    INLINE_STRING_CHARAT = 1,
+    INLINE_STRING_COMPARETO = 2,
+    INLINE_STRING_EQUALS = 3,
+    INLINE_STRING_FASTINDEXOF_II = 4,
+    INLINE_STRING_IS_EMPTY = 5,
+    INLINE_STRING_LENGTH = 6,
+    INLINE_MATH_ABS_INT = 7,
+    INLINE_MATH_ABS_LONG = 8,
+    INLINE_MATH_ABS_FLOAT = 9,
+    INLINE_MATH_ABS_DOUBLE = 10,
+    INLINE_MATH_MIN_INT = 11,
+    INLINE_MATH_MAX_INT = 12,
+    INLINE_MATH_SQRT = 13,
+    INLINE_MATH_COS = 14,
+    INLINE_MATH_SIN = 15,
+    INLINE_FLOAT_TO_INT_BITS = 16,
+    INLINE_FLOAT_TO_RAW_INT_BITS = 17,
+    INLINE_INT_BITS_TO_FLOAT = 18,
+    INLINE_DOUBLE_TO_LONG_BITS = 19,
+    INLINE_DOUBLE_TO_RAW_LONG_BITS = 20,
+    INLINE_LONG_BITS_TO_DOUBLE = 21,
+    INLINE_STRICT_MATH_ABS_INT = 22,
+    INLINE_STRICT_MATH_ABS_LONG = 23,
+    INLINE_STRICT_MATH_ABS_FLOAT = 24,
+    INLINE_STRICT_MATH_ABS_DOUBLE = 25,
+    INLINE_STRICT_MATH_MIN_INT = 26,
+    INLINE_STRICT_MATH_MAX_INT = 27,
+    INLINE_STRICT_MATH_SQRT = 28,
+};
+
+/*
+ * Get the inlineops table.
+ */
+const InlineOperation* dvmGetInlineOpsTable(void);
+int dvmGetInlineOpsTableLength(void);
+
+/*
+ * The table, exposed so we can access it with C inlines.  Prefer access
+ * through dvmGetInlineOpsTable().
+ */
+extern const InlineOperation gDvmInlineOpsTable[];
+
+/*
+ * Perform the operation specified by "opIndex".
+ *
+ * We want the arguments to appear in the first 4 registers so they can
+ * be passed straight through to the handler function.  Ideally on ARM
+ * they'll go into r0-r3 and stay there.
+ *
+ * Returns "true" if everything went normally, "false" if an exception
+ * was thrown.
+ */
+INLINE bool dvmPerformInlineOp4Std(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult, int opIndex)
+{
+    return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult);
+}
+
+/*
+ * Like the "std" version, but will emit profiling info.
+ */
+bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult, int opIndex);
+
+/*
+ * Return method & populate the table on first use.
+ */
+extern "C" Method* dvmResolveInlineNative(int opIndex);
+
+/*
+ * The actual inline native definitions.
+ */
+bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                              JValue* pResult);
+
+bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangString_isEmpty(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                            JValue* pResult);
+
+bool javaLangString_fastIndexOf_II(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                                   JValue* pResult);
+
+bool javaLangMath_abs_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                          JValue* pResult);
+
+bool javaLangMath_abs_long(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangMath_abs_float(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                            JValue* pResult);
+
+bool javaLangMath_abs_double(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                             JValue* pResult);
+
+bool javaLangMath_min_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                          JValue* pResult);
+
+bool javaLangMath_max_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                          JValue* pResult);
+
+bool javaLangMath_sqrt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                       JValue* pResult);
+
+bool javaLangMath_cos(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                      JValue* pResult);
+
+bool javaLangMath_sin(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                      JValue* pResult);
+
+bool javaLangFloat_floatToIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                  JValue* pResult);
+
+bool javaLangFloat_floatToRawIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+bool javaLangFloat_intBitsToFloat(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                  JValue* pResult);
+
+bool javaLangDouble_doubleToLongBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+bool javaLangDouble_doubleToRawLongBits(u4 arg0, u4 arg1, u4 arg2,
+                                        u4 arg, JValue* pResult);
+
+bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+#endif  // DALVIK_INLINENATIVE_H_
diff --git a/vm/Inlines.cpp b/vm/Inlines.cpp
new file mode 100644
index 0000000..3ab7717
--- /dev/null
+++ b/vm/Inlines.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Generate non-inline copies of inline functions declared in header files.
+ */
+
+#define _DALVIK_GEN_INLINES
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/RegisterMap.h"
+#include "mterp/c/header.cpp"
+
+#undef LOG_TAG
+#include "jdwp/JdwpPriv.h"
diff --git a/vm/Inlines.h b/vm/Inlines.h
new file mode 100644
index 0000000..f1580e3
--- /dev/null
+++ b/vm/Inlines.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * In gcc, "extern inline" ensures that the copy in the header is never
+ * turned into a separate function.  This prevents us from having multiple
+ * non-inline copies.  However, we still need to provide a non-inline
+ * version in the library for the benefit of applications that include our
+ * headers and are built with optimizations disabled.  Either that, or use
+ * the "always_inline" gcc attribute to ensure that the non-inline version
+ * is never needed.
+ *
+ * (Note C99 has different notions about what the keyword combos mean.)
+ */
+#ifndef _DALVIK_GEN_INLINES             /* only defined by Inlines.c */
+# define INLINE extern __inline__
+#else
+# define INLINE
+#endif
diff --git a/vm/Intern.cpp b/vm/Intern.cpp
new file mode 100644
index 0000000..7754bb3
--- /dev/null
+++ b/vm/Intern.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * String interning.
+ */
+#include "Dalvik.h"
+
+#include <stddef.h>
+
+/*
+ * Prep string interning.
+ */
+bool dvmStringInternStartup()
+{
+    dvmInitMutex(&gDvm.internLock);
+    gDvm.internedStrings = dvmHashTableCreate(256, NULL);
+    if (gDvm.internedStrings == NULL)
+        return false;
+    gDvm.literalStrings = dvmHashTableCreate(256, NULL);
+    if (gDvm.literalStrings == NULL)
+        return false;
+    return true;
+}
+
+/*
+ * Chuck the intern list.
+ *
+ * The contents of the list are StringObjects that live on the GC heap.
+ */
+void dvmStringInternShutdown()
+{
+    if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) {
+        dvmDestroyMutex(&gDvm.internLock);
+    }
+    dvmHashTableFree(gDvm.internedStrings);
+    gDvm.internedStrings = NULL;
+    dvmHashTableFree(gDvm.literalStrings);
+    gDvm.literalStrings = NULL;
+}
+
+static StringObject* lookupString(HashTable* table, u4 key, StringObject* value)
+{
+    void* entry = dvmHashTableLookup(table, key, (void*)value,
+                                     dvmHashcmpStrings, false);
+    return (StringObject*)entry;
+}
+
+static StringObject* insertString(HashTable* table, u4 key, StringObject* value)
+{
+    if (dvmIsNonMovingObject(value) == false) {
+        value = (StringObject*)dvmCloneObject(value, ALLOC_NON_MOVING);
+    }
+    void* entry = dvmHashTableLookup(table, key, (void*)value,
+                                     dvmHashcmpStrings, true);
+    return (StringObject*)entry;
+}
+
+static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral)
+{
+    StringObject* found;
+
+    assert(strObj != NULL);
+    u4 key = dvmComputeStringHash(strObj);
+    dvmLockMutex(&gDvm.internLock);
+    if (isLiteral) {
+        /*
+         * Check the literal table for a match.
+         */
+        StringObject* literal = lookupString(gDvm.literalStrings, key, strObj);
+        if (literal != NULL) {
+            /*
+             * A match was found in the literal table, the easy case.
+             */
+            found = literal;
+        } else {
+            /*
+             * There is no match in the literal table, check the
+             * interned string table.
+             */
+            StringObject* interned = lookupString(gDvm.internedStrings, key, strObj);
+            if (interned != NULL) {
+                /*
+                 * A match was found in the interned table.  Move the
+                 * matching string to the literal table.
+                 */
+                dvmHashTableRemove(gDvm.internedStrings, key, interned);
+                found = insertString(gDvm.literalStrings, key, interned);
+                assert(found == interned);
+            } else {
+                /*
+                 * No match in the literal table or the interned
+                 * table.  Insert into the literal table.
+                 */
+                found = insertString(gDvm.literalStrings, key, strObj);
+                assert(found == strObj);
+            }
+        }
+    } else {
+        /*
+         * Check the literal table for a match.
+         */
+        found = lookupString(gDvm.literalStrings, key, strObj);
+        if (found == NULL) {
+            /*
+             * No match was found in the literal table.  Insert into
+             * the intern table if it does not already exist.
+             */
+            found = insertString(gDvm.internedStrings, key, strObj);
+        }
+    }
+    assert(found != NULL);
+    dvmUnlockMutex(&gDvm.internLock);
+    return found;
+}
+
+/*
+ * Find an entry in the interned string table.
+ *
+ * If the string doesn't already exist, the StringObject is added to
+ * the table.  Otherwise, the existing entry is returned.
+ */
+StringObject* dvmLookupInternedString(StringObject* strObj)
+{
+    return lookupInternedString(strObj, false);
+}
+
+/*
+ * Same as dvmLookupInternedString(), but guarantees that the
+ * returned string is a literal.
+ */
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj)
+{
+    return lookupInternedString(strObj, true);
+}
+
+/*
+ * Returns true if the object is a weak interned string.  Any string
+ * interned by the user is weak.
+ */
+bool dvmIsWeakInternedString(StringObject* strObj)
+{
+    assert(strObj != NULL);
+    if (gDvm.internedStrings == NULL) {
+        return false;
+    }
+    dvmLockMutex(&gDvm.internLock);
+    u4 key = dvmComputeStringHash(strObj);
+    StringObject* found = lookupString(gDvm.internedStrings, key, strObj);
+    dvmUnlockMutex(&gDvm.internLock);
+    return found == strObj;
+}
+
+/*
+ * Clear white references from the intern table.
+ */
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *))
+{
+    /* It's possible for a GC to happen before dvmStringInternStartup()
+     * is called.
+     */
+    if (gDvm.internedStrings != NULL) {
+        dvmLockMutex(&gDvm.internLock);
+        dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject);
+        dvmUnlockMutex(&gDvm.internLock);
+    }
+}
diff --git a/vm/Intern.h b/vm/Intern.h
new file mode 100644
index 0000000..9944e5c
--- /dev/null
+++ b/vm/Intern.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interned strings.
+ */
+#ifndef DALVIK_INTERN_H_
+#define DALVIK_INTERN_H_
+
+bool dvmStringInternStartup(void);
+void dvmStringInternShutdown(void);
+StringObject* dvmLookupInternedString(StringObject* strObj);
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj);
+bool dvmIsWeakInternedString(StringObject* strObj);
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *));
+
+#endif  // DALVIK_INTERN_H_
diff --git a/vm/JarFile.cpp b/vm/JarFile.cpp
new file mode 100644
index 0000000..3a037a0
--- /dev/null
+++ b/vm/JarFile.cpp
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 = (char*)malloc(bufLen);
+    if (buf == NULL) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    /* Copy the original filename into the buffer, find
+     * the last dot, and copy the suffix to just after it.
+     */
+    memcpy(buf, fileName, fileNameLen + 1);
+    c = strrchr(buf, '.');
+    if (c == NULL) {
+        errno = ENOENT;
+        goto bail;
+    }
+    memcpy(c + 1, suffix, suffixLen + 1);
+
+    fd = open(buf, flags);
+    if (fd >= 0) {
+        *pCachedName = buf;
+        return fd;
+    }
+    ALOGV("Couldn't open %s: %s", buf, strerror(errno));
+bail:
+    free(buf);
+    return -1;
+}
+
+/*
+ * Checks the dependencies of the dex cache file corresponding
+ * to the jar file at the absolute path "fileName".
+ */
+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.
+         */
+        ALOGV("dvmDexCacheStatus: Checking cache for %s", fileName);
+        cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName);
+        if (cachedName == NULL)
+            return DEX_CACHE_BAD_ARCHIVE;
+
+        fd = dvmOpenCachedDexFile(fileName, cachedName,
+                dexGetZipEntryModTime(&archive, entry),
+                dexGetZipEntryCrc32(&archive, entry),
+                /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false);
+        ALOGV("dvmOpenCachedDexFile returned fd %d", fd);
+        if (fd < 0) {
+            result = DEX_CACHE_STALE;
+            goto bail;
+        }
+
+        /* dvmOpenCachedDexFile locks the file as a side-effect.
+         * Unlock and close it.
+         */
+        if (!dvmUnlockCachedDexFile(fd)) {
+            /* uh oh -- this process needs to exit or we'll wedge the system */
+            ALOGE("Unable to unlock DEX file");
+            goto bail;
+        }
+
+        /* 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) {
+            ALOGI("Zip is good, but no %s inside, and no .odex "
+                    "file in the same directory", kDexInJarName);
+            result = DEX_CACHE_BAD_ARCHIVE;
+            goto bail;
+        }
+
+        ALOGV("Using alternate file (odex) for %s ...", fileName);
+        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+            ALOGE("%s odex has stale dependencies", fileName);
+            ALOGE("odex source not available -- failing");
+            result = DEX_CACHE_STALE_ODEX;
+            goto bail;
+        } else {
+            ALOGV("%s odex has good dependencies", 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)
+{
+    /*
+     * TODO: This function has been duplicated and modified to become
+     * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored.
+     */
+
+    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) {
+        ALOGV("Using alternate file (odex) for %s ...", fileName);
+        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+            ALOGE("%s odex has stale dependencies", fileName);
+            free(cachedName);
+            cachedName = NULL;
+            close(fd);
+            fd = -1;
+            goto tryArchive;
+        } else {
+            ALOGV("%s odex has good dependencies", fileName);
+            //TODO: make sure that the .odex actually corresponds
+            //      to the classes.dex inside the archive (if present).
+            //      For typical use there will be no classes.dex.
+        }
+    } else {
+        ZipEntry entry;
+
+tryArchive:
+        /*
+         * Pre-created .odex absent or stale.  Look inside the jar for a
+         * "classes.dex".
+         */
+        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);
+            }
+            ALOGV("dvmJarFileOpen: Checking cache for %s (%s)",
+                fileName, cachedName);
+            fd = dvmOpenCachedDexFile(fileName, cachedName,
+                    dexGetZipEntryModTime(&archive, entry),
+                    dexGetZipEntryCrc32(&archive, entry),
+                    isBootstrap, &newFile, /*createIfMissing=*/true);
+            if (fd < 0) {
+                ALOGI("Unable to open or create cache for %s (%s)",
+                    fileName, cachedName);
+                goto bail;
+            }
+            locked = true;
+
+            /*
+             * If fd points to a new file (because there was no cached version,
+             * or the cached version was stale), generate the optimized DEX.
+             * The file descriptor returned is still locked, and is positioned
+             * just past the optimization header.
+             */
+            if (newFile) {
+                u8 startWhen, extractWhen, endWhen;
+                bool result;
+                off_t dexOffset;
+
+                dexOffset = lseek(fd, 0, SEEK_CUR);
+                result = (dexOffset > 0);
+
+                if (result) {
+                    startWhen = dvmGetRelativeTimeUsec();
+                    result = dexZipExtractEntryToFile(&archive, entry, fd) == 0;
+                    extractWhen = dvmGetRelativeTimeUsec();
+                }
+                if (result) {
+                    result = dvmOptimizeDexFile(fd, dexOffset,
+                                dexGetZipEntryUncompLen(&archive, entry),
+                                fileName,
+                                dexGetZipEntryModTime(&archive, entry),
+                                dexGetZipEntryCrc32(&archive, entry),
+                                isBootstrap);
+                }
+
+                if (!result) {
+                    ALOGE("Unable to extract+optimize DEX from '%s'",
+                        fileName);
+                    goto bail;
+                }
+
+                endWhen = dvmGetRelativeTimeUsec();
+                ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms",
+                    fileName,
+                    (int) (extractWhen - startWhen) / 1000,
+                    (int) (endWhen - extractWhen) / 1000);
+            }
+        } else {
+            ALOGI("Zip is good, but no %s inside, and no valid .odex "
+                    "file in the same directory", kDexInJarName);
+            goto bail;
+        }
+    }
+
+    /*
+     * Map the cached version.  This immediately rewinds the fd, so it
+     * doesn't have to be seeked anywhere in particular.
+     */
+    if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
+        ALOGI("Unable to map %s in %s", kDexInJarName, fileName);
+        goto bail;
+    }
+
+    if (locked) {
+        /* unlock the fd */
+        if (!dvmUnlockCachedDexFile(fd)) {
+            /* uh oh -- this process needs to exit or we'll wedge the system */
+            ALOGE("Unable to unlock DEX file");
+            goto bail;
+        }
+        locked = false;
+    }
+
+    ALOGV("Successfully opened '%s' in '%s'", kDexInJarName, fileName);
+
+    *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
+    (*ppJarFile)->archive = archive;
+    (*ppJarFile)->cacheFileName = cachedName;
+    (*ppJarFile)->pDvmDex = pDvmDex;
+    cachedName = NULL;      // don't free it below
+    result = 0;
+
+bail:
+    /* clean up, closing the open file */
+    if (archiveOpen && result != 0)
+        dexZipCloseArchive(&archive);
+    free(cachedName);
+    if (fd >= 0) {
+        if (locked)
+            (void) dvmUnlockCachedDexFile(fd);
+        close(fd);
+    }
+    return result;
+}
+
+/*
+ * Close a Jar file and free the struct.
+ */
+void dvmJarFileFree(JarFile* pJarFile)
+{
+    if (pJarFile == NULL)
+        return;
+
+    dvmDexFileFree(pJarFile->pDvmDex);
+    dexZipCloseArchive(&pJarFile->archive);
+    free(pJarFile->cacheFileName);
+    free(pJarFile);
+}
diff --git a/vm/JarFile.h b/vm/JarFile.h
new file mode 100644
index 0000000..d8fd998
--- /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_H_
+#define DALVIK_JARFILE_H_
+
+/*
+ * This represents an open, scanned Jar file.  (It's actually for any Zip
+ * archive that happens to hold a Dex file.)
+ */
+struct JarFile {
+    ZipArchive  archive;
+    //MemMapping  map;
+    char*       cacheFileName;
+    DvmDex*     pDvmDex;
+};
+
+/*
+ * Open the Zip archive and get a list of the classfile entries.
+ *
+ * On success, returns 0 and sets "*ppJarFile" to a newly-allocated JarFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
+    JarFile** ppJarFile, bool isBootstrap);
+
+/*
+ * Free a JarFile structure, along with any associated structures.
+ */
+void dvmJarFileFree(JarFile* pJarFile);
+
+/* pry the DexFile out of a JarFile */
+INLINE DvmDex* dvmGetJarFileDex(JarFile* pJarFile) {
+    return pJarFile->pDvmDex;
+}
+
+/* get full path of optimized DEX file */
+INLINE const char* dvmGetJarFileCacheFileName(JarFile* pJarFile) {
+    return pJarFile->cacheFileName;
+}
+
+enum DexCacheStatus {
+    DEX_CACHE_ERROR = -2,
+    DEX_CACHE_BAD_ARCHIVE = -1,
+    DEX_CACHE_OK = 0,
+    DEX_CACHE_STALE,
+    DEX_CACHE_STALE_ODEX,
+};
+
+/*
+ * Checks the dependencies of the dex cache file corresponding
+ * to the jar file at the absolute path "fileName".
+ */
+DexCacheStatus dvmDexCacheStatus(const char *fileName);
+
+#endif  // DALVIK_JARFILE_H_
diff --git a/vm/Jni.cpp b/vm/Jni.cpp
new file mode 100644
index 0000000..74442b7
--- /dev/null
+++ b/vm/Jni.cpp
@@ -0,0 +1,3556 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik implementation of JNI interfaces.
+ */
+#include "Dalvik.h"
+#include "JniInternal.h"
+#include "Misc.h"
+#include "ScopedPthreadMutexLock.h"
+#include "UniquePtr.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+
+/*
+Native methods and interaction with the GC
+
+All JNI methods must start by changing their thread status to
+THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
+returning to native code.  The switch to "running" triggers a thread
+suspension check.
+
+With a rudimentary GC we should be able to skip the status change for
+simple functions, e.g.  IsSameObject, GetJavaVM, GetStringLength, maybe
+even access to fields with primitive types.  Our options are more limited
+with a compacting GC.
+
+For performance reasons we do as little error-checking as possible here.
+For example, we don't check to make sure the correct type of Object is
+passed in when setting a field, and we don't prevent you from storing
+new values in a "final" field.  Such things are best handled in the
+"check" version.  For actions that are common, dangerous, and must be
+checked at runtime, such as array bounds checks, we do the tests here.
+
+
+General notes on local/global reference tracking
+
+JNI provides explicit control over natively-held references that the GC
+needs to know about.  These can be local, in which case they're released
+when the native method returns into the VM, or global, which are held
+until explicitly released.  (There are also weak-global references,
+which have the lifespan and visibility of global references, but the
+object they refer to may be collected.)
+
+The references can be created with explicit JNI NewLocalRef / NewGlobalRef
+calls.  The former is very unusual, the latter is reasonably common
+(e.g. for caching references to class objects).
+
+Local references are most often created as a side-effect of JNI functions.
+For example, the AllocObject/NewObject functions must create local
+references to the objects returned, because nothing else in the GC root
+set has a reference to the new objects.
+
+The most common mode of operation is for a method to create zero or
+more local references and return.  Explicit "local delete" operations
+are expected to be exceedingly rare, except when walking through an
+object array, and the Push/PopLocalFrame calls are expected to be used
+infrequently.  For efficient operation, we want to add new local refs
+with a simple store/increment operation; to avoid infinite growth in
+pathological situations, we need to reclaim the space used by deleted
+entries.
+
+If we just want to maintain a list for the GC root set, we can use an
+expanding append-only array that compacts when objects are deleted.
+In typical situations, e.g. running through an array of objects, we will
+be deleting one of the most recently added entries, so we can minimize
+the number of elements moved (or avoid having to move any).
+
+If we want to conceal the pointer values from native code, which is
+necessary to allow the GC to move JNI-referenced objects around, then we
+have to use a more complicated indirection mechanism.
+
+The spec says, "Local references are only valid in the thread in which
+they are created.  The native code must not pass local references from
+one thread to another."
+
+
+Pinned objects
+
+For some large chunks of data, notably primitive arrays and String data,
+JNI allows the VM to choose whether it wants to pin the array object or
+make a copy.  We currently pin the memory for better execution performance.
+
+TODO: we're using simple root set references to pin primitive array data,
+because they have the property we need (i.e. the pointer we return is
+guaranteed valid until we explicitly release it).  However, if we have a
+compacting GC and don't want to pin all memory held by all global refs,
+we need to treat these differently.
+
+
+Global reference tracking
+
+There should be a small "active" set centered around the most-recently
+added items.
+
+Because it's global, access to it has to be synchronized.  Additions and
+removals require grabbing a mutex.  If the table serves as an indirection
+mechanism (i.e. it's not just a list for the benefit of the garbage
+collector), reference lookups may also require grabbing a mutex.
+
+The JNI spec does not define any sort of limit, so the list must be able
+to expand to a reasonable size.  It may be useful to log significant
+increases in usage to help identify resource leaks.
+
+
+Weak-global reference tracking
+
+[TBD]
+
+
+Local reference tracking
+
+Each Thread/JNIEnv points to an IndirectRefTable.
+
+We implement Push/PopLocalFrame with actual stack frames.  Before a JNI
+frame gets popped, we set "nextEntry" to the "top" pointer of the current
+frame, effectively releasing the references.
+
+The GC will scan all references in the table.
+
+*/
+
+static void ReportJniError() {
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+}
+
+#ifdef WITH_JNI_STACK_CHECK
+# define COMPUTE_STACK_SUM(_self)   computeStackSum(_self);
+# define CHECK_STACK_SUM(_self)     checkStackSum(_self);
+
+/*
+ * Compute a CRC on the entire interpreted stack.
+ *
+ * Would be nice to compute it on "self" as well, but there are parts of
+ * the Thread that can be altered by other threads (e.g. prev/next pointers).
+ */
+static void computeStackSum(Thread* self) {
+    const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    u4 crc = dvmInitCrc32();
+    self->stackCrc = 0;
+    crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
+    self->stackCrc = crc;
+}
+
+/*
+ * Compute a CRC on the entire interpreted stack, and compare it to what
+ * we previously computed.
+ *
+ * We can execute JNI directly from native code without calling in from
+ * interpreted code during VM initialization and immediately after JNI
+ * thread attachment.  Another opportunity exists during JNI_OnLoad.  Rather
+ * than catching these cases we just ignore them here, which is marginally
+ * less accurate but reduces the amount of code we have to touch with #ifdefs.
+ */
+static void checkStackSum(Thread* self) {
+    const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    u4 stackCrc = self->stackCrc;
+    self->stackCrc = 0;
+    u4 crc = dvmInitCrc32();
+    crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
+    if (crc != stackCrc) {
+        const Method* meth = dvmGetCurrentJNIMethod();
+        if (dvmComputeExactFrameDepth(self->interpSave.curFrame) == 1) {
+            ALOGD("JNI: bad stack CRC (0x%08x) -- okay during init", stackCrc);
+        } else if (strcmp(meth->name, "nativeLoad") == 0 &&
+                (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0)) {
+            ALOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad", stackCrc);
+        } else {
+            ALOGW("JNI: bad stack CRC (%08x vs %08x)", crc, stackCrc);
+            ReportJniError();
+        }
+    }
+    self->stackCrc = (u4) -1;       /* make logic errors more noticeable */
+}
+
+#else
+# define COMPUTE_STACK_SUM(_self)   ((void)0)
+# define CHECK_STACK_SUM(_self)     ((void)0)
+#endif
+
+
+/*
+ * ===========================================================================
+ *      Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Entry/exit processing for all JNI calls.
+ *
+ * We skip the (curiously expensive) thread-local storage lookup on our Thread*.
+ * If the caller has passed the wrong JNIEnv in, we're going to be accessing unsynchronized
+ * structures from more than one thread, and things are going to fail
+ * in bizarre ways.  This is only sensible if the native code has been
+ * fully exercised with CheckJNI enabled.
+ */
+class ScopedJniThreadState {
+public:
+    explicit ScopedJniThreadState(JNIEnv* env) {
+        mSelf = ((JNIEnvExt*) env)->self;
+
+        if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
+            // When emulating direct pointers with indirect references, it's critical
+            // that we use the correct per-thread indirect reference table.
+            Thread* self = gDvmJni.workAroundAppJniBugs ? dvmThreadSelf() : mSelf;
+            if (self != mSelf) {
+                ALOGE("JNI ERROR: env->self != thread-self (%p vs. %p); auto-correcting", mSelf, self);
+                mSelf = self;
+            }
+        }
+
+        CHECK_STACK_SUM(mSelf);
+        dvmChangeStatus(mSelf, THREAD_RUNNING);
+    }
+
+    ~ScopedJniThreadState() {
+        dvmChangeStatus(mSelf, THREAD_NATIVE);
+        COMPUTE_STACK_SUM(mSelf);
+    }
+
+    inline Thread* self() {
+        return mSelf;
+    }
+
+private:
+    Thread* mSelf;
+
+    // Disallow copy and assignment.
+    ScopedJniThreadState(const ScopedJniThreadState&);
+    void operator=(const ScopedJniThreadState&);
+};
+
+#define kGlobalRefsTableInitialSize 512
+#define kGlobalRefsTableMaxSize     51200       /* arbitrary, must be < 64K */
+#define kGrefWaterInterval          100
+#define kTrackGrefUsage             true
+
+#define kWeakGlobalRefsTableInitialSize 16
+
+#define kPinTableInitialSize        16
+#define kPinTableMaxSize            1024
+#define kPinComplainThreshold       10
+
+bool dvmJniStartup() {
+    if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize,
+                                 kGlobalRefsTableMaxSize,
+                                 kIndirectKindGlobal)) {
+        return false;
+    }
+    if (!gDvm.jniWeakGlobalRefTable.init(kWeakGlobalRefsTableInitialSize,
+                                 kGlobalRefsTableMaxSize,
+                                 kIndirectKindWeakGlobal)) {
+        return false;
+    }
+
+    dvmInitMutex(&gDvm.jniGlobalRefLock);
+    dvmInitMutex(&gDvm.jniWeakGlobalRefLock);
+    gDvm.jniGlobalRefLoMark = 0;
+    gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
+
+    if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, kPinTableInitialSize, kPinTableMaxSize)) {
+        return false;
+    }
+
+    dvmInitMutex(&gDvm.jniPinRefLock);
+
+    return true;
+}
+
+void dvmJniShutdown() {
+    gDvm.jniGlobalRefTable.destroy();
+    gDvm.jniWeakGlobalRefTable.destroy();
+    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() {
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        return NULL;
+    }
+    return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
+}
+
+/*
+ * Convert an indirect reference to an Object reference.  The indirect
+ * reference may be local, global, or weak-global.
+ *
+ * If "jobj" is NULL, or is a weak global reference whose reference has
+ * been cleared, this returns NULL.  If jobj is an invalid indirect
+ * reference, kInvalidIndirectRefObject is returned.
+ *
+ * Note "env" may be NULL when decoding global references.
+ */
+Object* dvmDecodeIndirectRef(Thread* self, jobject jobj) {
+    if (jobj == NULL) {
+        return NULL;
+    }
+
+    switch (indirectRefKind(jobj)) {
+    case kIndirectKindLocal:
+        {
+            Object* result = self->jniLocalRefTable.get(jobj);
+            if (UNLIKELY(result == NULL)) {
+                ALOGE("JNI ERROR (app bug): use of deleted local reference (%p)", jobj);
+                ReportJniError();
+            }
+            return result;
+        }
+    case kIndirectKindGlobal:
+        {
+            // TODO: find a way to avoid the mutex activity here
+            IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+            ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
+            Object* result = pRefTable->get(jobj);
+            if (UNLIKELY(result == NULL)) {
+                ALOGE("JNI ERROR (app bug): use of deleted global reference (%p)", jobj);
+                ReportJniError();
+            }
+            return result;
+        }
+    case kIndirectKindWeakGlobal:
+        {
+            // TODO: find a way to avoid the mutex activity here
+            IndirectRefTable* pRefTable = &gDvm.jniWeakGlobalRefTable;
+            ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
+            Object* result = pRefTable->get(jobj);
+            if (result == kClearedJniWeakGlobal) {
+                result = NULL;
+            } else if (UNLIKELY(result == NULL)) {
+                ALOGE("JNI ERROR (app bug): use of deleted weak global reference (%p)", jobj);
+                ReportJniError();
+            }
+            return result;
+        }
+    case kIndirectKindInvalid:
+    default:
+        if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
+            // Assume an invalid local reference is actually a direct pointer.
+            return reinterpret_cast<Object*>(jobj);
+        }
+        ALOGW("Invalid indirect reference %p in decodeIndirectRef", jobj);
+        ReportJniError();
+        return kInvalidIndirectRefObject;
+    }
+}
+
+static void AddLocalReferenceFailure(IndirectRefTable* pRefTable) {
+    pRefTable->dump("JNI local");
+    ALOGE("Failed adding to JNI local ref table (has %zd entries)", pRefTable->capacity());
+    ReportJniError(); // spec says call FatalError; this is equivalent
+}
+
+/*
+ * Add a local reference for an object to the current stack frame.  When
+ * the native function returns, the reference will be discarded.
+ *
+ * We need to allow the same reference to be added multiple times.
+ *
+ * This will be called on otherwise unreferenced objects.  We cannot do
+ * GC allocations here, and it's best if we don't grab a mutex.
+ */
+static inline jobject addLocalReference(Thread* self, Object* obj) {
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    IndirectRefTable* pRefTable = &self->jniLocalRefTable;
+    void* curFrame = self->interpSave.curFrame;
+    u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
+    jobject jobj = (jobject) pRefTable->add(cookie, obj);
+    if (UNLIKELY(jobj == NULL)) {
+        AddLocalReferenceFailure(pRefTable);
+    }
+
+    if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
+        // Hand out direct pointers to support broken old apps.
+        return reinterpret_cast<jobject>(obj);
+    }
+    return jobj;
+}
+
+/*
+ * Ensure that at least "capacity" references can be held in the local
+ * refs table of the current thread.
+ */
+static bool ensureLocalCapacity(Thread* self, int capacity) {
+    int numEntries = self->jniLocalRefTable.capacity();
+    // TODO: this isn't quite right, since "numEntries" includes holes
+    return ((kJniLocalRefMax - numEntries) >= capacity);
+}
+
+/*
+ * Explicitly delete a reference from the local list.
+ */
+static void deleteLocalReference(Thread* self, jobject jobj) {
+    if (jobj == NULL) {
+        return;
+    }
+
+    IndirectRefTable* pRefTable = &self->jniLocalRefTable;
+    void* curFrame = self->interpSave.curFrame;
+    u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
+    if (!pRefTable->remove(cookie, jobj)) {
+        /*
+         * Attempting to delete a local reference that is not in the
+         * topmost local reference frame is a no-op.  DeleteLocalRef returns
+         * void and doesn't throw any exceptions, but we should probably
+         * complain about it so the user will notice that things aren't
+         * going quite the way they expect.
+         */
+        ALOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry", jobj);
+    }
+}
+
+/*
+ * Add a global reference for an object.
+ *
+ * We may add the same object more than once.  Add/remove calls are paired,
+ * so it needs to appear on the list multiple times.
+ */
+static jobject addGlobalReference(Object* obj) {
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    //ALOGI("adding obj=%p", obj);
+    //dvmDumpThread(dvmThreadSelf(), false);
+
+    if (false && dvmIsClassObject((Object*)obj)) {
+        ClassObject* clazz = (ClassObject*) obj;
+        ALOGI("-------");
+        ALOGI("Adding global ref on class %s", clazz->descriptor);
+        dvmDumpThread(dvmThreadSelf(), false);
+    }
+    if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
+        StringObject* strObj = (StringObject*) obj;
+        char* str = dvmCreateCstrFromString(strObj);
+        if (strcmp(str, "sync-response") == 0) {
+            ALOGI("-------");
+            ALOGI("Adding global ref on string '%s'", str);
+            dvmDumpThread(dvmThreadSelf(), false);
+            //dvmAbort();
+        }
+        free(str);
+    }
+    if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
+        ArrayObject* arrayObj = (ArrayObject*) obj;
+        if (arrayObj->length == 8192 /*&&
+            dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
+        {
+            ALOGI("Adding global ref on byte array %p (len=%d)",
+                arrayObj, arrayObj->length);
+            dvmDumpThread(dvmThreadSelf(), false);
+        }
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
+
+    /*
+     * Throwing an exception on failure is problematic, because JNI code
+     * may not be expecting an exception, and things sort of cascade.  We
+     * want to have a hard limit to catch leaks during debugging, but this
+     * otherwise needs to expand until memory is consumed.  As a practical
+     * matter, if we have many thousands of global references, chances are
+     * we're either leaking global ref table entries or we're going to
+     * run out of space in the GC heap.
+     */
+    jobject jobj = (jobject) gDvm.jniGlobalRefTable.add(IRT_FIRST_SEGMENT, obj);
+    if (jobj == NULL) {
+        gDvm.jniGlobalRefTable.dump("JNI global");
+        ALOGE("Failed adding to JNI global ref table (%zd entries)",
+                gDvm.jniGlobalRefTable.capacity());
+        ReportJniError();
+    }
+
+    LOGVV("GREF add %p  (%s.%s)", obj,
+        dvmGetCurrentJNIMethod()->clazz->descriptor,
+        dvmGetCurrentJNIMethod()->name);
+
+    /* GREF usage tracking; should probably be disabled for production env */
+    if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+        int count = gDvm.jniGlobalRefTable.capacity();
+        // TODO: adjust for "holes"
+        if (count > gDvm.jniGlobalRefHiMark) {
+            ALOGD("GREF has increased to %d", count);
+            gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
+            gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
+
+            /* watch for "excessive" use; not generally appropriate */
+            if (count >= gDvm.jniGrefLimit) {
+                if (gDvmJni.warnOnly) {
+                    ALOGW("Excessive JNI global references (%d)", count);
+                } else {
+                    gDvm.jniGlobalRefTable.dump("JNI global");
+                    ALOGE("Excessive JNI global references (%d)", count);
+                    ReportJniError();
+                }
+            }
+        }
+    }
+    return jobj;
+}
+
+static jobject addWeakGlobalReference(Object* obj) {
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
+    IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable;
+    jobject jobj = (jobject) table->add(IRT_FIRST_SEGMENT, obj);
+    if (jobj == NULL) {
+        gDvm.jniWeakGlobalRefTable.dump("JNI weak global");
+        ALOGE("Failed adding to JNI weak global ref table (%zd entries)", table->capacity());
+        ReportJniError();
+    }
+    return jobj;
+}
+
+static void deleteWeakGlobalReference(jobject jobj) {
+    if (jobj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
+    IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable;
+    if (!table->remove(IRT_FIRST_SEGMENT, jobj)) {
+        ALOGW("JNI: DeleteWeakGlobalRef(%p) failed to find entry", jobj);
+    }
+}
+
+/*
+ * Remove a global reference.  In most cases it's the entry most recently
+ * added, which makes this pretty quick.
+ *
+ * Thought: if it's not the most recent entry, just null it out.  When we
+ * fill up, do a compaction pass before we expand the list.
+ */
+static void deleteGlobalReference(jobject jobj) {
+    if (jobj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
+    if (!gDvm.jniGlobalRefTable.remove(IRT_FIRST_SEGMENT, jobj)) {
+        ALOGW("JNI: DeleteGlobalRef(%p) failed to find entry", jobj);
+        return;
+    }
+
+    if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+        int count = gDvm.jniGlobalRefTable.capacity();
+        // TODO: not quite right, need to subtract holes
+        if (count < gDvm.jniGlobalRefLoMark) {
+            ALOGD("GREF has decreased to %d", count);
+            gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
+            gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
+        }
+    }
+}
+
+/*
+ * Objects don't currently move, so we just need to create a reference
+ * that will ensure the array object isn't collected.
+ *
+ * We use a separate reference table, which is part of the GC root set.
+ */
+static void pinPrimitiveArray(ArrayObject* arrayObj) {
+    if (arrayObj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock);
+
+    if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
+        dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+        ALOGE("Failed adding to JNI pinned array ref table (%d entries)",
+           (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
+        ReportJniError();
+    }
+
+    /*
+     * 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) {
+            ALOGW("JNI: pin count on array %p (%s) is now %d",
+                arrayObj, arrayObj->clazz->descriptor, count);
+            /* keep going */
+        }
+    }
+}
+
+/*
+ * Un-pin the array object.  If an object was pinned twice, it must be
+ * unpinned twice before it's free to move.
+ */
+static void unpinPrimitiveArray(ArrayObject* arrayObj) {
+    if (arrayObj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock);
+    if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
+            gDvm.jniPinRefTable.table, (Object*) arrayObj))
+    {
+        ALOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)",
+            arrayObj, dvmIsHeapAddress((Object*) arrayObj));
+        return;
+    }
+}
+
+/*
+ * Dump the contents of the JNI reference tables to the log file.
+ *
+ * We only dump the local refs associated with the current thread.
+ */
+void dvmDumpJniReferenceTables() {
+    Thread* self = dvmThreadSelf();
+    self->jniLocalRefTable.dump("JNI local");
+    gDvm.jniGlobalRefTable.dump("JNI global");
+    dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+}
+
+void dvmDumpJniStats(DebugOutputTarget* target) {
+    dvmPrintDebugMessage(target, "JNI: CheckJNI is %s", gDvmJni.useCheckJni ? "on" : "off");
+    if (gDvmJni.forceCopy) {
+        dvmPrintDebugMessage(target, " (with forcecopy)");
+    }
+    dvmPrintDebugMessage(target, "; workarounds are %s", gDvmJni.workAroundAppJniBugs ? "on" : "off");
+
+    dvmLockMutex(&gDvm.jniPinRefLock);
+    dvmPrintDebugMessage(target, "; pins=%d", dvmReferenceTableEntries(&gDvm.jniPinRefTable));
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
+
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
+    dvmPrintDebugMessage(target, "; globals=%d", gDvm.jniGlobalRefTable.capacity());
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+
+    dvmLockMutex(&gDvm.jniWeakGlobalRefLock);
+    size_t weaks = gDvm.jniWeakGlobalRefTable.capacity();
+    if (weaks > 0) {
+        dvmPrintDebugMessage(target, " (plus %d weak)", weaks);
+    }
+    dvmUnlockMutex(&gDvm.jniWeakGlobalRefLock);
+
+    dvmPrintDebugMessage(target, "\n\n");
+}
+
+/*
+ * Verify that a reference passed in from native code is one that the
+ * code is allowed to have.
+ *
+ * It's okay for native code to pass us a reference that:
+ *  - was passed in as an argument when invoked by native code (and hence
+ *    is in the JNI local refs table)
+ *  - was returned to it from JNI (and is now in the local refs table)
+ *  - is present in the JNI global refs table
+ *
+ * Used by -Xcheck:jni and GetObjectRefType.
+ */
+jobjectRefType dvmGetJNIRefType(Thread* self, jobject jobj) {
+    /*
+     * IndirectRefKind is currently defined as an exact match of
+     * jobjectRefType, so this is easy.  We have to decode it to determine
+     * if it's a valid reference and not merely valid-looking.
+     */
+    assert(jobj != NULL);
+
+    Object* obj = dvmDecodeIndirectRef(self, jobj);
+    if (obj == reinterpret_cast<Object*>(jobj) && gDvmJni.workAroundAppJniBugs) {
+        // If we're handing out direct pointers, check whether 'jobj' is a direct reference
+        // to a local reference.
+        return self->jniLocalRefTable.contains(obj) ? JNILocalRefType : JNIInvalidRefType;
+    } else if (obj == kInvalidIndirectRefObject) {
+        return JNIInvalidRefType;
+    } else {
+        return (jobjectRefType) indirectRefKind(jobj);
+    }
+}
+
+static void dumpMethods(Method* methods, size_t methodCount, const char* name) {
+    size_t i;
+    for (i = 0; i < methodCount; ++i) {
+        Method* method = &methods[i];
+        if (strcmp(name, method->name) == 0) {
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            ALOGE("Candidate: %s.%s:%s", method->clazz->descriptor, name, desc);
+            free(desc);
+        }
+    }
+}
+
+static void dumpCandidateMethods(ClassObject* clazz, const char* methodName, const char* signature) {
+    ALOGE("ERROR: couldn't find native method");
+    ALOGE("Requested: %s.%s:%s", clazz->descriptor, methodName, signature);
+    dumpMethods(clazz->virtualMethods, clazz->virtualMethodCount, methodName);
+    dumpMethods(clazz->directMethods, clazz->directMethodCount, methodName);
+}
+
+/*
+ * Register a method that uses JNI calling conventions.
+ */
+static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
+    const char* signature, void* fnPtr)
+{
+    if (fnPtr == NULL) {
+        return false;
+    }
+
+    // If a signature starts with a '!', we take that as a sign that the native code doesn't
+    // need the extra JNI arguments (the JNIEnv* and the jclass).
+    bool fastJni = false;
+    if (*signature == '!') {
+        fastJni = true;
+        ++signature;
+        ALOGV("fast JNI method %s.%s:%s detected", clazz->descriptor, methodName, signature);
+    }
+
+    Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
+    if (method == NULL) {
+        method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
+    }
+    if (method == NULL) {
+        dumpCandidateMethods(clazz, methodName, signature);
+        return false;
+    }
+
+    if (!dvmIsNativeMethod(method)) {
+        ALOGW("Unable to register: not native: %s.%s:%s", clazz->descriptor, methodName, signature);
+        return false;
+    }
+
+    if (fastJni) {
+        // In this case, we have extra constraints to check...
+        if (dvmIsSynchronizedMethod(method)) {
+            // Synchronization is usually provided by the JNI bridge,
+            // but we won't have one.
+            ALOGE("fast JNI method %s.%s:%s cannot be synchronized",
+                    clazz->descriptor, methodName, signature);
+            return false;
+        }
+        if (!dvmIsStaticMethod(method)) {
+            // There's no real reason for this constraint, but since we won't
+            // be supplying a JNIEnv* or a jobject 'this', you're effectively
+            // static anyway, so it seems clearer to say so.
+            ALOGE("fast JNI method %s.%s:%s cannot be non-static",
+                    clazz->descriptor, methodName, signature);
+            return false;
+        }
+    }
+
+    if (method->nativeFunc != dvmResolveNativeMethod) {
+        /* this is allowed, but unusual */
+        ALOGV("Note: %s.%s:%s was already registered", clazz->descriptor, methodName, signature);
+    }
+
+    method->fastJni = fastJni;
+    dvmUseJNIBridge(method, fnPtr);
+
+    ALOGV("JNI-registered %s.%s:%s", clazz->descriptor, methodName, signature);
+    return true;
+}
+
+static const char* builtInPrefixes[] = {
+    "Landroid/",
+    "Lcom/android/",
+    "Lcom/google/android/",
+    "Ldalvik/",
+    "Ljava/",
+    "Ljavax/",
+    "Llibcore/",
+    "Lorg/apache/harmony/",
+};
+
+static bool shouldTrace(Method* method) {
+    const char* className = method->clazz->descriptor;
+    // Return true if the -Xjnitrace setting implies we should trace 'method'.
+    if (gDvm.jniTrace && strstr(className, gDvm.jniTrace)) {
+        return true;
+    }
+    // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
+    // like part of Android.
+    if (gDvmJni.logThirdPartyJni) {
+        for (size_t i = 0; i < NELEM(builtInPrefixes); ++i) {
+            if (strstr(className, builtInPrefixes[i]) == className) {
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+/*
+ * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
+ * to point at the actual function.
+ */
+void dvmUseJNIBridge(Method* method, void* func) {
+    method->shouldTrace = shouldTrace(method);
+
+    // Does the method take any reference arguments?
+    method->noRef = true;
+    const char* cp = method->shorty;
+    while (*++cp != '\0') { // Pre-increment to skip return type.
+        if (*cp == 'L') {
+            method->noRef = false;
+            break;
+        }
+    }
+
+    DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod;
+    dvmSetNativeFunc(method, bridge, (const u2*) func);
+}
+
+// TODO: rewrite this to share code with CheckJNI's tracing...
+static void appendValue(char type, const JValue value, char* buf, size_t n, bool appendComma)
+{
+    size_t len = strlen(buf);
+    if (len >= n - 32) { // 32 should be longer than anything we could append.
+        buf[len - 1] = '.';
+        buf[len - 2] = '.';
+        buf[len - 3] = '.';
+        return;
+    }
+    char* p = buf + len;
+    switch (type) {
+    case 'B':
+        if (value.b >= 0 && value.b < 10) {
+            sprintf(p, "%d", value.b);
+        } else {
+            sprintf(p, "%#x (%d)", value.b, value.b);
+        }
+        break;
+    case 'C':
+        if (value.c < 0x7f && value.c >= ' ') {
+            sprintf(p, "U+%x ('%c')", value.c, value.c);
+        } else {
+            sprintf(p, "U+%x", value.c);
+        }
+        break;
+    case 'D':
+        sprintf(p, "%g", value.d);
+        break;
+    case 'F':
+        sprintf(p, "%g", value.f);
+        break;
+    case 'I':
+        sprintf(p, "%d", value.i);
+        break;
+    case 'L':
+        sprintf(p, "%#x", value.i);
+        break;
+    case 'J':
+        sprintf(p, "%lld", value.j);
+        break;
+    case 'S':
+        sprintf(p, "%d", value.s);
+        break;
+    case 'V':
+        strcpy(p, "void");
+        break;
+    case 'Z':
+        strcpy(p, value.z ? "true" : "false");
+        break;
+    default:
+        sprintf(p, "unknown type '%c'", type);
+        break;
+    }
+
+    if (appendComma) {
+        strcat(p, ", ");
+    }
+}
+
+static void logNativeMethodEntry(const Method* method, const u4* args)
+{
+    char thisString[32] = { 0 };
+    const u4* sp = args;
+    if (!dvmIsStaticMethod(method)) {
+        sprintf(thisString, "this=0x%08x ", *sp++);
+    }
+
+    char argsString[128]= { 0 };
+    const char* desc = &method->shorty[1];
+    while (*desc != '\0') {
+        char argType = *desc++;
+        JValue value;
+        if (argType == 'D' || argType == 'J') {
+            value.j = dvmGetArgLong(sp, 0);
+            sp += 2;
+        } else {
+            value.i = *sp++;
+        }
+        appendValue(argType, value, argsString, sizeof(argsString),
+        *desc != '\0');
+    }
+
+    std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor));
+    char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    ALOGI("-> %s %s%s %s(%s)", className.c_str(), method->name, signature, thisString, argsString);
+    free(signature);
+}
+
+static void logNativeMethodExit(const Method* method, Thread* self, const JValue returnValue)
+{
+    std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor));
+    char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    if (dvmCheckException(self)) {
+        Object* exception = dvmGetException(self);
+        std::string exceptionClassName(dvmHumanReadableDescriptor(exception->clazz->descriptor));
+        ALOGI("<- %s %s%s threw %s", className.c_str(),
+                method->name, signature, exceptionClassName.c_str());
+    } else {
+        char returnValueString[128] = { 0 };
+        char returnType = method->shorty[0];
+        appendValue(returnType, returnValue, returnValueString, sizeof(returnValueString), false);
+        ALOGI("<- %s %s%s returned %s", className.c_str(),
+                method->name, signature, returnValueString);
+    }
+    free(signature);
+}
+
+/*
+ * Get the method currently being executed by examining the interp stack.
+ */
+const Method* dvmGetCurrentJNIMethod() {
+    assert(dvmThreadSelf() != NULL);
+
+    void* fp = dvmThreadSelf()->interpSave.curFrame;
+    const Method* meth = SAVEAREA_FROM_FP(fp)->method;
+
+    assert(meth != NULL);
+    assert(dvmIsNativeMethod(meth));
+    return meth;
+}
+
+/*
+ * Track a JNI MonitorEnter in the current thread.
+ *
+ * The goal is to be able to "implicitly" release all JNI-held monitors
+ * when the thread detaches.
+ *
+ * Monitors may be entered multiple times, so we add a new entry for each
+ * enter call.  It would be more efficient to keep a counter.  At present
+ * there's no real motivation to improve this however.
+ */
+static void trackMonitorEnter(Thread* self, Object* obj) {
+    static const int kInitialSize = 16;
+    ReferenceTable* refTable = &self->jniMonitorRefTable;
+
+    /* init table on first use */
+    if (refTable->table == NULL) {
+        assert(refTable->maxEntries == 0);
+
+        if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
+            ALOGE("Unable to initialize monitor tracking table");
+            ReportJniError();
+        }
+    }
+
+    if (!dvmAddToReferenceTable(refTable, obj)) {
+        /* ran out of memory? could throw exception instead */
+        ALOGE("Unable to add entry to monitor tracking table");
+        ReportJniError();
+    } else {
+        LOGVV("--- added monitor %p", obj);
+    }
+}
+
+/*
+ * Track a JNI MonitorExit in the current thread.
+ */
+static void trackMonitorExit(Thread* self, Object* obj) {
+    ReferenceTable* pRefTable = &self->jniMonitorRefTable;
+
+    if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
+        ALOGE("JNI monitor %p not found in tracking list", obj);
+        /* keep going? */
+    } else {
+        LOGVV("--- removed monitor %p", obj);
+    }
+}
+
+/*
+ * Release all monitors held by the jniMonitorRefTable list.
+ */
+void dvmReleaseJniMonitors(Thread* self) {
+    ReferenceTable* pRefTable = &self->jniMonitorRefTable;
+    Object** top = pRefTable->table;
+
+    if (top == NULL) {
+        return;
+    }
+    Object** ptr = pRefTable->nextEntry;
+    while (--ptr >= top) {
+        if (!dvmUnlockObject(self, *ptr)) {
+            ALOGW("Unable to unlock monitor %p at thread detach", *ptr);
+        } else {
+            LOGVV("--- detach-releasing monitor %p", *ptr);
+        }
+    }
+
+    /* zap it */
+    pRefTable->nextEntry = pRefTable->table;
+}
+
+/*
+ * Determine if the specified class can be instantiated from JNI.  This
+ * is used by AllocObject / NewObject, which are documented as throwing
+ * an exception for abstract and interface classes, and not accepting
+ * array classes.  We also want to reject attempts to create new Class
+ * objects, since only DefineClass should do that.
+ */
+static bool canAllocClass(ClassObject* clazz) {
+    if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) {
+        /* JNI spec defines what this throws */
+        dvmThrowInstantiationException(clazz, "abstract class or interface");
+        return false;
+    } else if (dvmIsArrayClass(clazz) || dvmIsTheClassClass(clazz)) {
+        /* spec says "must not" for arrays, ignores Class */
+        dvmThrowInstantiationException(clazz, "wrong JNI function");
+        return false;
+    }
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI call bridge
+ * ===========================================================================
+ */
+
+/*
+ * The functions here form a bridge between interpreted code and JNI native
+ * functions.  The basic task is to convert an array of primitives and
+ * references into C-style function arguments.  This is architecture-specific
+ * and usually requires help from assembly code.
+ *
+ * The bridge takes four arguments: the array of parameters, a place to
+ * store the function result (if any), the method to call, and a pointer
+ * to the current thread.
+ *
+ * These functions aren't called directly from elsewhere in the VM.
+ * A pointer in the Method struct points to one of these, and when a native
+ * method is invoked the interpreter jumps to it.
+ *
+ * (The "internal native" methods are invoked the same way, but instead
+ * of calling through a bridge, the target method is called directly.)
+ *
+ * The "args" array should not be modified, but we do so anyway for
+ * performance reasons.  We know that it points to the "outs" area on
+ * the current method's interpreted stack.  This area is ignored by the
+ * precise GC, because there is no register map for a native method (for
+ * an interpreted method the args would be listed in the argument set).
+ * We know all of the values exist elsewhere on the interpreted stack,
+ * because the method call setup copies them right before making the call,
+ * so we don't have to worry about concealing stuff from the GC.
+ *
+ * If we don't want to modify "args", we either have to create a local
+ * copy and modify it before calling dvmPlatformInvoke, or we have to do
+ * the local reference replacement within dvmPlatformInvoke.  The latter
+ * has some performance advantages, though if we can inline the local
+ * reference adds we may win when there's a lot of reference args (unless
+ * we want to code up some local ref table manipulation in assembly.
+ */
+
+/*
+ * If necessary, convert the value in pResult from a local/global reference
+ * to an object pointer.
+ *
+ * If the returned reference is invalid, kInvalidIndirectRefObject will
+ * be returned in pResult.
+ */
+static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    if (method->shorty[0] == 'L' && !dvmCheckException(self) && pResult->l != NULL) {
+        pResult->l = dvmDecodeIndirectRef(self, (jobject) pResult->l);
+    }
+}
+
+/*
+ * General form, handles all cases.
+ */
+void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) {
+    u4* modArgs = (u4*) args;
+    jclass staticMethodClass = NULL;
+
+    u4 accessFlags = method->accessFlags;
+    bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;
+
+    //ALOGI("JNI calling %p (%s.%s:%s):", method->insns,
+    //    method->clazz->descriptor, method->name, method->shorty);
+
+    /*
+     * Walk the argument list, creating local references for appropriate
+     * arguments.
+     */
+    int idx = 0;
+    Object* lockObj;
+    if ((accessFlags & ACC_STATIC) != 0) {
+        lockObj = (Object*) method->clazz;
+        /* add the class object we pass in */
+        staticMethodClass = (jclass) addLocalReference(self, (Object*) method->clazz);
+    } else {
+        lockObj = (Object*) args[0];
+        /* add "this" */
+        modArgs[idx++] = (u4) addLocalReference(self, (Object*) modArgs[0]);
+    }
+
+    if (!method->noRef) {
+        const char* shorty = &method->shorty[1];        /* skip return type */
+        while (*shorty != '\0') {
+            switch (*shorty++) {
+            case 'L':
+                //ALOGI("  local %d: 0x%08x", idx, modArgs[idx]);
+                if (modArgs[idx] != 0) {
+                    modArgs[idx] = (u4) addLocalReference(self, (Object*) modArgs[idx]);
+                }
+                break;
+            case 'D':
+            case 'J':
+                idx++;
+                break;
+            default:
+                /* Z B C S I -- do nothing */
+                break;
+            }
+            idx++;
+        }
+    }
+
+    if (UNLIKELY(method->shouldTrace)) {
+        logNativeMethodEntry(method, args);
+    }
+    if (UNLIKELY(isSynchronized)) {
+        dvmLockObject(self, lockObj);
+    }
+
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+    ANDROID_MEMBAR_FULL();      /* guarantee ordering on method->insns */
+    assert(method->insns != NULL);
+
+    JNIEnv* env = self->jniEnv;
+    COMPUTE_STACK_SUM(self);
+    dvmPlatformInvoke(env,
+            (ClassObject*) staticMethodClass,
+            method->jniArgInfo, method->insSize, modArgs, method->shorty,
+            (void*) method->insns, pResult);
+    CHECK_STACK_SUM(self);
+
+    dvmChangeStatus(self, oldStatus);
+
+    convertReferenceResult(env, pResult, method, self);
+
+    if (UNLIKELY(isSynchronized)) {
+        dvmUnlockObject(self, lockObj);
+    }
+    if (UNLIKELY(method->shouldTrace)) {
+        logNativeMethodExit(method, self, *pResult);
+    }
+}
+
+/*
+ * ===========================================================================
+ *      JNI implementation
+ * ===========================================================================
+ */
+
+/*
+ * Return the version of the native method interface.
+ */
+static jint GetVersion(JNIEnv* env) {
+    /*
+     * There is absolutely no need to toggle the mode for correct behavior.
+     * However, it does provide native code with a simple "suspend self
+     * if necessary" call.
+     */
+    ScopedJniThreadState ts(env);
+    return JNI_VERSION_1_6;
+}
+
+/*
+ * Create a new class from a bag of bytes.
+ *
+ * This is not currently supported within Dalvik.
+ */
+static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
+    const jbyte* buf, jsize bufLen)
+{
+    UNUSED_PARAMETER(name);
+    UNUSED_PARAMETER(loader);
+    UNUSED_PARAMETER(buf);
+    UNUSED_PARAMETER(bufLen);
+
+    ScopedJniThreadState ts(env);
+    ALOGW("JNI DefineClass is not supported");
+    return NULL;
+}
+
+/*
+ * Find a class by name.
+ *
+ * We have to use the "no init" version of FindClass here, because we might
+ * be getting the class prior to registering native methods that will be
+ * used in <clinit>.
+ *
+ * We need to get the class loader associated with the current native
+ * method.  If there is no native method, e.g. we're calling this from native
+ * code right after creating the VM, the spec says we need to use the class
+ * loader returned by "ClassLoader.getBaseClassLoader".  There is no such
+ * method, but it's likely they meant ClassLoader.getSystemClassLoader.
+ * We can't get that until after the VM has initialized though.
+ */
+static jclass FindClass(JNIEnv* env, const char* name) {
+    ScopedJniThreadState ts(env);
+
+    const Method* thisMethod = dvmGetCurrentJNIMethod();
+    assert(thisMethod != NULL);
+
+    Object* loader;
+    Object* trackedLoader = NULL;
+    if (ts.self()->classLoaderOverride != NULL) {
+        /* hack for JNI_OnLoad */
+        assert(strcmp(thisMethod->name, "nativeLoad") == 0);
+        loader = ts.self()->classLoaderOverride;
+    } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||
+               thisMethod == gDvm.methDalvikSystemNativeStart_run) {
+        /* start point of invocation interface */
+        if (!gDvm.initializing) {
+            loader = trackedLoader = dvmGetSystemClassLoader();
+        } else {
+            loader = NULL;
+        }
+    } else {
+        loader = thisMethod->clazz->classLoader;
+    }
+
+    char* descriptor = dvmNameToDescriptor(name);
+    if (descriptor == NULL) {
+        return NULL;
+    }
+    ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);
+    free(descriptor);
+
+    jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);
+    dvmReleaseTrackedAlloc(trackedLoader, ts.self());
+    return jclazz;
+}
+
+/*
+ * Return the superclass of a class.
+ */
+static jclass GetSuperclass(JNIEnv* env, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    return (jclass) addLocalReference(ts.self(), (Object*)clazz->super);
+}
+
+/*
+ * Determine whether an object of clazz1 can be safely cast to clazz2.
+ *
+ * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
+ */
+static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz1);
+    ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz2);
+    return dvmInstanceof(clazz1, clazz2);
+}
+
+/*
+ * Given a java.lang.reflect.Method or .Constructor, return a methodID.
+ */
+static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) {
+    ScopedJniThreadState ts(env);
+    Object* method = dvmDecodeIndirectRef(ts.self(), jmethod);
+    return (jmethodID) dvmGetMethodFromReflectObj(method);
+}
+
+/*
+ * Given a java.lang.reflect.Field, return a fieldID.
+ */
+static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) {
+    ScopedJniThreadState ts(env);
+    Object* field = dvmDecodeIndirectRef(ts.self(), jfield);
+    return (jfieldID) dvmGetFieldFromReflectObj(field);
+}
+
+/*
+ * Convert a methodID to a java.lang.reflect.Method or .Constructor.
+ *
+ * (The "isStatic" field does not appear in the spec.)
+ *
+ * Throws OutOfMemory and returns NULL on failure.
+ */
+static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, jboolean isStatic) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jcls);
+    Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
+    dvmReleaseTrackedAlloc(obj, NULL);
+    return addLocalReference(ts.self(), obj);
+}
+
+/*
+ * Convert a fieldID to a java.lang.reflect.Field.
+ *
+ * (The "isStatic" field does not appear in the spec.)
+ *
+ * Throws OutOfMemory and returns NULL on failure.
+ */
+static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, jboolean isStatic) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jcls);
+    Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);
+    dvmReleaseTrackedAlloc(obj, NULL);
+    return addLocalReference(ts.self(), obj);
+}
+
+/*
+ * Take this exception and throw it.
+ */
+static jint Throw(JNIEnv* env, jthrowable jobj) {
+    ScopedJniThreadState ts(env);
+    if (jobj != NULL) {
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+        dvmSetException(ts.self(), obj);
+        return JNI_OK;
+    }
+    return JNI_ERR;
+}
+
+/*
+ * Constructs an exception object from the specified class with the message
+ * specified by "message", and throws it.
+ */
+static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    dvmThrowException(clazz, message);
+    // TODO: should return failure if this didn't work (e.g. OOM)
+    return JNI_OK;
+}
+
+/*
+ * If an exception is being thrown, return the exception object.  Otherwise,
+ * return NULL.
+ *
+ * TODO: if there is no pending exception, we should be able to skip the
+ * enter/exit checks.  If we find one, we need to enter and then re-fetch
+ * the exception (in case it got moved by a compacting GC).
+ */
+static jthrowable ExceptionOccurred(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    Object* exception = dvmGetException(ts.self());
+    jthrowable localException = (jthrowable) addLocalReference(ts.self(), exception);
+    if (localException == NULL && exception != NULL) {
+        /*
+         * We were unable to add a new local reference, and threw a new
+         * exception.  We can't return "exception", because it's not a
+         * local reference.  So we have to return NULL, indicating that
+         * there was no exception, even though it's pretty much raining
+         * exceptions in here.
+         */
+        ALOGW("JNI WARNING: addLocal/exception combo");
+    }
+    return localException;
+}
+
+/*
+ * Print an exception and stack trace to stderr.
+ */
+static void ExceptionDescribe(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    Object* exception = dvmGetException(ts.self());
+    if (exception != NULL) {
+        dvmPrintExceptionStackTrace();
+    } else {
+        ALOGI("Odd: ExceptionDescribe called, but no exception pending");
+    }
+}
+
+/*
+ * Clear the exception currently being thrown.
+ *
+ * TODO: we should be able to skip the enter/exit stuff.
+ */
+static void ExceptionClear(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    dvmClearException(ts.self());
+}
+
+/*
+ * Kill the VM.  This function does not return.
+ */
+static void FatalError(JNIEnv* env, const char* msg) {
+    //dvmChangeStatus(NULL, THREAD_RUNNING);
+    ALOGE("JNI posting fatal error: %s", msg);
+    ReportJniError();
+}
+
+/*
+ * Push a new JNI frame on the stack, with a new set of locals.
+ *
+ * The new frame must have the same method pointer.  (If for no other
+ * reason than FindClass needs it to get the appropriate class loader.)
+ */
+static jint PushLocalFrame(JNIEnv* env, jint capacity) {
+    ScopedJniThreadState ts(env);
+    if (!ensureLocalCapacity(ts.self(), capacity) ||
+            !dvmPushLocalFrame(ts.self(), dvmGetCurrentJNIMethod()))
+    {
+        /* yes, OutOfMemoryError, not StackOverflowError */
+        dvmClearException(ts.self());
+        dvmThrowOutOfMemoryError("out of stack in JNI PushLocalFrame");
+        return JNI_ERR;
+    }
+    return JNI_OK;
+}
+
+/*
+ * Pop the local frame off.  If "jresult" is not null, add it as a
+ * local reference on the now-current frame.
+ */
+static jobject PopLocalFrame(JNIEnv* env, jobject jresult) {
+    ScopedJniThreadState ts(env);
+    Object* result = dvmDecodeIndirectRef(ts.self(), jresult);
+    if (!dvmPopLocalFrame(ts.self())) {
+        ALOGW("JNI WARNING: too many PopLocalFrame calls");
+        dvmClearException(ts.self());
+        dvmThrowRuntimeException("too many PopLocalFrame calls");
+    }
+    return addLocalReference(ts.self(), result);
+}
+
+/*
+ * Add a reference to the global list.
+ */
+static jobject NewGlobalRef(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return addGlobalReference(obj);
+}
+
+/*
+ * Delete a reference from the global list.
+ */
+static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) {
+    ScopedJniThreadState ts(env);
+    deleteGlobalReference(jglobalRef);
+}
+
+
+/*
+ * Add a reference to the local list.
+ */
+static jobject NewLocalRef(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return addLocalReference(ts.self(), obj);
+}
+
+/*
+ * Delete a reference from the local list.
+ */
+static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) {
+    ScopedJniThreadState ts(env);
+    deleteLocalReference(ts.self(), jlocalRef);
+}
+
+/*
+ * Ensure that the local references table can hold at least this many
+ * references.
+ */
+static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) {
+    ScopedJniThreadState ts(env);
+    bool okay = ensureLocalCapacity(ts.self(), capacity);
+    if (!okay) {
+        dvmThrowOutOfMemoryError("can't ensure local reference capacity");
+    }
+    return okay ? 0 : -1;
+}
+
+
+/*
+ * Determine whether two Object references refer to the same underlying object.
+ */
+static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) {
+    ScopedJniThreadState ts(env);
+    Object* obj1 = dvmDecodeIndirectRef(ts.self(), jref1);
+    Object* obj2 = dvmDecodeIndirectRef(ts.self(), jref2);
+    return (obj1 == obj2);
+}
+
+/*
+ * Allocate a new object without invoking any constructors.
+ */
+static jobject AllocObject(JNIEnv* env, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!canAllocClass(clazz) ||
+        (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+    {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    return addLocalReference(ts.self(), newObj);
+}
+
+/*
+ * Allocate a new object and invoke the supplied constructor.
+ */
+static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    jobject result = addLocalReference(ts.self(), newObj);
+    if (newObj != NULL) {
+        JValue unused;
+        va_list args;
+        va_start(args, methodID);
+        dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);
+        va_end(args);
+    }
+    return result;
+}
+
+static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, va_list args) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    jobject result = addLocalReference(ts.self(), newObj);
+    if (newObj != NULL) {
+        JValue unused;
+        dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);
+    }
+    return result;
+}
+
+static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, jvalue* args) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    jobject result = addLocalReference(ts.self(), newObj);
+    if (newObj != NULL) {
+        JValue unused;
+        dvmCallMethodA(ts.self(), (Method*) methodID, newObj, true, &unused, args);
+    }
+    return result;
+}
+
+/*
+ * Returns the class of an object.
+ *
+ * JNI spec says: obj must not be NULL.
+ */
+static jclass GetObjectClass(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+
+    assert(jobj != NULL);
+
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return (jclass) addLocalReference(ts.self(), (Object*) obj->clazz);
+}
+
+/*
+ * Determine whether "obj" is an instance of "clazz".
+ */
+static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+
+    assert(jclazz != NULL);
+    if (jobj == NULL) {
+        return true;
+    }
+
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    return dvmInstanceof(obj->clazz, clazz);
+}
+
+/*
+ * Get a method ID for an instance method.
+ *
+ * While Dalvik bytecode has distinct instructions for virtual, super,
+ * static, direct, and interface method invocation, JNI only provides
+ * two functions for acquiring a method ID.  This call handles everything
+ * but static methods.
+ *
+ * JNI defines <init> as an instance method, but Dalvik considers it a
+ * "direct" method, so we have to special-case it here.
+ *
+ * Dalvik also puts all private methods into the "direct" list, so we
+ * really need to just search both lists.
+ */
+static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+    } else if (dvmIsInterfaceClass(clazz)) {
+        Method* meth = dvmFindInterfaceMethodHierByDescriptor(clazz, name, sig);
+        if (meth == NULL) {
+            dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
+                "no method with name='%s' signature='%s' in interface %s",
+                name, sig, clazz->descriptor);
+        }
+        return (jmethodID) meth;
+    }
+    Method* meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
+    if (meth == NULL) {
+        /* search private methods and constructors; non-hierarchical */
+        meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
+    }
+    if (meth != NULL && dvmIsStaticMethod(meth)) {
+        IF_ALOGD() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            ALOGD("GetMethodID: not returning static method %s.%s %s",
+                    clazz->descriptor, meth->name, desc);
+            free(desc);
+        }
+        meth = NULL;
+    }
+    if (meth == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
+                "no method with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    } else {
+        /*
+         * The method's class may not be the same as clazz, but if
+         * it isn't this must be a virtual method and the class must
+         * be a superclass (and, hence, already initialized).
+         */
+        assert(dvmIsClassInitialized(meth->clazz) || dvmIsClassInitializing(meth->clazz));
+    }
+    return (jmethodID) meth;
+}
+
+/*
+ * Get a field ID (instance fields).
+ */
+static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    jfieldID id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
+    if (id == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,
+                "no field with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    }
+    return id;
+}
+
+/*
+ * Get the method ID for a static method in a class.
+ */
+static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Method* meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
+
+    /* make sure it's static, not virtual+private */
+    if (meth != NULL && !dvmIsStaticMethod(meth)) {
+        IF_ALOGD() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            ALOGD("GetStaticMethodID: not returning nonstatic method %s.%s %s",
+                    clazz->descriptor, meth->name, desc);
+            free(desc);
+        }
+        meth = NULL;
+    }
+
+    jmethodID id = (jmethodID) meth;
+    if (id == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
+                "no static method with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    }
+    return id;
+}
+
+/*
+ * Get a field ID (static fields).
+ */
+static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    jfieldID id = (jfieldID) dvmFindStaticFieldHier(clazz, name, sig);
+    if (id == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,
+                "no static field with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    }
+    return id;
+}
+
+/*
+ * Get a static field.
+ *
+ * If we get an object reference, add it to the local refs list.
+ */
+#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref)                       \
+    static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz,      \
+        jfieldID fieldID)                                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        StaticField* sfield = (StaticField*) fieldID;                       \
+        _ctype value;                                                       \
+        if (dvmIsVolatileField(sfield)) {                                   \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* obj = dvmGetStaticFieldObjectVolatile(sfield);      \
+                value = (_ctype)(u4)addLocalReference(ts.self(), obj);            \
+            } else {                                                        \
+                value = (_ctype) dvmGetStaticField##_jname##Volatile(sfield);\
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* obj = dvmGetStaticFieldObject(sfield);              \
+                value = (_ctype)(u4)addLocalReference(ts.self(), obj);            \
+            } else {                                                        \
+                value = (_ctype) dvmGetStaticField##_jname(sfield);         \
+            }                                                               \
+        }                                                                   \
+        return value;                                                       \
+    }
+GET_STATIC_TYPE_FIELD(jobject, Object, true);
+GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
+GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
+GET_STATIC_TYPE_FIELD(jchar, Char, false);
+GET_STATIC_TYPE_FIELD(jshort, Short, false);
+GET_STATIC_TYPE_FIELD(jint, Int, false);
+GET_STATIC_TYPE_FIELD(jlong, Long, false);
+GET_STATIC_TYPE_FIELD(jfloat, Float, false);
+GET_STATIC_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Set a static field.
+ */
+#define SET_STATIC_TYPE_FIELD(_ctype, _ctype2, _jname, _isref)              \
+    static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz,        \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        StaticField* sfield = (StaticField*) fieldID;                       \
+        if (dvmIsVolatileField(sfield)) {                                   \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetStaticFieldObjectVolatile(sfield, valObj);            \
+            } else {                                                        \
+                dvmSetStaticField##_jname##Volatile(sfield, (_ctype2)value);\
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetStaticFieldObject(sfield, valObj);                    \
+            } else {                                                        \
+                dvmSetStaticField##_jname(sfield, (_ctype2)value);          \
+            }                                                               \
+        }                                                                   \
+    }
+SET_STATIC_TYPE_FIELD(jobject, Object*, Object, true);
+SET_STATIC_TYPE_FIELD(jboolean, bool, Boolean, false);
+SET_STATIC_TYPE_FIELD(jbyte, s1, Byte, false);
+SET_STATIC_TYPE_FIELD(jchar, u2, Char, false);
+SET_STATIC_TYPE_FIELD(jshort, s2, Short, false);
+SET_STATIC_TYPE_FIELD(jint, s4, Int, false);
+SET_STATIC_TYPE_FIELD(jlong, s8, Long, false);
+SET_STATIC_TYPE_FIELD(jfloat, float, Float, false);
+SET_STATIC_TYPE_FIELD(jdouble, double, Double, false);
+
+/*
+ * Get an instance field.
+ *
+ * If we get an object reference, add it to the local refs list.
+ */
+#define GET_TYPE_FIELD(_ctype, _jname, _isref)                              \
+    static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj,             \
+        jfieldID fieldID)                                                   \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        InstField* field = (InstField*) fieldID;                            \
+        _ctype value;                                                       \
+        if (dvmIsVolatileField(field)) {                            \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj =                                            \
+                    dvmGetFieldObjectVolatile(obj, field->byteOffset);      \
+                value = (_ctype)(u4)addLocalReference(ts.self(), valObj);         \
+            } else {                                                        \
+                value = (_ctype)                                            \
+                    dvmGetField##_jname##Volatile(obj, field->byteOffset);  \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
+                value = (_ctype)(u4)addLocalReference(ts.self(), valObj);         \
+            } else {                                                        \
+                value = (_ctype) dvmGetField##_jname(obj, field->byteOffset);\
+            }                                                               \
+        }                                                                   \
+        return value;                                                       \
+    }
+GET_TYPE_FIELD(jobject, Object, true);
+GET_TYPE_FIELD(jboolean, Boolean, false);
+GET_TYPE_FIELD(jbyte, Byte, false);
+GET_TYPE_FIELD(jchar, Char, false);
+GET_TYPE_FIELD(jshort, Short, false);
+GET_TYPE_FIELD(jint, Int, false);
+GET_TYPE_FIELD(jlong, Long, false);
+GET_TYPE_FIELD(jfloat, Float, false);
+GET_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Set an instance field.
+ */
+#define SET_TYPE_FIELD(_ctype, _ctype2, _jname, _isref)                     \
+    static void Set##_jname##Field(JNIEnv* env, jobject jobj,               \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); \
+        InstField* field = (InstField*) fieldID;                            \
+        if (dvmIsVolatileField(field)) {                                    \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj);  \
+            } else {                                                        \
+                dvmSetField##_jname##Volatile(obj,                          \
+                    field->byteOffset, (_ctype2)value);                     \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetFieldObject(obj, field->byteOffset, valObj);          \
+            } else {                                                        \
+                dvmSetField##_jname(obj,                                    \
+                    field->byteOffset, (_ctype2)value);                     \
+            }                                                               \
+        }                                                                   \
+    }
+SET_TYPE_FIELD(jobject, Object*, Object, true);
+SET_TYPE_FIELD(jboolean, bool, Boolean, false);
+SET_TYPE_FIELD(jbyte, s1, Byte, false);
+SET_TYPE_FIELD(jchar, u2, Char, false);
+SET_TYPE_FIELD(jshort, s2, Short, false);
+SET_TYPE_FIELD(jint, s4, Int, false);
+SET_TYPE_FIELD(jlong, s8, Long, false);
+SET_TYPE_FIELD(jfloat, float, Float, false);
+SET_TYPE_FIELD(jdouble, double, Double, false);
+
+/*
+ * Make a virtual method call.
+ *
+ * Three versions (..., va_list, jvalue[]) for each return type.  If we're
+ * returning an Object, we have to add it to the local references table.
+ */
+#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref)              \
+    static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj,           \
+        jmethodID methodID, ...)                                            \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        const Method* meth;                                                 \
+        va_list args;                                                       \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        va_end(args);                                                       \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj,          \
+        jmethodID methodID, va_list args)                                   \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj,          \
+        jmethodID methodID, jvalue* args)                                   \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodA(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }
+CALL_VIRTUAL(jobject, Object, NULL, (jobject) result.l, true);
+CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
+CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
+CALL_VIRTUAL(jchar, Char, 0, result.c, false);
+CALL_VIRTUAL(jshort, Short, 0, result.s, false);
+CALL_VIRTUAL(jint, Int, 0, result.i, false);
+CALL_VIRTUAL(jlong, Long, 0, result.j, false);
+CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
+CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
+CALL_VIRTUAL(void, Void, , , false);
+
+/*
+ * Make a "non-virtual" method call.  We're still calling a virtual method,
+ * but this time we're not doing an indirection through the object's vtable.
+ * The "clazz" parameter defines which implementation of a method we want.
+ *
+ * Three versions (..., va_list, jvalue[]) for each return type.
+ */
+#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref)           \
+    static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
+        jclass jclazz, jmethodID methodID, ...)                             \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); \
+        const Method* meth;                                                 \
+        va_list args;                                                       \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        va_end(args);                                                       \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, va_list args)                    \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, jvalue* args)                    \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); \
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodA(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }
+CALL_NONVIRTUAL(jobject, Object, NULL, (jobject) result.l, true);
+CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
+CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
+CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
+CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
+CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
+CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
+CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
+CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
+CALL_NONVIRTUAL(void, Void, , , false);
+
+
+/*
+ * Call a static method.
+ */
+#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               \
+    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz,    \
+        jmethodID methodID, ...)                                            \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        JValue result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\
+        va_end(args);                                                       \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \
+        jmethodID methodID, va_list args)                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        JValue result;                                                      \
+        dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz,   \
+        jmethodID methodID, jvalue* args)                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        JValue result;                                                      \
+        dvmCallMethodA(ts.self(), (Method*)methodID, NULL, true, &result, args);\
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }
+CALL_STATIC(jobject, Object, NULL, (jobject) result.l, true);
+CALL_STATIC(jboolean, Boolean, 0, result.z, false);
+CALL_STATIC(jbyte, Byte, 0, result.b, false);
+CALL_STATIC(jchar, Char, 0, result.c, false);
+CALL_STATIC(jshort, Short, 0, result.s, false);
+CALL_STATIC(jint, Int, 0, result.i, false);
+CALL_STATIC(jlong, Long, 0, result.j, false);
+CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
+CALL_STATIC(jdouble, Double, 0.0, result.d, false);
+CALL_STATIC(void, Void, , , false);
+
+/*
+ * Create a new String from Unicode data.
+ *
+ * If "len" is zero, we will return an empty string even if "unicodeChars"
+ * is NULL.  (The JNI spec is vague here.)
+ */
+static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
+    ScopedJniThreadState ts(env);
+    StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
+    if (jstr == NULL) {
+        return NULL;
+    }
+    dvmReleaseTrackedAlloc((Object*) jstr, NULL);
+    return (jstring) addLocalReference(ts.self(), (Object*) jstr);
+}
+
+/*
+ * Return the length of a String in Unicode character units.
+ */
+static jsize GetStringLength(JNIEnv* env, jstring jstr) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    return strObj->length();
+}
+
+
+/*
+ * Get a string's character data.
+ *
+ * The result is guaranteed to be valid until ReleaseStringChars is
+ * called, which means we have to pin it or return a copy.
+ */
+static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+
+    pinPrimitiveArray(strChars);
+
+    const u2* data = strObj->chars();
+    if (isCopy != NULL) {
+        *isCopy = JNI_FALSE;
+    }
+    return (jchar*) data;
+}
+
+/*
+ * Release our grip on some characters from a string.
+ */
+static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+    unpinPrimitiveArray(strChars);
+}
+
+/*
+ * Create a new java.lang.String object from chars in modified UTF-8 form.
+ *
+ * The spec doesn't say how to handle a NULL string.  Popular desktop VMs
+ * accept it and return a NULL pointer in response.
+ */
+static jstring NewStringUTF(JNIEnv* env, const char* bytes) {
+    ScopedJniThreadState ts(env);
+    if (bytes == NULL) {
+        return NULL;
+    }
+    /* note newStr could come back NULL on OOM */
+    StringObject* newStr = dvmCreateStringFromCstr(bytes);
+    jstring result = (jstring) addLocalReference(ts.self(), (Object*) newStr);
+    dvmReleaseTrackedAlloc((Object*)newStr, NULL);
+    return result;
+}
+
+/*
+ * Return the length in bytes of the modified UTF-8 form of the string.
+ */
+static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    if (strObj == NULL) {
+        return 0; // Should we throw something or assert?
+    }
+    return strObj->utfLength();
+}
+
+/*
+ * Convert "string" to modified UTF-8 and return a pointer.  The returned
+ * value must be released with ReleaseStringUTFChars.
+ *
+ * According to the JNI reference, "Returns a pointer to a UTF-8 string,
+ * or NULL if the operation fails. Returns NULL if and only if an invocation
+ * of this function has thrown an exception."
+ *
+ * The behavior here currently follows that of other open-source VMs, which
+ * quietly return NULL if "string" is NULL.  We should consider throwing an
+ * NPE.  (The CheckJNI code blows up if you try to pass in a NULL string,
+ * which should catch this sort of thing during development.)  Certain other
+ * VMs will crash with a segmentation fault.
+ */
+static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+    if (jstr == NULL) {
+        /* this shouldn't happen; throw NPE? */
+        return NULL;
+    }
+    if (isCopy != NULL) {
+        *isCopy = JNI_TRUE;
+    }
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    char* newStr = dvmCreateCstrFromString(strObj);
+    if (newStr == NULL) {
+        /* assume memory failure */
+        dvmThrowOutOfMemoryError("native heap string alloc failed");
+    }
+    return newStr;
+}
+
+/*
+ * Release a string created by GetStringUTFChars().
+ */
+static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) {
+    ScopedJniThreadState ts(env);
+    free((char*) utf);
+}
+
+/*
+ * Return the capacity of the array.
+ */
+static jsize GetArrayLength(JNIEnv* env, jarray jarr) {
+    ScopedJniThreadState ts(env);
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    return arrObj->length;
+}
+
+/*
+ * Construct a new array that holds objects from class "elementClass".
+ */
+static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
+    jclass jelementClass, jobject jinitialElement)
+{
+    ScopedJniThreadState ts(env);
+
+    if (jelementClass == NULL) {
+        dvmThrowNullPointerException("JNI NewObjectArray elementClass == NULL");
+        return NULL;
+    }
+
+    ClassObject* elemClassObj = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jelementClass);
+    ClassObject* arrayClass = dvmFindArrayClassForElement(elemClassObj);
+    ArrayObject* newObj = dvmAllocArrayByClass(arrayClass, length, ALLOC_DEFAULT);
+    if (newObj == NULL) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+    jobjectArray newArray = (jobjectArray) addLocalReference(ts.self(), (Object*) newObj);
+    dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+
+    /*
+     * Initialize the array.
+     */
+    if (jinitialElement != NULL) {
+        Object* initialElement = dvmDecodeIndirectRef(ts.self(), jinitialElement);
+        Object** arrayData = (Object**) (void*) newObj->contents;
+        for (jsize i = 0; i < length; ++i) {
+            arrayData[i] = initialElement;
+        }
+    }
+
+    return newArray;
+}
+
+static bool checkArrayElementBounds(ArrayObject* arrayObj, jsize index) {
+    assert(arrayObj != NULL);
+    if (index < 0 || index >= (int) arrayObj->length) {
+        dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, index);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Get one element of an Object array.
+ *
+ * Add the object to the local references table in case the array goes away.
+ */
+static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index) {
+    ScopedJniThreadState ts(env);
+
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    if (!checkArrayElementBounds(arrayObj, index)) {
+        return NULL;
+    }
+
+    Object* value = ((Object**) (void*) arrayObj->contents)[index];
+    return addLocalReference(ts.self(), value);
+}
+
+/*
+ * Set one element of an Object array.
+ */
+static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index, jobject jobj) {
+    ScopedJniThreadState ts(env);
+
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    if (!checkArrayElementBounds(arrayObj, index)) {
+        return;
+    }
+
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+
+    if (obj != NULL && !dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+      ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+            obj->clazz->descriptor, obj,
+            arrayObj->clazz->descriptor, arrayObj);
+      dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+      return;
+    }
+
+    //ALOGV("JNI: set element %d in array %p to %p", index, array, value);
+
+    dvmSetObjectArrayElement(arrayObj, index, obj);
+}
+
+/*
+ * Create a new array of primitive elements.
+ */
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
+    static _artype New##_jname##Array(JNIEnv* env, jsize length) { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = dvmAllocPrimitiveArray(_typechar, length, ALLOC_DEFAULT); \
+        if (arrayObj == NULL) { \
+            return NULL; \
+        } \
+        _artype result = (_artype) addLocalReference(ts.self(), (Object*) arrayObj); \
+        dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
+        return result; \
+    }
+NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
+NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
+NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
+NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
+NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
+NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
+NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
+NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
+
+/*
+ * Get a pointer to a C array of primitive elements from an array object
+ * of the matching type.
+ *
+ * In a compacting GC, we either need to return a copy of the elements or
+ * "pin" the memory.  Otherwise we run the risk of native code using the
+ * buffer as the destination of e.g. a blocking read() call that wakes up
+ * during a GC.
+ */
+#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+    static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
+        _ctype##Array jarr, jboolean* isCopy) \
+    { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+        pinPrimitiveArray(arrayObj); \
+        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
+        if (isCopy != NULL) { \
+            *isCopy = JNI_FALSE; \
+        } \
+        return data; \
+    }
+
+/*
+ * Release the storage locked down by the "get" function.
+ *
+ * The spec says, "'mode' has no effect if 'elems' is not a copy of the
+ * elements in 'array'."  They apparently did not anticipate the need to
+ * un-pin memory.
+ */
+#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                    \
+    static void Release##_jname##ArrayElements(JNIEnv* env,                 \
+        _ctype##Array jarr, _ctype* elems, jint mode)                       \
+    {                                                                       \
+        UNUSED_PARAMETER(elems);                                            \
+        if (mode != JNI_COMMIT) {                                           \
+            ScopedJniThreadState ts(env);                                   \
+            ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+            unpinPrimitiveArray(arrayObj);                                  \
+        }                                                                   \
+    }
+
+static void throwArrayRegionOutOfBounds(ArrayObject* arrayObj, jsize start,
+    jsize len, const char* arrayIdentifier)
+{
+    dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
+        "%s offset=%d length=%d %s.length=%d",
+        arrayObj->clazz->descriptor, start, len, arrayIdentifier,
+        arrayObj->length);
+}
+
+/*
+ * Copy a section of a primitive array to a buffer.
+ */
+#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Get##_jname##ArrayRegion(JNIEnv* env, \
+        _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
+    { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
+        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+            throwArrayRegionOutOfBounds(arrayObj, start, len, "src"); \
+        } else { \
+            memcpy(buf, data + start, len * sizeof(_ctype)); \
+        } \
+    }
+
+/*
+ * Copy a section of a primitive array from a buffer.
+ */
+#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Set##_jname##ArrayRegion(JNIEnv* env, \
+        _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
+    { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
+        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+            throwArrayRegionOutOfBounds(arrayObj, start, len, "dst"); \
+        } else { \
+            memcpy(data + start, buf, len * sizeof(_ctype)); \
+        } \
+    }
+
+/*
+ * 4-in-1:
+ *  Get<Type>ArrayElements
+ *  Release<Type>ArrayElements
+ *  Get<Type>ArrayRegion
+ *  Set<Type>ArrayRegion
+ */
+#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname)                           \
+    GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname);                           \
+    RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname);                       \
+    GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);                             \
+    SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
+
+PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
+PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
+PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
+PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
+PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
+PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
+PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
+PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
+
+/*
+ * Register one or more native functions in one class.
+ *
+ * This can be called multiple times on the same method, allowing the
+ * caller to redefine the method implementation at will.
+ */
+static jint RegisterNatives(JNIEnv* env, jclass jclazz,
+    const JNINativeMethod* methods, jint nMethods)
+{
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (gDvm.verboseJni) {
+        ALOGI("[Registering JNI native methods for class %s]",
+            clazz->descriptor);
+    }
+
+    for (int i = 0; i < nMethods; i++) {
+        if (!dvmRegisterJNIMethod(clazz, methods[i].name,
+                methods[i].signature, methods[i].fnPtr))
+        {
+            return JNI_ERR;
+        }
+    }
+    return JNI_OK;
+}
+
+/*
+ * Un-register all native methods associated with the class.
+ *
+ * The JNI docs refer to this as a way to reload/relink native libraries,
+ * and say it "should not be used in normal native code".  In particular,
+ * there is no need to do this during shutdown, and you do not need to do
+ * this before redefining a method implementation with RegisterNatives.
+ *
+ * It's chiefly useful for a native "plugin"-style library that wasn't
+ * loaded with System.loadLibrary() (since there's no way to unload those).
+ * For example, the library could upgrade itself by:
+ *
+ *  1. call UnregisterNatives to unbind the old methods
+ *  2. ensure that no code is still executing inside it (somehow)
+ *  3. dlclose() the library
+ *  4. dlopen() the new library
+ *  5. use RegisterNatives to bind the methods from the new library
+ *
+ * The above can work correctly without the UnregisterNatives call, but
+ * creates a window of opportunity in which somebody might try to call a
+ * method that is pointing at unmapped memory, crashing the VM.  In theory
+ * the same guards that prevent dlclose() from unmapping executing code could
+ * prevent that anyway, but with this we can be more thorough and also deal
+ * with methods that only exist in the old or new form of the library (maybe
+ * the lib wants to try the call and catch the UnsatisfiedLinkError).
+ */
+static jint UnregisterNatives(JNIEnv* env, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (gDvm.verboseJni) {
+        ALOGI("[Unregistering JNI native methods for class %s]",
+            clazz->descriptor);
+    }
+    dvmUnregisterJNINativeMethods(clazz);
+    return JNI_OK;
+}
+
+/*
+ * Lock the monitor.
+ *
+ * We have to track all monitor enters and exits, so that we can undo any
+ * outstanding synchronization before the thread exits.
+ */
+static jint MonitorEnter(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    dvmLockObject(ts.self(), obj);
+    trackMonitorEnter(ts.self(), obj);
+    return JNI_OK;
+}
+
+/*
+ * Unlock the monitor.
+ *
+ * Throws an IllegalMonitorStateException if the current thread
+ * doesn't own the monitor.  (dvmUnlockObject() takes care of the throw.)
+ *
+ * According to the 1.6 spec, it's legal to call here with an exception
+ * pending.  If this fails, we'll stomp the original exception.
+ */
+static jint MonitorExit(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    bool success = dvmUnlockObject(ts.self(), obj);
+    if (success) {
+        trackMonitorExit(ts.self(), obj);
+    }
+    return success ? JNI_OK : JNI_ERR;
+}
+
+/*
+ * Return the JavaVM interface associated with the current thread.
+ */
+static jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
+    ScopedJniThreadState ts(env);
+    *vm = gDvmJni.jniVm;
+    return (*vm == NULL) ? JNI_ERR : JNI_OK;
+}
+
+/*
+ * Copies "len" Unicode characters, from offset "start".
+ */
+static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, jchar* buf) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    int strLen = strObj->length();
+    if (((start|len) < 0) || (start + len > strLen)) {
+        dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);
+        return;
+    }
+    memcpy(buf, strObj->chars() + start, len * sizeof(u2));
+}
+
+/*
+ * Translates "len" Unicode characters, from offset "start", into
+ * modified UTF-8 encoding.
+ */
+static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, char* buf) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    int strLen = strObj->length();
+    if (((start|len) < 0) || (start + len > strLen)) {
+        dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);
+        return;
+    }
+    dvmGetStringUtfRegion(strObj, start, len, buf);
+}
+
+/*
+ * Get a raw pointer to array data.
+ *
+ * The caller is expected to call "release" before doing any JNI calls
+ * or blocking I/O operations.
+ *
+ * We need to pin the memory or block GC.
+ */
+static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    pinPrimitiveArray(arrayObj);
+    void* data = arrayObj->contents;
+    if (UNLIKELY(isCopy != NULL)) {
+        *isCopy = JNI_FALSE;
+    }
+    return data;
+}
+
+/*
+ * Release an array obtained with GetPrimitiveArrayCritical.
+ */
+static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, void* carray, jint mode) {
+    if (mode != JNI_COMMIT) {
+        ScopedJniThreadState ts(env);
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+        unpinPrimitiveArray(arrayObj);
+    }
+}
+
+/*
+ * Like GetStringChars, but with restricted use.
+ */
+static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+
+    pinPrimitiveArray(strChars);
+
+    const u2* data = strObj->chars();
+    if (isCopy != NULL) {
+        *isCopy = JNI_FALSE;
+    }
+    return (jchar*) data;
+}
+
+/*
+ * Like ReleaseStringChars, but with restricted use.
+ */
+static void ReleaseStringCritical(JNIEnv* env, jstring jstr, const jchar* carray) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+    unpinPrimitiveArray(strChars);
+}
+
+/*
+ * Create a new weak global reference.
+ */
+static jweak NewWeakGlobalRef(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object *obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return (jweak) addWeakGlobalReference(obj);
+}
+
+/*
+ * Delete the specified weak global reference.
+ */
+static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref) {
+    ScopedJniThreadState ts(env);
+    deleteWeakGlobalReference(wref);
+}
+
+/*
+ * Quick check for pending exceptions.
+ *
+ * TODO: we should be able to skip the enter/exit macros here.
+ */
+static jboolean ExceptionCheck(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    return dvmCheckException(ts.self());
+}
+
+/*
+ * Returns the type of the object referred to by "obj".  It can be local,
+ * global, or weak global.
+ *
+ * In the current implementation, references can be global and local at
+ * the same time, so while the return value is accurate it may not tell
+ * the whole story.
+ */
+static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    return dvmGetJNIRefType(ts.self(), jobj);
+}
+
+/*
+ * Allocate and return a new java.nio.ByteBuffer for this block of memory.
+ */
+static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
+    ScopedJniThreadState ts(env);
+
+    if (capacity < 0) {
+        ALOGE("JNI ERROR (app bug): negative buffer capacity: %lld", capacity);
+        ReportJniError();
+    }
+    if (address == NULL && capacity != 0) {
+        ALOGE("JNI ERROR (app bug): non-zero capacity for NULL pointer: %lld", capacity);
+        ReportJniError();
+    }
+
+    /* create an instance of java.nio.ReadWriteDirectByteBuffer */
+    ClassObject* bufferClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
+    if (!dvmIsClassInitialized(bufferClazz) && !dvmInitClass(bufferClazz)) {
+        return NULL;
+    }
+    Object* newObj = dvmAllocObject(bufferClazz, ALLOC_DONT_TRACK);
+    if (newObj == NULL) {
+        return NULL;
+    }
+    /* call the constructor */
+    jobject result = addLocalReference(ts.self(), newObj);
+    JValue unused;
+    dvmCallMethod(ts.self(), gDvm.methJavaNioReadWriteDirectByteBuffer_init,
+            newObj, &unused, (jint) address, (jint) capacity);
+    if (dvmGetException(ts.self()) != NULL) {
+        deleteLocalReference(ts.self(), result);
+        return NULL;
+    }
+    return result;
+}
+
+/*
+ * Get the starting address of the buffer for the specified java.nio.Buffer.
+ *
+ * If this is not a "direct" buffer, we return NULL.
+ */
+static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) {
+    ScopedJniThreadState ts(env);
+
+    // All Buffer objects have an effectiveDirectAddress field.
+    Object* bufObj = dvmDecodeIndirectRef(ts.self(), jbuf);
+    return (void*) dvmGetFieldInt(bufObj, gDvm.offJavaNioBuffer_effectiveDirectAddress);
+}
+
+/*
+ * Get the capacity of the buffer for the specified java.nio.Buffer.
+ *
+ * Returns -1 if the object is not a direct buffer.  (We actually skip
+ * this check, since it's expensive to determine, and just return the
+ * capacity regardless.)
+ */
+static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) {
+    ScopedJniThreadState ts(env);
+
+    /*
+     * The capacity is always in the Buffer.capacity field.
+     *
+     * (The "check" version should verify that this is actually a Buffer,
+     * but we're not required to do so here.)
+     */
+    Object* buf = dvmDecodeIndirectRef(ts.self(), jbuf);
+    return dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI invocation functions
+ * ===========================================================================
+ */
+
+/*
+ * Handle AttachCurrentThread{AsDaemon}.
+ *
+ * We need to make sure the VM is actually running.  For example, if we start
+ * up, issue an Attach, and the VM exits almost immediately, by the time the
+ * attaching happens the VM could already be shutting down.
+ *
+ * It's hard to avoid a race condition here because we don't want to hold
+ * a lock across the entire operation.  What we can do is temporarily
+ * increment the thread count to prevent a VM exit.
+ *
+ * This could potentially still have problems if a daemon thread calls here
+ * while the VM is shutting down.  dvmThreadSelf() will work, since it just
+ * uses pthread TLS, but dereferencing "vm" could fail.  Such is life when
+ * you shut down a VM while threads are still running inside it.
+ *
+ * Remember that some code may call this as a way to find the per-thread
+ * JNIEnv pointer.  Don't do excess work for that case.
+ */
+static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, bool isDaemon) {
+    JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
+
+    /*
+     * Return immediately if we're already one with the VM.
+     */
+    Thread* self = dvmThreadSelf();
+    if (self != NULL) {
+        *p_env = self->jniEnv;
+        return JNI_OK;
+    }
+
+    /*
+     * No threads allowed in zygote mode.
+     */
+    if (gDvm.zygote) {
+        return JNI_ERR;
+    }
+
+    /* increment the count to keep the VM from bailing while we run */
+    dvmLockThreadList(NULL);
+    if (gDvm.nonDaemonThreadCount == 0) {
+        // dead or dying
+        ALOGV("Refusing to attach thread '%s' -- VM is shutting down",
+            (thr_args == NULL) ? "(unknown)" : args->name);
+        dvmUnlockThreadList();
+        return JNI_ERR;
+    }
+    gDvm.nonDaemonThreadCount++;
+    dvmUnlockThreadList();
+
+    /* tweak the JavaVMAttachArgs as needed */
+    JavaVMAttachArgs argsCopy;
+    if (args == NULL) {
+        /* allow the v1.1 calling convention */
+        argsCopy.version = JNI_VERSION_1_2;
+        argsCopy.name = NULL;
+        argsCopy.group = (jobject) dvmGetMainThreadGroup();
+    } else {
+        assert(args->version >= JNI_VERSION_1_2);
+
+        argsCopy.version = args->version;
+        argsCopy.name = args->name;
+        if (args->group != NULL) {
+            argsCopy.group = (jobject) dvmDecodeIndirectRef(NULL, args->group);
+        } else {
+            argsCopy.group = (jobject) dvmGetMainThreadGroup();
+        }
+    }
+
+    bool result = dvmAttachCurrentThread(&argsCopy, isDaemon);
+
+    /* restore the count */
+    dvmLockThreadList(NULL);
+    gDvm.nonDaemonThreadCount--;
+    dvmUnlockThreadList();
+
+    /*
+     * Change the status to indicate that we're out in native code.  This
+     * call is not guarded with state-change macros, so we have to do it
+     * by hand.
+     */
+    if (result) {
+        self = dvmThreadSelf();
+        assert(self != NULL);
+        dvmChangeStatus(self, THREAD_NATIVE);
+        *p_env = self->jniEnv;
+        return JNI_OK;
+    } else {
+        return JNI_ERR;
+    }
+}
+
+/*
+ * Attach the current thread to the VM.  If the thread is already attached,
+ * this is a no-op.
+ */
+static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    return attachThread(vm, p_env, thr_args, false);
+}
+
+/*
+ * Like AttachCurrentThread, but set the "daemon" flag.
+ */
+static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args)
+{
+    return attachThread(vm, p_env, thr_args, true);
+}
+
+/*
+ * Dissociate the current thread from the VM.
+ */
+static jint DetachCurrentThread(JavaVM* vm) {
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        /* not attached, can't do anything */
+        return JNI_ERR;
+    }
+
+    /* switch to "running" to check for suspension */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /* detach the thread */
+    dvmDetachCurrentThread();
+
+    /* (no need to change status back -- we have no status) */
+    return JNI_OK;
+}
+
+/*
+ * If current thread is attached to VM, return the associated JNIEnv.
+ * Otherwise, stuff NULL in and return JNI_EDETACHED.
+ *
+ * JVMTI overloads this by specifying a magic value for "version", so we
+ * do want to check that here.
+ */
+static jint GetEnv(JavaVM* vm, void** env, jint version) {
+    Thread* self = dvmThreadSelf();
+
+    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);
+    }
+    return (*env != NULL) ? JNI_OK : JNI_EDETACHED;
+}
+
+/*
+ * Destroy the VM.  This may be called from any thread.
+ *
+ * If the current thread is attached, wait until the current thread is
+ * the only non-daemon user-level thread.  If the current thread is not
+ * attached, we attach it and do the processing as usual.  (If the attach
+ * fails, it's probably because all the non-daemon threads have already
+ * exited and the VM doesn't want to let us back in.)
+ *
+ * TODO: we don't really deal with the situation where more than one thread
+ * has called here.  One thread wins, the other stays trapped waiting on
+ * the condition variable forever.  Not sure this situation is interesting
+ * in real life.
+ */
+static jint DestroyJavaVM(JavaVM* vm) {
+    JavaVMExt* ext = (JavaVMExt*) vm;
+    if (ext == NULL) {
+        return JNI_ERR;
+    }
+
+    if (gDvm.verboseShutdown) {
+        ALOGD("DestroyJavaVM waiting for non-daemon threads to exit");
+    }
+
+    /*
+     * Sleep on a condition variable until it's okay to exit.
+     */
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        JNIEnv* tmpEnv;
+        if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
+            ALOGV("Unable to reattach main for Destroy; assuming VM is shutting down (count=%d)",
+                gDvm.nonDaemonThreadCount);
+            goto shutdown;
+        } else {
+            ALOGV("Attached to wait for shutdown in Destroy");
+        }
+    }
+    dvmChangeStatus(self, THREAD_VMWAIT);
+
+    dvmLockThreadList(self);
+    gDvm.nonDaemonThreadCount--;    // remove current thread from count
+
+    while (gDvm.nonDaemonThreadCount > 0) {
+        pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
+    }
+
+    dvmUnlockThreadList();
+    self = NULL;
+
+shutdown:
+    // TODO: call System.exit() to run any registered shutdown hooks
+    // (this may not return -- figure out how this should work)
+
+    if (gDvm.verboseShutdown) {
+        ALOGD("DestroyJavaVM shutting VM down");
+    }
+    dvmShutdown();
+
+    // TODO - free resources associated with JNI-attached daemon threads
+    free(ext->envList);
+    free(ext);
+
+    return JNI_OK;
+}
+
+
+/*
+ * ===========================================================================
+ *      Function tables
+ * ===========================================================================
+ */
+
+static const struct JNINativeInterface gNativeInterface = {
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+
+    GetVersion,
+
+    DefineClass,
+    FindClass,
+
+    FromReflectedMethod,
+    FromReflectedField,
+    ToReflectedMethod,
+
+    GetSuperclass,
+    IsAssignableFrom,
+
+    ToReflectedField,
+
+    Throw,
+    ThrowNew,
+    ExceptionOccurred,
+    ExceptionDescribe,
+    ExceptionClear,
+    FatalError,
+
+    PushLocalFrame,
+    PopLocalFrame,
+
+    NewGlobalRef,
+    DeleteGlobalRef,
+    DeleteLocalRef,
+    IsSameObject,
+    NewLocalRef,
+    EnsureLocalCapacity,
+
+    AllocObject,
+    NewObject,
+    NewObjectV,
+    NewObjectA,
+
+    GetObjectClass,
+    IsInstanceOf,
+
+    GetMethodID,
+
+    CallObjectMethod,
+    CallObjectMethodV,
+    CallObjectMethodA,
+    CallBooleanMethod,
+    CallBooleanMethodV,
+    CallBooleanMethodA,
+    CallByteMethod,
+    CallByteMethodV,
+    CallByteMethodA,
+    CallCharMethod,
+    CallCharMethodV,
+    CallCharMethodA,
+    CallShortMethod,
+    CallShortMethodV,
+    CallShortMethodA,
+    CallIntMethod,
+    CallIntMethodV,
+    CallIntMethodA,
+    CallLongMethod,
+    CallLongMethodV,
+    CallLongMethodA,
+    CallFloatMethod,
+    CallFloatMethodV,
+    CallFloatMethodA,
+    CallDoubleMethod,
+    CallDoubleMethodV,
+    CallDoubleMethodA,
+    CallVoidMethod,
+    CallVoidMethodV,
+    CallVoidMethodA,
+
+    CallNonvirtualObjectMethod,
+    CallNonvirtualObjectMethodV,
+    CallNonvirtualObjectMethodA,
+    CallNonvirtualBooleanMethod,
+    CallNonvirtualBooleanMethodV,
+    CallNonvirtualBooleanMethodA,
+    CallNonvirtualByteMethod,
+    CallNonvirtualByteMethodV,
+    CallNonvirtualByteMethodA,
+    CallNonvirtualCharMethod,
+    CallNonvirtualCharMethodV,
+    CallNonvirtualCharMethodA,
+    CallNonvirtualShortMethod,
+    CallNonvirtualShortMethodV,
+    CallNonvirtualShortMethodA,
+    CallNonvirtualIntMethod,
+    CallNonvirtualIntMethodV,
+    CallNonvirtualIntMethodA,
+    CallNonvirtualLongMethod,
+    CallNonvirtualLongMethodV,
+    CallNonvirtualLongMethodA,
+    CallNonvirtualFloatMethod,
+    CallNonvirtualFloatMethodV,
+    CallNonvirtualFloatMethodA,
+    CallNonvirtualDoubleMethod,
+    CallNonvirtualDoubleMethodV,
+    CallNonvirtualDoubleMethodA,
+    CallNonvirtualVoidMethod,
+    CallNonvirtualVoidMethodV,
+    CallNonvirtualVoidMethodA,
+
+    GetFieldID,
+
+    GetObjectField,
+    GetBooleanField,
+    GetByteField,
+    GetCharField,
+    GetShortField,
+    GetIntField,
+    GetLongField,
+    GetFloatField,
+    GetDoubleField,
+    SetObjectField,
+    SetBooleanField,
+    SetByteField,
+    SetCharField,
+    SetShortField,
+    SetIntField,
+    SetLongField,
+    SetFloatField,
+    SetDoubleField,
+
+    GetStaticMethodID,
+
+    CallStaticObjectMethod,
+    CallStaticObjectMethodV,
+    CallStaticObjectMethodA,
+    CallStaticBooleanMethod,
+    CallStaticBooleanMethodV,
+    CallStaticBooleanMethodA,
+    CallStaticByteMethod,
+    CallStaticByteMethodV,
+    CallStaticByteMethodA,
+    CallStaticCharMethod,
+    CallStaticCharMethodV,
+    CallStaticCharMethodA,
+    CallStaticShortMethod,
+    CallStaticShortMethodV,
+    CallStaticShortMethodA,
+    CallStaticIntMethod,
+    CallStaticIntMethodV,
+    CallStaticIntMethodA,
+    CallStaticLongMethod,
+    CallStaticLongMethodV,
+    CallStaticLongMethodA,
+    CallStaticFloatMethod,
+    CallStaticFloatMethodV,
+    CallStaticFloatMethodA,
+    CallStaticDoubleMethod,
+    CallStaticDoubleMethodV,
+    CallStaticDoubleMethodA,
+    CallStaticVoidMethod,
+    CallStaticVoidMethodV,
+    CallStaticVoidMethodA,
+
+    GetStaticFieldID,
+
+    GetStaticObjectField,
+    GetStaticBooleanField,
+    GetStaticByteField,
+    GetStaticCharField,
+    GetStaticShortField,
+    GetStaticIntField,
+    GetStaticLongField,
+    GetStaticFloatField,
+    GetStaticDoubleField,
+
+    SetStaticObjectField,
+    SetStaticBooleanField,
+    SetStaticByteField,
+    SetStaticCharField,
+    SetStaticShortField,
+    SetStaticIntField,
+    SetStaticLongField,
+    SetStaticFloatField,
+    SetStaticDoubleField,
+
+    NewString,
+
+    GetStringLength,
+    GetStringChars,
+    ReleaseStringChars,
+
+    NewStringUTF,
+    GetStringUTFLength,
+    GetStringUTFChars,
+    ReleaseStringUTFChars,
+
+    GetArrayLength,
+    NewObjectArray,
+    GetObjectArrayElement,
+    SetObjectArrayElement,
+
+    NewBooleanArray,
+    NewByteArray,
+    NewCharArray,
+    NewShortArray,
+    NewIntArray,
+    NewLongArray,
+    NewFloatArray,
+    NewDoubleArray,
+
+    GetBooleanArrayElements,
+    GetByteArrayElements,
+    GetCharArrayElements,
+    GetShortArrayElements,
+    GetIntArrayElements,
+    GetLongArrayElements,
+    GetFloatArrayElements,
+    GetDoubleArrayElements,
+
+    ReleaseBooleanArrayElements,
+    ReleaseByteArrayElements,
+    ReleaseCharArrayElements,
+    ReleaseShortArrayElements,
+    ReleaseIntArrayElements,
+    ReleaseLongArrayElements,
+    ReleaseFloatArrayElements,
+    ReleaseDoubleArrayElements,
+
+    GetBooleanArrayRegion,
+    GetByteArrayRegion,
+    GetCharArrayRegion,
+    GetShortArrayRegion,
+    GetIntArrayRegion,
+    GetLongArrayRegion,
+    GetFloatArrayRegion,
+    GetDoubleArrayRegion,
+    SetBooleanArrayRegion,
+    SetByteArrayRegion,
+    SetCharArrayRegion,
+    SetShortArrayRegion,
+    SetIntArrayRegion,
+    SetLongArrayRegion,
+    SetFloatArrayRegion,
+    SetDoubleArrayRegion,
+
+    RegisterNatives,
+    UnregisterNatives,
+
+    MonitorEnter,
+    MonitorExit,
+
+    GetJavaVM,
+
+    GetStringRegion,
+    GetStringUTFRegion,
+
+    GetPrimitiveArrayCritical,
+    ReleasePrimitiveArrayCritical,
+
+    GetStringCritical,
+    ReleaseStringCritical,
+
+    NewWeakGlobalRef,
+    DeleteWeakGlobalRef,
+
+    ExceptionCheck,
+
+    NewDirectByteBuffer,
+    GetDirectBufferAddress,
+    GetDirectBufferCapacity,
+
+    GetObjectRefType
+};
+
+static const struct JNIInvokeInterface gInvokeInterface = {
+    NULL,
+    NULL,
+    NULL,
+
+    DestroyJavaVM,
+    AttachCurrentThread,
+    DetachCurrentThread,
+
+    GetEnv,
+
+    AttachCurrentThreadAsDaemon,
+};
+
+/*
+ * ===========================================================================
+ *      VM/Env creation
+ * ===========================================================================
+ */
+
+/*
+ * Create a new JNIEnv struct and add it to the VM's list.
+ *
+ * "self" will be NULL for the main thread, since the VM hasn't started
+ * yet; the value will be filled in later.
+ */
+JNIEnv* dvmCreateJNIEnv(Thread* self) {
+    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
+
+    //if (self != NULL)
+    //    ALOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);
+
+    assert(vm != NULL);
+
+    JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
+    newEnv->funcTable = &gNativeInterface;
+    if (self != NULL) {
+        dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
+        assert(newEnv->envThreadId != 0);
+    } else {
+        /* make it obvious if we fail to initialize these later */
+        newEnv->envThreadId = 0x77777775;
+        newEnv->self = (Thread*) 0x77777779;
+    }
+    if (gDvmJni.useCheckJni) {
+        dvmUseCheckedJniEnv(newEnv);
+    }
+
+    ScopedPthreadMutexLock lock(&vm->envListLock);
+
+    /* insert at head of list */
+    newEnv->next = vm->envList;
+    assert(newEnv->prev == NULL);
+    if (vm->envList == NULL) {
+        // rare, but possible
+        vm->envList = newEnv;
+    } else {
+        vm->envList->prev = newEnv;
+    }
+    vm->envList = newEnv;
+
+    //if (self != NULL)
+    //    ALOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
+    return (JNIEnv*) newEnv;
+}
+
+/*
+ * Remove a JNIEnv struct from the list and free it.
+ */
+void dvmDestroyJNIEnv(JNIEnv* env) {
+    if (env == NULL) {
+        return;
+    }
+
+    //ALOGI("Ent DestroyJNIEnv: threadid=%d %p", self->threadId, self);
+
+    JNIEnvExt* extEnv = (JNIEnvExt*) env;
+    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
+
+    ScopedPthreadMutexLock lock(&vm->envListLock);
+
+    if (extEnv == vm->envList) {
+        assert(extEnv->prev == NULL);
+        vm->envList = extEnv->next;
+    } else {
+        assert(extEnv->prev != NULL);
+        extEnv->prev->next = extEnv->next;
+    }
+    if (extEnv->next != NULL) {
+        extEnv->next->prev = extEnv->prev;
+    }
+
+    free(env);
+    //ALOGI("Xit DestroyJNIEnv: threadid=%d %p", self->threadId, self);
+}
+
+/*
+ * Enable "checked JNI" after the VM has partially started.  This must
+ * only be called in "zygote" mode, when we have one thread running.
+ *
+ * This doesn't attempt to rewrite the JNI call bridge associated with
+ * native methods, so we won't get those checks for any methods that have
+ * already been resolved.
+ */
+void dvmLateEnableCheckedJni() {
+    JNIEnvExt* extEnv = dvmGetJNIEnvForThread();
+    if (extEnv == NULL) {
+        ALOGE("dvmLateEnableCheckedJni: thread has no JNIEnv");
+        return;
+    }
+    JavaVMExt* extVm = (JavaVMExt*) gDvmJni.jniVm;
+    assert(extVm != NULL);
+
+    if (!gDvmJni.useCheckJni) {
+        ALOGD("Late-enabling CheckJNI");
+        dvmUseCheckedJniVm(extVm);
+        dvmUseCheckedJniEnv(extEnv);
+    } else {
+        ALOGD("Not late-enabling CheckJNI (already on)");
+    }
+}
+
+/*
+ * Not supported.
+ */
+jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
+    return JNI_ERR;
+}
+
+/*
+ * Return a buffer full of created VMs.
+ *
+ * We always have zero or one.
+ */
+jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) {
+    if (gDvmJni.jniVm != NULL) {
+        *nVMs = 1;
+        if (bufLen > 0) {
+            *vmBuf++ = gDvmJni.jniVm;
+        }
+    } else {
+        *nVMs = 0;
+    }
+    return JNI_OK;
+}
+
+/*
+ * Create a new VM instance.
+ *
+ * The current thread becomes the main VM thread.  We return immediately,
+ * which effectively means the caller is executing in a native method.
+ */
+jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+    const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
+    if (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.
+     */
+    JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
+    pVM->funcTable = &gInvokeInterface;
+    pVM->envList = NULL;
+    dvmInitMutex(&pVM->envListLock);
+
+    UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
+    memset(argv.get(), 0, sizeof(char*) * (args->nOptions));
+
+    /*
+     * Convert JNI args to argv.
+     *
+     * We have to pull out vfprintf/exit/abort, because they use the
+     * "extraInfo" field to pass function pointer "hooks" in.  We also
+     * look for the -Xcheck:jni stuff here.
+     */
+    int argc = 0;
+    for (int i = 0; i < args->nOptions; i++) {
+        const char* optStr = args->options[i].optionString;
+        if (optStr == NULL) {
+            dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);
+            return JNI_ERR;
+        } else if (strcmp(optStr, "vfprintf") == 0) {
+            gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
+        } else if (strcmp(optStr, "exit") == 0) {
+            gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
+        } else if (strcmp(optStr, "abort") == 0) {
+            gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
+        } else if (strcmp(optStr, "sensitiveThread") == 0) {
+            gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
+        } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
+            gDvmJni.useCheckJni = true;
+        } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
+            char* jniOpts = strdup(optStr + 10);
+            size_t jniOptCount = 1;
+            for (char* p = jniOpts; *p != 0; ++p) {
+                if (*p == ',') {
+                    ++jniOptCount;
+                    *p = 0;
+                }
+            }
+            char* jniOpt = jniOpts;
+            for (size_t i = 0; i < jniOptCount; ++i) {
+                if (strcmp(jniOpt, "warnonly") == 0) {
+                    gDvmJni.warnOnly = true;
+                } else if (strcmp(jniOpt, "forcecopy") == 0) {
+                    gDvmJni.forceCopy = true;
+                } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {
+                    gDvmJni.logThirdPartyJni = true;
+                } else {
+                    dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",
+                            jniOpt);
+                    return JNI_ERR;
+                }
+                jniOpt += strlen(jniOpt) + 1;
+            }
+            free(jniOpts);
+        } else {
+            /* regular option */
+            argv[argc++] = optStr;
+        }
+    }
+
+    if (gDvmJni.useCheckJni) {
+        dvmUseCheckedJniVm(pVM);
+    }
+
+    if (gDvmJni.jniVm != NULL) {
+        dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
+        return JNI_ERR;
+    }
+    gDvmJni.jniVm = (JavaVM*) pVM;
+
+    /*
+     * Create a JNIEnv for the main thread.  We need to have something set up
+     * here because some of the class initialization we do when starting
+     * up the VM will call into native code.
+     */
+    JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
+
+    /* Initialize VM. */
+    gDvm.initializing = true;
+    std::string status =
+            dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
+    gDvm.initializing = false;
+
+    if (!status.empty()) {
+        free(pEnv);
+        free(pVM);
+        ALOGW("CreateJavaVM failed: %s", status.c_str());
+        return JNI_ERR;
+    }
+
+    /*
+     * Success!  Return stuff to caller.
+     */
+    dvmChangeStatus(NULL, THREAD_NATIVE);
+    *p_env = (JNIEnv*) pEnv;
+    *p_vm = (JavaVM*) pVM;
+    ALOGV("CreateJavaVM succeeded");
+    return JNI_OK;
+}
diff --git a/vm/JniInternal.h b/vm/JniInternal.h
new file mode 100644
index 0000000..8bb5975
--- /dev/null
+++ b/vm/JniInternal.h
@@ -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.
+ */
+/*
+ * JNI innards, common to the regular and "checked" interfaces.
+ */
+#ifndef DALVIK_JNIINTERNAL_H_
+#define DALVIK_JNIINTERNAL_H_
+
+#include "jni.h"
+
+/* system init/shutdown */
+bool dvmJniStartup(void);
+void dvmJniShutdown(void);
+
+/*
+ * Our data structures for JNIEnv and JavaVM.
+ *
+ * Native code thinks it has a pointer to a pointer.  We know better.
+ */
+struct JavaVMExt;
+
+struct JNIEnvExt {
+    const struct JNINativeInterface* funcTable;     /* must be first */
+
+    const struct JNINativeInterface* baseFuncTable;
+
+    u4      envThreadId;
+    Thread* self;
+
+    /* if nonzero, we are in a "critical" JNI call */
+    int     critical;
+
+    struct JNIEnvExt* prev;
+    struct JNIEnvExt* next;
+};
+
+struct JavaVMExt {
+    const struct JNIInvokeInterface* funcTable;     /* must be first */
+
+    const struct JNIInvokeInterface* baseFuncTable;
+
+    /* head of list of JNIEnvs associated with this VM */
+    JNIEnvExt*      envList;
+    pthread_mutex_t envListLock;
+};
+
+/*
+ * Native function return type; used by dvmPlatformInvoke().
+ *
+ * This is part of Method.jniArgInfo, and must fit in 3 bits.
+ * Note: Assembly code in arch/<arch>/Call<arch>.S relies on
+ * the enum values defined here.
+ */
+enum DalvikJniReturnType {
+    DALVIK_JNI_RETURN_VOID = 0,     /* must be zero */
+    DALVIK_JNI_RETURN_FLOAT = 1,
+    DALVIK_JNI_RETURN_DOUBLE = 2,
+    DALVIK_JNI_RETURN_S8 = 3,
+    DALVIK_JNI_RETURN_S4 = 4,
+    DALVIK_JNI_RETURN_S2 = 5,
+    DALVIK_JNI_RETURN_U2 = 6,
+    DALVIK_JNI_RETURN_S1 = 7
+};
+
+#define DALVIK_JNI_NO_ARG_INFO  0x80000000
+#define DALVIK_JNI_RETURN_MASK  0x70000000
+#define DALVIK_JNI_RETURN_SHIFT 28
+#define DALVIK_JNI_COUNT_MASK   0x0f000000
+#define DALVIK_JNI_COUNT_SHIFT  24
+
+
+/*
+ * Pop the JNI local stack when we return from a native method.  "saveArea"
+ * points to the StackSaveArea for the method we're leaving.
+ *
+ * (This may be implemented directly in assembly in mterp, so changes here
+ * may only affect the portable interpreter.)
+ */
+INLINE void dvmPopJniLocals(Thread* self, StackSaveArea* saveArea)
+{
+    self->jniLocalRefTable.segmentState.all = saveArea->xtra.localRefCookie;
+}
+
+/*
+ * Set the envThreadId field.
+ */
+INLINE void dvmSetJniEnvThreadId(JNIEnv* pEnv, Thread* self)
+{
+    ((JNIEnvExt*)pEnv)->envThreadId = self->threadId;
+    ((JNIEnvExt*)pEnv)->self = self;
+}
+
+void dvmCallJNIMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCheckCallJNIMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+
+/*
+ * Configure "method" to use the JNI bridge to call "func".
+ */
+void dvmUseJNIBridge(Method* method, void* func);
+
+
+/*
+ * Enable the "checked" versions.
+ */
+void dvmUseCheckedJniEnv(JNIEnvExt* pEnv);
+void dvmUseCheckedJniVm(JavaVMExt* pVm);
+void dvmLateEnableCheckedJni(void);
+
+/*
+ * Decode a local, global, or weak-global reference.
+ */
+Object* dvmDecodeIndirectRef(Thread* self, jobject jobj);
+
+/*
+ * Verify that a reference passed in from native code is valid.  Returns
+ * an indication of local/global/invalid.
+ */
+jobjectRefType dvmGetJNIRefType(Thread* self, jobject jobj);
+
+/*
+ * Get the last method called on the interp stack.  This is the method
+ * "responsible" for calling into JNI.
+ */
+const Method* dvmGetCurrentJNIMethod(void);
+
+/*
+ * Create/destroy a JNIEnv for the current thread.
+ */
+JNIEnv* dvmCreateJNIEnv(Thread* self);
+void dvmDestroyJNIEnv(JNIEnv* env);
+
+/*
+ * Find the JNIEnv associated with the current thread.
+ */
+JNIEnvExt* dvmGetJNIEnvForThread(void);
+
+/*
+ * Release all MonitorEnter-acquired locks that are still held.  Called at
+ * DetachCurrentThread time.
+ */
+void dvmReleaseJniMonitors(Thread* self);
+
+/*
+ * Dump the contents of the JNI reference tables to the log file.
+ *
+ * The local ref tables associated with other threads are not included.
+ */
+void dvmDumpJniReferenceTables(void);
+
+// Dumps JNI statistics in response to SIGQUIT.
+struct DebugOutputTarget;
+void dvmDumpJniStats(DebugOutputTarget* target);
+
+#endif  // DALVIK_JNIINTERNAL_H_
diff --git a/vm/LinearAlloc.cpp b/vm/LinearAlloc.cpp
new file mode 100644
index 0000000..359893f
--- /dev/null
+++ b/vm/LinearAlloc.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Linear memory allocation, tied to class loaders.
+ */
+#include "Dalvik.h"
+
+#include <sys/mman.h>
+#include <limits.h>
+#include <errno.h>
+
+//#define DISABLE_LINEAR_ALLOC
+
+// Use ashmem to name the LinearAlloc section
+#define USE_ASHMEM 1
+
+#ifdef USE_ASHMEM
+#include <cutils/ashmem.h>
+#endif /* USE_ASHMEM */
+
+/*
+Overview
+
+This is intended to be a simple, fast allocator for "write-once" storage.
+The expectation is that this will hold small allocations that don't change,
+such as parts of classes (vtables, fields, methods, interfaces).  Because
+the lifetime of these items is tied to classes, which in turn are tied
+to class loaders, we associate the storage with a ClassLoader object.
+
+[ We don't yet support class unloading, and our ClassLoader implementation
+is in flux, so for now we just have a single global region and the
+"classLoader" argument is ignored. ]
+
+By storing the data here, rather than on the system heap, we reduce heap
+clutter, speed class loading, reduce the memory footprint (reduced heap
+structure overhead), and most importantly we increase the number of pages
+that remain shared between processes launched in "Zygote mode".
+
+The 4 bytes preceding each block contain the block length.  This allows us
+to support "free" and "realloc" calls in a limited way.  We don't free
+storage once it has been allocated, but in some circumstances it could be
+useful to erase storage to garbage values after a "free" or "realloc".
+(Bad idea if we're trying to share pages.)  We need to align to 8-byte
+boundaries for some architectures, so we have a 50-50 chance of getting
+this for free in a given block.
+
+A NULL value for the "classLoader" argument refers to the bootstrap class
+loader, which is never unloaded (until the VM shuts down).
+
+Because the memory is not expected to be updated, we can use mprotect to
+guard the pages on debug builds.  Handy when tracking down corruption.
+*/
+
+/* alignment for allocations; must be power of 2, and currently >= hdr_xtra */
+#define BLOCK_ALIGN         8
+
+/* default length of memory segment (worst case is probably "dexopt") */
+#define DEFAULT_MAX_LENGTH  (16*1024*1024)
+
+/* leave enough space for a length word */
+#define HEADER_EXTRA        4
+
+/* overload the length word */
+#define LENGTHFLAG_FREE    0x80000000
+#define LENGTHFLAG_RW      0x40000000
+#define LENGTHFLAG_MASK    (~(LENGTHFLAG_FREE|LENGTHFLAG_RW))
+
+
+/* fwd */
+static void checkAllFree(Object* classLoader);
+
+
+/*
+ * Someday, retrieve the linear alloc struct associated with a particular
+ * class loader.  For now, always use the boostrap loader's instance.
+ */
+static inline LinearAllocHdr* getHeader(Object* classLoader)
+{
+    return gDvm.pBootLoaderAlloc;
+}
+
+/*
+ * Convert a pointer to memory to a pointer to the block header (which is
+ * currently just a length word).
+ */
+static inline u4* getBlockHeader(void* mem)
+{
+    return ((u4*) mem) -1;
+}
+
+/*
+ * Create a new linear allocation block.
+ */
+LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return (LinearAllocHdr*) 0x12345;
+#endif
+    LinearAllocHdr* pHdr;
+
+    pHdr = (LinearAllocHdr*) malloc(sizeof(*pHdr));
+
+
+    /*
+     * "curOffset" points to the location of the next pre-block header,
+     * which means we have to advance to the next BLOCK_ALIGN address and
+     * back up.
+     *
+     * Note we leave the first page empty (see below), and start the
+     * first entry on the second page at an offset that ensures the next
+     * chunk of data will be properly aligned.
+     */
+    assert(BLOCK_ALIGN >= HEADER_EXTRA);
+    pHdr->curOffset = pHdr->firstOffset =
+        (BLOCK_ALIGN-HEADER_EXTRA) + SYSTEM_PAGE_SIZE;
+    pHdr->mapLength = DEFAULT_MAX_LENGTH;
+
+#ifdef USE_ASHMEM
+    int fd;
+
+    fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH);
+    if (fd < 0) {
+        ALOGE("ashmem LinearAlloc failed %s", strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+
+    pHdr->mapAddr = (char*)mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE, fd, 0);
+    if (pHdr->mapAddr == MAP_FAILED) {
+        ALOGE("LinearAlloc mmap(%d) failed: %s", pHdr->mapLength,
+            strerror(errno));
+        free(pHdr);
+        close(fd);
+        return NULL;
+    }
+
+    close(fd);
+#else /*USE_ASHMEM*/
+    // MAP_ANON is listed as "deprecated" on Linux,
+    // but MAP_ANONYMOUS is not defined under Mac OS X.
+    pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (pHdr->mapAddr == MAP_FAILED) {
+        ALOGE("LinearAlloc mmap(%d) failed: %s", pHdr->mapLength,
+            strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+#endif /*USE_ASHMEM*/
+
+    /* region expected to begin on a page boundary */
+    assert(((int) pHdr->mapAddr & (SYSTEM_PAGE_SIZE-1)) == 0);
+
+    /* the system should initialize newly-mapped memory to zero */
+    assert(*(u4*) (pHdr->mapAddr + pHdr->curOffset) == 0);
+
+    /*
+     * Disable access to all except starting page.  We will enable pages
+     * as we use them.  This helps prevent bad pointers from working.  The
+     * pages start out PROT_NONE, become read/write while we access them,
+     * then go to read-only after we finish our changes.
+     *
+     * We have to make the first page readable because we have 4 pad bytes,
+     * followed by 4 length bytes, giving an initial offset of 8.  The
+     * generic code below assumes that there could have been a previous
+     * allocation that wrote into those 4 pad bytes, therefore the page
+     * must have been marked readable by the previous allocation.
+     *
+     * We insert an extra page in here to force a break in the memory map
+     * so we can see ourselves more easily in "showmap".  Otherwise this
+     * stuff blends into the neighboring pages.  [TODO: do we still need
+     * the extra page now that we have ashmem?]
+     */
+    if (mprotect(pHdr->mapAddr, pHdr->mapLength, PROT_NONE) != 0) {
+        ALOGW("LinearAlloc init mprotect failed: %s", strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+    if (mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE, SYSTEM_PAGE_SIZE,
+            ENFORCE_READ_ONLY ? PROT_READ : PROT_READ|PROT_WRITE) != 0)
+    {
+        ALOGW("LinearAlloc init mprotect #2 failed: %s", strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+
+    if (ENFORCE_READ_ONLY) {
+        /* allocate the per-page ref count */
+        int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+        pHdr->writeRefCount = (short*)calloc(numPages, sizeof(short));
+        if (pHdr->writeRefCount == NULL) {
+            free(pHdr);
+            return NULL;
+        }
+    }
+
+    dvmInitMutex(&pHdr->lock);
+
+    ALOGV("LinearAlloc: created region at %p-%p",
+        pHdr->mapAddr, pHdr->mapAddr + pHdr->mapLength-1);
+
+    return pHdr;
+}
+
+/*
+ * Destroy a linear allocation area.
+ *
+ * We do a trivial "has everything been freed?" check before unmapping the
+ * memory and freeing the LinearAllocHdr.
+ */
+void dvmLinearAllocDestroy(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+    if (pHdr == NULL)
+        return;
+
+    checkAllFree(classLoader);
+
+    //dvmLinearAllocDump(classLoader);
+
+    if (gDvm.verboseShutdown) {
+        ALOGV("Unmapping linear allocator base=%p", pHdr->mapAddr);
+        ALOGD("LinearAlloc %p used %d of %d (%d%%)",
+            classLoader, pHdr->curOffset, pHdr->mapLength,
+            (pHdr->curOffset * 100) / pHdr->mapLength);
+    }
+
+    if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
+        ALOGW("LinearAlloc munmap(%p, %d) failed: %s",
+            pHdr->mapAddr, pHdr->mapLength, strerror(errno));
+    }
+    free(pHdr);
+}
+
+/*
+ * Allocate "size" bytes of storage, associated with a particular class
+ * loader.
+ *
+ * It's okay for size to be zero.
+ *
+ * We always leave "curOffset" pointing at the next place where we will
+ * store the header that precedes the returned storage.
+ *
+ * This aborts the VM on failure, so it's not necessary to check for a
+ * NULL return value.
+ */
+void* dvmLinearAlloc(Object* classLoader, size_t size)
+{
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+    int startOffset, nextOffset;
+    int lastGoodOff, firstWriteOff, lastWriteOff;
+
+#ifdef DISABLE_LINEAR_ALLOC
+    return calloc(1, size);
+#endif
+
+    LOGVV("--- LinearAlloc(%p, %d)", classLoader, size);
+
+    /*
+     * What we'd like to do is just determine the new end-of-alloc size
+     * and atomic-swap the updated value in.  The trouble is that, the
+     * first time we reach a new page, we need to call mprotect() to
+     * make the page available, and we don't want to call mprotect() on
+     * every allocation.  The troubled situation is:
+     *  - thread A allocs across a page boundary, but gets preempted
+     *    before mprotect() completes
+     *  - thread B allocs within the new page, and doesn't call mprotect()
+     */
+    dvmLockMutex(&pHdr->lock);
+
+    startOffset = pHdr->curOffset;
+    assert(((startOffset + HEADER_EXTRA) & (BLOCK_ALIGN-1)) == 0);
+
+    /*
+     * Compute the new offset.  The old offset points at the address where
+     * we will store the hidden block header, so we advance past that,
+     * add the size of data they want, add another header's worth so we
+     * know we have room for that, and round up to BLOCK_ALIGN.  That's
+     * the next location where we'll put user data.  We then subtract the
+     * chunk header size off so we're back to the header pointer.
+     *
+     * Examples:
+     *   old=12 size=3 new=((12+(4*2)+3+7) & ~7)-4 = 24-4 --> 20
+     *   old=12 size=5 new=((12+(4*2)+5+7) & ~7)-4 = 32-4 --> 28
+     */
+    nextOffset = ((startOffset + HEADER_EXTRA*2 + size + (BLOCK_ALIGN-1))
+                    & ~(BLOCK_ALIGN-1)) - HEADER_EXTRA;
+    LOGVV("--- old=%d size=%d new=%d", startOffset, size, nextOffset);
+
+    if (nextOffset > pHdr->mapLength) {
+        /*
+         * We don't have to abort here.  We could fall back on the system
+         * malloc(), and have our "free" call figure out what to do.  Only
+         * works if the users of these functions actually free everything
+         * they allocate.
+         */
+        ALOGE("LinearAlloc exceeded capacity (%d), last=%d",
+            pHdr->mapLength, (int) size);
+        dvmAbort();
+    }
+
+    /*
+     * Round up "size" to encompass the entire region, including the 0-7
+     * pad bytes before the next chunk header.  This way we get maximum
+     * utility out of "realloc", and when we're doing ENFORCE_READ_ONLY
+     * stuff we always treat the full extent.
+     */
+    size = nextOffset - (startOffset + HEADER_EXTRA);
+    LOGVV("--- (size now %d)", size);
+
+    /*
+     * See if we are starting on or have crossed into a new page.  If so,
+     * call mprotect on the page(s) we're about to write to.  We have to
+     * page-align the start address, but don't have to make the length a
+     * SYSTEM_PAGE_SIZE multiple (but we do it anyway).
+     *
+     * Note that "startOffset" is not the last *allocated* byte, but rather
+     * the offset of the first *unallocated* byte (which we are about to
+     * write the chunk header to).  "nextOffset" is similar.
+     *
+     * If ENFORCE_READ_ONLY is enabled, we have to call mprotect even if
+     * we've written to this page before, because it might be read-only.
+     */
+    lastGoodOff = (startOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+    firstWriteOff = startOffset & ~(SYSTEM_PAGE_SIZE-1);
+    lastWriteOff = (nextOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+    LOGVV("---  lastGood=0x%04x firstWrite=0x%04x lastWrite=0x%04x",
+        lastGoodOff, firstWriteOff, lastWriteOff);
+    if (lastGoodOff != lastWriteOff || ENFORCE_READ_ONLY) {
+        int cc, start, len;
+
+        start = firstWriteOff;
+        assert(start <= nextOffset);
+        len = (lastWriteOff - firstWriteOff) + SYSTEM_PAGE_SIZE;
+
+        LOGVV("---    calling mprotect(start=%d len=%d RW)", start, len);
+        cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
+        if (cc != 0) {
+            ALOGE("LinearAlloc mprotect (+%d %d) failed: %s",
+                start, len, strerror(errno));
+            /* we're going to fail soon, might as do it now */
+            dvmAbort();
+        }
+    }
+
+    /* update the ref counts on the now-writable pages */
+    if (ENFORCE_READ_ONLY) {
+        int i, start, end;
+
+        start = firstWriteOff / SYSTEM_PAGE_SIZE;
+        end = lastWriteOff / SYSTEM_PAGE_SIZE;
+
+        LOGVV("---  marking pages %d-%d RW (alloc %d at %p)",
+            start, end, size, pHdr->mapAddr + startOffset + HEADER_EXTRA);
+        for (i = start; i <= end; i++)
+            pHdr->writeRefCount[i]++;
+    }
+
+    /* stow the size in the header */
+    if (ENFORCE_READ_ONLY)
+        *(u4*)(pHdr->mapAddr + startOffset) = size | LENGTHFLAG_RW;
+    else
+        *(u4*)(pHdr->mapAddr + startOffset) = size;
+
+    /*
+     * Update data structure.
+     */
+    pHdr->curOffset = nextOffset;
+
+    dvmUnlockMutex(&pHdr->lock);
+    return pHdr->mapAddr + startOffset + HEADER_EXTRA;
+}
+
+/*
+ * Helper function, replaces strdup().
+ */
+char* dvmLinearStrdup(Object* classLoader, const char* str)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return strdup(str);
+#endif
+    int len = strlen(str);
+    void* mem = dvmLinearAlloc(classLoader, len+1);
+    memcpy(mem, str, len+1);
+    if (ENFORCE_READ_ONLY)
+        dvmLinearSetReadOnly(classLoader, mem);
+    return (char*) mem;
+}
+
+/*
+ * "Reallocate" a piece of memory.
+ *
+ * If the new size is <= the old size, we return the original pointer
+ * without doing anything.
+ *
+ * If the new size is > the old size, we allocate new storage, copy the
+ * old stuff over, and mark the new stuff as free.
+ */
+void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return realloc(mem, newSize);
+#endif
+    /* make sure we have the right region (and mem != NULL) */
+    assert(mem != NULL);
+    assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+           mem < (void*) (getHeader(classLoader)->mapAddr +
+                          getHeader(classLoader)->curOffset));
+
+    const u4* pLen = getBlockHeader(mem);
+    ALOGV("--- LinearRealloc(%d) old=%d", newSize, *pLen);
+
+    /* handle size reduction case */
+    if (*pLen >= newSize) {
+        if (ENFORCE_READ_ONLY)
+            dvmLinearSetReadWrite(classLoader, mem);
+        return mem;
+    }
+
+    void* newMem;
+
+    newMem = dvmLinearAlloc(classLoader, newSize);
+    assert(newMem != NULL);
+    memcpy(newMem, mem, *pLen);
+    dvmLinearFree(classLoader, mem);
+
+    return newMem;
+}
+
+
+/*
+ * Update the read/write status of one or more pages.
+ */
+static void updatePages(Object* classLoader, void* mem, int direction)
+{
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+    dvmLockMutex(&pHdr->lock);
+
+    /* make sure we have the right region */
+    assert(mem >= (void*) pHdr->mapAddr &&
+           mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
+
+    u4* pLen = getBlockHeader(mem);
+    u4 len = *pLen & LENGTHFLAG_MASK;
+    int firstPage, lastPage;
+
+    firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / SYSTEM_PAGE_SIZE;
+    lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / SYSTEM_PAGE_SIZE;
+    LOGVV("--- updating pages %d-%d (%d)", firstPage, lastPage, direction);
+
+    int i, cc;
+
+    /*
+     * Update individual pages.  We could do some sort of "lazy update" to
+     * combine mprotect calls, but that's almost certainly more trouble
+     * than it's worth.
+     */
+    for (i = firstPage; i <= lastPage; i++) {
+        if (direction < 0) {
+            /*
+             * Trying to mark read-only.
+             */
+            if (i == firstPage) {
+                if ((*pLen & LENGTHFLAG_RW) == 0) {
+                    ALOGW("Double RO on %p", mem);
+                    dvmAbort();
+                } else
+                    *pLen &= ~LENGTHFLAG_RW;
+            }
+
+            if (pHdr->writeRefCount[i] == 0) {
+                ALOGE("Can't make page %d any less writable", i);
+                dvmAbort();
+            }
+            pHdr->writeRefCount[i]--;
+            if (pHdr->writeRefCount[i] == 0) {
+                LOGVV("---  prot page %d RO", i);
+                cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+                        SYSTEM_PAGE_SIZE, PROT_READ);
+                assert(cc == 0);
+            }
+        } else {
+            /*
+             * Trying to mark writable.
+             */
+            if (pHdr->writeRefCount[i] >= 32767) {
+                ALOGE("Can't make page %d any more writable", i);
+                dvmAbort();
+            }
+            if (pHdr->writeRefCount[i] == 0) {
+                LOGVV("---  prot page %d RW", i);
+                cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+                        SYSTEM_PAGE_SIZE, PROT_READ | PROT_WRITE);
+                assert(cc == 0);
+            }
+            pHdr->writeRefCount[i]++;
+
+            if (i == firstPage) {
+                if ((*pLen & LENGTHFLAG_RW) != 0) {
+                    ALOGW("Double RW on %p", mem);
+                    dvmAbort();
+                } else
+                    *pLen |= LENGTHFLAG_RW;
+            }
+        }
+    }
+
+    dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Try to mark the pages in which a chunk of memory lives as read-only.
+ * Whether or not the pages actually change state depends on how many
+ * others are trying to access the same pages.
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadOnly(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    updatePages(classLoader, mem, -1);
+}
+
+/*
+ * Make the pages on which "mem" sits read-write.
+ *
+ * This covers the header as well as the data itself.  (We could add a
+ * "header-only" mode for dvmLinearFree.)
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadWrite(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    updatePages(classLoader, mem, 1);
+}
+
+/*
+ * Mark an allocation as free.
+ */
+void dvmLinearFree(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    free(mem);
+    return;
+#endif
+    if (mem == NULL)
+        return;
+
+    /* make sure we have the right region */
+    assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+           mem < (void*) (getHeader(classLoader)->mapAddr +
+                          getHeader(classLoader)->curOffset));
+
+    if (ENFORCE_READ_ONLY)
+        dvmLinearSetReadWrite(classLoader, mem);
+
+    u4* pLen = getBlockHeader(mem);
+    *pLen |= LENGTHFLAG_FREE;
+
+    if (ENFORCE_READ_ONLY)
+        dvmLinearSetReadOnly(classLoader, mem);
+}
+
+/*
+ * For debugging, dump the contents of a linear alloc area.
+ *
+ * We grab the lock so that the header contents and list output are
+ * consistent.
+ */
+void dvmLinearAllocDump(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+
+    dvmLockMutex(&pHdr->lock);
+
+    ALOGI("LinearAlloc classLoader=%p", classLoader);
+    ALOGI("  mapAddr=%p mapLength=%d firstOffset=%d",
+        pHdr->mapAddr, pHdr->mapLength, pHdr->firstOffset);
+    ALOGI("  curOffset=%d", pHdr->curOffset);
+
+    int off = pHdr->firstOffset;
+    u4 rawLen, fullLen;
+
+    while (off < pHdr->curOffset) {
+        rawLen = *(u4*) (pHdr->mapAddr + off);
+        fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+                    & ~(BLOCK_ALIGN-1));
+
+        ALOGI("  %p (%3d): %clen=%d%s", pHdr->mapAddr + off + HEADER_EXTRA,
+            (int) ((off + HEADER_EXTRA) / SYSTEM_PAGE_SIZE),
+            (rawLen & LENGTHFLAG_FREE) != 0 ? '*' : ' ',
+            rawLen & LENGTHFLAG_MASK,
+            (rawLen & LENGTHFLAG_RW) != 0 ? " [RW]" : "");
+
+        off += fullLen;
+    }
+
+    if (ENFORCE_READ_ONLY) {
+        ALOGI("writeRefCount map:");
+
+        int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+        int zstart = 0;
+        int i;
+
+        for (i = 0; i < numPages; i++) {
+            int count = pHdr->writeRefCount[i];
+
+            if (count != 0) {
+                if (zstart < i-1)
+                    printf(" %d-%d: zero\n", zstart, i-1);
+                else if (zstart == i-1)
+                    printf(" %d: zero\n", zstart);
+                zstart = i+1;
+                printf(" %d: %d\n", i, count);
+            }
+        }
+        if (zstart < i)
+            printf(" %d-%d: zero\n", zstart, i-1);
+    }
+
+    ALOGD("LinearAlloc %p using %d of %d (%d%%)",
+        classLoader, pHdr->curOffset, pHdr->mapLength,
+        (pHdr->curOffset * 100) / pHdr->mapLength);
+
+    dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Verify that all blocks are freed.
+ *
+ * This should only be done as we're shutting down, but there could be a
+ * daemon thread that's still trying to do something, so we grab the locks.
+ */
+static void checkAllFree(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+
+    dvmLockMutex(&pHdr->lock);
+
+    int off = pHdr->firstOffset;
+    u4 rawLen, fullLen;
+
+    while (off < pHdr->curOffset) {
+        rawLen = *(u4*) (pHdr->mapAddr + off);
+        fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+                    & ~(BLOCK_ALIGN-1));
+
+        if ((rawLen & LENGTHFLAG_FREE) == 0) {
+            ALOGW("LinearAlloc %p not freed: %p len=%d", classLoader,
+                pHdr->mapAddr + off + HEADER_EXTRA, rawLen & LENGTHFLAG_MASK);
+        }
+
+        off += fullLen;
+    }
+
+    dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Determine if [start, start+length) is contained in the in-use area of
+ * a single LinearAlloc.  The full set of linear allocators is scanned.
+ *
+ * [ Since we currently only have one region, this is pretty simple.  In
+ * the future we'll need to traverse a table of class loaders. ]
+ */
+bool dvmLinearAllocContains(const void* start, size_t length)
+{
+    LinearAllocHdr* pHdr = getHeader(NULL);
+
+    if (pHdr == NULL)
+        return false;
+
+    return (char*) start >= pHdr->mapAddr &&
+           ((char*)start + length) <= (pHdr->mapAddr + pHdr->curOffset);
+}
diff --git a/vm/LinearAlloc.h b/vm/LinearAlloc.h
new file mode 100644
index 0000000..743b7cc
--- /dev/null
+++ b/vm/LinearAlloc.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Simple linear memory allocator.
+ */
+#ifndef DALVIK_LINEARALLOC_H_
+#define DALVIK_LINEARALLOC_H_
+
+/*
+ * If this is set, we create additional data structures and make many
+ * additional mprotect() calls.
+ */
+#define ENFORCE_READ_ONLY   false
+
+/*
+ * Linear allocation state.  We could tuck this into the start of the
+ * allocated region, but that would prevent us from sharing the rest of
+ * that first page.
+ */
+struct LinearAllocHdr {
+    int     curOffset;          /* offset where next data goes */
+    pthread_mutex_t lock;       /* controls updates to this struct */
+
+    char*   mapAddr;            /* start of mmap()ed region */
+    int     mapLength;          /* length of region */
+    int     firstOffset;        /* for chasing through */
+
+    short*  writeRefCount;      /* for ENFORCE_READ_ONLY */
+};
+
+
+/*
+ * Create a new alloc region.
+ */
+LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader);
+
+/*
+ * Destroy a region.
+ */
+void dvmLinearAllocDestroy(Object* classLoader);
+
+/*
+ * Allocate a chunk of memory.  The memory will be zeroed out.
+ *
+ * For ENFORCE_READ_ONLY, call dvmLinearReadOnly on the result.
+ */
+void* dvmLinearAlloc(Object* classLoader, size_t size);
+
+/*
+ * Reallocate a chunk.  The original storage is not released, but may be
+ * erased to aid debugging.
+ *
+ * For ENFORCE_READ_ONLY, call dvmLinearReadOnly on the result.  Also, the
+ * caller should probably mark the "mem" argument read-only before calling.
+ */
+void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize);
+
+/* don't call these directly */
+void dvmLinearSetReadOnly(Object* classLoader, void* mem);
+void dvmLinearSetReadWrite(Object* classLoader, void* mem);
+
+/*
+ * Mark a chunk of memory from Alloc or Realloc as read-only.  This must
+ * be done after all changes to the block of memory have been made.  This
+ * actually operates on a page granularity.
+ */
+INLINE void dvmLinearReadOnly(Object* classLoader, void* mem)
+{
+    if (ENFORCE_READ_ONLY && mem != NULL)
+        dvmLinearSetReadOnly(classLoader, mem);
+}
+
+/*
+ * Make a chunk of memory writable again.
+ */
+INLINE void dvmLinearReadWrite(Object* classLoader, void* mem)
+{
+    if (ENFORCE_READ_ONLY && mem != NULL)
+        dvmLinearSetReadWrite(classLoader, mem);
+}
+
+/*
+ * Free a chunk.  Does not increase available storage, but the freed area
+ * may be erased to aid debugging.
+ */
+void dvmLinearFree(Object* classLoader, void* mem);
+
+/*
+ * Helper function; allocates new storage and copies "str" into it.
+ *
+ * For ENFORCE_READ_ONLY, do *not* call dvmLinearReadOnly on the result.
+ * This is done automatically.
+ */
+char* dvmLinearStrdup(Object* classLoader, const char* str);
+
+/*
+ * Dump the contents of a linear alloc area.
+ */
+void dvmLinearAllocDump(Object* classLoader);
+
+/*
+ * Determine if [start, start+length) is contained in the in-use area of
+ * a single LinearAlloc.  The full set of linear allocators is scanned.
+ */
+bool dvmLinearAllocContains(const void* start, size_t length);
+
+#endif  // DALVIK_LINEARALLOC_H_
diff --git a/vm/Misc.cpp b/vm/Misc.cpp
new file mode 100644
index 0000000..057b90d
--- /dev/null
+++ b/vm/Misc.cpp
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+
+/*
+ * Print a hex dump in this format:
+ *
+01234567: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef\n
+ *
+ * If "mode" is kHexDumpLocal, we start at offset zero, and show a full
+ * 16 bytes on the first line.  If it's kHexDumpMem, we make this look
+ * like a memory dump, using the actual address, outputting a partial line
+ * if "vaddr" isn't aligned on a 16-byte boundary.
+ *
+ * "priority" and "tag" determine the values passed to the log calls.
+ *
+ * Does not use printf() or other string-formatting calls.
+ */
+void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
+    size_t length, HexDumpMode mode)
+{
+    static const char gHexDigit[] = "0123456789abcdef";
+    const unsigned char* addr = (const unsigned char*)vaddr;
+    char out[77];           /* exact fit */
+    unsigned int offset;    /* offset to show while printing */
+    char* hex;
+    char* asc;
+    int gap;
+    //int trickle = 0;
+
+    if (mode == kHexDumpLocal)
+        offset = 0;
+    else
+        offset = (int) addr;
+
+    memset(out, ' ', sizeof(out)-1);
+    out[8] = ':';
+    out[sizeof(out)-2] = '\n';
+    out[sizeof(out)-1] = '\0';
+
+    gap = (int) offset & 0x0f;
+    while (length) {
+        unsigned int lineOffset = offset & ~0x0f;
+        int i, count;
+
+        hex = out;
+        asc = out + 59;
+
+        for (i = 0; i < 8; i++) {
+            *hex++ = gHexDigit[lineOffset >> 28];
+            lineOffset <<= 4;
+        }
+        hex++;
+        hex++;
+
+        count = ((int)length > 16-gap) ? 16-gap : (int)length; /* cap length */
+        assert(count != 0);
+        assert(count+gap <= 16);
+
+        if (gap) {
+            /* only on first line */
+            hex += gap * 3;
+            asc += gap;
+        }
+
+        for (i = gap ; i < count+gap; i++) {
+            *hex++ = gHexDigit[*addr >> 4];
+            *hex++ = gHexDigit[*addr & 0x0f];
+            hex++;
+            if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/)
+                *asc++ = *addr;
+            else
+                *asc++ = '.';
+            addr++;
+        }
+        for ( ; i < 16; i++) {
+            /* erase extra stuff; only happens on last line */
+            *hex++ = ' ';
+            *hex++ = ' ';
+            hex++;
+            *asc++ = ' ';
+        }
+
+        LOG_PRI(priority, tag, "%s", out);
+#if 0 //def HAVE_ANDROID_OS
+        /*
+         * We can overrun logcat easily by writing at full speed.  On the
+         * other hand, we can make Eclipse time out if we're showing
+         * packet dumps while debugging JDWP.
+         */
+        {
+            if (trickle++ == 8) {
+                trickle = 0;
+                usleep(20000);
+            }
+        }
+#endif
+
+        gap = 0;
+        length -= count;
+        offset += count;
+    }
+}
+
+
+/*
+ * Fill out a DebugOutputTarget, suitable for printing to the log.
+ */
+void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
+    const char* tag)
+{
+    assert(target != NULL);
+    assert(tag != NULL);
+
+    target->which = kDebugTargetLog;
+    target->data.log.priority = priority;
+    target->data.log.tag = tag;
+}
+
+/*
+ * Fill out a DebugOutputTarget suitable for printing to a file pointer.
+ */
+void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp)
+{
+    assert(target != NULL);
+    assert(fp != NULL);
+
+    target->which = kDebugTargetFile;
+    target->data.file.fp = fp;
+}
+
+/*
+ * Free "target" and any associated data.
+ */
+void dvmFreeOutputTarget(DebugOutputTarget* target)
+{
+    free(target);
+}
+
+/*
+ * Print a debug message, to either a file or the log.
+ */
+void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
+    ...)
+{
+    va_list args;
+
+    va_start(args, format);
+
+    switch (target->which) {
+    case kDebugTargetLog:
+        LOG_PRI_VA(target->data.log.priority, target->data.log.tag,
+            format, args);
+        break;
+    case kDebugTargetFile:
+        vfprintf(target->data.file.fp, format, args);
+        break;
+    default:
+        ALOGE("unexpected 'which' %d", target->which);
+        break;
+    }
+
+    va_end(args);
+}
+
+
+/*
+ * Return a newly-allocated string in which all occurrences of '.' have
+ * been changed to '/'.  If we find a '/' in the original string, NULL
+ * is returned to avoid ambiguity.
+ */
+char* dvmDotToSlash(const char* str)
+{
+    char* newStr = strdup(str);
+    char* cp = newStr;
+
+    if (newStr == NULL)
+        return NULL;
+
+    while (*cp != '\0') {
+        if (*cp == '/') {
+            assert(false);
+            return NULL;
+        }
+        if (*cp == '.')
+            *cp = '/';
+        cp++;
+    }
+
+    return newStr;
+}
+
+std::string dvmHumanReadableDescriptor(const char* descriptor) {
+    // Count the number of '['s to get the dimensionality.
+    const char* c = descriptor;
+    size_t dim = 0;
+    while (*c == '[') {
+        dim++;
+        c++;
+    }
+
+    // Reference or primitive?
+    if (*c == 'L') {
+        // "[[La/b/C;" -> "a.b.C[][]".
+        c++; // Skip the 'L'.
+    } else {
+        // "[[B" -> "byte[][]".
+        // To make life easier, we make primitives look like unqualified
+        // reference types.
+        switch (*c) {
+        case 'B': c = "byte;"; break;
+        case 'C': c = "char;"; break;
+        case 'D': c = "double;"; break;
+        case 'F': c = "float;"; break;
+        case 'I': c = "int;"; break;
+        case 'J': c = "long;"; break;
+        case 'S': c = "short;"; break;
+        case 'Z': c = "boolean;"; break;
+        default: return descriptor;
+        }
+    }
+
+    // At this point, 'c' is a string of the form "fully/qualified/Type;"
+    // or "primitive;". Rewrite the type with '.' instead of '/':
+    std::string result;
+    const char* p = c;
+    while (*p != ';') {
+        char ch = *p++;
+        if (ch == '/') {
+          ch = '.';
+        }
+        result.push_back(ch);
+    }
+    // ...and replace the semicolon with 'dim' "[]" pairs:
+    while (dim--) {
+        result += "[]";
+    }
+    return result;
+}
+
+std::string dvmHumanReadableType(const Object* obj)
+{
+    if (obj == NULL) {
+        return "null";
+    }
+    if (obj->clazz == NULL) {
+        /* should only be possible right after a plain dvmMalloc() */
+        return "(raw)";
+    }
+    std::string result(dvmHumanReadableDescriptor(obj->clazz->descriptor));
+    if (dvmIsClassObject(obj)) {
+        const ClassObject* clazz = reinterpret_cast<const ClassObject*>(obj);
+        result += "<" + dvmHumanReadableDescriptor(clazz->descriptor) + ">";
+    }
+    return result;
+}
+
+std::string dvmHumanReadableField(const Field* field)
+{
+    if (field == NULL) {
+        return "(null)";
+    }
+    std::string result(dvmHumanReadableDescriptor(field->clazz->descriptor));
+    result += '.';
+    result += field->name;
+    return result;
+}
+
+std::string dvmHumanReadableMethod(const Method* method, bool withSignature)
+{
+    if (method == NULL) {
+        return "(null)";
+    }
+    std::string result(dvmHumanReadableDescriptor(method->clazz->descriptor));
+    result += '.';
+    result += method->name;
+    if (withSignature) {
+        // TODO: the types in this aren't human readable!
+        char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+        result += signature;
+        free(signature);
+    }
+    return result;
+}
+
+/*
+ * Return a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ * See also dvmHumanReadableDescriptor.
+ */
+char* dvmDescriptorToDot(const char* str)
+{
+    size_t at = strlen(str);
+    char* newStr;
+
+    if ((at >= 2) && (str[0] == 'L') && (str[at - 1] == ';')) {
+        at -= 2; /* Two fewer chars to copy. */
+        str++; /* Skip the 'L'. */
+    }
+
+    newStr = (char*)malloc(at + 1); /* Add one for the '\0'. */
+    if (newStr == NULL)
+        return NULL;
+
+    newStr[at] = '\0';
+
+    while (at > 0) {
+        at--;
+        newStr[at] = (str[at] == '/') ? '.' : str[at];
+    }
+
+    return newStr;
+}
+
+/*
+ * Return a newly-allocated string for the type descriptor
+ * corresponding to the "dot version" of the given class name. That
+ * is, non-array names are surrounded by "L" and ";", and all
+ * occurrences of '.' have been changed to '/'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ */
+char* dvmDotToDescriptor(const char* str)
+{
+    size_t length = strlen(str);
+    int wrapElSemi = 0;
+    char* newStr;
+    char* at;
+
+    if (str[0] != '[') {
+        length += 2; /* for "L" and ";" */
+        wrapElSemi = 1;
+    }
+
+    newStr = at = (char*)malloc(length + 1); /* + 1 for the '\0' */
+
+    if (newStr == NULL) {
+        return NULL;
+    }
+
+    if (wrapElSemi) {
+        *(at++) = 'L';
+    }
+
+    while (*str) {
+        char c = *(str++);
+        if (c == '.') {
+            c = '/';
+        }
+        *(at++) = c;
+    }
+
+    if (wrapElSemi) {
+        *(at++) = ';';
+    }
+
+    *at = '\0';
+    return newStr;
+}
+
+/*
+ * Return a newly-allocated string for the internal-form class name for
+ * the given type descriptor. That is, the initial "L" and final ";" (if
+ * any) have been removed.
+ */
+char* dvmDescriptorToName(const char* str)
+{
+    if (str[0] == 'L') {
+        size_t length = strlen(str) - 1;
+        char* newStr = (char*)malloc(length);
+
+        if (newStr == NULL) {
+            return NULL;
+        }
+
+        strlcpy(newStr, str + 1, length);
+        return newStr;
+    }
+
+    return strdup(str);
+}
+
+/*
+ * Return a newly-allocated string for the type descriptor for the given
+ * internal-form class name. That is, a non-array class name will get
+ * surrounded by "L" and ";", while array names are left as-is.
+ */
+char* dvmNameToDescriptor(const char* str)
+{
+    if (str[0] != '[') {
+        size_t length = strlen(str);
+        char* descriptor = (char*)malloc(length + 3);
+
+        if (descriptor == NULL) {
+            return NULL;
+        }
+
+        descriptor[0] = 'L';
+        strcpy(descriptor + 1, str);
+        descriptor[length + 1] = ';';
+        descriptor[length + 2] = '\0';
+
+        return descriptor;
+    }
+
+    return strdup(str);
+}
+
+/*
+ * Get a notion of the current time, in nanoseconds.  This is meant for
+ * computing durations (e.g. "operation X took 52nsec"), so the result
+ * should not be used to get the current date/time.
+ */
+u8 dvmGetRelativeTimeNsec()
+{
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    return (u8)now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
+#endif
+}
+
+/*
+ * Get the per-thread CPU time, in nanoseconds.
+ *
+ * Only useful for time deltas.
+ */
+u8 dvmGetThreadCpuTimeNsec()
+{
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
+    return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+    return (u8) -1;
+#endif
+}
+
+/*
+ * Get the per-thread CPU time, in nanoseconds, for the specified thread.
+ */
+u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread)
+{
+#if 0 /*def HAVE_POSIX_CLOCKS*/
+    int clockId;
+
+    if (pthread_getcpuclockid(thread, &clockId) != 0)
+        return (u8) -1;
+
+    struct timespec now;
+    clock_gettime(clockId, &now);
+    return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+    return (u8) -1;
+#endif
+}
+
+
+/*
+ * Call this repeatedly, with successively higher values for "iteration",
+ * to sleep for a period of time not to exceed "maxTotalSleep".
+ *
+ * For example, when called with iteration==0 we will sleep for a very
+ * brief time.  On the next call we will sleep for a longer time.  When
+ * the sum total of all sleeps reaches "maxTotalSleep", this returns false.
+ *
+ * The initial start time value for "relStartTime" MUST come from the
+ * dvmGetRelativeTimeUsec call.  On the device this must come from the
+ * monotonic clock source, not the wall clock.
+ *
+ * This should be used wherever you might be tempted to call sched_yield()
+ * in a loop.  The problem with sched_yield is that, for a high-priority
+ * thread, the kernel might not actually transfer control elsewhere.
+ *
+ * Returns "false" if we were unable to sleep because our time was up.
+ */
+bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime)
+{
+    /*
+     * Minimum sleep is one millisecond, it is important to keep this value
+     * low to ensure short GC pauses since dvmSuspendAllThreads() uses this
+     * function.
+     */
+    const int minSleep = 1000;
+    u8 curTime;
+    int curDelay;
+
+    /*
+     * Get current time, and see if we've already exceeded the limit.
+     */
+    curTime = dvmGetRelativeTimeUsec();
+    if (curTime >= relStartTime + maxTotalSleep) {
+        LOGVV("exsl: sleep exceeded (start=%llu max=%d now=%llu)",
+            relStartTime, maxTotalSleep, curTime);
+        return false;
+    }
+
+    /*
+     * Compute current delay.  We're bounded by "maxTotalSleep", so no
+     * real risk of overflow assuming "usleep" isn't returning early.
+     * (Besides, 2^30 usec is about 18 minutes by itself.)
+     *
+     * For iteration==0 we just call sched_yield(), so the first sleep
+     * at iteration==1 is actually (minSleep * 2).
+     */
+    curDelay = minSleep;
+    while (iteration-- > 0)
+        curDelay *= 2;
+    assert(curDelay > 0);
+
+    if (curTime + curDelay >= relStartTime + maxTotalSleep) {
+        LOGVV("exsl: reduced delay from %d to %d",
+            curDelay, (int) ((relStartTime + maxTotalSleep) - curTime));
+        curDelay = (int) ((relStartTime + maxTotalSleep) - curTime);
+    }
+
+    if (iteration == 0) {
+        LOGVV("exsl: yield");
+        sched_yield();
+    } else {
+        LOGVV("exsl: sleep for %d", curDelay);
+        usleep(curDelay);
+    }
+    return true;
+}
+
+
+/*
+ * Set the "close on exec" flag so we don't expose our file descriptors
+ * to processes launched by us.
+ */
+bool dvmSetCloseOnExec(int fd)
+{
+    int flags;
+
+    /*
+     * There's presently only one flag defined, so getting the previous
+     * value of the fd flags is probably unnecessary.
+     */
+    flags = fcntl(fd, F_GETFD);
+    if (flags < 0) {
+        ALOGW("Unable to get fd flags for fd %d", fd);
+        return false;
+    }
+    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+        ALOGW("Unable to set close-on-exec for fd %d", fd);
+        return false;
+    }
+    return true;
+}
+
+#if (!HAVE_STRLCPY)
+/* Implementation of strlcpy() for platforms that don't already have it. */
+size_t strlcpy(char *dst, const char *src, size_t size) {
+    size_t srcLength = strlen(src);
+    size_t copyLength = srcLength;
+
+    if (srcLength > (size - 1)) {
+        copyLength = size - 1;
+    }
+
+    if (size != 0) {
+        strncpy(dst, src, copyLength);
+        dst[copyLength] = '\0';
+    }
+
+    return srcLength;
+}
+#endif
+
+/*
+ *  Allocates a memory region using ashmem and mmap, initialized to
+ *  zero.  Actual allocation rounded up to page multiple.  Returns
+ *  NULL on failure.
+ */
+void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
+    void *base;
+    int fd, ret;
+
+    byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
+    fd = ashmem_create_region(name, byteCount);
+    if (fd == -1) {
+        return NULL;
+    }
+    base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
+    ret = close(fd);
+    if (base == MAP_FAILED) {
+        return NULL;
+    }
+    if (ret == -1) {
+        munmap(base, byteCount);
+        return NULL;
+    }
+    return base;
+}
+
+/*
+ * Get some per-thread stats.
+ *
+ * This is currently generated by opening the appropriate "stat" file
+ * in /proc and reading the pile of stuff that comes out.
+ */
+bool dvmGetThreadStats(ProcStatData* pData, pid_t tid)
+{
+    /*
+    int pid;
+    char comm[128];
+    char state;
+    int ppid, pgrp, session, tty_nr, tpgid;
+    unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
+    long cutime, cstime, priority, nice, zero, itrealvalue;
+    unsigned long starttime, vsize;
+    long rss;
+    unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
+    unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
+    int exit_signal, processor;
+    unsigned long rt_priority, policy;
+
+    scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
+          "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
+          "%lu %lu %lu %d %d %lu %lu",
+        &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
+        &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
+        &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
+        &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
+        &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
+        &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
+        &rt_priority, &policy);
+
+        (new: delayacct_blkio_ticks %llu (since Linux 2.6.18))
+    */
+
+    char nameBuf[64];
+    int i, fd;
+
+    /*
+     * Open and read the appropriate file.  This is expected to work on
+     * Linux but will fail on other platforms (e.g. Mac sim).
+     */
+    sprintf(nameBuf, "/proc/self/task/%d/stat", (int) tid);
+    fd = open(nameBuf, O_RDONLY);
+    if (fd < 0) {
+        ALOGV("Unable to open '%s': %s", nameBuf, strerror(errno));
+        return false;
+    }
+
+    char lineBuf[512];      /* > 2x typical */
+    int cc = read(fd, lineBuf, sizeof(lineBuf)-1);
+    if (cc <= 0) {
+        const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
+        ALOGI("Unable to read '%s': %s", nameBuf, msg);
+        close(fd);
+        return false;
+    }
+    close(fd);
+    lineBuf[cc] = '\0';
+
+    /*
+     * Skip whitespace-separated tokens.  For the most part we can assume
+     * that tokens do not contain spaces, and are separated by exactly one
+     * space character.  The only exception is the second field ("comm")
+     * which may contain spaces but is surrounded by parenthesis.
+     */
+    char* cp = strchr(lineBuf, ')');
+    if (cp == NULL)
+        goto parse_fail;
+    cp += 2;
+    pData->state = *cp++;
+
+    for (i = 3; i < 13; i++) {
+        cp = strchr(cp+1, ' ');
+        if (cp == NULL)
+            goto parse_fail;
+    }
+
+    /*
+     * Grab utime/stime.
+     */
+    char* endp;
+    pData->utime = strtoul(cp+1, &endp, 10);
+    if (endp == cp+1)
+        ALOGI("Warning: strtoul failed on utime ('%.30s...')", cp);
+
+    cp = strchr(cp+1, ' ');
+    if (cp == NULL)
+        goto parse_fail;
+
+    pData->stime = strtoul(cp+1, &endp, 10);
+    if (endp == cp+1)
+        ALOGI("Warning: strtoul failed on stime ('%.30s...')", cp);
+
+    /*
+     * Skip more stuff we don't care about.
+     */
+    for (i = 14; i < 38; i++) {
+        cp = strchr(cp+1, ' ');
+        if (cp == NULL)
+            goto parse_fail;
+    }
+
+    /*
+     * Grab processor number.
+     */
+    pData->processor = strtol(cp+1, &endp, 10);
+    if (endp == cp+1)
+        ALOGI("Warning: strtoul failed on processor ('%.30s...')", cp);
+
+    return true;
+
+parse_fail:
+    ALOGI("stat parse failed (%s)", lineBuf);
+    return false;
+}
+
+/* documented in header file */
+const char* dvmPathToAbsolutePortion(const char* path) {
+    if (path == NULL) {
+        return NULL;
+    }
+
+    if (path[0] == '/') {
+        /* It's a regular absolute path. Return it. */
+        return path;
+    }
+
+    const char* sentinel = strstr(path, "/./");
+
+    if (sentinel != NULL) {
+        /* It's got the sentinel. Return a pointer to the second slash. */
+        return sentinel + 2;
+    }
+
+    return NULL;
+}
+
+// From RE2.
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+    // First try with a small fixed size buffer
+    char space[1024];
+
+    // It's possible for methods that use a va_list to invalidate
+    // the data in it upon use.  The fix is to make a copy
+    // of the structure before using it and use that copy instead.
+    va_list backup_ap;
+    va_copy(backup_ap, ap);
+    int result = vsnprintf(space, sizeof(space), format, backup_ap);
+    va_end(backup_ap);
+
+    if ((result >= 0) && ((size_t) result < sizeof(space))) {
+        // It fit
+        dst->append(space, result);
+        return;
+    }
+
+    // Repeatedly increase buffer size until it fits
+    int length = sizeof(space);
+    while (true) {
+        if (result < 0) {
+            // Older behavior: just try doubling the buffer size
+            length *= 2;
+        } else {
+            // We need exactly "result+1" characters
+            length = result+1;
+        }
+        char* buf = new char[length];
+
+        // Restore the va_list before we use it again
+        va_copy(backup_ap, ap);
+        result = vsnprintf(buf, length, format, backup_ap);
+        va_end(backup_ap);
+
+        if ((result >= 0) && (result < length)) {
+            // It fit
+            dst->append(buf, result);
+            delete[] buf;
+            return;
+        }
+        delete[] buf;
+    }
+}
+
+std::string StringPrintf(const char* fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    std::string result;
+    StringAppendV(&result, fmt, ap);
+    va_end(ap);
+    return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    StringAppendV(dst, format, ap);
+    va_end(ap);
+}
diff --git a/vm/Misc.h b/vm/Misc.h
new file mode 100644
index 0000000..4c42576
--- /dev/null
+++ b/vm/Misc.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#ifndef DALVIK_MISC_H_
+#define DALVIK_MISC_H_
+
+#include <string>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "Inlines.h"
+
+/*
+ * Used to shut up the compiler when a parameter isn't used.
+ */
+#define UNUSED_PARAMETER(p)     (void)(p)
+
+/*
+ * Floating point conversion functions.  These are necessary to avoid
+ * strict-aliasing problems ("dereferencing type-punned pointer will break
+ * strict-aliasing rules").  According to the gcc info page, this usage
+ * is allowed, even with "-fstrict-aliasing".
+ *
+ * The code generated by gcc-4.1.1 appears to be much better than a
+ * type cast dereference ("int foo = *(int*)&myfloat") when the conversion
+ * function is inlined.  It also allows us to take advantage of the
+ * optimizations that strict aliasing rules allow.
+ */
+INLINE float dvmU4ToFloat(u4 val) {
+    union { u4 in; float out; } conv;
+    conv.in = val;
+    return conv.out;
+}
+INLINE u4 dvmFloatToU4(float val) {
+    union { float in; u4 out; } conv;
+    conv.in = val;
+    return conv.out;
+}
+
+/*
+ * Print a hex dump to the log file.
+ *
+ * "local" mode prints a hex dump starting from offset 0 (roughly equivalent
+ * to "xxd -g1").
+ *
+ * "mem" mode shows the actual memory address, and will offset the start
+ * so that the low nibble of the address is always zero.
+ *
+ * If "tag" is NULL the default tag ("dalvikvm") will be used.
+ */
+enum HexDumpMode { kHexDumpLocal, kHexDumpMem };
+void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
+    size_t length, HexDumpMode mode);
+
+/*
+ * Print a hex dump, at INFO level.
+ */
+INLINE void dvmPrintHexDump(const void* vaddr, size_t length) {
+    dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
+        vaddr, length, kHexDumpLocal);
+}
+
+/*
+ * Print a hex dump at VERBOSE level. This does nothing in non-debug builds.
+ */
+INLINE void dvmPrintHexDumpDbg(const void* vaddr, size_t length,const char* tag)
+{
+#if !LOG_NDEBUG
+    dvmPrintHexDumpEx(ANDROID_LOG_VERBOSE, (tag != NULL) ? tag : LOG_TAG,
+        vaddr, length, kHexDumpLocal);
+#endif
+}
+
+enum DebugTargetKind {
+    kDebugTargetUnknown = 0,
+    kDebugTargetLog,
+    kDebugTargetFile,
+};
+
+/*
+ * We pass one of these around when we want code to be able to write debug
+ * info to either the log or to a file (or stdout/stderr).
+ */
+struct DebugOutputTarget {
+    /* where to? */
+    DebugTargetKind which;
+
+    /* additional bits */
+    union {
+        struct {
+            int priority;
+            const char* tag;
+        } log;
+        struct {
+            FILE* fp;
+        } file;
+    } data;
+};
+
+/*
+ * Fill in a DebugOutputTarget struct.
+ */
+void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
+    const char* tag);
+void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp);
+
+/*
+ * Print a debug message.
+ */
+void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
+    ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+
+/*
+ * Return a newly-allocated string in which all occurrences of '.' have
+ * been changed to '/'.  If we find a '/' in the original string, NULL
+ * is returned to avoid ambiguity.
+ */
+char* dvmDotToSlash(const char* str);
+
+/*
+ * Return a newly-allocated string containing a human-readable equivalent
+ * of 'descriptor'. So "I" would be "int", "[[I" would be "int[][]",
+ * "[Ljava/lang/String;" would be "java.lang.String[]", and so forth.
+ */
+std::string dvmHumanReadableDescriptor(const char* descriptor);
+
+/**
+ * Returns a human-readable string form of the name of the class of
+ * the given object. So given a java.lang.String, the output would
+ * be "java.lang.String". Given an array of int, the output would be "int[]".
+ * Given String.class, the output would be "java.lang.Class<java.lang.String>".
+ */
+std::string dvmHumanReadableType(const Object* obj);
+
+/**
+ * Returns a human-readable string of the form "package.Class.fieldName".
+ */
+struct Field;
+std::string dvmHumanReadableField(const Field* field);
+
+/**
+ * Returns a human-readable string of the form "package.Class.methodName"
+ * or "package.Class.methodName(Ljava/lang/String;I)V".
+ */
+struct Method;
+std::string dvmHumanReadableMethod(const Method* method, bool withSignature);
+
+/*
+ * Return a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ * See also dvmHumanReadableDescriptor.
+ */
+char* dvmDescriptorToDot(const char* str);
+
+/*
+ * Return a newly-allocated string for the type descriptor
+ * corresponding to the "dot version" of the given class name. That
+ * is, non-array names are surrounded by "L" and ";", and all
+ * occurrences of '.' have been changed to '/'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ */
+char* dvmDotToDescriptor(const char* str);
+
+/*
+ * Return a newly-allocated string for the internal-form class name for
+ * the given type descriptor. That is, the initial "L" and final ";" (if
+ * any) have been removed.
+ */
+char* dvmDescriptorToName(const char* str);
+
+/*
+ * Return a newly-allocated string for the type descriptor for the given
+ * internal-form class name. That is, a non-array class name will get
+ * surrounded by "L" and ";", while array names are left as-is.
+ */
+char* dvmNameToDescriptor(const char* str);
+
+/*
+ * Get the current time, in nanoseconds.  This is "relative" time, meaning
+ * it could be wall-clock time or a monotonic counter, and is only suitable
+ * for computing time deltas.
+ */
+u8 dvmGetRelativeTimeNsec(void);
+
+/*
+ * Get the current time, in microseconds.  This is "relative" time, meaning
+ * it could be wall-clock time or a monotonic counter, and is only suitable
+ * for computing time deltas.
+ */
+INLINE u8 dvmGetRelativeTimeUsec(void) {
+    return dvmGetRelativeTimeNsec() / 1000;
+}
+
+/*
+ * Get the current time, in milliseconds.  This is "relative" time,
+ * meaning it could be wall-clock time or a monotonic counter, and is
+ * only suitable for computing time deltas.  The value returned from
+ * this function is a u4 and should only be used for debugging
+ * messages.  TODO: make this value relative to the start-up time of
+ * the VM.
+ */
+INLINE u4 dvmGetRelativeTimeMsec(void) {
+    return (u4)(dvmGetRelativeTimeUsec() / 1000);
+}
+
+/*
+ * Get the current per-thread CPU time.  This clock increases monotonically
+ * when the thread is running, but not when it's sleeping or blocked on a
+ * synchronization object.
+ *
+ * The absolute value of the clock may not be useful, so this should only
+ * be used for time deltas.
+ *
+ * If the thread CPU clock is not available, this always returns (u8)-1.
+ */
+u8 dvmGetThreadCpuTimeNsec(void);
+
+/*
+ * Per-thread CPU time, in micros.
+ */
+INLINE u8 dvmGetThreadCpuTimeUsec(void) {
+    return dvmGetThreadCpuTimeNsec() / 1000;
+}
+
+/*
+ * Like dvmGetThreadCpuTimeNsec, but for a different thread.
+ */
+u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread);
+INLINE u8 dvmGetOtherThreadCpuTimeUsec(pthread_t thread) {
+    return dvmGetOtherThreadCpuTimeNsec(thread) / 1000;
+}
+
+/*
+ * Sleep for increasingly longer periods, until "maxTotalSleep" microseconds
+ * have elapsed.  Pass in the start time, which must be a value returned by
+ * dvmGetRelativeTimeUsec().
+ *
+ * Returns "false" if we were unable to sleep because our time is up.
+ */
+bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime);
+
+/*
+ * Set the "close on exec" flag on a file descriptor.
+ */
+bool dvmSetCloseOnExec(int fd);
+
+/*
+ * Unconditionally abort the entire VM.  Try not to use this.
+ *
+ * NOTE: if this is marked ((noreturn)), gcc will merge multiple dvmAbort()
+ * calls in a single function together.  This is good, in that it reduces
+ * code size slightly, but also bad, because the native stack trace we
+ * get from the abort may point at the wrong call site.  Best to leave
+ * it undecorated.
+ */
+extern "C" void dvmAbort(void);
+void dvmPrintNativeBackTrace(void);
+
+#if (!HAVE_STRLCPY)
+/* Implementation of strlcpy() for platforms that don't already have it. */
+extern "C" size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+/*
+ *  Allocates a memory region using ashmem and mmap, initialized to
+ *  zero.  Actual allocation rounded up to page multiple.  Returns
+ *  NULL on failure.
+ */
+void *dvmAllocRegion(size_t size, int prot, const char *name);
+
+/*
+ * Get some per-thread stats from /proc/self/task/N/stat.
+ */
+struct ProcStatData {
+    char state;             /* process state, e.g. 'R', 'S', 'D' */
+    unsigned long utime;    /* number of jiffies scheduled in user mode */
+    unsigned long stime;    /* number of jiffies scheduled in kernel mode */
+    int processor;          /* number of CPU that last executed thread */
+};
+bool dvmGetThreadStats(ProcStatData* pData, pid_t tid);
+
+/*
+ * Returns the pointer to the "absolute path" part of the given path
+ * string, treating first (if any) instance of "/./" as a sentinel
+ * indicating the start of the absolute path. If the path isn't absolute
+ * in the usual way (i.e., starts with "/") and doesn't have the sentinel,
+ * then this returns NULL.
+ *
+ * For example:
+ *     "/foo/bar/baz" returns "/foo/bar/baz"
+ *     "foo/./bar/baz" returns "/bar/baz"
+ *     "foo/bar/baz" returns NULL
+ *
+ * The sentinel is used specifically to aid in cross-optimization, where
+ * a host is processing dex files in a build tree, and where we don't want
+ * the build tree's directory structure to be baked into the output (such
+ * as, for example, in the dependency paths of optimized dex files).
+ */
+const char* dvmPathToAbsolutePortion(const char* path);
+
+/**
+ * Returns a string corresponding to printf-like formatting of the arguments.
+ */
+std::string StringPrintf(const char* fmt, ...)
+        __attribute__((__format__ (__printf__, 1, 2)));
+
+/**
+ * Appends a printf-like formatting of the arguments to 'dst'.
+ */
+void StringAppendF(std::string* dst, const char* fmt, ...)
+        __attribute__((__format__ (__printf__, 2, 3)));
+
+/**
+ * Appends a printf-like formatting of the arguments to 'dst'.
+ */
+void StringAppendV(std::string* dst, const char* format, va_list ap);
+
+#endif  // DALVIK_MISC_H_
diff --git a/vm/Native.cpp b/vm/Native.cpp
new file mode 100644
index 0000000..8892c2a
--- /dev/null
+++ b/vm/Native.cpp
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Native method resolution.
+ *
+ * Currently the "Dalvik native" methods are only used for internal methods.
+ * Someday we may want to export the interface as a faster but riskier
+ * alternative to JNI.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+
+static void freeSharedLibEntry(void* ptr);
+static void* lookupSharedLibMethod(const Method* method);
+
+
+/*
+ * Initialize the native code loader.
+ */
+bool dvmNativeStartup()
+{
+    gDvm.nativeLibs = dvmHashTableCreate(4, freeSharedLibEntry);
+    if (gDvm.nativeLibs == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Free up our tables.
+ */
+void dvmNativeShutdown()
+{
+    dvmHashTableFree(gDvm.nativeLibs);
+    gDvm.nativeLibs = NULL;
+}
+
+
+/*
+ * Resolve a native method and invoke it.
+ *
+ * This is executed as if it were a native bridge or function.  If the
+ * resolution succeeds, method->insns is replaced, and we don't go through
+ * here again unless the method is unregistered.
+ *
+ * Initializes method's class if necessary.
+ *
+ * An exception is thrown on resolution failure.
+ *
+ * (This should not be taking "const Method*", because it modifies the
+ * structure, but the declaration needs to match the DalvikBridgeFunc
+ * type definition.)
+ */
+void dvmResolveNativeMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    ClassObject* clazz = method->clazz;
+
+    /*
+     * If this is a static method, it could be called before the class
+     * has been initialized.
+     */
+    if (dvmIsStaticMethod(method)) {
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return;
+        }
+    } else {
+        assert(dvmIsClassInitialized(clazz) ||
+               dvmIsClassInitializing(clazz));
+    }
+
+    /* start with our internal-native methods */
+    DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);
+    if (infunc != NULL) {
+        /* resolution always gets the same answer, so no race here */
+        IF_LOGVV() {
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            LOGVV("+++ resolved native %s.%s %s, invoking",
+                clazz->descriptor, method->name, desc);
+            free(desc);
+        }
+        if (dvmIsSynchronizedMethod(method)) {
+            ALOGE("ERROR: internal-native can't be declared 'synchronized'");
+            ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);
+            dvmAbort();     // harsh, but this is VM-internal problem
+        }
+        DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
+        dvmSetNativeFunc((Method*) method, dfunc, NULL);
+        dfunc(args, pResult, method, self);
+        return;
+    }
+
+    /* now scan any DLLs we have loaded for JNI signatures */
+    void* func = lookupSharedLibMethod(method);
+    if (func != NULL) {
+        /* found it, point it at the JNI bridge and then call it */
+        dvmUseJNIBridge((Method*) method, func);
+        (*method->nativeFunc)(args, pResult, method, self);
+        return;
+    }
+
+    IF_ALOGW() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGW("No implementation found for native %s.%s:%s",
+            clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    dvmThrowUnsatisfiedLinkError("Native method not found", method);
+}
+
+
+/*
+ * ===========================================================================
+ *      Native shared library support
+ * ===========================================================================
+ */
+
+// TODO? if a ClassLoader is unloaded, we need to unload all DLLs that
+// are associated with it.  (Or not -- can't determine if native code
+// is still using parts of it.)
+
+enum OnLoadState {
+    kOnLoadPending = 0,     /* initial state, must be zero */
+    kOnLoadFailed,
+    kOnLoadOkay,
+};
+
+/*
+ * We add one of these to the hash table for every library we load.  The
+ * hash is on the "pathName" field.
+ */
+struct SharedLib {
+    char*       pathName;           /* absolute path to library */
+    void*       handle;             /* from dlopen */
+    Object*     classLoader;        /* ClassLoader we are associated with */
+
+    pthread_mutex_t onLoadLock;     /* guards remaining items */
+    pthread_cond_t  onLoadCond;     /* wait for JNI_OnLoad in other thread */
+    u4              onLoadThreadId; /* recursive invocation guard */
+    OnLoadState     onLoadResult;   /* result of earlier JNI_OnLoad */
+};
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Find an entry that matches the string.
+ */
+static int hashcmpNameStr(const void* ventry, const void* vname)
+{
+    const SharedLib* pLib = (const SharedLib*) ventry;
+    const char* name = (const char*) vname;
+
+    return strcmp(pLib->pathName, name);
+}
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Find an entry that matches the new entry.
+ *
+ * We don't compare the class loader here, because you're not allowed to
+ * have the same shared library associated with more than one CL.
+ */
+static int hashcmpSharedLib(const void* ventry, const void* vnewEntry)
+{
+    const SharedLib* pLib = (const SharedLib*) ventry;
+    const SharedLib* pNewLib = (const SharedLib*) vnewEntry;
+
+    ALOGD("--- comparing %p '%s' %p '%s'",
+        pLib, pLib->pathName, pNewLib, pNewLib->pathName);
+    return strcmp(pLib->pathName, pNewLib->pathName);
+}
+
+/*
+ * Check to see if an entry with the same pathname already exists.
+ */
+static SharedLib* findSharedLibEntry(const char* pathName)
+{
+    u4 hash = dvmComputeUtf8Hash(pathName);
+    void* ent;
+
+    ent = dvmHashTableLookup(gDvm.nativeLibs, hash, (void*)pathName,
+                hashcmpNameStr, false);
+    return (SharedLib*)ent;
+}
+
+/*
+ * Add the new entry to the table.
+ *
+ * Returns the table entry, which will not be the same as "pLib" if the
+ * entry already exists.
+ */
+static SharedLib* addSharedLibEntry(SharedLib* pLib)
+{
+    u4 hash = dvmComputeUtf8Hash(pLib->pathName);
+
+    /*
+     * Do the lookup with the "add" flag set.  If we add it, we will get
+     * our own pointer back.  If somebody beat us to the punch, we'll get
+     * their pointer back instead.
+     */
+    return (SharedLib*)dvmHashTableLookup(gDvm.nativeLibs, hash, pLib,
+                hashcmpSharedLib, true);
+}
+
+/*
+ * Free up an entry.  (This is a dvmHashTableFree callback.)
+ */
+static void freeSharedLibEntry(void* ptr)
+{
+    SharedLib* pLib = (SharedLib*) ptr;
+
+    /*
+     * Calling dlclose() here is somewhat dangerous, because it's possible
+     * that a thread outside the VM is still accessing the code we loaded.
+     */
+    if (false)
+        dlclose(pLib->handle);
+    free(pLib->pathName);
+    free(pLib);
+}
+
+/*
+ * Convert library name to system-dependent form, e.g. "jpeg" becomes
+ * "libjpeg.so".
+ *
+ * (Should we have this take buffer+len and avoid the alloc?  It gets
+ * called very rarely.)
+ */
+char* dvmCreateSystemLibraryName(char* libName)
+{
+    char buf[256];
+    int len;
+
+    len = snprintf(buf, sizeof(buf), OS_SHARED_LIB_FORMAT_STR, libName);
+    if (len >= (int) sizeof(buf))
+        return NULL;
+    else
+        return strdup(buf);
+}
+
+/*
+ * Check the result of an earlier call to JNI_OnLoad on this library.  If
+ * the call has not yet finished in another thread, wait for it.
+ */
+static bool checkOnLoadResult(SharedLib* pEntry)
+{
+    Thread* self = dvmThreadSelf();
+    if (pEntry->onLoadThreadId == self->threadId) {
+        /*
+         * Check this so we don't end up waiting for ourselves.  We need
+         * to return "true" so the caller can continue.
+         */
+        ALOGI("threadid=%d: recursive native library load attempt (%s)",
+            self->threadId, pEntry->pathName);
+        return true;
+    }
+
+    ALOGV("+++ retrieving %s OnLoad status", pEntry->pathName);
+    bool result;
+
+    dvmLockMutex(&pEntry->onLoadLock);
+    while (pEntry->onLoadResult == kOnLoadPending) {
+        ALOGD("threadid=%d: waiting for %s OnLoad status",
+            self->threadId, pEntry->pathName);
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        pthread_cond_wait(&pEntry->onLoadCond, &pEntry->onLoadLock);
+        dvmChangeStatus(self, oldStatus);
+    }
+    if (pEntry->onLoadResult == kOnLoadOkay) {
+        ALOGV("+++ earlier OnLoad(%s) okay", pEntry->pathName);
+        result = true;
+    } else {
+        ALOGV("+++ earlier OnLoad(%s) failed", pEntry->pathName);
+        result = false;
+    }
+    dvmUnlockMutex(&pEntry->onLoadLock);
+    return result;
+}
+
+typedef int (*OnLoadFunc)(JavaVM*, void*);
+
+/*
+ * Load native code from the specified absolute pathname.  Per the spec,
+ * if we've already loaded a library with the specified pathname, we
+ * return without doing anything.
+ *
+ * TODO? for better results we should absolutify the pathname.  For fully
+ * correct results we should stat to get the inode and compare that.  The
+ * existing implementation is fine so long as everybody is using
+ * System.loadLibrary.
+ *
+ * The library will be associated with the specified class loader.  The JNI
+ * spec says we can't load the same library into more than one class loader.
+ *
+ * Returns "true" on success. On failure, sets *detail to a
+ * human-readable description of the error or NULL if no detail is
+ * available; ownership of the string is transferred to the caller.
+ */
+bool dvmLoadNativeCode(const char* pathName, Object* classLoader,
+        char** detail)
+{
+    SharedLib* pEntry;
+    void* handle;
+    bool verbose;
+
+    /* reduce noise by not chattering about system libraries */
+    verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);
+    verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);
+
+    if (verbose)
+        ALOGD("Trying to load lib %s %p", pathName, classLoader);
+
+    *detail = NULL;
+
+    /*
+     * See if we've already loaded it.  If we have, and the class loader
+     * matches, return successfully without doing anything.
+     */
+    pEntry = findSharedLibEntry(pathName);
+    if (pEntry != NULL) {
+        if (pEntry->classLoader != classLoader) {
+            ALOGW("Shared lib '%s' already opened by CL %p; can't open in %p",
+                pathName, pEntry->classLoader, classLoader);
+            return false;
+        }
+        if (verbose) {
+            ALOGD("Shared lib '%s' already loaded in same CL %p",
+                pathName, classLoader);
+        }
+        if (!checkOnLoadResult(pEntry))
+            return false;
+        return true;
+    }
+
+    /*
+     * Open the shared library.  Because we're using a full path, the system
+     * doesn't have to search through LD_LIBRARY_PATH.  (It may do so to
+     * resolve this library's dependencies though.)
+     *
+     * Failures here are expected when java.library.path has several entries
+     * and we have to hunt for the lib.
+     *
+     * The current version of the dynamic linker prints detailed information
+     * about dlopen() failures.  Some things to check if the message is
+     * cryptic:
+     *   - make sure the library exists on the device
+     *   - verify that the right path is being opened (the debug log message
+     *     above can help with that)
+     *   - check to see if the library is valid (e.g. not zero bytes long)
+     *   - check config/prelink-linux-arm.map to ensure that the library
+     *     is listed and is not being overrun by the previous entry (if
+     *     loading suddenly stops working on a prelinked library, this is
+     *     a good one to check)
+     *   - write a trivial app that calls sleep() then dlopen(), attach
+     *     to it with "strace -p <pid>" while it sleeps, and watch for
+     *     attempts to open nonexistent dependent shared libs
+     *
+     * This can execute slowly for a large library on a busy system, so we
+     * want to switch from RUNNING to VMWAIT while it executes.  This allows
+     * the GC to ignore us.
+     */
+    Thread* self = dvmThreadSelf();
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+    handle = dlopen(pathName, RTLD_LAZY);
+    dvmChangeStatus(self, oldStatus);
+
+    if (handle == NULL) {
+        *detail = strdup(dlerror());
+        ALOGE("dlopen(\"%s\") failed: %s", pathName, *detail);
+        return false;
+    }
+
+    /* create a new entry */
+    SharedLib* pNewEntry;
+    pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));
+    pNewEntry->pathName = strdup(pathName);
+    pNewEntry->handle = handle;
+    pNewEntry->classLoader = classLoader;
+    dvmInitMutex(&pNewEntry->onLoadLock);
+    pthread_cond_init(&pNewEntry->onLoadCond, NULL);
+    pNewEntry->onLoadThreadId = self->threadId;
+
+    /* try to add it to the list */
+    SharedLib* pActualEntry = addSharedLibEntry(pNewEntry);
+
+    if (pNewEntry != pActualEntry) {
+        ALOGI("WOW: we lost a race to add a shared lib (%s CL=%p)",
+            pathName, classLoader);
+        freeSharedLibEntry(pNewEntry);
+        return checkOnLoadResult(pActualEntry);
+    } else {
+        if (verbose)
+            ALOGD("Added shared lib %s %p", pathName, classLoader);
+
+        bool result = true;
+        void* vonLoad;
+        int version;
+
+        vonLoad = dlsym(handle, "JNI_OnLoad");
+        if (vonLoad == NULL) {
+            ALOGD("No JNI_OnLoad found in %s %p, skipping init",
+                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 = (OnLoadFunc)vonLoad;
+            Object* prevOverride = self->classLoaderOverride;
+
+            self->classLoaderOverride = classLoader;
+            oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+            if (gDvm.verboseJni) {
+                ALOGI("[Calling JNI_OnLoad for \"%s\"]", pathName);
+            }
+            version = (*func)(gDvmJni.jniVm, NULL);
+            dvmChangeStatus(self, oldStatus);
+            self->classLoaderOverride = prevOverride;
+
+            if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 &&
+                version != JNI_VERSION_1_6)
+            {
+                ALOGW("JNI_OnLoad returned bad version (%d) in %s %p",
+                    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 {
+                if (gDvm.verboseJni) {
+                    ALOGI("[Returned from JNI_OnLoad for \"%s\"]", pathName);
+                }
+            }
+        }
+
+        if (result)
+            pNewEntry->onLoadResult = kOnLoadOkay;
+        else
+            pNewEntry->onLoadResult = kOnLoadFailed;
+
+        pNewEntry->onLoadThreadId = 0;
+
+        /*
+         * Broadcast a wakeup to anybody sleeping on the condition variable.
+         */
+        dvmLockMutex(&pNewEntry->onLoadLock);
+        pthread_cond_broadcast(&pNewEntry->onLoadCond);
+        dvmUnlockMutex(&pNewEntry->onLoadLock);
+        return result;
+    }
+}
+
+
+/*
+ * Un-register JNI native methods.
+ *
+ * There are two relevant fields in struct Method, "nativeFunc" and
+ * "insns".  The former holds a function pointer to a "bridge" function
+ * (or, for internal native, the actual implementation).  The latter holds
+ * a pointer to the actual JNI method.
+ *
+ * The obvious approach is to reset both fields to their initial state
+ * (nativeFunc points at dvmResolveNativeMethod, insns holds NULL), but
+ * that creates some unpleasant race conditions.  In particular, if another
+ * thread is executing inside the call bridge for the method in question,
+ * and we reset insns to NULL, the VM will crash.  (See the comments above
+ * dvmSetNativeFunc() for additional commentary.)
+ *
+ * We can't rely on being able to update two 32-bit fields in one atomic
+ * operation (e.g. no 64-bit atomic ops on ARMv5TE), so we want to change
+ * only one field.  It turns out we can simply reset nativeFunc to its
+ * initial state, leaving insns alone, because dvmResolveNativeMethod
+ * ignores "insns" entirely.
+ *
+ * When the method is re-registered, both fields will be updated, but
+ * dvmSetNativeFunc guarantees that "insns" is updated first.  This means
+ * we shouldn't be in a situation where we have a "live" call bridge and
+ * a stale implementation pointer.
+ */
+static void unregisterJNINativeMethods(Method* methods, size_t count)
+{
+    while (count != 0) {
+        count--;
+
+        Method* meth = &methods[count];
+        if (!dvmIsNativeMethod(meth))
+            continue;
+        if (dvmIsAbstractMethod(meth))      /* avoid abstract method stubs */
+            continue;
+
+        /*
+         * Strictly speaking this ought to test the function pointer against
+         * the various JNI bridge functions to ensure that we only undo
+         * methods that were registered through JNI.  In practice, any
+         * native method with a non-NULL "insns" is a registered JNI method.
+         *
+         * If we inadvertently unregister an internal-native, it'll get
+         * re-resolved on the next call; unregistering an unregistered
+         * JNI method is a no-op.  So we don't really need to test for
+         * anything.
+         */
+
+        ALOGD("Unregistering JNI method %s.%s:%s",
+            meth->clazz->descriptor, meth->name, meth->shorty);
+        dvmSetNativeFunc(meth, dvmResolveNativeMethod, NULL);
+    }
+}
+
+/*
+ * Un-register all JNI native methods from a class.
+ */
+void dvmUnregisterJNINativeMethods(ClassObject* clazz)
+{
+    unregisterJNINativeMethods(clazz->directMethods, clazz->directMethodCount);
+    unregisterJNINativeMethods(clazz->virtualMethods, clazz->virtualMethodCount);
+}
+
+
+/*
+ * ===========================================================================
+ *      Signature-based method lookup
+ * ===========================================================================
+ */
+
+/*
+ * Create the pre-mangled form of the class+method string.
+ *
+ * Returns a newly-allocated string, and sets "*pLen" to the length.
+ */
+static char* createJniNameString(const char* classDescriptor,
+    const char* methodName, int* pLen)
+{
+    char* result;
+    size_t descriptorLength = strlen(classDescriptor);
+
+    *pLen = 4 + descriptorLength + strlen(methodName);
+
+    result = (char*)malloc(*pLen +1);
+    if (result == NULL)
+        return NULL;
+
+    /*
+     * Add one to classDescriptor to skip the "L", and then replace
+     * the final ";" with a "/" after the sprintf() call.
+     */
+    sprintf(result, "Java/%s%s", classDescriptor + 1, methodName);
+    result[5 + (descriptorLength - 2)] = '/';
+
+    return result;
+}
+
+/*
+ * Returns a newly-allocated, mangled copy of "str".
+ *
+ * "str" is a "modified UTF-8" string.  We convert it to UTF-16 first to
+ * make life simpler.
+ */
+static char* mangleString(const char* str, int len)
+{
+    //ALOGI("mangling '%s' %d", str, len);
+
+    assert(str[len] == '\0');
+
+    size_t charLen = dvmUtf8Len(str);
+    u2* utf16 = (u2*) malloc(sizeof(u2) * charLen);
+    if (utf16 == NULL)
+        return NULL;
+
+    dvmConvertUtf8ToUtf16(utf16, str);
+
+    /*
+     * Compute the length of the mangled string.
+     */
+    size_t mangleLen = 0;
+    for (size_t i = 0; i < charLen; i++) {
+        u2 ch = utf16[i];
+
+        if (ch == '$' || ch > 127) {
+            mangleLen += 6;
+        } else {
+            switch (ch) {
+            case '_':
+            case ';':
+            case '[':
+                mangleLen += 2;
+                break;
+            default:
+                mangleLen++;
+                break;
+            }
+        }
+    }
+
+    char* mangle = (char*) malloc(mangleLen +1);
+    if (mangle == NULL) {
+        free(utf16);
+        return NULL;
+    }
+
+    char* cp = mangle;
+    for (size_t i = 0; i < charLen; i++) {
+        u2 ch = utf16[i];
+
+        if (ch == '$' || ch > 127) {
+            sprintf(cp, "_0%04x", ch);
+            cp += 6;
+        } else {
+            switch (ch) {
+            case '_':
+                *cp++ = '_';
+                *cp++ = '1';
+                break;
+            case ';':
+                *cp++ = '_';
+                *cp++ = '2';
+                break;
+            case '[':
+                *cp++ = '_';
+                *cp++ = '3';
+                break;
+            case '/':
+                *cp++ = '_';
+                break;
+            default:
+                *cp++ = (char) ch;
+                break;
+            }
+        }
+    }
+
+    *cp = '\0';
+
+    free(utf16);
+    return mangle;
+}
+
+/*
+ * Create the mangled form of the parameter types.
+ */
+static char* createMangledSignature(const DexProto* proto)
+{
+    DexStringCache sigCache;
+    const char* interim;
+    char* result;
+
+    dexStringCacheInit(&sigCache);
+    interim = dexProtoGetParameterDescriptors(proto, &sigCache);
+    result = mangleString(interim, strlen(interim));
+    dexStringCacheRelease(&sigCache);
+
+    return result;
+}
+
+/*
+ * (This is a dvmHashForeach callback.)
+ *
+ * Search for a matching method in this shared library.
+ *
+ * TODO: we may want to skip libraries for which JNI_OnLoad failed.
+ */
+static int findMethodInLib(void* vlib, void* vmethod)
+{
+    const SharedLib* pLib = (const SharedLib*) vlib;
+    const Method* meth = (const Method*) vmethod;
+    char* preMangleCM = NULL;
+    char* mangleCM = NULL;
+    char* mangleSig = NULL;
+    char* mangleCMSig = NULL;
+    void* func = NULL;
+    int len;
+
+    if (meth->clazz->classLoader != pLib->classLoader) {
+        ALOGV("+++ not scanning '%s' for '%s' (wrong CL)",
+            pLib->pathName, meth->name);
+        return 0;
+    } else
+        ALOGV("+++ scanning '%s' for '%s'", pLib->pathName, meth->name);
+
+    /*
+     * First, we try it without the signature.
+     */
+    preMangleCM =
+        createJniNameString(meth->clazz->descriptor, meth->name, &len);
+    if (preMangleCM == NULL)
+        goto bail;
+
+    mangleCM = mangleString(preMangleCM, len);
+    if (mangleCM == NULL)
+        goto bail;
+
+    ALOGV("+++ calling dlsym(%s)", mangleCM);
+    func = dlsym(pLib->handle, mangleCM);
+    if (func == NULL) {
+        mangleSig =
+            createMangledSignature(&meth->prototype);
+        if (mangleSig == NULL)
+            goto bail;
+
+        mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3);
+        if (mangleCMSig == NULL)
+            goto bail;
+
+        sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig);
+
+        ALOGV("+++ calling dlsym(%s)", mangleCMSig);
+        func = dlsym(pLib->handle, mangleCMSig);
+        if (func != NULL) {
+            ALOGV("Found '%s' with dlsym", mangleCMSig);
+        }
+    } else {
+        ALOGV("Found '%s' with dlsym", mangleCM);
+    }
+
+bail:
+    free(preMangleCM);
+    free(mangleCM);
+    free(mangleSig);
+    free(mangleCMSig);
+    return (int) func;
+}
+
+/*
+ * See if the requested method lives in any of the currently-loaded
+ * shared libraries.  We do this by checking each of them for the expected
+ * method signature.
+ */
+static void* lookupSharedLibMethod(const Method* method)
+{
+    if (gDvm.nativeLibs == NULL) {
+        ALOGE("Unexpected init state: nativeLibs not ready");
+        dvmAbort();
+    }
+    return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
+        (void*) method);
+}
diff --git a/vm/Native.h b/vm/Native.h
new file mode 100644
index 0000000..65ff8dc
--- /dev/null
+++ b/vm/Native.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Dalvik's native call interface.
+ *
+ * You should follow the JNI function naming conventions, but prefix with
+ * "Dalvik_" instead of "Java_".
+ */
+#ifndef DALVIK_NATIVE_H_
+#define DALVIK_NATIVE_H_
+
+/*
+ * Method description; equivalent to a JNI struct.
+ */
+struct DalvikNativeMethod {
+    const char* name;
+    const char* signature;
+    DalvikNativeFunc  fnPtr;
+};
+
+/*
+ * All methods for one class.  The last "methodInfo" has a NULL "name".
+ */
+struct DalvikNativeClass {
+    const char* classDescriptor;
+    const DalvikNativeMethod* methodInfo;
+    u4          classDescriptorHash;          /* initialized at runtime */
+};
+
+
+/* init/shutdown */
+bool dvmNativeStartup(void);
+void dvmNativeShutdown(void);
+
+
+/*
+ * Convert argc/argv into a function call.  This is platform-specific.
+ */
+extern "C" void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo,
+    int argc, const u4* argv, const char* signature, void* func, JValue* pResult);
+
+/*
+ * Generate hints to speed native calls.  This is platform specific.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto);
+
+/*
+ * Convert a short library name ("jpeg") to a system-dependent name
+ * ("libjpeg.so").  Returns a newly-allocated string.
+ */
+char* dvmCreateSystemLibraryName(char* libName);
+bool dvmLoadNativeCode(const char* fileName, Object* classLoader,
+        char** detail);
+
+
+/*
+ * Resolve a native method.  This uses the same prototype as a
+ * DalvikBridgeFunc, because it takes the place of the actual function
+ * until the first time that it's invoked.
+ *
+ * Causes the method's class to be initialized.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+void dvmResolveNativeMethod(const u4* args, JValue* pResult,
+    const Method* method, struct Thread* self);
+
+/*
+ * Unregister all JNI native methods associated with a class.
+ */
+void dvmUnregisterJNINativeMethods(ClassObject* clazz);
+
+#define GET_ARG_LONG(_args, _elem)          dvmGetArgLong(_args, _elem)
+
+/*
+ * Helpful function for accessing long integers in "u4* args".
+ *
+ * We can't just return *(s8*)(&args[elem]), because that breaks if our
+ * architecture requires 64-bit alignment of 64-bit values.
+ *
+ * Big/little endian shouldn't matter here -- ordering of words within a
+ * long seems consistent across our supported platforms.
+ */
+INLINE s8 dvmGetArgLong(const u4* args, int elem)
+{
+    s8 val;
+    memcpy(&val, &args[elem], sizeof(val));
+    return val;
+}
+
+#endif  // DALVIK_NATIVE_H_
diff --git a/vm/PointerSet.cpp b/vm/PointerSet.cpp
new file mode 100644
index 0000000..21fea15
--- /dev/null
+++ b/vm/PointerSet.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Maintain an expanding set of unique pointer values.
+ */
+#include "Dalvik.h"
+
+/*
+ * Sorted, expanding list of pointers.
+ */
+struct PointerSet {
+    u2          alloc;
+    u2          count;
+    const void** list;
+};
+
+/*
+ * Verify that the set is in sorted order.
+ */
+#ifndef NDEBUG
+static bool verifySorted(PointerSet* pSet)
+{
+    const void* last = NULL;
+    int i;
+
+    for (i = 0; i < pSet->count; i++) {
+        const void* cur = pSet->list[i];
+        if (cur < last)
+            return false;
+        last = cur;
+    }
+
+    return true;
+}
+#endif
+
+/*
+ * Allocate a new PointerSet.
+ *
+ * Returns NULL on failure.
+ */
+PointerSet* dvmPointerSetAlloc(int initialSize)
+{
+    PointerSet* pSet = (PointerSet*)calloc(1, sizeof(PointerSet));
+    if (pSet != NULL) {
+        if (initialSize > 0) {
+            pSet->list = (const void**)malloc(sizeof(void*) * initialSize);
+            if (pSet->list == NULL) {
+                free(pSet);
+                return NULL;
+            }
+            pSet->alloc = initialSize;
+        }
+    }
+
+    return pSet;
+}
+
+/*
+ * Free up a PointerSet.
+ */
+void dvmPointerSetFree(PointerSet* pSet)
+{
+    if (pSet == NULL)
+        return;
+
+    if (pSet->list != NULL) {
+        free(pSet->list);
+        pSet->list = NULL;
+    }
+    free(pSet);
+}
+
+/*
+ * Clear the contents of a pointer set.
+ */
+void dvmPointerSetClear(PointerSet* pSet)
+{
+    pSet->count = 0;
+}
+
+/*
+ * Get the number of pointers currently stored in the list.
+ */
+int dvmPointerSetGetCount(const PointerSet* pSet)
+{
+    return pSet->count;
+}
+
+/*
+ * Get the Nth entry from the list.
+ */
+const void* dvmPointerSetGetEntry(const PointerSet* pSet, int i)
+{
+    return pSet->list[i];
+}
+
+/*
+ * Insert a new entry into the list.  If it already exists, this returns
+ * without doing anything.
+ *
+ * Returns "true" if the value was added.
+ */
+bool dvmPointerSetAddEntry(PointerSet* pSet, const void* ptr)
+{
+    int nearby;
+
+    if (dvmPointerSetHas(pSet, ptr, &nearby))
+        return false;
+
+    /* ensure we have space to add one more */
+    if (pSet->count == pSet->alloc) {
+        /* time to expand */
+        const void** newList;
+
+        if (pSet->alloc == 0)
+            pSet->alloc = 4;
+        else
+            pSet->alloc *= 2;
+        LOGVV("expanding %p to %d", pSet, pSet->alloc);
+        newList = (const void**)realloc(pSet->list, pSet->alloc * sizeof(void*));
+        if (newList == NULL) {
+            ALOGE("Failed expanding ptr set (alloc=%d)", pSet->alloc);
+            dvmAbort();
+        }
+        pSet->list = newList;
+    }
+
+    if (pSet->count == 0) {
+        /* empty list */
+        assert(nearby == 0);
+    } else {
+        /*
+         * Determine the insertion index.  The binary search might have
+         * terminated "above" or "below" the value.
+         */
+        if (nearby != 0 && ptr < pSet->list[nearby-1]) {
+            //ALOGD("nearby-1=%d %p, inserting %p at -1",
+            //    nearby-1, pSet->list[nearby-1], ptr);
+            nearby--;
+        } else if (ptr < pSet->list[nearby]) {
+            //ALOGD("nearby=%d %p, inserting %p at +0",
+            //    nearby, pSet->list[nearby], ptr);
+        } else {
+            //ALOGD("nearby+1=%d %p, inserting %p at +1",
+            //    nearby+1, pSet->list[nearby+1], ptr);
+            nearby++;
+        }
+
+        /*
+         * Move existing values, if necessary.
+         */
+        if (nearby != pSet->count) {
+            /* shift up */
+            memmove(&pSet->list[nearby+1], &pSet->list[nearby],
+                (pSet->count - nearby) * sizeof(pSet->list[0]));
+        }
+    }
+
+    pSet->list[nearby] = ptr;
+    pSet->count++;
+
+    assert(verifySorted(pSet));
+    return true;
+}
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+bool dvmPointerSetRemoveEntry(PointerSet* pSet, const void* ptr)
+{
+    int where;
+
+    if (!dvmPointerSetHas(pSet, ptr, &where))
+        return false;
+
+    if (where != pSet->count-1) {
+        /* shift down */
+        memmove(&pSet->list[where], &pSet->list[where+1],
+            (pSet->count-1 - where) * sizeof(pSet->list[0]));
+    }
+
+    pSet->count--;
+    pSet->list[pSet->count] = (const void*) 0xdecadead;     // debug
+    return true;
+}
+
+/*
+ * Returns the index if "ptr" appears in the list.  If it doesn't appear,
+ * this returns a negative index for a nearby element.
+ */
+bool dvmPointerSetHas(const PointerSet* pSet, const void* ptr, int* pIndex)
+{
+    int hi, lo, mid;
+
+    lo = mid = 0;
+    hi = pSet->count-1;
+
+    /* array is sorted, use a binary search */
+    while (lo <= hi) {
+        mid = (lo + hi) / 2;
+        const void* listVal = pSet->list[mid];
+
+        if (ptr > listVal) {
+            lo = mid + 1;
+        } else if (ptr < listVal) {
+            hi = mid - 1;
+        } else /* listVal == ptr */ {
+            if (pIndex != NULL)
+                *pIndex = mid;
+            return true;
+        }
+    }
+
+    if (pIndex != NULL)
+        *pIndex = mid;
+    return false;
+}
+
+/*
+ * Compute the intersection of the set and the array of pointers passed in.
+ *
+ * Any pointer in "pSet" that does not appear in "ptrArray" is removed.
+ */
+void dvmPointerSetIntersect(PointerSet* pSet, const void** ptrArray, int count)
+{
+    int i, j;
+
+    for (i = 0; i < pSet->count; i++) {
+        for (j = 0; j < count; j++) {
+            if (pSet->list[i] == ptrArray[j]) {
+                /* match, keep this one */
+                break;
+            }
+        }
+
+        if (j == count) {
+            /* no match, remove entry */
+            if (i != pSet->count-1) {
+                /* shift down */
+                memmove(&pSet->list[i], &pSet->list[i+1],
+                    (pSet->count-1 - i) * sizeof(pSet->list[0]));
+            }
+
+            pSet->count--;
+            pSet->list[pSet->count] = (const void*) 0xdecadead;     // debug
+            i--;        /* adjust loop counter */
+        }
+    }
+}
+
+/*
+ * Print the list contents to stdout.  For debugging.
+ */
+void dvmPointerSetDump(const PointerSet* pSet)
+{
+    ALOGI("PointerSet %p", pSet);
+    int i;
+    for (i = 0; i < pSet->count; i++)
+        ALOGI(" %2d: %p", i, pSet->list[i]);
+}
diff --git a/vm/PointerSet.h b/vm/PointerSet.h
new file mode 100644
index 0000000..7a1cddf
--- /dev/null
+++ b/vm/PointerSet.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Maintain an expanding set of unique pointer values.  The set is
+ * kept in sorted order.
+ */
+#ifndef DALVIK_POINTERSET_H_
+#define DALVIK_POINTERSET_H_
+
+struct PointerSet;   /* private */
+
+/*
+ * Allocate a new PointerSet.
+ *
+ * Returns NULL on failure.
+ */
+PointerSet* dvmPointerSetAlloc(int initialSize);
+
+/*
+ * Free up a PointerSet.
+ */
+void dvmPointerSetFree(PointerSet* pSet);
+
+/*
+ * Clear the contents of a pointer set.
+ */
+void dvmPointerSetClear(PointerSet* pSet);
+
+/*
+ * Get the number of pointers currently stored in the list.
+ */
+int dvmPointerSetGetCount(const PointerSet* pSet);
+
+/*
+ * Get the Nth entry from the list.
+ */
+const void* dvmPointerSetGetEntry(const PointerSet* pSet, int i);
+
+/*
+ * Insert a new entry into the list.  If it already exists, this returns
+ * without doing anything.
+ *
+ * Returns "true" if the pointer was added.
+ */
+bool dvmPointerSetAddEntry(PointerSet* pSet, const void* ptr);
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+bool dvmPointerSetRemoveEntry(PointerSet* pSet, const void* ptr);
+
+/*
+ * Returns "true" if the value appears, "false" otherwise.  If "pIndex" is
+ * non-NULL, it will receive the matching index or the index of a nearby
+ * element.
+ */
+bool dvmPointerSetHas(const PointerSet* pSet, const void* ptr, int* pIndex);
+
+/*
+ * Find an entry in the set.  Returns the index, or -1 if not found.
+ */
+INLINE int dvmPointerSetFind(const PointerSet* pSet, const void* ptr) {
+    int idx;
+    if (!dvmPointerSetHas(pSet, ptr, &idx))
+        idx = -1;
+    return idx;
+}
+
+/*
+ * Compute the intersection of the set and the array of pointers passed in.
+ *
+ * Any pointer in "pSet" that does not appear in "ptrArray" is removed.
+ */
+void dvmPointerSetIntersect(PointerSet* pSet, const void** ptrArray, int count);
+
+/*
+ * Print the list contents to stdout.  For debugging.
+ */
+void dvmPointerSetDump(const PointerSet* pSet);
+
+#endif  // DALVIK_POINTERSET_H_
diff --git a/vm/Profile.cpp b/vm/Profile.cpp
new file mode 100644
index 0000000..dfb50b3
--- /dev/null
+++ b/vm/Profile.cpp
@@ -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.
+ */
+
+/*
+ * Android's method call profiling goodies.
+ */
+#include "Dalvik.h"
+#include <interp/InterpDefs.h>
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sched.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <cutils/open_memstream.h>
+
+#ifdef HAVE_ANDROID_OS
+# define UPDATE_MAGIC_PAGE      1
+#endif
+
+/*
+ * File format:
+ *  header
+ *  record 0
+ *  record 1
+ *  ...
+ *
+ * Header format:
+ *  u4  magic ('SLOW')
+ *  u2  version
+ *  u2  offset to data
+ *  u8  start date/time in usec
+ *  u2  record size in bytes (version >= 2 only)
+ *  ... padding to 32 bytes
+ *
+ * Record format v1:
+ *  u1  thread ID
+ *  u4  method ID | method action
+ *  u4  time delta since start, in usec
+ *
+ * Record format v2:
+ *  u2  thread ID
+ *  u4  method ID | method action
+ *  u4  time delta since start, in usec
+ *
+ * Record format v3:
+ *  u2  thread ID
+ *  u4  method ID | method action
+ *  u4  time delta since start, in usec
+ *  u4  wall time since start, in usec (when clock == "dual" only)
+ *
+ * 32 bits of microseconds is 70 minutes.
+ *
+ * All values are stored in little-endian order.
+ */
+#define TRACE_REC_SIZE_SINGLE_CLOCK  10 // using v2
+#define TRACE_REC_SIZE_DUAL_CLOCK    14 // using v3 with two timestamps
+#define TRACE_MAGIC         0x574f4c53
+#define TRACE_HEADER_LEN    32
+
+#define FILL_PATTERN        0xeeeeeeee
+
+
+/*
+ * Returns true if the thread CPU clock should be used.
+ */
+static inline bool useThreadCpuClock() {
+#if defined(HAVE_POSIX_CLOCKS)
+    return gDvm.profilerClockSource != kProfilerClockSourceWall;
+#else
+    return false;
+#endif
+}
+
+/*
+ * Returns true if the wall clock should be used.
+ */
+static inline bool useWallClock() {
+#if defined(HAVE_POSIX_CLOCKS)
+    return gDvm.profilerClockSource != kProfilerClockSourceThreadCpu;
+#else
+    return true;
+#endif
+}
+
+/*
+ * Get the wall-clock date/time, in usec.
+ */
+static inline u8 getWallTimeInUsec()
+{
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000000LL + tv.tv_usec;
+}
+
+#if defined(HAVE_POSIX_CLOCKS)
+/*
+ * Get the thread-cpu time, in usec.
+ * We use this clock when we can because it enables us to track the time that
+ * a thread spends running and not blocked.
+ */
+static inline u8 getThreadCpuTimeInUsec()
+{
+    struct timespec tm;
+
+    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+    if (!(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000)) {
+        ALOGE("bad nsec: %ld", tm.tv_nsec);
+        dvmAbort();
+    }
+    return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
+}
+#endif
+
+/*
+ * Get the clock used for stopwatch-like timing measurements on a single thread.
+ */
+static inline u8 getStopwatchClock()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    return getThreadCpuTimeInUsec();
+#else
+    return getWallTimeInUsec();
+#endif
+}
+
+/*
+ * Write little-endian data.
+ */
+static inline void storeShortLE(u1* buf, u2 val)
+{
+    *buf++ = (u1) val;
+    *buf++ = (u1) (val >> 8);
+}
+static inline void storeIntLE(u1* buf, u4 val)
+{
+    *buf++ = (u1) val;
+    *buf++ = (u1) (val >> 8);
+    *buf++ = (u1) (val >> 16);
+    *buf++ = (u1) (val >> 24);
+}
+static inline void storeLongLE(u1* buf, u8 val)
+{
+    *buf++ = (u1) val;
+    *buf++ = (u1) (val >> 8);
+    *buf++ = (u1) (val >> 16);
+    *buf++ = (u1) (val >> 24);
+    *buf++ = (u1) (val >> 32);
+    *buf++ = (u1) (val >> 40);
+    *buf++ = (u1) (val >> 48);
+    *buf++ = (u1) (val >> 56);
+}
+
+/*
+ * Boot-time init.
+ */
+bool dvmProfilingStartup()
+{
+    /*
+     * Initialize "dmtrace" method profiling.
+     */
+    memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
+    dvmInitMutex(&gDvm.methodTrace.startStopLock);
+    pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
+
+    assert(!dvmCheckException(dvmThreadSelf()));
+
+    /*
+     * Allocate storage for instruction counters.
+     */
+    gDvm.executedInstrCounts = (int*) calloc(kNumPackedOpcodes, sizeof(int));
+    if (gDvm.executedInstrCounts == NULL)
+        return false;
+
+#ifdef UPDATE_MAGIC_PAGE
+    /*
+     * If we're running on the emulator, there's a magic page into which
+     * we can put interpreted method information.  This allows interpreted
+     * methods to show up in the emulator's code traces.
+     *
+     * We could key this off of the "ro.kernel.qemu" property, but there's
+     * no real harm in doing this on a real device.
+     */
+    int fd = open("/dev/qemu_trace", O_RDWR);
+    if (fd < 0) {
+        ALOGV("Unable to open /dev/qemu_trace");
+    } else {
+        gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
+                                      MAP_SHARED, fd, 0);
+        close(fd);
+        if (gDvm.emulatorTracePage == MAP_FAILED) {
+            ALOGE("Unable to mmap /dev/qemu_trace");
+            gDvm.emulatorTracePage = NULL;
+        } else {
+            *(u4*) gDvm.emulatorTracePage = 0;
+        }
+    }
+#else
+    assert(gDvm.emulatorTracePage == NULL);
+#endif
+
+    return true;
+}
+
+/*
+ * Free up profiling resources.
+ */
+void dvmProfilingShutdown()
+{
+#ifdef UPDATE_MAGIC_PAGE
+    if (gDvm.emulatorTracePage != NULL)
+        munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
+#endif
+    free(gDvm.executedInstrCounts);
+}
+
+/*
+ * Update the set of active profilers
+ */
+static void updateActiveProfilers(ExecutionSubModes newMode, bool enable)
+{
+    int oldValue, newValue;
+
+    // Update global count
+    do {
+        oldValue = gDvm.activeProfilers;
+        newValue = oldValue + (enable ? 1 : -1);
+        if (newValue < 0) {
+            ALOGE("Can't have %d active profilers", newValue);
+            dvmAbort();
+        }
+    } while (android_atomic_release_cas(oldValue, newValue,
+            &gDvm.activeProfilers) != 0);
+
+    // Tell the threads
+    if (enable) {
+        dvmEnableAllSubMode(newMode);
+    } else {
+        dvmDisableAllSubMode(newMode);
+    }
+
+#if defined(WITH_JIT)
+    dvmCompilerUpdateGlobalState();
+#endif
+
+    ALOGD("+++ active profiler count now %d", newValue);
+}
+
+
+/*
+ * Reset the "cpuClockBase" field in all threads.
+ */
+static void resetCpuClockBase()
+{
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        thread->cpuClockBaseSet = false;
+        thread->cpuClockBase = 0;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Dump the thread list to the specified file.
+ */
+static void dumpThreadList(FILE* fp) {
+    dvmLockThreadList(NULL);
+    for (Thread* thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        std::string threadName(dvmGetThreadName(thread));
+        fprintf(fp, "%d\t%s\n", thread->threadId, threadName.c_str());
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * This is a dvmHashForeach callback.
+ */
+static int dumpMarkedMethods(void* vclazz, void* vfp)
+{
+    DexStringCache stringCache;
+    ClassObject* clazz = (ClassObject*) vclazz;
+    FILE* fp = (FILE*) vfp;
+    Method* meth;
+    char* name;
+    int i;
+
+    dexStringCacheInit(&stringCache);
+
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        meth = &clazz->virtualMethods[i];
+        if (meth->inProfile) {
+            name = dvmDescriptorToName(meth->clazz->descriptor);
+            fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
+                name, meth->name,
+                dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
+                dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
+            meth->inProfile = false;
+            free(name);
+        }
+    }
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        meth = &clazz->directMethods[i];
+        if (meth->inProfile) {
+            name = dvmDescriptorToName(meth->clazz->descriptor);
+            fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
+                name, meth->name,
+                dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
+                dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
+            meth->inProfile = false;
+            free(name);
+        }
+    }
+
+    dexStringCacheRelease(&stringCache);
+
+    return 0;
+}
+
+/*
+ * Dump the list of "marked" methods to the specified file.
+ */
+static void dumpMethodList(FILE* fp)
+{
+    dvmHashTableLock(gDvm.loadedClasses);
+    dvmHashForeach(gDvm.loadedClasses, dumpMarkedMethods, (void*) fp);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Start method tracing.  Method tracing is global to the VM (i.e. we
+ * trace all threads).
+ *
+ * This opens the output file (if an already open fd has not been supplied,
+ * and we're not going direct to DDMS) and allocates the data buffer.  This
+ * takes ownership of the file descriptor, closing it on completion.
+ *
+ * On failure, we throw an exception and return.
+ */
+void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
+    int flags, bool directToDdms)
+{
+    MethodTraceState* state = &gDvm.methodTrace;
+
+    assert(bufferSize > 0);
+
+    dvmLockMutex(&state->startStopLock);
+    while (state->traceEnabled != 0) {
+        ALOGI("TRACE start requested, but already in progress; stopping");
+        dvmUnlockMutex(&state->startStopLock);
+        dvmMethodTraceStop();
+        dvmLockMutex(&state->startStopLock);
+    }
+    ALOGI("TRACE STARTED: '%s' %dKB", traceFileName, bufferSize / 1024);
+
+    /*
+     * Allocate storage and open files.
+     *
+     * We don't need to initialize the buffer, but doing so might remove
+     * some fault overhead if the pages aren't mapped until touched.
+     */
+    state->buf = (u1*) malloc(bufferSize);
+    if (state->buf == NULL) {
+        dvmThrowInternalError("buffer alloc failed");
+        goto fail;
+    }
+    if (!directToDdms) {
+        if (traceFd < 0) {
+            state->traceFile = fopen(traceFileName, "w");
+        } else {
+            state->traceFile = fdopen(traceFd, "w");
+        }
+        if (state->traceFile == NULL) {
+            int err = errno;
+            ALOGE("Unable to open trace file '%s': %s",
+                traceFileName, strerror(err));
+            dvmThrowExceptionFmt(gDvm.exRuntimeException,
+                "Unable to open trace file '%s': %s",
+                traceFileName, strerror(err));
+            goto fail;
+        }
+    }
+    traceFd = -1;
+    memset(state->buf, (char)FILL_PATTERN, bufferSize);
+
+    state->directToDdms = directToDdms;
+    state->bufferSize = bufferSize;
+    state->overflow = false;
+
+    /*
+     * Enable alloc counts if we've been requested to do so.
+     */
+    state->flags = flags;
+    if ((flags & TRACE_ALLOC_COUNTS) != 0)
+        dvmStartAllocCounting();
+
+    /* reset our notion of the start time for all CPU threads */
+    resetCpuClockBase();
+
+    state->startWhen = getWallTimeInUsec();
+
+    if (useThreadCpuClock() && useWallClock()) {
+        state->traceVersion = 3;
+        state->recordSize = TRACE_REC_SIZE_DUAL_CLOCK;
+    } else {
+        state->traceVersion = 2;
+        state->recordSize = TRACE_REC_SIZE_SINGLE_CLOCK;
+    }
+
+    /*
+     * Output the header.
+     */
+    memset(state->buf, 0, TRACE_HEADER_LEN);
+    storeIntLE(state->buf + 0, TRACE_MAGIC);
+    storeShortLE(state->buf + 4, state->traceVersion);
+    storeShortLE(state->buf + 6, TRACE_HEADER_LEN);
+    storeLongLE(state->buf + 8, state->startWhen);
+    if (state->traceVersion >= 3) {
+        storeShortLE(state->buf + 16, state->recordSize);
+    }
+    state->curOffset = TRACE_HEADER_LEN;
+
+    /*
+     * Set the "enabled" flag.  Once we do this, threads will wait to be
+     * signaled before exiting, so we have to make sure we wake them up.
+     */
+    android_atomic_release_store(true, &state->traceEnabled);
+
+    /*
+     * ENHANCEMENT: To trace just a single thread, modify the
+     * following to take a Thread* argument, and set the appropriate
+     * interpBreak flags only on the target thread.
+     */
+    updateActiveProfilers(kSubModeMethodTrace, true);
+
+    dvmUnlockMutex(&state->startStopLock);
+    return;
+
+fail:
+    if (state->traceFile != NULL) {
+        fclose(state->traceFile);
+        state->traceFile = NULL;
+    }
+    if (state->buf != NULL) {
+        free(state->buf);
+        state->buf = NULL;
+    }
+    if (traceFd >= 0)
+        close(traceFd);
+    dvmUnlockMutex(&state->startStopLock);
+}
+
+/*
+ * Run through the data buffer and pull out the methods that were visited.
+ * Set a mark so that we know which ones to output.
+ */
+static void markTouchedMethods(int endOffset)
+{
+    u1* ptr = gDvm.methodTrace.buf + TRACE_HEADER_LEN;
+    u1* end = gDvm.methodTrace.buf + endOffset;
+    size_t recordSize = gDvm.methodTrace.recordSize;
+    unsigned int methodVal;
+    Method* method;
+
+    while (ptr < end) {
+        methodVal = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16)
+                    | (ptr[5] << 24);
+        method = (Method*) METHOD_ID(methodVal);
+
+        method->inProfile = true;
+        ptr += recordSize;
+    }
+}
+
+/*
+ * Exercises the clocks in the same way they will be during profiling.
+ */
+static inline void measureClockOverhead()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    if (useThreadCpuClock()) {
+        getThreadCpuTimeInUsec();
+    }
+#endif
+    if (useWallClock()) {
+        getWallTimeInUsec();
+    }
+}
+
+/*
+ * Compute the amount of overhead in a clock call, in nsec.
+ *
+ * This value is going to vary depending on what else is going on in the
+ * system.  When examined across several runs a pattern should emerge.
+ */
+static u4 getClockOverhead()
+{
+    u8 calStart, calElapsed;
+    int i;
+
+    calStart = getStopwatchClock();
+    for (i = 1000 * 4; i > 0; i--) {
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+    }
+
+    calElapsed = getStopwatchClock() - calStart;
+    return (int) (calElapsed / (8*4));
+}
+
+/*
+ * Returns "true" if method tracing is currently active.
+ */
+bool dvmIsMethodTraceActive()
+{
+    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()
+{
+    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 */
+        ALOGD("TRACE stop requested, but not running");
+        dvmUnlockMutex(&state->startStopLock);
+        return;
+    } else {
+        updateActiveProfilers(kSubModeMethodTrace, false);
+    }
+
+    /* compute elapsed time */
+    elapsed = getWallTimeInUsec() - state->startWhen;
+
+    /*
+     * Globally disable it, and allow other threads to notice.  We want
+     * to stall here for at least as long as dvmMethodTraceAdd needs
+     * to finish.  There's no real risk though -- it will take a while to
+     * write the data to disk, and we don't clear the buffer pointer until
+     * after that completes.
+     */
+    state->traceEnabled = false;
+    ANDROID_MEMBAR_FULL();
+    sched_yield();
+    usleep(250 * 1000);
+
+    if ((state->flags & TRACE_ALLOC_COUNTS) != 0)
+        dvmStopAllocCounting();
+
+    /*
+     * It's possible under some circumstances for a thread to have advanced
+     * the data pointer but not written the method value.  It's possible
+     * (though less likely) for the data pointer to be advanced, or partial
+     * data written, while we're doing work here.
+     *
+     * To avoid seeing partially-written data, we grab state->curOffset here,
+     * and use our local copy from here on.  We then scan through what's
+     * already written.  If we see the fill pattern in what should be the
+     * method pointer, we cut things off early.  (If we don't, we'll fail
+     * when we dereference the pointer.)
+     *
+     * There's a theoretical possibility of interrupting another thread
+     * after it has partially written the method pointer, in which case
+     * we'll likely crash when we dereference it.  The possibility of
+     * this actually happening should be at or near zero.  Fixing it
+     * completely could be done by writing the thread number last and
+     * using a sentinel value to indicate a partially-written record,
+     * but that requires memory barriers.
+     */
+    int finalCurOffset = state->curOffset;
+
+    size_t recordSize = state->recordSize;
+    if (finalCurOffset > TRACE_HEADER_LEN) {
+        u4 fillVal = METHOD_ID(FILL_PATTERN);
+        u1* scanPtr = state->buf + TRACE_HEADER_LEN;
+
+        while (scanPtr < state->buf + finalCurOffset) {
+            u4 methodVal = scanPtr[2] | (scanPtr[3] << 8) | (scanPtr[4] << 16)
+                        | (scanPtr[5] << 24);
+            if (METHOD_ID(methodVal) == fillVal) {
+                u1* scanBase = state->buf + TRACE_HEADER_LEN;
+                ALOGW("Found unfilled record at %d (of %d)",
+                    (scanPtr - scanBase) / recordSize,
+                    (finalCurOffset - TRACE_HEADER_LEN) / recordSize);
+                finalCurOffset = scanPtr - state->buf;
+                break;
+            }
+
+            scanPtr += recordSize;
+        }
+    }
+
+    ALOGI("TRACE STOPPED%s: writing %d records",
+        state->overflow ? " (NOTE: overflowed buffer)" : "",
+        (finalCurOffset - TRACE_HEADER_LEN) / recordSize);
+    if (gDvm.debuggerActive) {
+        ALOGW("WARNING: a debugger is active; method-tracing results "
+             "will be skewed");
+    }
+
+    /*
+     * Do a quick calibration test to see how expensive our clock call is.
+     */
+    u4 clockNsec = getClockOverhead();
+
+    markTouchedMethods(finalCurOffset);
+
+    char* memStreamPtr;
+    size_t memStreamSize;
+    if (state->directToDdms) {
+        assert(state->traceFile == NULL);
+        state->traceFile = open_memstream(&memStreamPtr, &memStreamSize);
+        if (state->traceFile == NULL) {
+            /* not expected */
+            ALOGE("Unable to open memstream");
+            dvmAbort();
+        }
+    }
+    assert(state->traceFile != NULL);
+
+    fprintf(state->traceFile, "%cversion\n", TOKEN_CHAR);
+    fprintf(state->traceFile, "%d\n", state->traceVersion);
+    fprintf(state->traceFile, "data-file-overflow=%s\n",
+        state->overflow ? "true" : "false");
+    if (useThreadCpuClock()) {
+        if (useWallClock()) {
+            fprintf(state->traceFile, "clock=dual\n");
+        } else {
+            fprintf(state->traceFile, "clock=thread-cpu\n");
+        }
+    } else {
+        fprintf(state->traceFile, "clock=wall\n");
+    }
+    fprintf(state->traceFile, "elapsed-time-usec=%llu\n", elapsed);
+    fprintf(state->traceFile, "num-method-calls=%d\n",
+        (finalCurOffset - TRACE_HEADER_LEN) / state->recordSize);
+    fprintf(state->traceFile, "clock-call-overhead-nsec=%d\n", clockNsec);
+    fprintf(state->traceFile, "vm=dalvik\n");
+    if ((state->flags & TRACE_ALLOC_COUNTS) != 0) {
+        fprintf(state->traceFile, "alloc-count=%d\n",
+            gDvm.allocProf.allocCount);
+        fprintf(state->traceFile, "alloc-size=%d\n",
+            gDvm.allocProf.allocSize);
+        fprintf(state->traceFile, "gc-count=%d\n",
+            gDvm.allocProf.gcCount);
+    }
+    fprintf(state->traceFile, "%cthreads\n", TOKEN_CHAR);
+    dumpThreadList(state->traceFile);
+    fprintf(state->traceFile, "%cmethods\n", TOKEN_CHAR);
+    dumpMethodList(state->traceFile);
+    fprintf(state->traceFile, "%cend\n", TOKEN_CHAR);
+
+    if (state->directToDdms) {
+        /*
+         * Data is in two places: memStreamPtr and state->buf.  Send
+         * the whole thing to DDMS, wrapped in an MPSE packet.
+         */
+        fflush(state->traceFile);
+
+        struct iovec iov[2];
+        iov[0].iov_base = memStreamPtr;
+        iov[0].iov_len = memStreamSize;
+        iov[1].iov_base = state->buf;
+        iov[1].iov_len = finalCurOffset;
+        dvmDbgDdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2);
+    } else {
+        /* append the profiling data */
+        if (fwrite(state->buf, finalCurOffset, 1, state->traceFile) != 1) {
+            int err = errno;
+            ALOGE("trace fwrite(%d) failed: %s",
+                finalCurOffset, strerror(err));
+            dvmThrowExceptionFmt(gDvm.exRuntimeException,
+                "Trace data write failed: %s", strerror(err));
+        }
+    }
+
+    /* done! */
+    free(state->buf);
+    state->buf = NULL;
+    fclose(state->traceFile);
+    state->traceFile = NULL;
+
+    /* 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 methodVal;
+    int oldOffset, newOffset;
+    u1* ptr;
+
+    assert(method != NULL);
+
+#if defined(HAVE_POSIX_CLOCKS)
+    /*
+     * 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 = getThreadCpuTimeInUsec();
+        self->cpuClockBaseSet = true;
+        //ALOGI("thread base id=%d 0x%llx",
+        //    self->threadId, self->cpuClockBase);
+    }
+#endif
+
+    /*
+     * Advance "curOffset" atomically.
+     */
+    do {
+        oldOffset = state->curOffset;
+        newOffset = oldOffset + state->recordSize;
+        if (newOffset > state->bufferSize) {
+            state->overflow = true;
+            return;
+        }
+    } while (android_atomic_release_cas(oldOffset, newOffset,
+            &state->curOffset) != 0);
+
+    //assert(METHOD_ACTION((u4) method) == 0);
+
+    methodVal = METHOD_COMBINE((u4) method, action);
+
+    /*
+     * Write data into "oldOffset".
+     */
+    ptr = state->buf + oldOffset;
+    *ptr++ = (u1) self->threadId;
+    *ptr++ = (u1) (self->threadId >> 8);
+    *ptr++ = (u1) methodVal;
+    *ptr++ = (u1) (methodVal >> 8);
+    *ptr++ = (u1) (methodVal >> 16);
+    *ptr++ = (u1) (methodVal >> 24);
+
+#if defined(HAVE_POSIX_CLOCKS)
+    if (useThreadCpuClock()) {
+        u4 cpuClockDiff = (u4) (getThreadCpuTimeInUsec() - self->cpuClockBase);
+        *ptr++ = (u1) cpuClockDiff;
+        *ptr++ = (u1) (cpuClockDiff >> 8);
+        *ptr++ = (u1) (cpuClockDiff >> 16);
+        *ptr++ = (u1) (cpuClockDiff >> 24);
+    }
+#endif
+
+    if (useWallClock()) {
+        u4 wallClockDiff = (u4) (getWallTimeInUsec() - state->startWhen);
+        *ptr++ = (u1) wallClockDiff;
+        *ptr++ = (u1) (wallClockDiff >> 8);
+        *ptr++ = (u1) (wallClockDiff >> 16);
+        *ptr++ = (u1) (wallClockDiff >> 24);
+    }
+}
+
+
+/*
+ * Register the METHOD_TRACE_ENTER action for the fast interpreter and
+ * JIT'ed code.
+ */
+void dvmFastMethodTraceEnter(const Method* method, Thread* self)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        dvmMethodTraceAdd(self, method, METHOD_TRACE_ENTER);
+    }
+}
+
+/*
+ * Register the METHOD_TRACE_EXIT action for the fast interpreter and
+ * JIT'ed code for methods. The about-to-return callee method can be
+ * retrieved from self->interpSave.method.
+ */
+void dvmFastMethodTraceExit(Thread* self)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        dvmMethodTraceAdd(self, self->interpSave.method,
+                          METHOD_TRACE_EXIT);
+    }
+}
+
+/*
+ * Register the METHOD_TRACE_EXIT action for the fast interpreter and
+ * JIT'ed code for JNI methods. The about-to-return JNI callee method is passed
+ * in explicitly.  Also used for inline-execute.
+ */
+void dvmFastNativeMethodTraceExit(const Method* method, Thread* self)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        dvmMethodTraceAdd(self, method, METHOD_TRACE_EXIT);
+    }
+}
+
+/*
+ * We just did something with a method.  Emit a record by setting a value
+ * in a magic memory location.
+ */
+void dvmEmitEmulatorTrace(const Method* method, int action)
+{
+#ifdef UPDATE_MAGIC_PAGE
+    /*
+     * We store the address of the Dalvik bytecodes to the memory-mapped
+     * trace page for normal methods.  We also trace calls to native
+     * functions by storing the address of the native function to the
+     * trace page.
+     * Abstract methods don't have any bytecodes, so we don't trace them.
+     * (Abstract methods are never called, but in Dalvik they can be
+     * because we do a "late trap" to a native method to generate the
+     * abstract method exception.)
+     */
+    if (dvmIsAbstractMethod(method))
+        return;
+
+    u4* pMagic = (u4*) gDvm.emulatorTracePage;
+    u4 addr;
+
+    if (dvmIsNativeMethod(method)) {
+        /*
+         * The "action" parameter is one of:
+         *   0 = ENTER
+         *   1 = EXIT
+         *   2 = UNROLL
+         * To help the trace tools reconstruct the runtime stack containing
+         * a mix of normal plus native methods, we add 4 to the action if this
+         * is a native method.
+         */
+        action += 4;
+
+        /*
+         * Get the address of the native function.
+         * This isn't the right address -- how do I get it?
+         * Fortunately, the trace tools can get by without the address, but
+         * it would be nice to fix this.
+         */
+         addr = (u4) method->nativeFunc;
+    } else {
+        /*
+         * The dexlist output shows the &DexCode.insns offset value, which
+         * is offset from the start of the base DEX header. Method.insns
+         * is the absolute address, effectively offset from the start of
+         * the optimized DEX header. We either need to return the
+         * optimized DEX base file address offset by the right amount, or
+         * take the "real" address and subtract off the size of the
+         * optimized DEX header.
+         *
+         * Would be nice to factor this out at dexlist time, but we can't count
+         * on having access to the correct optimized DEX file.
+         */
+        assert(method->insns != NULL);
+        const DexOptHeader* pOptHdr = method->clazz->pDvmDex->pDexFile->pOptHeader;
+        addr = (u4) method->insns - pOptHdr->dexOffset;
+    }
+
+    *(pMagic+action) = addr;
+    LOGVV("Set %p = 0x%08x (%s.%s)",
+        pMagic+action, addr, method->clazz->descriptor, method->name);
+#endif
+}
+
+/*
+ * The GC calls this when it's about to start.  We add a marker to the
+ * trace output so the tool can exclude the GC cost from the results.
+ */
+void dvmMethodTraceGCBegin()
+{
+    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTraceGcMethod);
+}
+void dvmMethodTraceGCEnd()
+{
+    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTraceGcMethod);
+}
+
+/*
+ * The class loader calls this when it's loading or initializing a class.
+ */
+void dvmMethodTraceClassPrepBegin()
+{
+    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTraceClassPrepMethod);
+}
+void dvmMethodTraceClassPrepEnd()
+{
+    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTraceClassPrepMethod);
+}
+
+
+/*
+ * Enable emulator trace info.
+ */
+void dvmEmulatorTraceStart()
+{
+    /* If we could not map the emulator trace page, then do not enable tracing */
+    if (gDvm.emulatorTracePage == NULL)
+        return;
+
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.emulatorTraceEnableCount++;
+    if (gDvm.emulatorTraceEnableCount == 1)
+        ALOGD("--- emulator method traces enabled");
+    updateActiveProfilers(kSubModeEmulatorTrace, true);
+}
+
+/*
+ * Disable emulator trace info.
+ */
+void dvmEmulatorTraceStop()
+{
+    if (gDvm.emulatorTraceEnableCount == 0) {
+        ALOGE("ERROR: emulator tracing not enabled");
+        return;
+    }
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.emulatorTraceEnableCount--;
+    if (gDvm.emulatorTraceEnableCount == 0)
+        ALOGD("--- emulator method traces disabled");
+    updateActiveProfilers(kSubModeEmulatorTrace,
+                          (gDvm.emulatorTraceEnableCount != 0));
+}
+
+
+/*
+ * Start instruction counting.
+ */
+void dvmStartInstructionCounting()
+{
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.instructionCountEnableCount++;
+    updateActiveProfilers(kSubModeInstCounting, true);
+}
+
+/*
+ * Stop instruction counting.
+ */
+void dvmStopInstructionCounting()
+{
+    if (gDvm.instructionCountEnableCount == 0) {
+        ALOGE("ERROR: instruction counting not enabled");
+        dvmAbort();
+    }
+    gDvm.instructionCountEnableCount--;
+    updateActiveProfilers(kSubModeInstCounting,
+                          (gDvm.instructionCountEnableCount != 0));
+}
+
+
+/*
+ * Start alloc counting.  Note this doesn't affect the "active profilers"
+ * count, since the interpreter loop is not involved.
+ */
+void dvmStartAllocCounting()
+{
+    gDvm.allocProf.enabled = true;
+}
+
+/*
+ * Stop alloc counting.
+ */
+void dvmStopAllocCounting()
+{
+    gDvm.allocProf.enabled = false;
+}
diff --git a/vm/Profile.h b/vm/Profile.h
new file mode 100644
index 0000000..6c9e1c7
--- /dev/null
+++ b/vm/Profile.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.
+ */
+
+/*
+ * Android's method call profiling goodies.
+ */
+#ifndef DALVIK_PROFILE_H_
+#define DALVIK_PROFILE_H_
+
+#ifndef NOT_VM      /* for utilities that sneakily include this file */
+
+#include <stdio.h>
+
+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.
+ */
+struct MethodTraceState {
+    /* 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;
+
+    int     traceVersion;
+    size_t  recordSize;
+};
+
+/*
+ * Memory allocation profiler state.  This is used both globally and
+ * per-thread.
+ *
+ * If you add a field here, zero it out in dvmStartAllocCounting().
+ */
+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)
+};
+
+
+/*
+ * 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 (_self->interpBreak.ctl.subMode & kSubModeMethodTrace)           \
+            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_ENTER);          \
+        if (_self->interpBreak.ctl.subMode & kSubModeEmulatorTrace)         \
+            dvmEmitEmulatorTrace(_method, METHOD_TRACE_ENTER);              \
+    } while(0);
+#define TRACE_METHOD_EXIT(_self, _method)                                   \
+    do {                                                                    \
+        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace)           \
+            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_EXIT);           \
+        if (_self->interpBreak.ctl.subMode & kSubModeEmulatorTrace)         \
+            dvmEmitEmulatorTrace(_method, METHOD_TRACE_EXIT);               \
+    } while(0);
+#define TRACE_METHOD_UNROLL(_self, _method)                                 \
+    do {                                                                    \
+        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace)           \
+            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_UNROLL);         \
+        if (_self->interpBreak.ctl.subMode & kSubModeEmulatorTrace)         \
+            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);
+
+extern "C" void dvmFastMethodTraceEnter(const Method* method, struct Thread* self);
+extern "C" void dvmFastMethodTraceExit(struct Thread* self);
+extern "C" void dvmFastNativeMethodTraceExit(const Method* method, struct Thread* self);
+
+/*
+ * 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      '*'
+
+/*
+ * 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_H_
diff --git a/vm/README.txt b/vm/README.txt
new file mode 100644
index 0000000..1be1a63
--- /dev/null
+++ b/vm/README.txt
@@ -0,0 +1,19 @@
+Dalvik Virtual Machine
+
+
+Source code rules of the road:
+
+- All externally-visible function names must start with "dvm" to avoid
+namespace clashes.  Use static functions when possible.
+
+- Do not create static variables (globally or locally).  Do not create
+global variables.  Keep everything with non-local lifespan in "gDvm",
+defined in Globals.h, so that all global VM state is in one place.
+
+- Use "startup" and "shutdown" functions to clean up gDvm.  The VM must
+exit cleanly in valgrind.
+
+- The primary target is ARM Linux.  Others are secondary, but must still
+work correctly.
+
+- Use of gcc-specific and C99 constructs is allowed.
diff --git a/vm/RawDexFile.cpp b/vm/RawDexFile.cpp
new file mode 100644
index 0000000..4598767
--- /dev/null
+++ b/vm/RawDexFile.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Open an unoptimized DEX file.
+ */
+
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * Copy the given number of bytes from one fd to another, first
+ * seeking the source fd to the start of the file.
+ */
+static int copyFileToFile(int destFd, int srcFd, size_t size)
+{
+    if (lseek(srcFd, 0, SEEK_SET) != 0) {
+        ALOGE("lseek failure: %s", strerror(errno));
+        return -1;
+    }
+
+    return sysCopyFileToFile(destFd, srcFd, size);
+}
+
+/*
+ * Get the modification time and size in bytes for the given fd.
+ */
+static int getModTimeAndSize(int fd, u4* modTime, size_t* size)
+{
+    struct stat buf;
+    int result = fstat(fd, &buf);
+
+    if (result < 0) {
+        ALOGE("Unable to determine mod time: %s", strerror(errno));
+        return -1;
+    }
+
+    *modTime = (u4) buf.st_mtime;
+    *size = (size_t) buf.st_size;
+    assert((size_t) buf.st_size == buf.st_size);
+
+    return 0;
+}
+
+/*
+ * Verify the dex file magic number, and get the adler32 checksum out
+ * of the given fd, which is presumed to be a reference to a dex file
+ * with the cursor at the start of the file. The fd's cursor is
+ * modified by this operation.
+ */
+static int verifyMagicAndGetAdler32(int fd, u4 *adler32)
+{
+    /*
+     * The start of a dex file is eight bytes of magic followed by
+     * four bytes of checksum.
+     */
+    u1 headerStart[12];
+    ssize_t amt = read(fd, headerStart, sizeof(headerStart));
+
+    if (amt < 0) {
+        ALOGE("Unable to read header: %s", strerror(errno));
+        return -1;
+    }
+
+    if (amt != sizeof(headerStart)) {
+        ALOGE("Unable to read full header (only got %d bytes)", (int) amt);
+        return -1;
+    }
+
+    if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) {
+        return -1;
+    }
+
+    /*
+     * We can't just cast the data to a u4 and read it, since the
+     * platform might be big-endian (also, because that would make the
+     * compiler complain about type-punned pointers). We assume here
+     * that the dex file is in the standard little-endian format; if
+     * that assumption turns out to be invalid, code that runs later
+     * will notice and complain.
+     */
+    *adler32 = (u4) headerStart[8]
+        | (((u4) headerStart[9]) << 8)
+        | (((u4) headerStart[10]) << 16)
+        | (((u4) headerStart[11]) << 24);
+
+    return 0;
+}
+
+/* See documentation comment in header. */
+int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
+    RawDexFile** ppRawDexFile, bool isBootstrap)
+{
+    /*
+     * TODO: This duplicates a lot of code from dvmJarFileOpen() in
+     * JarFile.c. This should be refactored.
+     */
+
+    DvmDex* pDvmDex = NULL;
+    char* cachedName = NULL;
+    int result = -1;
+    int dexFd = -1;
+    int optFd = -1;
+    u4 modTime = 0;
+    u4 adler32 = 0;
+    size_t fileSize = 0;
+    bool newFile = false;
+    bool locked = false;
+
+    dexFd = open(fileName, O_RDONLY);
+    if (dexFd < 0) goto bail;
+
+    /* If we fork/exec into dexopt, don't let it inherit the open fd. */
+    dvmSetCloseOnExec(dexFd);
+
+    if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
+        ALOGE("Error with header for %s", fileName);
+        goto bail;
+    }
+
+    if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
+        ALOGE("Error with stat for %s", fileName);
+        goto bail;
+    }
+
+    /*
+     * See if the cached file matches. If so, optFd will become a reference
+     * to the cached file and will have been seeked to just past the "opt"
+     * header.
+     */
+
+    if (odexOutputName == NULL) {
+        cachedName = dexOptGenerateCacheFileName(fileName, NULL);
+        if (cachedName == NULL)
+            goto bail;
+    } else {
+        cachedName = strdup(odexOutputName);
+    }
+
+    ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)",
+            fileName, cachedName);
+
+    optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
+        adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
+
+    if (optFd < 0) {
+        ALOGI("Unable to open or create cache for %s (%s)",
+                fileName, cachedName);
+        goto bail;
+    }
+    locked = true;
+
+    /*
+     * If optFd points to a new file (because there was no cached
+     * version, or the cached version was stale), generate the
+     * optimized DEX. The file descriptor returned is still locked,
+     * and is positioned just past the optimization header.
+     */
+    if (newFile) {
+        u8 startWhen, copyWhen, endWhen;
+        bool result;
+        off_t dexOffset;
+
+        dexOffset = lseek(optFd, 0, SEEK_CUR);
+        result = (dexOffset > 0);
+
+        if (result) {
+            startWhen = dvmGetRelativeTimeUsec();
+            result = copyFileToFile(optFd, dexFd, fileSize) == 0;
+            copyWhen = dvmGetRelativeTimeUsec();
+        }
+
+        if (result) {
+            result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
+                fileName, modTime, adler32, isBootstrap);
+        }
+
+        if (!result) {
+            ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
+            goto bail;
+        }
+
+        endWhen = dvmGetRelativeTimeUsec();
+        ALOGD("DEX prep '%s': copy in %dms, rewrite %dms",
+            fileName,
+            (int) (copyWhen - startWhen) / 1000,
+            (int) (endWhen - copyWhen) / 1000);
+    }
+
+    /*
+     * Map the cached version.  This immediately rewinds the fd, so it
+     * doesn't have to be seeked anywhere in particular.
+     */
+    if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
+        ALOGI("Unable to map cached %s", fileName);
+        goto bail;
+    }
+
+    if (locked) {
+        /* unlock the fd */
+        if (!dvmUnlockCachedDexFile(optFd)) {
+            /* uh oh -- this process needs to exit or we'll wedge the system */
+            ALOGE("Unable to unlock DEX file");
+            goto bail;
+        }
+        locked = false;
+    }
+
+    ALOGV("Successfully opened '%s'", fileName);
+
+    *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
+    (*ppRawDexFile)->cacheFileName = cachedName;
+    (*ppRawDexFile)->pDvmDex = pDvmDex;
+    cachedName = NULL;      // don't free it below
+    result = 0;
+
+bail:
+    free(cachedName);
+    if (dexFd >= 0) {
+        close(dexFd);
+    }
+    if (optFd >= 0) {
+        if (locked)
+            (void) dvmUnlockCachedDexFile(optFd);
+        close(optFd);
+    }
+    return result;
+}
+
+/* See documentation comment in header. */
+int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile)
+{
+    DvmDex* pDvmDex = NULL;
+
+    if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) {
+        ALOGD("Unable to open raw DEX from array");
+        return -1;
+    }
+    assert(pDvmDex != NULL);
+
+    *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
+    (*ppRawDexFile)->pDvmDex = pDvmDex;
+
+    return 0;
+}
+
+/*
+ * Close a RawDexFile and free the struct.
+ */
+void dvmRawDexFileFree(RawDexFile* pRawDexFile)
+{
+    if (pRawDexFile == NULL)
+        return;
+
+    dvmDexFileFree(pRawDexFile->pDvmDex);
+    free(pRawDexFile->cacheFileName);
+    free(pRawDexFile);
+}
diff --git a/vm/RawDexFile.h b/vm/RawDexFile.h
new file mode 100644
index 0000000..358c669
--- /dev/null
+++ b/vm/RawDexFile.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * This represents a "raw" unswapped, unoptimized DEX file.  We don't open
+ * them directly, except to create the optimized version that we tuck in
+ * the cache area.
+ */
+#ifndef DALVIK_RAWDEXFILE_H_
+#define DALVIK_RAWDEXFILE_H_
+
+/*
+ * Structure representing a "raw" DEX file, in its unswapped unoptimized
+ * state.
+ */
+struct RawDexFile {
+    char*       cacheFileName;
+    DvmDex*     pDvmDex;
+};
+
+/*
+ * Open a raw ".dex" file, optimize it, and load it.
+ *
+ * On success, returns 0 and sets "*ppDexFile" to a newly-allocated DexFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
+    RawDexFile** ppDexFile, bool isBootstrap);
+
+/*
+ * Open a raw ".dex" file based on the given chunk of memory, and load
+ * it. The bytes are assumed to be owned by the caller for the
+ * purposes of memory management and further assumed to not be touched
+ * by the caller while the raw dex file remains open. The bytes *may*
+ * be modified as the result of issuing this call.
+ *
+ * On success, returns 0 and sets "*ppDexFile" to a newly-allocated DexFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppDexFile);
+
+/*
+ * Free a RawDexFile structure, along with any associated structures.
+ */
+void dvmRawDexFileFree(RawDexFile* pRawDexFile);
+
+/*
+ * Pry the DexFile out of a RawDexFile.
+ */
+INLINE DvmDex* dvmGetRawDexFileDex(RawDexFile* pRawDexFile) {
+    return pRawDexFile->pDvmDex;
+}
+
+/* get full path of optimized DEX file */
+INLINE const char* dvmGetRawDexFileCacheFileName(RawDexFile* pRawDexFile) {
+    return pRawDexFile->cacheFileName;
+}
+
+#endif  // DALVIK_RAWDEXFILE_H_
diff --git a/vm/ReconfigureDvm.mk b/vm/ReconfigureDvm.mk
new file mode 100644
index 0000000..6e161c9
--- /dev/null
+++ b/vm/ReconfigureDvm.mk
@@ -0,0 +1,39 @@
+# 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)
+
+include $(LOCAL_PATH)/Dvm.mk
+
+LOCAL_SHARED_LIBRARIES += \
+	libcorkscrew \
+	libcutils \
+	libdl \
+	liblog \
+	libnativehelper \
+	libselinux \
+	libz
+
+LOCAL_STATIC_LIBRARIES += libdex
+
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_SHARED_LIBRARIES += libstlport
+
+# Don't install on any build by default
+LOCAL_MODULE_TAGS := optional
diff --git a/vm/ReferenceTable.cpp b/vm/ReferenceTable.cpp
new file mode 100644
index 0000000..24180d4
--- /dev/null
+++ b/vm/ReferenceTable.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Reference table management.
+ */
+#include "Dalvik.h"
+
+/*
+ * Initialize a ReferenceTable structure.
+ */
+bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
+    int maxCount)
+{
+    assert(initialCount > 0);
+    assert(initialCount <= maxCount);
+
+    pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
+    if (pRef->table == NULL)
+        return false;
+#ifndef NDEBUG
+    memset(pRef->table, 0xdd, initialCount * sizeof(Object*));
+#endif
+    pRef->nextEntry = pRef->table;
+    pRef->allocEntries = initialCount;
+    pRef->maxEntries = maxCount;
+
+    return true;
+}
+
+/*
+ * Clears out the contents of a ReferenceTable, freeing allocated storage.
+ */
+void dvmClearReferenceTable(ReferenceTable* pRef)
+{
+    free(pRef->table);
+    pRef->table = pRef->nextEntry = NULL;
+    pRef->allocEntries = pRef->maxEntries = -1;
+}
+
+/*
+ * Add "obj" to "pRef".
+ */
+bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj)
+{
+    assert(obj != NULL);
+    assert(dvmIsHeapAddress(obj));
+    assert(pRef->table != NULL);
+    assert(pRef->allocEntries <= pRef->maxEntries);
+
+    if (pRef->nextEntry == pRef->table + pRef->allocEntries) {
+        /* reached end of allocated space; did we hit buffer max? */
+        if (pRef->nextEntry == pRef->table + pRef->maxEntries) {
+            ALOGW("ReferenceTable overflow (max=%d)", pRef->maxEntries);
+            return false;
+        }
+
+        Object** newTable;
+        int newSize;
+
+        newSize = pRef->allocEntries * 2;
+        if (newSize > pRef->maxEntries)
+            newSize = pRef->maxEntries;
+        assert(newSize > pRef->allocEntries);
+
+        newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
+        if (newTable == NULL) {
+            ALOGE("Unable to expand ref table (from %d to %d %d-byte entries)",
+                pRef->allocEntries, newSize, sizeof(Object*));
+            return false;
+        }
+        LOGVV("Growing %p from %d to %d", pRef, pRef->allocEntries, newSize);
+
+        /* update entries; adjust "nextEntry" in case memory moved */
+        pRef->nextEntry = newTable + (pRef->nextEntry - pRef->table);
+        pRef->table = newTable;
+        pRef->allocEntries = newSize;
+    }
+
+    *pRef->nextEntry++ = obj;
+    return true;
+}
+
+/*
+ * Returns NULL if not found.
+ */
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
+    Object* obj)
+{
+    Object** ptr;
+
+    ptr = pRef->nextEntry;
+    while (--ptr >= bottom) {
+        if (*ptr == obj)
+            return ptr;
+    }
+    return NULL;
+}
+
+/*
+ * Remove "obj" from "pRef".  We start at the end of the list (where the
+ * most-recently-added element is), and stop searching for a match after
+ * examining the element at "bottom".
+ *
+ * Most of the time "obj" is at or near the end of the list.  If not, we
+ * compact it down.
+ */
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
+    Object* obj)
+{
+    Object** ptr;
+
+    assert(pRef->table != NULL);
+
+    /*
+     * Scan from the most-recently-added entry up to the bottom entry for
+     * this frame.
+     */
+    ptr = dvmFindInReferenceTable(pRef, bottom, obj);
+    if (ptr == NULL)
+        return false;
+
+    /*
+     * Delete the entry.
+     */
+    pRef->nextEntry--;
+    int moveCount = pRef->nextEntry - ptr;
+    if (moveCount != 0) {
+        /* remove from middle, slide the rest down */
+        memmove(ptr, ptr+1, moveCount * sizeof(Object*));
+        //ALOGV("LREF delete %p, shift %d down", obj, moveCount);
+    } else {
+        /* last entry, falls off the end */
+        //ALOGV("LREF delete %p from end", obj);
+    }
+
+    return true;
+}
+
+/*
+ * If "obj" is an array, return the number of elements in the array.
+ * Otherwise, return zero.
+ */
+static size_t getElementCount(const Object* obj)
+{
+    const ArrayObject* arrayObj = (ArrayObject*) obj;
+    if (arrayObj == NULL || arrayObj == kClearedJniWeakGlobal ||
+            arrayObj->clazz == NULL || !dvmIsArray(arrayObj)) {
+        return 0;
+    }
+    return arrayObj->length;
+}
+
+/*
+ * This is a qsort() callback.  We sort Object* by class, allocation size,
+ * and then by the Object* itself.
+ */
+static int compareObject(const void* vobj1, const void* vobj2)
+{
+    const Object* obj1 = *((Object* const*) vobj1);
+    const Object* obj2 = *((Object* const*) vobj2);
+
+    // Ensure null references and cleared jweaks appear at the end.
+    if (obj1 == NULL) {
+        if (obj2 == NULL) {
+            return 0;
+        } else {
+            return 1;
+        }
+    } else if (obj2 == NULL) {
+        return -1;
+    }
+    if (obj1 == kClearedJniWeakGlobal) {
+        if (obj2 == kClearedJniWeakGlobal) {
+            return 0;
+        } else {
+            return 1;
+        }
+    } else if (obj2 == kClearedJniWeakGlobal) {
+        return -1;
+    }
+
+    if (obj1->clazz != obj2->clazz) {
+        return (u1*)obj1->clazz - (u1*)obj2->clazz;
+    } else {
+        size_t count1 = getElementCount(obj1);
+        size_t count2 = getElementCount(obj2);
+        if (count1 != count2) {
+            return count1 - count2;
+        } else {
+            return (u1*)obj1 - (u1*)obj2;
+        }
+    }
+}
+
+/*
+ * Log an object with some additional info.
+ *
+ * Pass in the number of elements in the array (or 0 if this is not an
+ * array object), and the number of additional objects that are identical
+ * or equivalent to the original.
+ */
+static void logSummaryLine(const Object* obj, size_t elems, int identical, int equiv)
+{
+    if (obj == NULL) {
+        ALOGW("    NULL reference (count=%d)", equiv);
+        return;
+    }
+    if (obj == kClearedJniWeakGlobal) {
+        ALOGW("    cleared jweak (count=%d)", equiv);
+        return;
+    }
+
+    std::string className(dvmHumanReadableType(obj));
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        // We're summarizing multiple instances, so using the exemplar
+        // Class' type parameter here would be misleading.
+        className = "java.lang.Class";
+    }
+    if (elems != 0) {
+        StringAppendF(&className, " (%zd elements)", elems);
+    }
+
+    size_t total = identical + equiv + 1;
+    std::string msg(StringPrintf("%5d of %s", total, className.c_str()));
+    if (identical + equiv != 0) {
+        StringAppendF(&msg, " (%d unique instances)", equiv + 1);
+    }
+    ALOGW("    %s", msg.c_str());
+}
+
+/*
+ * Dump a summary of an array of references to the log file.
+ *
+ * This is used to dump the contents of ReferenceTable and IndirectRefTable
+ * structs.
+ */
+void dvmDumpReferenceTableContents(Object* const* refs, size_t count,
+    const char* descr)
+{
+    ALOGW("%s reference table (%p) dump:", descr, refs);
+
+    if (count == 0) {
+        ALOGW("  (empty)");
+        return;
+    }
+
+    // Dump the most recent N entries.
+    const size_t kLast = 10;
+    int first = count - kLast;
+    if (first < 0) {
+        first = 0;
+    }
+    ALOGW("  Last %d entries (of %d):", (count - first), count);
+    for (int idx = count - 1; idx >= first; --idx) {
+        const Object* ref = refs[idx];
+        if (ref == NULL) {
+            continue;
+        }
+        if (ref == kClearedJniWeakGlobal) {
+            ALOGW("    %5d: cleared jweak", idx);
+            continue;
+        }
+        if (ref->clazz == NULL) {
+            // should only be possible right after a plain dvmMalloc().
+            size_t size = dvmObjectSizeInHeap(ref);
+            ALOGW("    %5d: %p (raw) (%zd bytes)", idx, ref, size);
+            continue;
+        }
+
+        std::string className(dvmHumanReadableType(ref));
+
+        std::string extras;
+        size_t elems = getElementCount(ref);
+        if (elems != 0) {
+            StringAppendF(&extras, " (%zd elements)", elems);
+        } else if (ref->clazz == gDvm.classJavaLangString) {
+            const StringObject* str =
+                    reinterpret_cast<const StringObject*>(ref);
+            extras += " \"";
+            size_t count = 0;
+            char* s = dvmCreateCstrFromString(str);
+            char* p = s;
+            for (; *p && count < 16; ++p, ++count) {
+                extras += *p;
+            }
+            if (*p == 0) {
+                extras += "\"";
+            } else {
+                StringAppendF(&extras, "... (%d chars)", str->length());
+            }
+            free(s);
+        }
+        ALOGW("    %5d: %p %s%s", idx, ref, className.c_str(), extras.c_str());
+    }
+
+    // Make a copy of the table, and sort it.
+    Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
+    if (tableCopy == NULL) {
+        ALOGE("Unable to copy table with %d elements", count);
+        return;
+    }
+    memcpy(tableCopy, refs, sizeof(Object*) * count);
+    qsort(tableCopy, count, sizeof(Object*), compareObject);
+    refs = tableCopy;       // use sorted list
+
+    // Remove any uninteresting stuff from the list. The sort moved them all to the end.
+    while (count > 0 && refs[count-1] == NULL) {
+        --count;
+    }
+    while (count > 0 && refs[count-1] == kClearedJniWeakGlobal) {
+        --count;
+    }
+    if (count == 0) {
+        return;
+    }
+
+    // Dump a summary of the whole table.
+    ALOGW("  Summary:");
+    size_t equiv, identical;
+    equiv = identical = 0;
+    size_t idx;
+    size_t elems;
+    for (idx = 1; idx < count; idx++) {
+        elems = getElementCount(refs[idx-1]);
+
+        if (refs[idx] == refs[idx-1]) {
+            // same reference, added more than once.
+            identical++;
+        } else if (refs[idx]->clazz == refs[idx-1]->clazz &&
+            getElementCount(refs[idx]) == elems)
+        {
+            // same class / element count, different object.
+            equiv++;
+        } else {
+            // different class.
+            logSummaryLine(refs[idx-1], elems, identical, equiv);
+            equiv = identical = 0;
+        }
+    }
+
+    // Handle the last entry (everything above outputs refs[i-1]).
+    elems = getElementCount(refs[idx-1]);
+    logSummaryLine(refs[count-1], elems, identical, equiv);
+
+    free(tableCopy);
+}
+
+/*
+ * Dump the contents of a ReferenceTable to the log.
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr)
+{
+    dvmDumpReferenceTableContents(pRef->table, dvmReferenceTableEntries(pRef),
+        descr);
+}
diff --git a/vm/ReferenceTable.h b/vm/ReferenceTable.h
new file mode 100644
index 0000000..100a918
--- /dev/null
+++ b/vm/ReferenceTable.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Maintain a table of references.  Used for internal local references,
+ * JNI monitor references, and JNI pinned array references.
+ *
+ * None of the table functions are synchronized.
+ */
+#ifndef DALVIK_REFERENCETABLE_H_
+#define DALVIK_REFERENCETABLE_H_
+
+/*
+ * Table definition.
+ *
+ * The expected common operations are adding a new entry and removing a
+ * recently-added entry (usually the most-recently-added entry).
+ *
+ * If "allocEntries" is not equal to "maxEntries", the table may expand when
+ * entries are added, which means the memory may move.  If you want to keep
+ * pointers into "table" rather than offsets, use a fixed-size table.
+ *
+ * (This structure is still somewhat transparent; direct access to
+ * table/nextEntry is allowed.)
+ */
+struct ReferenceTable {
+    Object**        nextEntry;          /* top of the list */
+    Object**        table;              /* bottom of the list */
+
+    int             allocEntries;       /* #of entries we have space for */
+    int             maxEntries;         /* max #of entries allowed */
+};
+
+/*
+ * Initialize a ReferenceTable.
+ *
+ * If "initialCount" != "maxCount", the table will expand as required.
+ *
+ * Returns "false" if table allocation fails.
+ */
+bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
+    int maxCount);
+
+/*
+ * Clears out the contents of a ReferenceTable, freeing allocated storage.
+ * Does not free "pRef".
+ *
+ * You must call dvmInitReferenceTable() before you can re-use this table.
+ */
+void dvmClearReferenceTable(ReferenceTable* pRef);
+
+/*
+ * Return the #of entries currently stored in the ReferenceTable.
+ */
+INLINE size_t dvmReferenceTableEntries(const ReferenceTable* pRef)
+{
+    return pRef->nextEntry - pRef->table;
+}
+
+/*
+ * Returns "true" if the table is full.  The table is considered full if
+ * we would need to expand it to add another entry.
+ */
+INLINE size_t dvmIsReferenceTableFull(const ReferenceTable* pRef)
+{
+    return dvmReferenceTableEntries(pRef) == (size_t)pRef->allocEntries;
+}
+
+/*
+ * Add a new entry.  "obj" must be a valid non-NULL object reference
+ * (though it's okay if it's not fully-formed, e.g. the result from
+ * dvmMalloc doesn't have obj->clazz set).
+ *
+ * Returns "false" if the table is full.
+ */
+bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj);
+
+/*
+ * Determine if "obj" is present in "pRef".  Stops searching when we hit
+ * "bottom".  To include the entire table, pass in "pRef->table" as the
+ * bottom.
+ *
+ * Returns NULL if "obj" was not found.
+ */
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
+    Object* obj);
+
+/*
+ * Remove an existing entry.
+ *
+ * We stop searching for a match after examining the element at "bottom".
+ * This is useful when entries are associated with a stack frame.
+ *
+ * Returns "false" if the entry was not found.
+ */
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
+    Object* obj);
+
+/*
+ * Dump the contents of a reference table to the log file.
+ *
+ * The caller should lock any external sync before calling.
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr);
+
+/*
+ * Internal function, shared with IndirectRefTable.
+ */
+void dvmDumpReferenceTableContents(Object* const* refs, size_t count,
+    const char* descr);
+
+#endif  // DALVIK_REFERENCETABLE_H_
diff --git a/vm/SignalCatcher.cpp b/vm/SignalCatcher.cpp
new file mode 100644
index 0000000..a4beb6b
--- /dev/null
+++ b/vm/SignalCatcher.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This is a thread that catches signals and does something useful.  For
+ * example, when a SIGQUIT (Ctrl-\) arrives, suspend the VM and dump the
+ * status of all threads.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <cutils/open_memstream.h>
+
+static void* signalCatcherThreadStart(void* arg);
+
+/*
+ * Crank up the signal catcher thread.
+ *
+ * Returns immediately.
+ */
+bool dvmSignalCatcherStartup()
+{
+    gDvm.haltSignalCatcher = false;
+
+    if (!dvmCreateInternalThread(&gDvm.signalCatcherHandle,
+                "Signal Catcher", signalCatcherThreadStart, NULL))
+        return false;
+
+    return true;
+}
+
+/*
+ * Shut down the signal catcher thread if it was started.
+ *
+ * Since we know the thread is just sitting around waiting for signals
+ * to arrive, send it one.
+ */
+void dvmSignalCatcherShutdown()
+{
+    gDvm.haltSignalCatcher = true;
+    if (gDvm.signalCatcherHandle == 0)      // not started yet
+        return;
+
+    pthread_kill(gDvm.signalCatcherHandle, SIGQUIT);
+
+    pthread_join(gDvm.signalCatcherHandle, NULL);
+    ALOGV("signal catcher has shut down");
+}
+
+
+/*
+ * Print the name of the current process, if we can get it.
+ */
+static void printProcessName(const DebugOutputTarget* target)
+{
+    int fd = -1;
+
+    fd = open("/proc/self/cmdline", O_RDONLY, 0);
+    if (fd < 0)
+        goto bail;
+
+    char tmpBuf[256];
+    ssize_t actual;
+
+    actual = read(fd, tmpBuf, sizeof(tmpBuf)-1);
+    if (actual <= 0)
+        goto bail;
+
+    tmpBuf[actual] = '\0';
+    dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf);
+
+bail:
+    if (fd >= 0)
+        close(fd);
+}
+
+/*
+ * Dump the stack traces for all threads to the supplied file, putting
+ * a timestamp header on it.
+ */
+static void logThreadStacks(FILE* fp)
+{
+    DebugOutputTarget target;
+
+    dvmCreateFileOutputTarget(&target, fp);
+
+    pid_t pid = getpid();
+    time_t now = time(NULL);
+    struct tm* ptm;
+#ifdef HAVE_LOCALTIME_R
+    struct tm tmbuf;
+    ptm = localtime_r(&now, &tmbuf);
+#else
+    ptm = localtime(&now);
+#endif
+    dvmPrintDebugMessage(&target,
+        "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n",
+        pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
+        ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+    printProcessName(&target);
+    dvmPrintDebugMessage(&target, "\n");
+    dvmDumpJniStats(&target);
+    dvmDumpAllThreadsEx(&target, true);
+    fprintf(fp, "----- end %d -----\n", pid);
+}
+
+
+/*
+ * Respond to a SIGQUIT by dumping the thread stacks.  Optionally dump
+ * a few other things while we're at it.
+ *
+ * Thread stacks can either go to the log or to a file designated for holding
+ * ANR traces.  If we're writing to a file, we want to do it in one shot,
+ * so we can use a single O_APPEND write instead of contending for exclusive
+ * access with flock().  There may be an advantage in resuming the VM
+ * before doing the file write, so we don't stall the VM if disk I/O is
+ * bottlenecked.
+ *
+ * If JIT tuning is compiled in, dump compiler stats as well.
+ */
+static void handleSigQuit()
+{
+    char* traceBuf = NULL;
+    size_t traceLen;
+
+    dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
+
+    dvmDumpLoaderStats("sig");
+
+    if (gDvm.stackTraceFile == NULL) {
+        /* just dump to log */
+        DebugOutputTarget target;
+        dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+        dvmDumpJniStats(&target);
+        dvmDumpAllThreadsEx(&target, true);
+    } else {
+        /* write to memory buffer */
+        FILE* memfp = open_memstream(&traceBuf, &traceLen);
+        if (memfp == NULL) {
+            ALOGE("Unable to create memstream for stack traces");
+            traceBuf = NULL;        /* make sure it didn't touch this */
+            /* continue on */
+        } else {
+            logThreadStacks(memfp);
+            fclose(memfp);
+        }
+    }
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    dvmCompilerDumpStats();
+#endif
+
+    if (false) dvmDumpTrackedAllocations(true);
+
+    dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
+
+    if (traceBuf != NULL) {
+        /*
+         * We don't know how long it will take to do the disk I/O, so put us
+         * into VMWAIT for the duration.
+         */
+        ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);
+
+        /*
+         * Open the stack trace output file, creating it if necessary.  It
+         * needs to be world-writable so other processes can write to it.
+         */
+        int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
+        if (fd < 0) {
+            ALOGE("Unable to open stack trace file '%s': %s",
+                gDvm.stackTraceFile, strerror(errno));
+        } else {
+            ssize_t actual = write(fd, traceBuf, traceLen);
+            if (actual != (ssize_t) traceLen) {
+                ALOGE("Failed to write stack traces to %s (%d of %zd): %s",
+                    gDvm.stackTraceFile, (int) actual, traceLen,
+                    strerror(errno));
+            } else {
+                ALOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile);
+            }
+            close(fd);
+        }
+
+        free(traceBuf);
+        dvmChangeStatus(dvmThreadSelf(), oldStatus);
+    }
+}
+
+/*
+ * Respond to a SIGUSR1 by forcing a GC.
+ */
+static void handleSigUsr1()
+{
+    ALOGI("SIGUSR1 forcing GC (no HPROF)");
+    dvmCollectGarbage();
+}
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+/* Sample callback function for dvmJitScanAllClassPointers */
+void printAllClass(void *ptr)
+{
+    ClassObject **classPP = (ClassObject **) ptr;
+    ALOGE("class %s", (*classPP)->descriptor);
+
+}
+
+/*
+ * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
+ * the code cache.
+ */
+static void handleSigUsr2()
+{
+    static int codeCacheResetCount = 0;
+    gDvmJit.receivedSIGUSR2 ^= true;
+    if ((--codeCacheResetCount & 7) == 0) {
+        /* Dump all class pointers in the traces */
+        dvmJitScanAllClassPointers(printAllClass);
+        gDvmJit.codeCacheFull = true;
+    } else {
+        dvmCompilerDumpStats();
+        /* Stress-test unchain all */
+        dvmJitUnchainAll();
+        ALOGD("Send %d more signals to reset the code cache",
+             codeCacheResetCount & 7);
+    }
+    dvmCheckInterpStateConsistency();
+}
+#endif
+
+/*
+ * Sleep in sigwait() until a signal arrives.
+ */
+static void* signalCatcherThreadStart(void* arg)
+{
+    Thread* self = dvmThreadSelf();
+    sigset_t mask;
+    int cc;
+
+    UNUSED_PARAMETER(arg);
+
+    ALOGV("Signal catcher thread started (threadid=%d)", self->threadId);
+
+    /* set up mask with signals we want to handle */
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGQUIT);
+    sigaddset(&mask, SIGUSR1);
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    sigaddset(&mask, SIGUSR2);
+#endif
+
+    while (true) {
+        int rcvd;
+
+        dvmChangeStatus(self, THREAD_VMWAIT);
+
+        /*
+         * Signals for sigwait() must be blocked but not ignored.  We
+         * block signals like SIGQUIT for all threads, so the condition
+         * is met.  When the signal hits, we wake up, without any signal
+         * handlers being invoked.
+         *
+         * When running under GDB we occasionally return from sigwait()
+         * with EINTR (e.g. when other threads exit).
+         */
+loop:
+        cc = sigwait(&mask, &rcvd);
+        if (cc != 0) {
+            if (cc == EINTR) {
+                //ALOGV("sigwait: EINTR");
+                goto loop;
+            }
+            assert(!"bad result from sigwait");
+        }
+
+        if (!gDvm.haltSignalCatcher) {
+            ALOGI("threadid=%d: reacting to signal %d",
+                dvmThreadSelf()->threadId, rcvd);
+        }
+
+        /* set our status to RUNNING, self-suspending if GC in progress */
+        dvmChangeStatus(self, THREAD_RUNNING);
+
+        if (gDvm.haltSignalCatcher)
+            break;
+
+        switch (rcvd) {
+        case SIGQUIT:
+            handleSigQuit();
+            break;
+        case SIGUSR1:
+            handleSigUsr1();
+            break;
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+        case SIGUSR2:
+            handleSigUsr2();
+            break;
+#endif
+        default:
+            ALOGE("unexpected signal %d", rcvd);
+            break;
+        }
+    }
+
+    return NULL;
+}
diff --git a/vm/SignalCatcher.h b/vm/SignalCatcher.h
new file mode 100644
index 0000000..d1d7a18
--- /dev/null
+++ b/vm/SignalCatcher.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Signal catcher thread.
+ */
+#ifndef DALVIK_SIGNALCATCHER_H_
+#define DALVIK_SIGNALCATCHER_H_
+
+bool dvmSignalCatcherStartup(void);
+void dvmSignalCatcherShutdown(void);
+
+#endif  // DALVIK_SIGNALCATCHER_H_
diff --git a/vm/StdioConverter.cpp b/vm/StdioConverter.cpp
new file mode 100644
index 0000000..f420c4d
--- /dev/null
+++ b/vm/StdioConverter.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Thread that reads from stdout/stderr and converts them to log messages.
+ * (Sort of a hack.)
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define kFilenoStdout   1
+#define kFilenoStderr   2
+
+#define kMaxLine    512
+
+/*
+ * Hold some data.
+ */
+struct BufferedData {
+    char    buf[kMaxLine+1];
+    int     count;
+};
+
+// fwd
+static void* stdioConverterThreadStart(void* arg);
+static bool readAndLog(int fd, BufferedData* data, const char* tag);
+
+
+/*
+ * Crank up the stdout/stderr converter thread.
+ *
+ * Returns immediately.
+ */
+bool dvmStdioConverterStartup()
+{
+    gDvm.haltStdioConverter = false;
+
+    dvmInitMutex(&gDvm.stdioConverterLock);
+    pthread_cond_init(&gDvm.stdioConverterCond, NULL);
+
+    if (pipe(gDvm.stdoutPipe) != 0) {
+        ALOGW("pipe failed: %s", strerror(errno));
+        return false;
+    }
+    if (pipe(gDvm.stderrPipe) != 0) {
+        ALOGW("pipe failed: %s", strerror(errno));
+        return false;
+    }
+
+    if (dup2(gDvm.stdoutPipe[1], kFilenoStdout) != kFilenoStdout) {
+        ALOGW("dup2(1) failed: %s", strerror(errno));
+        return false;
+    }
+    close(gDvm.stdoutPipe[1]);
+    gDvm.stdoutPipe[1] = -1;
+#ifdef HAVE_ANDROID_OS
+    /* don't redirect stderr on sim -- logs get written there! */
+    /* (don't need this on the sim anyway) */
+    if (dup2(gDvm.stderrPipe[1], kFilenoStderr) != kFilenoStderr) {
+        ALOGW("dup2(2) failed: %d %s", errno, strerror(errno));
+        return false;
+    }
+    close(gDvm.stderrPipe[1]);
+    gDvm.stderrPipe[1] = -1;
+#endif
+
+
+    /*
+     * Create the thread.
+     */
+    dvmLockMutex(&gDvm.stdioConverterLock);
+
+    if (!dvmCreateInternalThread(&gDvm.stdioConverterHandle,
+                                 "Stdio Converter",
+                                 stdioConverterThreadStart,
+                                 NULL)) {
+        return false;
+    }
+
+    while (!gDvm.stdioConverterReady) {
+        dvmWaitCond(&gDvm.stdioConverterCond, &gDvm.stdioConverterLock);
+    }
+    dvmUnlockMutex(&gDvm.stdioConverterLock);
+
+    return true;
+}
+
+/*
+ * Shut down the stdio converter thread if it was started.
+ *
+ * Since we know the thread is just sitting around waiting for something
+ * to arrive on stdout, print something.
+ */
+void dvmStdioConverterShutdown()
+{
+    gDvm.haltStdioConverter = true;
+    if (gDvm.stdioConverterHandle == 0)    // not started, or still starting
+        return;
+
+    /* print something to wake it up */
+    printf("Shutting down\n");
+    fflush(stdout);
+
+    ALOGD("Joining stdio converter...");
+    pthread_join(gDvm.stdioConverterHandle, NULL);
+}
+
+/*
+ * Select on stdout/stderr pipes, waiting for activity.
+ *
+ * DO NOT use printf from here.
+ */
+static void* stdioConverterThreadStart(void* arg)
+{
+    int cc;
+
+    /* tell the main thread that we're ready */
+    dvmLockMutex(&gDvm.stdioConverterLock);
+    gDvm.stdioConverterReady = true;
+    cc = pthread_cond_signal(&gDvm.stdioConverterCond);
+    assert(cc == 0);
+    dvmUnlockMutex(&gDvm.stdioConverterLock);
+
+    /* we never do anything that affects the rest of the VM */
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+    /*
+     * Allocate read buffers.
+     */
+    BufferedData* stdoutData = new BufferedData;
+    BufferedData* stderrData = new BufferedData;
+    stdoutData->count = stderrData->count = 0;
+
+    /*
+     * Read until shutdown time.
+     */
+    while (!gDvm.haltStdioConverter) {
+        fd_set readfds;
+        int maxFd, fdCount;
+
+        FD_ZERO(&readfds);
+        FD_SET(gDvm.stdoutPipe[0], &readfds);
+        FD_SET(gDvm.stderrPipe[0], &readfds);
+        maxFd = MAX(gDvm.stdoutPipe[0], gDvm.stderrPipe[0]);
+
+        fdCount = select(maxFd+1, &readfds, NULL, NULL, NULL);
+
+        if (fdCount < 0) {
+            if (errno != EINTR) {
+                ALOGE("select on stdout/stderr failed");
+                break;
+            }
+            ALOGD("Got EINTR, ignoring");
+        } else if (fdCount == 0) {
+            ALOGD("WEIRD: select returned zero");
+        } else {
+            bool err = false;
+            if (FD_ISSET(gDvm.stdoutPipe[0], &readfds)) {
+                err |= !readAndLog(gDvm.stdoutPipe[0], stdoutData,
+                    "stdout");
+            }
+            if (FD_ISSET(gDvm.stderrPipe[0], &readfds)) {
+                err |= !readAndLog(gDvm.stderrPipe[0], stderrData,
+                    "stderr");
+            }
+
+            /* probably EOF; give up */
+            if (err) {
+                ALOGW("stdio converter got read error; shutting it down");
+                break;
+            }
+        }
+    }
+
+    close(gDvm.stdoutPipe[0]);
+    close(gDvm.stderrPipe[0]);
+
+    delete stdoutData;
+    delete stderrData;
+
+    /* change back for shutdown sequence */
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+    return NULL;
+}
+
+/*
+ * Data is pending on "fd".  Read as much as will fit in "data", then
+ * write out any full lines and compact "data".
+ */
+static bool readAndLog(int fd, BufferedData* data, const char* tag)
+{
+    ssize_t actual;
+    size_t want;
+
+    assert(data->count < kMaxLine);
+
+    want = kMaxLine - data->count;
+    actual = read(fd, data->buf + data->count, want);
+    if (actual <= 0) {
+        ALOGW("read %s: (%d,%d) failed (%d): %s",
+            tag, fd, want, (int)actual, strerror(errno));
+        return false;
+    } else {
+        //ALOGI("read %s: %d at %d", tag, actual, data->count);
+    }
+    data->count += actual;
+
+    /*
+     * Got more data, look for an EOL.  We expect LF or CRLF, but will
+     * try to handle a standalone CR.
+     */
+    char* cp = data->buf;
+    const char* start = data->buf;
+    int i = data->count;
+    for (i = data->count; i > 0; i--, cp++) {
+        if (*cp == '\n' || (*cp == '\r' && i != 0 && *(cp+1) != '\n')) {
+            *cp = '\0';
+            //ALOGW("GOT %d at %d '%s'", cp - start, start - data->buf, start);
+            ALOG(LOG_INFO, tag, "%s", start);
+            start = cp+1;
+        }
+    }
+
+    /*
+     * See if we overflowed.  If so, cut it off.
+     */
+    if (start == data->buf && data->count == kMaxLine) {
+        data->buf[kMaxLine] = '\0';
+        ALOG(LOG_INFO, tag, "%s!", start);
+        start = cp + kMaxLine;
+    }
+
+    /*
+     * Update "data" if we consumed some output.  If there's anything left
+     * in the buffer, it's because we didn't see an EOL and need to keep
+     * reading until we see one.
+     */
+    if (start != data->buf) {
+        if (start >= data->buf + data->count) {
+            /* consumed all available */
+            data->count = 0;
+        } else {
+            /* some left over */
+            int remaining = data->count - (start - data->buf);
+            memmove(data->buf, start, remaining);
+            data->count = remaining;
+        }
+    }
+
+    return true;
+}
diff --git a/vm/StdioConverter.h b/vm/StdioConverter.h
new file mode 100644
index 0000000..bd5d879
--- /dev/null
+++ b/vm/StdioConverter.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Stdout/stderr conversion thread.
+ */
+#ifndef DALVIK_STDOUTCONVERTER_H_
+#define DALVIK_STDOUTCONVERTER_H_
+
+bool dvmStdioConverterStartup(void);
+void dvmStdioConverterShutdown(void);
+
+#endif  // DALVIK_STDOUTCONVERTER_H_
diff --git a/vm/Sync.cpp b/vm/Sync.cpp
new file mode 100644
index 0000000..f42004c
--- /dev/null
+++ b/vm/Sync.cpp
@@ -0,0 +1,1374 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <errno.h>
+
+/*
+ * Every Object has a monitor associated with it, but not every Object is
+ * actually locked.  Even the ones that are locked do not need a
+ * full-fledged monitor until a) there is actual contention or b) wait()
+ * is called on the Object.
+ *
+ * For Dalvik, we have implemented a scheme similar to the one described
+ * in Bacon et al.'s "Thin locks: featherweight synchronization for Java"
+ * (ACM 1998).  Things are even easier for us, though, because we have
+ * a full 32 bits to work with.
+ *
+ * The two states of an Object's lock are referred to as "thin" and
+ * "fat".  A lock may transition from the "thin" state to the "fat"
+ * state and this transition is referred to as inflation.  Once a lock
+ * has been inflated it remains in the "fat" state indefinitely.
+ *
+ * The lock value itself is stored in Object.lock.  The LSB of the
+ * lock encodes its state.  When cleared, the lock is in the "thin"
+ * state and its bits are formatted as follows:
+ *
+ *    [31 ---- 19] [18 ---- 3] [2 ---- 1] [0]
+ *     lock count   thread id  hash state  0
+ *
+ * When set, the lock is in the "fat" state and its bits are formatted
+ * as follows:
+ *
+ *    [31 ---- 3] [2 ---- 1] [0]
+ *      pointer   hash state  1
+ *
+ * For an in-depth description of the mechanics of thin-vs-fat locking,
+ * read the paper referred to above.
+ */
+
+/*
+ * Monitors provide:
+ *  - mutually exclusive access to resources
+ *  - a way for multiple threads to wait for notification
+ *
+ * In effect, they fill the role of both mutexes and condition variables.
+ *
+ * Only one thread can own the monitor at any time.  There may be several
+ * threads waiting on it (the wait call unlocks it).  One or more waiting
+ * threads may be getting interrupted or notified at any given time.
+ *
+ * TODO: the various members of monitor are not SMP-safe.
+ */
+struct Monitor {
+    Thread*     owner;          /* which thread currently owns the lock? */
+    int         lockCount;      /* owner's recursive lock depth */
+    Object*     obj;            /* what object are we part of [debug only] */
+
+    Thread*     waitSet;	/* threads currently waiting on this monitor */
+
+    pthread_mutex_t lock;
+
+    Monitor*    next;
+
+    /*
+     * Who last acquired this monitor, when lock sampling is enabled.
+     * Even when enabled, ownerMethod may be NULL.
+     */
+    const Method* ownerMethod;
+    u4 ownerPc;
+};
+
+
+/*
+ * Create and initialize a monitor.
+ */
+Monitor* dvmCreateMonitor(Object* obj)
+{
+    Monitor* mon;
+
+    mon = (Monitor*) calloc(1, sizeof(Monitor));
+    if (mon == NULL) {
+        ALOGE("Unable to allocate monitor");
+        dvmAbort();
+    }
+    mon->obj = obj;
+    dvmInitMutex(&mon->lock);
+
+    /* replace the head of the list with the new monitor */
+    do {
+        mon->next = gDvm.monitorList;
+    } while (android_atomic_release_cas((int32_t)mon->next, (int32_t)mon,
+            (int32_t*)(void*)&gDvm.monitorList) != 0);
+
+    return mon;
+}
+
+/*
+ * Free the monitor list.  Only used when shutting the VM down.
+ */
+void dvmFreeMonitorList()
+{
+    Monitor* mon;
+    Monitor* nextMon;
+
+    mon = gDvm.monitorList;
+    while (mon != NULL) {
+        nextMon = mon->next;
+        free(mon);
+        mon = nextMon;
+    }
+}
+
+/*
+ * Get the object that a monitor is part of.
+ */
+Object* dvmGetMonitorObject(Monitor* mon)
+{
+    if (mon == NULL)
+        return NULL;
+    else
+        return mon->obj;
+}
+
+/*
+ * Returns the thread id of the thread owning the given lock.
+ */
+static u4 lockOwner(Object* obj)
+{
+    Thread *owner;
+    u4 lock;
+
+    assert(obj != NULL);
+    /*
+     * Since we're reading the lock value multiple times, latch it so
+     * that it doesn't change out from under us if we get preempted.
+     */
+    lock = obj->lock;
+    if (LW_SHAPE(lock) == LW_SHAPE_THIN) {
+        return LW_LOCK_OWNER(lock);
+    } else {
+        owner = LW_MONITOR(lock)->owner;
+        return owner ? owner->threadId : 0;
+    }
+}
+
+/*
+ * Get the thread that holds the lock on the specified object.  The
+ * object may be unlocked, thin-locked, or fat-locked.
+ *
+ * The caller must lock the thread list before calling here.
+ */
+Thread* dvmGetObjectLockHolder(Object* obj)
+{
+    u4 threadId = lockOwner(obj);
+
+    if (threadId == 0)
+        return NULL;
+    return dvmGetThreadByThreadId(threadId);
+}
+
+/*
+ * Checks whether the given thread holds the given
+ * objects's lock.
+ */
+bool dvmHoldsLock(Thread* thread, Object* obj)
+{
+    if (thread == NULL || obj == NULL) {
+        return false;
+    } else {
+        return thread->threadId == lockOwner(obj);
+    }
+}
+
+/*
+ * Free the monitor associated with an object and make the object's lock
+ * thin again.  This is called during garbage collection.
+ */
+static void freeMonitor(Monitor *mon)
+{
+    assert(mon != NULL);
+    assert(mon->obj != NULL);
+    assert(LW_SHAPE(mon->obj->lock) == LW_SHAPE_FAT);
+
+    /* This lock is associated with an object
+     * that's being swept.  The only possible way
+     * anyone could be holding this lock would be
+     * if some JNI code locked but didn't unlock
+     * the object, in which case we've got some bad
+     * native code somewhere.
+     */
+    assert(pthread_mutex_trylock(&mon->lock) == 0);
+    assert(pthread_mutex_unlock(&mon->lock) == 0);
+    dvmDestroyMutex(&mon->lock);
+    free(mon);
+}
+
+/*
+ * Frees monitor objects belonging to unmarked objects.
+ */
+void dvmSweepMonitorList(Monitor** mon, int (*isUnmarkedObject)(void*))
+{
+    Monitor handle;
+    Monitor *prev, *curr;
+    Object *obj;
+
+    assert(mon != NULL);
+    assert(isUnmarkedObject != NULL);
+    prev = &handle;
+    prev->next = curr = *mon;
+    while (curr != NULL) {
+        obj = curr->obj;
+        if (obj != NULL && (*isUnmarkedObject)(obj) != 0) {
+            prev->next = curr->next;
+            freeMonitor(curr);
+            curr = prev->next;
+        } else {
+            prev = curr;
+            curr = curr->next;
+        }
+    }
+    *mon = handle.next;
+}
+
+static char *logWriteInt(char *dst, int value)
+{
+    *dst++ = EVENT_TYPE_INT;
+    set4LE((u1 *)dst, value);
+    return dst + 4;
+}
+
+static char *logWriteString(char *dst, const char *value, size_t len)
+{
+    *dst++ = EVENT_TYPE_STRING;
+    len = len < 32 ? len : 32;
+    set4LE((u1 *)dst, len);
+    dst += 4;
+    memcpy(dst, value, len);
+    return dst + len;
+}
+
+#define EVENT_LOG_TAG_dvm_lock_sample 20003
+
+static void logContentionEvent(Thread *self, u4 waitMs, u4 samplePercent,
+                               const char *ownerFileName, u4 ownerLineNumber)
+{
+    const StackSaveArea *saveArea;
+    const Method *meth;
+    u4 relativePc;
+    char eventBuffer[174];
+    const char *fileName;
+    char procName[33];
+    char *cp;
+    size_t len;
+    int fd;
+
+    /* When a thread is being destroyed it is normal that the frame depth is zero */
+    if (self->interpSave.curFrame == NULL) {
+        return;
+    }
+
+    saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    meth = saveArea->method;
+    cp = eventBuffer;
+
+    /* Emit the event list length, 1 byte. */
+    *cp++ = 9;
+
+    /* Emit the process name, <= 37 bytes. */
+    fd = open("/proc/self/cmdline", O_RDONLY);
+    memset(procName, 0, sizeof(procName));
+    read(fd, procName, sizeof(procName) - 1);
+    close(fd);
+    len = strlen(procName);
+    cp = logWriteString(cp, procName, len);
+
+    /* Emit the sensitive thread ("main thread") status, 5 bytes. */
+    bool isSensitive = false;
+    if (gDvm.isSensitiveThreadHook != NULL) {
+        isSensitive = gDvm.isSensitiveThreadHook();
+    }
+    cp = logWriteInt(cp, isSensitive);
+
+    /* Emit self thread name string, <= 37 bytes. */
+    std::string selfName = dvmGetThreadName(self);
+    cp = logWriteString(cp, selfName.c_str(), selfName.size());
+
+    /* Emit the wait time, 5 bytes. */
+    cp = logWriteInt(cp, waitMs);
+
+    /* Emit the source code file name, <= 37 bytes. */
+    fileName = dvmGetMethodSourceFile(meth);
+    if (fileName == NULL) fileName = "";
+    cp = logWriteString(cp, fileName, strlen(fileName));
+
+    /* Emit the source code line number, 5 bytes. */
+    relativePc = saveArea->xtra.currentPc - saveArea->method->insns;
+    cp = logWriteInt(cp, dvmLineNumFromPC(meth, relativePc));
+
+    /* Emit the lock owner source code file name, <= 37 bytes. */
+    if (ownerFileName == NULL) {
+        ownerFileName = "";
+    } else if (strcmp(fileName, ownerFileName) == 0) {
+        /* Common case, so save on log space. */
+        ownerFileName = "-";
+    }
+    cp = logWriteString(cp, ownerFileName, strlen(ownerFileName));
+
+    /* Emit the source code line number, 5 bytes. */
+    cp = logWriteInt(cp, ownerLineNumber);
+
+    /* Emit the sample percentage, 5 bytes. */
+    cp = logWriteInt(cp, samplePercent);
+
+    assert((size_t)(cp - eventBuffer) <= sizeof(eventBuffer));
+    android_btWriteLog(EVENT_LOG_TAG_dvm_lock_sample,
+                       EVENT_TYPE_LIST,
+                       eventBuffer,
+                       (size_t)(cp - eventBuffer));
+}
+
+/*
+ * Lock a monitor.
+ */
+static void lockMonitor(Thread* self, Monitor* mon)
+{
+    ThreadStatus oldStatus;
+    u4 waitThreshold, samplePercent;
+    u8 waitStart, waitEnd, waitMs;
+
+    if (mon->owner == self) {
+        mon->lockCount++;
+        return;
+    }
+    if (dvmTryLockMutex(&mon->lock) != 0) {
+        oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
+        waitThreshold = gDvm.lockProfThreshold;
+        if (waitThreshold) {
+            waitStart = dvmGetRelativeTimeUsec();
+        }
+
+        const Method* currentOwnerMethod = mon->ownerMethod;
+        u4 currentOwnerPc = mon->ownerPc;
+
+        dvmLockMutex(&mon->lock);
+        if (waitThreshold) {
+            waitEnd = dvmGetRelativeTimeUsec();
+        }
+        dvmChangeStatus(self, oldStatus);
+        if (waitThreshold) {
+            waitMs = (waitEnd - waitStart) / 1000;
+            if (waitMs >= waitThreshold) {
+                samplePercent = 100;
+            } else {
+                samplePercent = 100 * waitMs / waitThreshold;
+            }
+            if (samplePercent != 0 && ((u4)rand() % 100 < samplePercent)) {
+                const char* currentOwnerFileName = "no_method";
+                u4 currentOwnerLineNumber = 0;
+                if (currentOwnerMethod != NULL) {
+                    currentOwnerFileName = dvmGetMethodSourceFile(currentOwnerMethod);
+                    if (currentOwnerFileName == NULL) {
+                        currentOwnerFileName = "no_method_file";
+                    }
+                    currentOwnerLineNumber = dvmLineNumFromPC(currentOwnerMethod, currentOwnerPc);
+                }
+                logContentionEvent(self, waitMs, samplePercent,
+                                   currentOwnerFileName, currentOwnerLineNumber);
+            }
+        }
+    }
+    mon->owner = self;
+    assert(mon->lockCount == 0);
+
+    // When debugging, save the current monitor holder for future
+    // acquisition failures to use in sampled logging.
+    if (gDvm.lockProfThreshold > 0) {
+        mon->ownerMethod = NULL;
+        mon->ownerPc = 0;
+        if (self->interpSave.curFrame == NULL) {
+            return;
+        }
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+        if (saveArea == NULL) {
+            return;
+        }
+        mon->ownerMethod = saveArea->method;
+        mon->ownerPc = (saveArea->xtra.currentPc - saveArea->method->insns);
+    }
+}
+
+/*
+ * Try to lock a monitor.
+ *
+ * Returns "true" on success.
+ */
+#ifdef WITH_COPYING_GC
+static bool tryLockMonitor(Thread* self, Monitor* mon)
+{
+    if (mon->owner == self) {
+        mon->lockCount++;
+        return true;
+    } else {
+        if (dvmTryLockMutex(&mon->lock) == 0) {
+            mon->owner = self;
+            assert(mon->lockCount == 0);
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
+#endif
+
+/*
+ * Unlock a monitor.
+ *
+ * Returns true if the unlock succeeded.
+ * If the unlock failed, an exception will be pending.
+ */
+static bool unlockMonitor(Thread* self, Monitor* mon)
+{
+    assert(self != NULL);
+    assert(mon != NULL);
+    if (mon->owner == self) {
+        /*
+         * We own the monitor, so nobody else can be in here.
+         */
+        if (mon->lockCount == 0) {
+            mon->owner = NULL;
+            mon->ownerMethod = NULL;
+            mon->ownerPc = 0;
+            dvmUnlockMutex(&mon->lock);
+        } else {
+            mon->lockCount--;
+        }
+    } else {
+        /*
+         * We don't own this, so we're not allowed to unlock it.
+         * The JNI spec says that we should throw IllegalMonitorStateException
+         * in this case.
+         */
+        dvmThrowIllegalMonitorStateException("unlock of unowned monitor");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Checks the wait set for circular structure.  Returns 0 if the list
+ * is not circular.  Otherwise, returns 1.  Used only by asserts.
+ */
+#ifndef NDEBUG
+static int waitSetCheck(Monitor *mon)
+{
+    Thread *fast, *slow;
+    size_t n;
+
+    assert(mon != NULL);
+    fast = slow = mon->waitSet;
+    n = 0;
+    for (;;) {
+        if (fast == NULL) return 0;
+        if (fast->waitNext == NULL) return 0;
+        if (fast == slow && n > 0) return 1;
+        n += 2;
+        fast = fast->waitNext->waitNext;
+        slow = slow->waitNext;
+    }
+}
+#endif
+
+/*
+ * Links a thread into a monitor's wait set.  The monitor lock must be
+ * held by the caller of this routine.
+ */
+static void waitSetAppend(Monitor *mon, Thread *thread)
+{
+    Thread *elt;
+
+    assert(mon != NULL);
+    assert(mon->owner == dvmThreadSelf());
+    assert(thread != NULL);
+    assert(thread->waitNext == NULL);
+    assert(waitSetCheck(mon) == 0);
+    if (mon->waitSet == NULL) {
+        mon->waitSet = thread;
+        return;
+    }
+    elt = mon->waitSet;
+    while (elt->waitNext != NULL) {
+        elt = elt->waitNext;
+    }
+    elt->waitNext = thread;
+}
+
+/*
+ * Unlinks a thread from a monitor's wait set.  The monitor lock must
+ * be held by the caller of this routine.
+ */
+static void waitSetRemove(Monitor *mon, Thread *thread)
+{
+    Thread *elt;
+
+    assert(mon != NULL);
+    assert(mon->owner == dvmThreadSelf());
+    assert(thread != NULL);
+    assert(waitSetCheck(mon) == 0);
+    if (mon->waitSet == NULL) {
+        return;
+    }
+    if (mon->waitSet == thread) {
+        mon->waitSet = thread->waitNext;
+        thread->waitNext = NULL;
+        return;
+    }
+    elt = mon->waitSet;
+    while (elt->waitNext != NULL) {
+        if (elt->waitNext == thread) {
+            elt->waitNext = thread->waitNext;
+            thread->waitNext = NULL;
+            return;
+        }
+        elt = elt->waitNext;
+    }
+}
+
+/*
+ * Converts the given relative waiting time into an absolute time.
+ */
+static void absoluteTime(s8 msec, s4 nsec, struct timespec *ts)
+{
+    s8 endSec;
+
+#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) {
+        ALOGV("NOTE: end time exceeds epoch");
+        endSec = 0x7ffffffe;
+    }
+    ts->tv_sec = endSec;
+    ts->tv_nsec = (ts->tv_nsec + (msec % 1000) * 1000000) + nsec;
+
+    /* catch rollover */
+    if (ts->tv_nsec >= 1000000000L) {
+        ts->tv_sec++;
+        ts->tv_nsec -= 1000000000L;
+    }
+}
+
+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;
+
+    assert(self != NULL);
+    assert(mon != NULL);
+
+    /* Make sure that we hold the lock. */
+    if (mon->owner != self) {
+        dvmThrowIllegalMonitorStateException(
+            "object not locked by thread before wait()");
+        return;
+    }
+
+    /*
+     * Enforce the timeout range.
+     */
+    if (msec < 0 || nsec < 0 || nsec > 999999) {
+        dvmThrowIllegalArgumentException("timeout arguments out of range");
+        return;
+    }
+
+    /*
+     * Compute absolute wakeup time, if necessary.
+     */
+    if (msec == 0 && nsec == 0) {
+        timed = false;
+    } else {
+        absoluteTime(msec, nsec, &ts);
+        timed = true;
+    }
+
+    /*
+     * Add ourselves to the set of threads waiting on this monitor, and
+     * release our hold.  We need to let it go even if we're a few levels
+     * deep in a recursive lock, and we need to restore that later.
+     *
+     * We append to the wait set ahead of clearing the count and owner
+     * fields so the subroutine can check that the calling thread owns
+     * the monitor.  Aside from that, the order of member updates is
+     * not order sensitive as we hold the pthread mutex.
+     */
+    waitSetAppend(mon, self);
+    int prevLockCount = mon->lockCount;
+    mon->lockCount = 0;
+    mon->owner = NULL;
+
+    const Method* savedMethod = mon->ownerMethod;
+    u4 savedPc = mon->ownerPc;
+    mon->ownerMethod = NULL;
+    mon->ownerPc = 0;
+
+    /*
+     * Update thread status.  If the GC wakes up, it'll ignore us, knowing
+     * that we won't touch any references in this state, and we'll check
+     * our suspend mode before we transition out.
+     */
+    if (timed)
+        dvmChangeStatus(self, THREAD_TIMED_WAIT);
+    else
+        dvmChangeStatus(self, THREAD_WAIT);
+
+    dvmLockMutex(&self->waitMutex);
+
+    /*
+     * Set waitMonitor to the monitor object we will be waiting on.
+     * When waitMonitor is non-NULL a notifying or interrupting thread
+     * must signal the thread's waitCond to wake it up.
+     */
+    assert(self->waitMonitor == NULL);
+    self->waitMonitor = mon;
+
+    /*
+     * Handle the case where the thread was interrupted before we called
+     * wait().
+     */
+    if (self->interrupted) {
+        wasInterrupted = true;
+        self->waitMonitor = NULL;
+        dvmUnlockMutex(&self->waitMutex);
+        goto done;
+    }
+
+    /*
+     * Release the monitor lock and wait for a notification or
+     * a timeout to occur.
+     */
+    dvmUnlockMutex(&mon->lock);
+
+    if (!timed) {
+        ret = pthread_cond_wait(&self->waitCond, &self->waitMutex);
+        assert(ret == 0);
+    } else {
+#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->ownerMethod = savedMethod;
+    mon->ownerPc = savedPc;
+    waitSetRemove(mon, self);
+
+    /* set self->status back to THREAD_RUNNING, and self-suspend if needed */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    if (wasInterrupted) {
+        /*
+         * We were interrupted while waiting, or somebody interrupted an
+         * un-interruptible thread earlier and we're bailing out immediately.
+         *
+         * The doc sayeth: "The interrupted status of the current thread is
+         * cleared when this exception is thrown."
+         */
+        self->interrupted = false;
+        if (interruptShouldThrow) {
+            dvmThrowInterruptedException(NULL);
+        }
+    }
+}
+
+/*
+ * Notify one thread waiting on this monitor.
+ */
+static void notifyMonitor(Thread* self, Monitor* mon)
+{
+    Thread* thread;
+
+    assert(self != NULL);
+    assert(mon != NULL);
+
+    /* Make sure that we hold the lock. */
+    if (mon->owner != self) {
+        dvmThrowIllegalMonitorStateException(
+            "object not locked by thread before notify()");
+        return;
+    }
+    /* Signal the first waiting thread in the wait set. */
+    while (mon->waitSet != NULL) {
+        thread = mon->waitSet;
+        mon->waitSet = thread->waitNext;
+        thread->waitNext = NULL;
+        dvmLockMutex(&thread->waitMutex);
+        /* Check to see if the thread is still waiting. */
+        if (thread->waitMonitor != NULL) {
+            pthread_cond_signal(&thread->waitCond);
+            dvmUnlockMutex(&thread->waitMutex);
+            return;
+        }
+        dvmUnlockMutex(&thread->waitMutex);
+    }
+}
+
+/*
+ * Notify all threads waiting on this monitor.
+ */
+static void notifyAllMonitor(Thread* self, Monitor* mon)
+{
+    Thread* thread;
+
+    assert(self != NULL);
+    assert(mon != NULL);
+
+    /* Make sure that we hold the lock. */
+    if (mon->owner != self) {
+        dvmThrowIllegalMonitorStateException(
+            "object not locked by thread before notifyAll()");
+        return;
+    }
+    /* Signal all threads in the wait set. */
+    while (mon->waitSet != NULL) {
+        thread = mon->waitSet;
+        mon->waitSet = thread->waitNext;
+        thread->waitNext = NULL;
+        dvmLockMutex(&thread->waitMutex);
+        /* Check to see if the thread is still waiting. */
+        if (thread->waitMonitor != NULL) {
+            pthread_cond_signal(&thread->waitCond);
+        }
+        dvmUnlockMutex(&thread->waitMutex);
+    }
+}
+
+/*
+ * Changes the shape of a monitor from thin to fat, preserving the
+ * internal lock state.  The calling thread must own the lock.
+ */
+static void inflateMonitor(Thread *self, Object *obj)
+{
+    Monitor *mon;
+    u4 thin;
+
+    assert(self != NULL);
+    assert(obj != NULL);
+    assert(LW_SHAPE(obj->lock) == LW_SHAPE_THIN);
+    assert(LW_LOCK_OWNER(obj->lock) == self->threadId);
+    /* Allocate and acquire a new monitor. */
+    mon = dvmCreateMonitor(obj);
+    lockMonitor(self, mon);
+    /* Propagate the lock state. */
+    thin = obj->lock;
+    mon->lockCount = LW_LOCK_COUNT(thin);
+    thin &= LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT;
+    thin |= (u4)mon | LW_SHAPE_FAT;
+    /* Publish the updated lock word. */
+    android_atomic_release_store(thin, (int32_t *)&obj->lock);
+}
+
+/*
+ * Implements monitorenter for "synchronized" stuff.
+ *
+ * This does not fail or throw an exception (unless deadlock prediction
+ * is enabled and set to "err" mode).
+ */
+void dvmLockObject(Thread* self, Object *obj)
+{
+    volatile u4 *thinp;
+    ThreadStatus oldStatus;
+    struct timespec tm;
+    long sleepDelayNs;
+    long minSleepDelayNs = 1000000;  /* 1 millisecond */
+    long maxSleepDelayNs = 1000000000;  /* 1 second */
+    u4 thin, newThin, threadId;
+
+    assert(self != NULL);
+    assert(obj != NULL);
+    threadId = self->threadId;
+    thinp = &obj->lock;
+retry:
+    thin = *thinp;
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /*
+         * The lock is a thin lock.  The owner field is used to
+         * determine the acquire method, ordered by cost.
+         */
+        if (LW_LOCK_OWNER(thin) == threadId) {
+            /*
+             * The calling thread owns the lock.  Increment the
+             * value of the recursion count field.
+             */
+            obj->lock += 1 << LW_LOCK_COUNT_SHIFT;
+            if (LW_LOCK_COUNT(obj->lock) == LW_LOCK_COUNT_MASK) {
+                /*
+                 * The reacquisition limit has been reached.  Inflate
+                 * the lock so the next acquire will not overflow the
+                 * recursion count field.
+                 */
+                inflateMonitor(self, obj);
+            }
+        } else if (LW_LOCK_OWNER(thin) == 0) {
+            /*
+             * The lock is unowned.  Install the thread id of the
+             * calling thread into the owner field.  This is the
+             * common case.  In performance critical code the JIT
+             * will have tried this before calling out to the VM.
+             */
+            newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
+            if (android_atomic_acquire_cas(thin, newThin,
+                    (int32_t*)thinp) != 0) {
+                /*
+                 * The acquire failed.  Try again.
+                 */
+                goto retry;
+            }
+        } else {
+            ALOGV("(%d) spin on lock %p: %#x (%#x) %#x",
+                 threadId, &obj->lock, 0, *thinp, thin);
+            /*
+             * The lock is owned by another thread.  Notify the VM
+             * that we are about to wait.
+             */
+            oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
+            /*
+             * Spin until the thin lock is released or inflated.
+             */
+            sleepDelayNs = 0;
+            for (;;) {
+                thin = *thinp;
+                /*
+                 * Check the shape of the lock word.  Another thread
+                 * may have inflated the lock while we were waiting.
+                 */
+                if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+                    if (LW_LOCK_OWNER(thin) == 0) {
+                        /*
+                         * The lock has been released.  Install the
+                         * thread id of the calling thread into the
+                         * owner field.
+                         */
+                        newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
+                        if (android_atomic_acquire_cas(thin, newThin,
+                                (int32_t *)thinp) == 0) {
+                            /*
+                             * The acquire succeed.  Break out of the
+                             * loop and proceed to inflate the lock.
+                             */
+                            break;
+                        }
+                    } else {
+                        /*
+                         * The lock has not been released.  Yield so
+                         * the owning thread can run.
+                         */
+                        if (sleepDelayNs == 0) {
+                            sched_yield();
+                            sleepDelayNs = minSleepDelayNs;
+                        } else {
+                            tm.tv_sec = 0;
+                            tm.tv_nsec = sleepDelayNs;
+                            nanosleep(&tm, NULL);
+                            /*
+                             * Prepare the next delay value.  Wrap to
+                             * avoid once a second polls for eternity.
+                             */
+                            if (sleepDelayNs < maxSleepDelayNs / 2) {
+                                sleepDelayNs *= 2;
+                            } else {
+                                sleepDelayNs = minSleepDelayNs;
+                            }
+                        }
+                    }
+                } else {
+                    /*
+                     * The thin lock was inflated by another thread.
+                     * Let the VM know we are no longer waiting and
+                     * try again.
+                     */
+                    ALOGV("(%d) lock %p surprise-fattened",
+                             threadId, &obj->lock);
+                    dvmChangeStatus(self, oldStatus);
+                    goto retry;
+                }
+            }
+            ALOGV("(%d) spin on lock done %p: %#x (%#x) %#x",
+                 threadId, &obj->lock, 0, *thinp, thin);
+            /*
+             * We have acquired the thin lock.  Let the VM know that
+             * we are no longer waiting.
+             */
+            dvmChangeStatus(self, oldStatus);
+            /*
+             * Fatten the lock.
+             */
+            inflateMonitor(self, obj);
+            ALOGV("(%d) lock %p fattened", threadId, &obj->lock);
+        }
+    } else {
+        /*
+         * The lock is a fat lock.
+         */
+        assert(LW_MONITOR(obj->lock) != NULL);
+        lockMonitor(self, LW_MONITOR(obj->lock));
+    }
+}
+
+/*
+ * Implements monitorexit for "synchronized" stuff.
+ *
+ * On failure, throws an exception and returns "false".
+ */
+bool dvmUnlockObject(Thread* self, Object *obj)
+{
+    u4 thin;
+
+    assert(self != NULL);
+    assert(self->status == THREAD_RUNNING);
+    assert(obj != NULL);
+    /*
+     * Cache the lock word as its value can change while we are
+     * examining its state.
+     */
+    thin = *(volatile u4 *)&obj->lock;
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /*
+         * The lock is thin.  We must ensure that the lock is owned
+         * by the given thread before unlocking it.
+         */
+        if (LW_LOCK_OWNER(thin) == self->threadId) {
+            /*
+             * We are the lock owner.  It is safe to update the lock
+             * without CAS as lock ownership guards the lock itself.
+             */
+            if (LW_LOCK_COUNT(thin) == 0) {
+                /*
+                 * The lock was not recursively acquired, the common
+                 * case.  Unlock by clearing all bits except for the
+                 * hash state.
+                 */
+                thin &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT);
+                android_atomic_release_store(thin, (int32_t*)&obj->lock);
+            } else {
+                /*
+                 * The object was recursively acquired.  Decrement the
+                 * lock recursion count field.
+                 */
+                obj->lock -= 1 << LW_LOCK_COUNT_SHIFT;
+            }
+        } else {
+            /*
+             * We do not own the lock.  The JVM spec requires that we
+             * throw an exception in this case.
+             */
+            dvmThrowIllegalMonitorStateException("unlock of unowned monitor");
+            return false;
+        }
+    } else {
+        /*
+         * The lock is fat.  We must check to see if unlockMonitor has
+         * raised any exceptions before continuing.
+         */
+        assert(LW_MONITOR(obj->lock) != NULL);
+        if (!unlockMonitor(self, LW_MONITOR(obj->lock))) {
+            /*
+             * An exception has been raised.  Do not fall through.
+             */
+            return false;
+        }
+    }
+    return true;
+}
+
+/*
+ * Object.wait().  Also called for class init.
+ */
+void dvmObjectWait(Thread* self, Object *obj, s8 msec, s4 nsec,
+    bool interruptShouldThrow)
+{
+    Monitor* mon;
+    u4 thin = *(volatile u4 *)&obj->lock;
+
+    /* If the lock is still thin, we need to fatten it.
+     */
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /* Make sure that 'self' holds the lock.
+         */
+        if (LW_LOCK_OWNER(thin) != self->threadId) {
+            dvmThrowIllegalMonitorStateException(
+                "object not locked by thread before wait()");
+            return;
+        }
+
+        /* This thread holds the lock.  We need to fatten the lock
+         * so 'self' can block on it.  Don't update the object lock
+         * field yet, because 'self' needs to acquire the lock before
+         * any other thread gets a chance.
+         */
+        inflateMonitor(self, obj);
+        ALOGV("(%d) lock %p fattened by wait()", self->threadId, &obj->lock);
+    }
+    mon = LW_MONITOR(obj->lock);
+    waitMonitor(self, mon, msec, nsec, interruptShouldThrow);
+}
+
+/*
+ * Object.notify().
+ */
+void dvmObjectNotify(Thread* self, Object *obj)
+{
+    u4 thin = *(volatile u4 *)&obj->lock;
+
+    /* If the lock is still thin, there aren't any waiters;
+     * waiting on an object forces lock fattening.
+     */
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /* Make sure that 'self' holds the lock.
+         */
+        if (LW_LOCK_OWNER(thin) != self->threadId) {
+            dvmThrowIllegalMonitorStateException(
+                "object not locked by thread before notify()");
+            return;
+        }
+
+        /* no-op;  there are no waiters to notify.
+         */
+    } else {
+        /* It's a fat lock.
+         */
+        notifyMonitor(self, LW_MONITOR(thin));
+    }
+}
+
+/*
+ * Object.notifyAll().
+ */
+void dvmObjectNotifyAll(Thread* self, Object *obj)
+{
+    u4 thin = *(volatile u4 *)&obj->lock;
+
+    /* If the lock is still thin, there aren't any waiters;
+     * waiting on an object forces lock fattening.
+     */
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /* Make sure that 'self' holds the lock.
+         */
+        if (LW_LOCK_OWNER(thin) != self->threadId) {
+            dvmThrowIllegalMonitorStateException(
+                "object not locked by thread before notifyAll()");
+            return;
+        }
+
+        /* no-op;  there are no waiters to notify.
+         */
+    } else {
+        /* It's a fat lock.
+         */
+        notifyAllMonitor(self, LW_MONITOR(thin));
+    }
+}
+
+/*
+ * This implements java.lang.Thread.sleep(long msec, int nsec).
+ *
+ * The sleep is interruptible by other threads, which means we can't just
+ * plop into an OS sleep call.  (We probably could if we wanted to send
+ * signals around and rely on EINTR, but that's inefficient and relies
+ * on native code respecting our signal mask.)
+ *
+ * We have to do all of this stuff for Object.wait() as well, so it's
+ * easiest to just sleep on a private Monitor.
+ *
+ * It appears that we want sleep(0,0) to go through the motions of sleeping
+ * for a very short duration, rather than just returning.
+ */
+void dvmThreadSleep(u8 msec, u4 nsec)
+{
+    Thread* self = dvmThreadSelf();
+    Monitor* mon = gDvm.threadSleepMon;
+
+    /* sleep(0,0) wakes up immediately, wait(0,0) means wait forever; adjust */
+    if (msec == 0 && nsec == 0)
+        nsec++;
+
+    lockMonitor(self, mon);
+    waitMonitor(self, mon, msec, nsec, true);
+    unlockMonitor(self, mon);
+}
+
+/*
+ * Implement java.lang.Thread.interrupt().
+ */
+void dvmThreadInterrupt(Thread* thread)
+{
+    assert(thread != NULL);
+
+    dvmLockMutex(&thread->waitMutex);
+
+    /*
+     * If the interrupted flag is already set no additional action is
+     * required.
+     */
+    if (thread->interrupted == true) {
+        dvmUnlockMutex(&thread->waitMutex);
+        return;
+    }
+
+    /*
+     * Raise the "interrupted" flag.  This will cause it to bail early out
+     * of the next wait() attempt, if it's not currently waiting on
+     * something.
+     */
+    thread->interrupted = true;
+
+    /*
+     * Is the thread waiting?
+     *
+     * Note that fat vs. thin doesn't matter here;  waitMonitor
+     * is only set when a thread actually waits on a monitor,
+     * which implies that the monitor has already been fattened.
+     */
+    if (thread->waitMonitor != NULL) {
+        pthread_cond_signal(&thread->waitCond);
+    }
+
+    dvmUnlockMutex(&thread->waitMutex);
+}
+
+#ifndef WITH_COPYING_GC
+u4 dvmIdentityHashCode(Object *obj)
+{
+    return (u4)obj;
+}
+#else
+/*
+ * Returns the identity hash code of the given object.
+ */
+u4 dvmIdentityHashCode(Object *obj)
+{
+    Thread *self, *thread;
+    volatile u4 *lw;
+    size_t size;
+    u4 lock, owner, hashState;
+
+    if (obj == NULL) {
+        /*
+         * Null is defined to have an identity hash code of 0.
+         */
+        return 0;
+    }
+    lw = &obj->lock;
+retry:
+    hashState = LW_HASH_STATE(*lw);
+    if (hashState == LW_HASH_STATE_HASHED) {
+        /*
+         * The object has been hashed but has not had its hash code
+         * relocated by the garbage collector.  Use the raw object
+         * address.
+         */
+        return (u4)obj >> 3;
+    } else if (hashState == LW_HASH_STATE_HASHED_AND_MOVED) {
+        /*
+         * The object has been hashed and its hash code has been
+         * relocated by the collector.  Use the value of the naturally
+         * aligned word following the instance data.
+         */
+        assert(!dvmIsClassObject(obj));
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+            size = dvmArrayObjectSize((ArrayObject *)obj);
+            size = (size + 2) & ~2;
+        } else {
+            size = obj->clazz->objectSize;
+        }
+        return *(u4 *)(((char *)obj) + size);
+    } else if (hashState == LW_HASH_STATE_UNHASHED) {
+        /*
+         * The object has never been hashed.  Change the hash state to
+         * hashed and use the raw object address.
+         */
+        self = dvmThreadSelf();
+        if (self->threadId == lockOwner(obj)) {
+            /*
+             * We already own the lock so we can update the hash state
+             * directly.
+             */
+            *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+            return (u4)obj >> 3;
+        }
+        /*
+         * We do not own the lock.  Try acquiring the lock.  Should
+         * this fail, we must suspend the owning thread.
+         */
+        if (LW_SHAPE(*lw) == LW_SHAPE_THIN) {
+            /*
+             * If the lock is thin assume it is unowned.  We simulate
+             * an acquire, update, and release with a single CAS.
+             */
+            lock = (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+            if (android_atomic_acquire_cas(
+                                0,
+                                (int32_t)lock,
+                                (int32_t *)lw) == 0) {
+                /*
+                 * A new lockword has been installed with a hash state
+                 * of hashed.  Use the raw object address.
+                 */
+                return (u4)obj >> 3;
+            }
+        } else {
+            if (tryLockMonitor(self, LW_MONITOR(*lw))) {
+                /*
+                 * The monitor lock has been acquired.  Change the
+                 * hash state to hashed and use the raw object
+                 * address.
+                 */
+                *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+                unlockMonitor(self, LW_MONITOR(*lw));
+                return (u4)obj >> 3;
+            }
+        }
+        /*
+         * At this point we have failed to acquire the lock.  We must
+         * identify the owning thread and suspend it.
+         */
+        dvmLockThreadList(self);
+        /*
+         * Cache the lock word as its value can change between
+         * determining its shape and retrieving its owner.
+         */
+        lock = *lw;
+        if (LW_SHAPE(lock) == LW_SHAPE_THIN) {
+            /*
+             * Find the thread with the corresponding thread id.
+             */
+            owner = LW_LOCK_OWNER(lock);
+            assert(owner != self->threadId);
+            /*
+             * If the lock has no owner do not bother scanning the
+             * thread list and fall through to the failure handler.
+             */
+            thread = owner ? gDvm.threadList : NULL;
+            while (thread != NULL) {
+                if (thread->threadId == owner) {
+                    break;
+                }
+                thread = thread->next;
+            }
+        } else {
+            thread = LW_MONITOR(lock)->owner;
+        }
+        /*
+         * If thread is NULL the object has been released since the
+         * thread list lock was acquired.  Try again.
+         */
+        if (thread == NULL) {
+            dvmUnlockThreadList();
+            goto retry;
+        }
+        /*
+         * Wait for the owning thread to suspend.
+         */
+        dvmSuspendThread(thread);
+        if (dvmHoldsLock(thread, obj)) {
+            /*
+             * The owning thread has been suspended.  We can safely
+             * change the hash state to hashed.
+             */
+            *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+            dvmResumeThread(thread);
+            dvmUnlockThreadList();
+            return (u4)obj >> 3;
+        }
+        /*
+         * The wrong thread has been suspended.  Try again.
+         */
+        dvmResumeThread(thread);
+        dvmUnlockThreadList();
+        goto retry;
+    }
+    ALOGE("object %p has an unknown hash state %#x", obj, hashState);
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+    return 0;  /* Quiet the compiler. */
+}
+#endif  /* WITH_COPYING_GC */
diff --git a/vm/Sync.h b/vm/Sync.h
new file mode 100644
index 0000000..2016c03
--- /dev/null
+++ b/vm/Sync.h
@@ -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.
+ */
+/*
+ * Object synchronization functions.
+ */
+#ifndef DALVIK_SYNC_H_
+#define DALVIK_SYNC_H_
+
+/*
+ * Monitor shape field.  Used to distinguish immediate thin locks from
+ * indirecting fat locks.
+ */
+#define LW_SHAPE_THIN 0
+#define LW_SHAPE_FAT 1
+#define LW_SHAPE_MASK 0x1
+#define LW_SHAPE(x) ((x) & LW_SHAPE_MASK)
+
+/*
+ * Hash state field.  Used to signify that an object has had its
+ * identity hash code exposed or relocated.
+ */
+#define LW_HASH_STATE_UNHASHED 0
+#define LW_HASH_STATE_HASHED 1
+#define LW_HASH_STATE_HASHED_AND_MOVED 3
+#define LW_HASH_STATE_MASK 0x3
+#define LW_HASH_STATE_SHIFT 1
+#define LW_HASH_STATE(x) (((x) >> LW_HASH_STATE_SHIFT) & LW_HASH_STATE_MASK)
+
+/*
+ * Monitor accessor.  Extracts a monitor structure pointer from a fat
+ * lock.  Performs no error checking.
+ */
+#define LW_MONITOR(x) \
+  ((Monitor*)((x) & ~((LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT) | \
+                      LW_SHAPE_MASK)))
+
+/*
+ * Lock owner field.  Contains the thread id of the thread currently
+ * holding the lock.
+ */
+#define LW_LOCK_OWNER_MASK 0xffff
+#define LW_LOCK_OWNER_SHIFT 3
+#define LW_LOCK_OWNER(x) (((x) >> LW_LOCK_OWNER_SHIFT) & LW_LOCK_OWNER_MASK)
+
+/*
+ * Lock recursion count field.  Contains a count of the numer of times
+ * a lock has been recursively acquired.
+ */
+#define LW_LOCK_COUNT_MASK 0x1fff
+#define LW_LOCK_COUNT_SHIFT 19
+#define LW_LOCK_COUNT(x) (((x) >> LW_LOCK_COUNT_SHIFT) & LW_LOCK_COUNT_MASK)
+
+struct Object;
+struct Monitor;
+struct Thread;
+
+/*
+ * Returns true if the lock has been fattened.
+ */
+#define IS_LOCK_FAT(lock)   (LW_SHAPE(*(lock)) == LW_SHAPE_FAT)
+
+/*
+ * Acquire the object's monitor.
+ */
+extern "C" void dvmLockObject(Thread* self, Object* obj);
+
+/* Returns true if the unlock succeeded.
+ * If the unlock failed, an exception will be pending.
+ */
+extern "C" bool dvmUnlockObject(Thread* self, Object* obj);
+
+/*
+ * Implementations of some java/lang/Object calls.
+ */
+void dvmObjectWait(Thread* self, Object* obj,
+    s8 timeout, s4 nanos, bool interruptShouldThrow);
+void dvmObjectNotify(Thread* self, Object* obj);
+void dvmObjectNotifyAll(Thread* self, Object* obj);
+
+/*
+ * Implementation of System.identityHashCode().
+ */
+u4 dvmIdentityHashCode(Object* obj);
+
+/*
+ * Implementation of Thread.sleep().
+ */
+void dvmThreadSleep(u8 msec, u4 nsec);
+
+/*
+ * Implementation of Thread.interrupt().
+ *
+ * Interrupt a thread.  If it's waiting on a monitor, wake it up.
+ */
+void dvmThreadInterrupt(Thread* thread);
+
+/* create a new Monitor struct */
+Monitor* dvmCreateMonitor(Object* obj);
+
+/*
+ * Frees unmarked monitors from the monitor list.  The given callback
+ * routine should return a non-zero value when passed a pointer to an
+ * unmarked object.
+ */
+void dvmSweepMonitorList(Monitor** mon, int (*isUnmarkedObject)(void*));
+
+/* free monitor list */
+void dvmFreeMonitorList(void);
+
+/*
+ * Get the object a monitor is part of.
+ *
+ * Returns NULL if "mon" is NULL or the monitor is not part of an object
+ * (which should only happen for Thread.sleep() in the current implementation).
+ */
+Object* dvmGetMonitorObject(Monitor* mon);
+
+/*
+ * Get the thread that holds the lock on the specified object.  The
+ * object may be unlocked, thin-locked, or fat-locked.
+ *
+ * The caller must lock the thread list before calling here.
+ */
+Thread* dvmGetObjectLockHolder(Object* obj);
+
+/*
+ * Checks whether the object is held by the specified thread.
+ */
+bool dvmHoldsLock(Thread* thread, Object* obj);
+
+/*
+ * Relative timed wait on condition
+ */
+int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex,
+                         s8 msec, s4 nsec);
+
+#endif  // DALVIK_SYNC_H_
diff --git a/vm/Thread.cpp b/vm/Thread.cpp
new file mode 100644
index 0000000..dbdeec1
--- /dev/null
+++ b/vm/Thread.cpp
@@ -0,0 +1,3577 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "os/os.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <dirent.h>
+#endif
+
+#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 ALOGV/ALOGD to debug thread activity
+#define LOG_THREAD  LOGVV
+
+/*
+Notes on Threading
+
+All threads are native pthreads.  All threads, except the JDWP debugger
+thread, are visible to code running in the VM and to the debugger.  (We
+don't want the debugger to try to manipulate the thread that listens for
+instructions from the debugger.)  Internal VM threads are in the "system"
+ThreadGroup, all others are in the "main" ThreadGroup, per convention.
+
+The GC only runs when all threads have been suspended.  Threads are
+expected to suspend themselves, using a "safe point" mechanism.  We check
+for a suspend request at certain points in the main interpreter loop,
+and on requests coming in from native code (e.g. all JNI functions).
+Certain debugger events may inspire threads to self-suspend.
+
+Native methods must use JNI calls to modify object references to avoid
+clashes with the GC.  JNI doesn't provide a way for native code to access
+arrays of objects as such -- code must always get/set individual entries --
+so it should be possible to fully control access through JNI.
+
+Internal native VM threads, such as the finalizer thread, must explicitly
+check for suspension periodically.  In most cases they will be sound
+asleep on a condition variable, and won't notice the suspension anyway.
+
+Threads may be suspended by the GC, debugger, or the SIGQUIT listener
+thread.  The debugger may suspend or resume individual threads, while the
+GC always suspends all threads.  Each thread has a "suspend count" that
+is incremented on suspend requests and decremented on resume requests.
+When the count is zero, the thread is runnable.  This allows us to fulfill
+a debugger requirement: if the debugger suspends a thread, the thread is
+not allowed to run again until the debugger resumes it (or disconnects,
+in which case we must resume all debugger-suspended threads).
+
+Paused threads sleep on a condition variable, and are awoken en masse.
+Certain "slow" VM operations, such as starting up a new thread, will be
+done in a separate "VMWAIT" state, so that the rest of the VM doesn't
+freeze up waiting for the operation to finish.  Threads must check for
+pending suspension when leaving VMWAIT.
+
+Because threads suspend themselves while interpreting code or when native
+code makes JNI calls, there is no risk of suspending while holding internal
+VM locks.  All threads can enter a suspended (or native-code-only) state.
+Also, we don't have to worry about object references existing solely
+in hardware registers.
+
+We do, however, have to worry about objects that were allocated internally
+and aren't yet visible to anything else in the VM.  If we allocate an
+object, and then go to sleep on a mutex after changing to a non-RUNNING
+state (e.g. while trying to allocate a second object), the first object
+could be garbage-collected out from under us while we sleep.  To manage
+this, we automatically add all allocated objects to an internal object
+tracking list, and only remove them when we know we won't be suspended
+before the object appears in the GC root set.
+
+The debugger may choose to suspend or resume a single thread, which can
+lead to application-level deadlocks; this is expected behavior.  The VM
+will only check for suspension of single threads when the debugger is
+active (the java.lang.Thread calls for this are deprecated and hence are
+not supported).  Resumption of a single thread is handled by decrementing
+the thread's suspend count and sending a broadcast signal to the condition
+variable.  (This will cause all threads to wake up and immediately go back
+to sleep, which isn't tremendously efficient, but neither is having the
+debugger attached.)
+
+The debugger is not allowed to resume threads suspended by the GC.  This
+is trivially enforced by ignoring debugger requests while the GC is running
+(the JDWP thread is suspended during GC).
+
+The VM maintains a Thread struct for every pthread known to the VM.  There
+is a java/lang/Thread object associated with every Thread.  At present,
+there is no safe way to go from a Thread object to a Thread struct except by
+locking and scanning the list; this is necessary because the lifetimes of
+the two are not closely coupled.  We may want to change this behavior,
+though at present the only performance impact is on the debugger (see
+threadObjToThread()).  See also notes about dvmDetachCurrentThread().
+*/
+/*
+Alternate implementation (signal-based):
+
+Threads run without safe points -- zero overhead.  The VM uses a signal
+(e.g. pthread_kill(SIGUSR1)) to notify threads of suspension or resumption.
+
+The trouble with using signals to suspend threads is that it means a thread
+can be in the middle of an operation when garbage collection starts.
+To prevent some sticky situations, we have to introduce critical sections
+to the VM code.
+
+Critical sections temporarily block suspension for a given thread.
+The thread must move to a non-blocked state (and self-suspend) after
+finishing its current task.  If the thread blocks on a resource held
+by a suspended thread, we're hosed.
+
+One approach is to require that no blocking operations, notably
+acquisition of mutexes, can be performed within a critical section.
+This is too limiting.  For example, if thread A gets suspended while
+holding the thread list lock, it will prevent the GC or debugger from
+being able to safely access the thread list.  We need to wrap the critical
+section around the entire operation (enter critical, get lock, do stuff,
+release lock, exit critical).
+
+A better approach is to declare that certain resources can only be held
+within critical sections.  A thread that enters a critical section and
+then gets blocked on the thread list lock knows that the thread it is
+waiting for is also in a critical section, and will release the lock
+before suspending itself.  Eventually all threads will complete their
+operations and self-suspend.  For this to work, the VM must:
+
+ (1) Determine the set of resources that may be accessed from the GC or
+     debugger threads.  The mutexes guarding those go into the "critical
+     resource set" (CRS).
+ (2) Ensure that no resource in the CRS can be acquired outside of a
+     critical section.  This can be verified with an assert().
+ (3) Ensure that only resources in the CRS can be held while in a critical
+     section.  This is harder to enforce.
+
+If any of these conditions are not met, deadlock can ensue when grabbing
+resources in the GC or debugger (#1) or waiting for threads to suspend
+(#2,#3).  (You won't actually deadlock in the GC, because if the semantics
+above are followed you don't need to lock anything in the GC.  The risk is
+rather that the GC will access data structures in an intermediate state.)
+
+This approach requires more care and awareness in the VM than
+safe-pointing.  Because the GC and debugger are fairly intrusive, there
+really aren't any internal VM resources that aren't shared.  Thus, the
+enter/exit critical calls can be added to internal mutex wrappers, which
+makes it easy to get #1 and #2 right.
+
+An ordering should be established for all locks to avoid deadlocks.
+
+Monitor locks, which are also implemented with pthread calls, should not
+cause any problems here.  Threads fighting over such locks will not be in
+critical sections and can be suspended freely.
+
+This can get tricky if we ever need exclusive access to VM and non-VM
+resources at the same time.  It's not clear if this is a real concern.
+
+There are (at least) two ways to handle the incoming signals:
+
+ (a) Always accept signals.  If we're in a critical section, the signal
+     handler just returns without doing anything (the "suspend level"
+     should have been incremented before the signal was sent).  Otherwise,
+     if the "suspend level" is nonzero, we go to sleep.
+ (b) Block signals in critical sections.  This ensures that we can't be
+     interrupted in a critical section, but requires pthread_sigmask()
+     calls on entry and exit.
+
+This is a choice between blocking the message and blocking the messenger.
+Because UNIX signals are unreliable (you can only know that you have been
+signaled, not whether you were signaled once or 10 times), the choice is
+not significant for correctness.  The choice depends on the efficiency
+of pthread_sigmask() and the desire to actually block signals.  Either way,
+it is best to ensure that there is only one indication of "blocked";
+having two (i.e. block signals and set a flag, then only send a signal
+if the flag isn't set) can lead to race conditions.
+
+The signal handler must take care to copy registers onto the stack (via
+setjmp), so that stack scans find all references.  Because we have to scan
+native stacks, "exact" GC is not possible with this approach.
+
+Some other concerns with flinging signals around:
+ - Odd interactions with some debuggers (e.g. gdb on the Mac)
+ - Restrictions on some standard library calls during GC (e.g. don't
+   use printf on stdout to print GC debug messages)
+*/
+
+#define kMaxThreadId        ((1 << 16) - 1)
+#define kMainThreadId       1
+
+
+static Thread* allocThread(int interpStackSize);
+static bool prepareThread(Thread* thread);
+static void setThreadSelf(Thread* thread);
+static void unlinkThread(Thread* thread);
+static void freeThread(Thread* thread);
+static void assignThreadId(Thread* thread);
+static bool createFakeEntryFrame(Thread* thread);
+static bool createFakeRunFrame(Thread* thread);
+static void* interpThreadStart(void* arg);
+static void* internalThreadStart(void* arg);
+static void threadExitUncaughtException(Thread* thread, Object* group);
+static void threadExitCheck(void* arg);
+static void waitForThreadSuspend(Thread* self, Thread* thread);
+
+/*
+ * Initialize thread list and main thread's environment.  We need to set
+ * up some basic stuff so that dvmThreadSelf() will work when we start
+ * loading classes (e.g. to check for exceptions).
+ */
+bool dvmThreadStartup()
+{
+    Thread* thread;
+
+    /* allocate a TLS slot */
+    if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
+        ALOGE("ERROR: pthread_key_create failed");
+        return false;
+    }
+
+    /* test our pthread lib */
+    if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
+        ALOGW("WARNING: newly-created pthread TLS slot is not NULL");
+
+    /* prep thread-related locks and conditions */
+    dvmInitMutex(&gDvm.threadListLock);
+    pthread_cond_init(&gDvm.threadStartCond, NULL);
+    pthread_cond_init(&gDvm.vmExitCond, NULL);
+    dvmInitMutex(&gDvm._threadSuspendLock);
+    dvmInitMutex(&gDvm.threadSuspendCountLock);
+    pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);
+
+    /*
+     * Dedicated monitor for Thread.sleep().
+     * TODO: change this to an Object* so we don't have to expose this
+     * call, and we interact better with JDWP monitor calls.  Requires
+     * deferring the object creation to much later (e.g. final "main"
+     * thread prep) or until first use.
+     */
+    gDvm.threadSleepMon = dvmCreateMonitor(NULL);
+
+    gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false);
+
+    thread = allocThread(gDvm.mainThreadStackSize);
+    if (thread == NULL)
+        return false;
+
+    /* switch mode for when we run initializers */
+    thread->status = THREAD_RUNNING;
+
+    /*
+     * We need to assign the threadId early so we can lock/notify
+     * object monitors.  We'll set the "threadObj" field later.
+     */
+    prepareThread(thread);
+    gDvm.threadList = thread;
+
+#ifdef COUNT_PRECISE_METHODS
+    gDvm.preciseMethods = dvmPointerSetAlloc(200);
+#endif
+
+    return true;
+}
+
+/*
+ * All threads should be stopped by now.  Clean up some thread globals.
+ */
+void dvmThreadShutdown()
+{
+    if (gDvm.threadList != NULL) {
+        /*
+         * If we walk through the thread list and try to free the
+         * lingering thread structures (which should only be for daemon
+         * threads), the daemon threads may crash if they execute before
+         * the process dies.  Let them leak.
+         */
+        freeThread(gDvm.threadList);
+        gDvm.threadList = NULL;
+    }
+
+    dvmFreeBitVector(gDvm.threadIdMap);
+
+    dvmFreeMonitorList();
+
+    pthread_key_delete(gDvm.pthreadKeySelf);
+}
+
+
+/*
+ * Grab the suspend count global lock.
+ */
+static inline void lockThreadSuspendCount()
+{
+    /*
+     * Don't try to change to VMWAIT here.  When we change back to RUNNING
+     * we have to check for a pending suspend, which results in grabbing
+     * this lock recursively.  Doesn't work with "fast" pthread mutexes.
+     *
+     * This lock is always held for very brief periods, so as long as
+     * mutex ordering is respected we shouldn't stall.
+     */
+    dvmLockMutex(&gDvm.threadSuspendCountLock);
+}
+
+/*
+ * Release the suspend count global lock.
+ */
+static inline void unlockThreadSuspendCount()
+{
+    dvmUnlockMutex(&gDvm.threadSuspendCountLock);
+}
+
+/*
+ * Grab the thread list global lock.
+ *
+ * This is held while "suspend all" is trying to make everybody stop.  If
+ * the shutdown is in progress, and somebody tries to grab the lock, they'll
+ * have to wait for the GC to finish.  Therefore it's important that the
+ * thread not be in RUNNING mode.
+ *
+ * We don't have to check to see if we should be suspended once we have
+ * the lock.  Nobody can suspend all threads without holding the thread list
+ * lock while they do it, so by definition there isn't a GC in progress.
+ *
+ * This function deliberately avoids the use of dvmChangeStatus(),
+ * which could grab threadSuspendCountLock.  To avoid deadlock, threads
+ * are required to grab the thread list lock before the thread suspend
+ * count lock.  (See comment in DvmGlobals.)
+ *
+ * TODO: consider checking for suspend after acquiring the lock, and
+ * backing off if set.  As stated above, it can't happen during normal
+ * execution, but it *can* happen during shutdown when daemon threads
+ * are being suspended.
+ */
+void dvmLockThreadList(Thread* self)
+{
+    ThreadStatus oldStatus;
+
+    if (self == NULL)       /* try to get it from TLS */
+        self = dvmThreadSelf();
+
+    if (self != NULL) {
+        oldStatus = self->status;
+        self->status = THREAD_VMWAIT;
+    } else {
+        /* happens during VM shutdown */
+        oldStatus = THREAD_UNDEFINED;  // shut up gcc
+    }
+
+    dvmLockMutex(&gDvm.threadListLock);
+
+    if (self != NULL)
+        self->status = oldStatus;
+}
+
+/*
+ * Try to lock the thread list.
+ *
+ * Returns "true" if we locked it.  This is a "fast" mutex, so if the
+ * current thread holds the lock this will fail.
+ */
+bool dvmTryLockThreadList()
+{
+    return (dvmTryLockMutex(&gDvm.threadListLock) == 0);
+}
+
+/*
+ * Release the thread list global lock.
+ */
+void dvmUnlockThreadList()
+{
+    dvmUnlockMutex(&gDvm.threadListLock);
+}
+
+/*
+ * Convert SuspendCause to a string.
+ */
+static const char* getSuspendCauseStr(SuspendCause why)
+{
+    switch (why) {
+    case SUSPEND_NOT:               return "NOT?";
+    case SUSPEND_FOR_GC:            return "gc";
+    case SUSPEND_FOR_DEBUG:         return "debug";
+    case SUSPEND_FOR_DEBUG_EVENT:   return "debug-event";
+    case SUSPEND_FOR_STACK_DUMP:    return "stack-dump";
+    case SUSPEND_FOR_VERIFY:        return "verify";
+    case SUSPEND_FOR_HPROF:         return "hprof";
+#if defined(WITH_JIT)
+    case SUSPEND_FOR_TBL_RESIZE:    return "table-resize";
+    case SUSPEND_FOR_IC_PATCH:      return "inline-cache-patch";
+    case SUSPEND_FOR_CC_RESET:      return "reset-code-cache";
+    case SUSPEND_FOR_REFRESH:       return "refresh jit status";
+#endif
+    default:                        return "UNKNOWN";
+    }
+}
+
+/*
+ * Grab the "thread suspend" lock.  This is required to prevent the
+ * GC and the debugger from simultaneously suspending all threads.
+ *
+ * If we fail to get the lock, somebody else is trying to suspend all
+ * threads -- including us.  If we go to sleep on the lock we'll deadlock
+ * the VM.  Loop until we get it or somebody puts us to sleep.
+ */
+static void lockThreadSuspend(const char* who, SuspendCause why)
+{
+    const int kSpinSleepTime = 3*1000*1000;        /* 3s */
+    u8 startWhen = 0;       // init req'd to placate gcc
+    int sleepIter = 0;
+    int cc;
+
+    do {
+        cc = dvmTryLockMutex(&gDvm._threadSuspendLock);
+        if (cc != 0) {
+            Thread* self = dvmThreadSelf();
+
+            if (!dvmCheckSuspendPending(self)) {
+                /*
+                 * Could be that a resume-all is in progress, and something
+                 * grabbed the CPU when the wakeup was broadcast.  The thread
+                 * performing the resume hasn't had a chance to release the
+                 * thread suspend lock.  (We release before the broadcast,
+                 * so this should be a narrow window.)
+                 *
+                 * Could be we hit the window as a suspend was started,
+                 * and the lock has been grabbed but the suspend counts
+                 * haven't been incremented yet.
+                 *
+                 * Could be an unusual JNI thread-attach thing.
+                 *
+                 * Could be the debugger telling us to resume at roughly
+                 * the same time we're posting an event.
+                 *
+                 * Could be two app threads both want to patch predicted
+                 * chaining cells around the same time.
+                 */
+                ALOGI("threadid=%d ODD: want thread-suspend lock (%s:%s),"
+                     " it's held, no suspend pending",
+                    self->threadId, who, getSuspendCauseStr(why));
+            } else {
+                /* we suspended; reset timeout */
+                sleepIter = 0;
+            }
+
+            /* give the lock-holder a chance to do some work */
+            if (sleepIter == 0)
+                startWhen = dvmGetRelativeTimeUsec();
+            if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) {
+                ALOGE("threadid=%d: couldn't get thread-suspend lock (%s:%s),"
+                     " bailing",
+                    self->threadId, who, getSuspendCauseStr(why));
+                /* threads are not suspended, thread dump could crash */
+                dvmDumpAllThreads(false);
+                dvmAbort();
+            }
+        }
+    } while (cc != 0);
+    assert(cc == 0);
+}
+
+/*
+ * Release the "thread suspend" lock.
+ */
+static inline void unlockThreadSuspend()
+{
+    dvmUnlockMutex(&gDvm._threadSuspendLock);
+}
+
+
+/*
+ * Kill any daemon threads that still exist.  All of ours should be
+ * stopped, so these should be Thread objects or JNI-attached threads
+ * started by the application.  Actively-running threads are likely
+ * to crash the process if they continue to execute while the VM
+ * shuts down, so we really need to kill or suspend them.  (If we want
+ * the VM to restart within this process, we need to kill them, but that
+ * leaves open the possibility of orphaned resources.)
+ *
+ * Waiting for the thread to suspend may be unwise at this point, but
+ * if one of these is wedged in a critical section then we probably
+ * would've locked up on the last GC attempt.
+ *
+ * It's possible for this function to get called after a failed
+ * initialization, so be careful with assumptions about the environment.
+ *
+ * This will be called from whatever thread calls DestroyJavaVM, usually
+ * but not necessarily the main thread.  It's likely, but not guaranteed,
+ * that the current thread has already been cleaned up.
+ */
+void dvmSlayDaemons()
+{
+    Thread* self = dvmThreadSelf();     // may be null
+    Thread* target;
+    int threadId = 0;
+    bool doWait = false;
+
+    dvmLockThreadList(self);
+
+    if (self != NULL)
+        threadId = self->threadId;
+
+    target = gDvm.threadList;
+    while (target != NULL) {
+        if (target == self) {
+            target = target->next;
+            continue;
+        }
+
+        if (!dvmGetFieldBoolean(target->threadObj,
+                gDvm.offJavaLangThread_daemon))
+        {
+            /* should never happen; suspend it with the rest */
+            ALOGW("threadid=%d: non-daemon id=%d still running at shutdown?!",
+                threadId, target->threadId);
+        }
+
+        std::string threadName(dvmGetThreadName(target));
+        ALOGV("threadid=%d: suspending daemon id=%d name='%s'",
+                threadId, target->threadId, threadName.c_str());
+
+        /* mark as suspended */
+        lockThreadSuspendCount();
+        dvmAddToSuspendCounts(target, 1, 0);
+        unlockThreadSuspendCount();
+        doWait = true;
+
+        target = target->next;
+    }
+
+    //dvmDumpAllThreads(false);
+
+    /*
+     * Unlock the thread list, relocking it later if necessary.  It's
+     * possible a thread is in VMWAIT after calling dvmLockThreadList,
+     * and that function *doesn't* check for pending suspend after
+     * acquiring the lock.  We want to let them finish their business
+     * and see the pending suspend before we continue here.
+     *
+     * There's no guarantee of mutex fairness, so this might not work.
+     * (The alternative is to have dvmLockThreadList check for suspend
+     * after acquiring the lock and back off, something we should consider.)
+     */
+    dvmUnlockThreadList();
+
+    if (doWait) {
+        bool complained = false;
+
+        usleep(200 * 1000);
+
+        dvmLockThreadList(self);
+
+        /*
+         * Sleep for a bit until the threads have suspended.  We're trying
+         * to exit, so don't wait for too long.
+         */
+        int i;
+        for (i = 0; i < 10; i++) {
+            bool allSuspended = true;
+
+            target = gDvm.threadList;
+            while (target != NULL) {
+                if (target == self) {
+                    target = target->next;
+                    continue;
+                }
+
+                if (target->status == THREAD_RUNNING) {
+                    if (!complained)
+                        ALOGD("threadid=%d not ready yet", target->threadId);
+                    allSuspended = false;
+                    /* keep going so we log each running daemon once */
+                }
+
+                target = target->next;
+            }
+
+            if (allSuspended) {
+                ALOGV("threadid=%d: all daemons have suspended", threadId);
+                break;
+            } else {
+                if (!complained) {
+                    complained = true;
+                    ALOGD("threadid=%d: waiting briefly for daemon suspension",
+                        threadId);
+                }
+            }
+
+            usleep(200 * 1000);
+        }
+        dvmUnlockThreadList();
+    }
+
+#if 0   /* bad things happen if they come out of JNI or "spuriously" wake up */
+    /*
+     * Abandon the threads and recover their resources.
+     */
+    target = gDvm.threadList;
+    while (target != NULL) {
+        Thread* nextTarget = target->next;
+        unlinkThread(target);
+        freeThread(target);
+        target = nextTarget;
+    }
+#endif
+
+    //dvmDumpAllThreads(true);
+}
+
+
+/*
+ * Finish preparing the parts of the Thread struct required to support
+ * JNI registration.
+ */
+bool dvmPrepMainForJni(JNIEnv* pEnv)
+{
+    Thread* self;
+
+    /* main thread is always first in list at this point */
+    self = gDvm.threadList;
+    assert(self->threadId == kMainThreadId);
+
+    /* create a "fake" JNI frame at the top of the main thread interp stack */
+    if (!createFakeEntryFrame(self))
+        return false;
+
+    /* fill these in, since they weren't ready at dvmCreateJNIEnv time */
+    dvmSetJniEnvThreadId(pEnv, self);
+    dvmSetThreadJNIEnv(self, (JNIEnv*) pEnv);
+
+    return true;
+}
+
+
+/*
+ * Finish preparing the main thread, allocating some objects to represent
+ * it.  As part of doing so, we finish initializing Thread and ThreadGroup.
+ * This will execute some interpreted code (e.g. class initializers).
+ */
+bool dvmPrepMainThread()
+{
+    Thread* thread;
+    Object* groupObj;
+    Object* threadObj;
+    Object* vmThreadObj;
+    StringObject* threadNameStr;
+    Method* init;
+    JValue unused;
+
+    ALOGV("+++ finishing prep on main VM thread");
+
+    /* main thread is always first in list at this point */
+    thread = gDvm.threadList;
+    assert(thread->threadId == kMainThreadId);
+
+    /*
+     * Make sure the classes are initialized.  We have to do this before
+     * we create an instance of them.
+     */
+    if (!dvmInitClass(gDvm.classJavaLangClass)) {
+        ALOGE("'Class' class failed to initialize");
+        return false;
+    }
+    if (!dvmInitClass(gDvm.classJavaLangThreadGroup) ||
+        !dvmInitClass(gDvm.classJavaLangThread) ||
+        !dvmInitClass(gDvm.classJavaLangVMThread))
+    {
+        ALOGE("thread classes failed to initialize");
+        return false;
+    }
+
+    groupObj = dvmGetMainThreadGroup();
+    if (groupObj == NULL)
+        return false;
+
+    /*
+     * Allocate and construct a Thread with the internal-creation
+     * constructor.
+     */
+    threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
+    if (threadObj == NULL) {
+        ALOGE("unable to allocate main thread object");
+        return false;
+    }
+    dvmReleaseTrackedAlloc(threadObj, NULL);
+
+    threadNameStr = dvmCreateStringFromCstr("main");
+    if (threadNameStr == NULL)
+        return false;
+    dvmReleaseTrackedAlloc((Object*)threadNameStr, NULL);
+
+    init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+            "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+    assert(init != NULL);
+    dvmCallMethod(thread, init, threadObj, &unused, groupObj, threadNameStr,
+        THREAD_NORM_PRIORITY, false);
+    if (dvmCheckException(thread)) {
+        ALOGE("exception thrown while constructing main thread object");
+        return false;
+    }
+
+    /*
+     * Allocate and construct a VMThread.
+     */
+    vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (vmThreadObj == NULL) {
+        ALOGE("unable to allocate main vmthread object");
+        return false;
+    }
+    dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+
+    init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangVMThread, "<init>",
+            "(Ljava/lang/Thread;)V");
+    dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj);
+    if (dvmCheckException(thread)) {
+        ALOGE("exception thrown while constructing main vmthread object");
+        return false;
+    }
+
+    /* set the VMThread.vmData field to our Thread struct */
+    assert(gDvm.offJavaLangVMThread_vmData != 0);
+    dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)thread);
+
+    /*
+     * Stuff the VMThread back into the Thread.  From this point on, other
+     * Threads will see that this Thread is running (at least, they would,
+     * if there were any).
+     */
+    dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread,
+        vmThreadObj);
+
+    thread->threadObj = threadObj;
+
+    /*
+     * Set the "context class loader" field in the system class loader.
+     *
+     * Retrieving the system class loader will cause invocation of
+     * ClassLoader.getSystemClassLoader(), which could conceivably call
+     * Thread.currentThread(), so we want the Thread to be fully configured
+     * before we do this.
+     */
+    Object* systemLoader = dvmGetSystemClassLoader();
+    if (systemLoader == NULL) {
+        ALOGW("WARNING: system class loader is NULL (setting main ctxt)");
+        /* keep going? */
+    } else {
+        dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_contextClassLoader,
+            systemLoader);
+        dvmReleaseTrackedAlloc(systemLoader, NULL);
+    }
+
+    /* include self in non-daemon threads (mainly for AttachCurrentThread) */
+    gDvm.nonDaemonThreadCount++;
+
+    return true;
+}
+
+
+/*
+ * Alloc and initialize a Thread struct.
+ *
+ * Does not create any objects, just stuff on the system (malloc) heap.
+ */
+static Thread* allocThread(int interpStackSize)
+{
+    Thread* thread;
+    u1* stackBottom;
+
+    thread = (Thread*) calloc(1, sizeof(Thread));
+    if (thread == NULL)
+        return NULL;
+
+    /* Check sizes and alignment */
+    assert((((uintptr_t)&thread->interpBreak.all) & 0x7) == 0);
+    assert(sizeof(thread->interpBreak) == sizeof(thread->interpBreak.all));
+
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (dvmSelfVerificationShadowSpaceAlloc(thread) == NULL)
+        return NULL;
+#endif
+
+    assert(interpStackSize >= kMinStackSize && interpStackSize <=kMaxStackSize);
+
+    thread->status = THREAD_INITIALIZING;
+
+    /*
+     * Allocate and initialize the interpreted code stack.  We essentially
+     * "lose" the alloc pointer, which points at the bottom of the stack,
+     * but we can get it back later because we know how big the stack is.
+     *
+     * The stack must be aligned on a 4-byte boundary.
+     */
+#ifdef MALLOC_INTERP_STACK
+    stackBottom = (u1*) malloc(interpStackSize);
+    if (stackBottom == NULL) {
+#if defined(WITH_SELF_VERIFICATION)
+        dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+        free(thread);
+        return NULL;
+    }
+    memset(stackBottom, 0xc5, interpStackSize);     // stop valgrind complaints
+#else
+    stackBottom = (u1*) mmap(NULL, interpStackSize, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (stackBottom == MAP_FAILED) {
+#if defined(WITH_SELF_VERIFICATION)
+        dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+        free(thread);
+        return NULL;
+    }
+#endif
+
+    assert(((u4)stackBottom & 0x03) == 0); // looks like our malloc ensures this
+    thread->interpStackSize = interpStackSize;
+    thread->interpStackStart = stackBottom + interpStackSize;
+    thread->interpStackEnd = stackBottom + STACK_OVERFLOW_RESERVE;
+
+#ifndef DVM_NO_ASM_INTERP
+    thread->mainHandlerTable = dvmAsmInstructionStart;
+    thread->altHandlerTable = dvmAsmAltInstructionStart;
+    thread->interpBreak.ctl.curHandlerTable = thread->mainHandlerTable;
+#endif
+
+    /* give the thread code a chance to set things up */
+    dvmInitInterpStack(thread, interpStackSize);
+
+    /* One-time setup for interpreter/JIT state */
+    dvmInitInterpreterState(thread);
+
+    return thread;
+}
+
+/*
+ * Get a meaningful thread ID.  At present this only has meaning under Linux,
+ * where getpid() and gettid() sometimes agree and sometimes don't depending
+ * on your thread model (try "export LD_ASSUME_KERNEL=2.4.19").
+ */
+pid_t dvmGetSysThreadId()
+{
+#ifdef HAVE_GETTID
+    return gettid();
+#else
+    return getpid();
+#endif
+}
+
+/*
+ * Finish initialization of a Thread struct.
+ *
+ * This must be called while executing in the new thread, but before the
+ * thread is added to the thread list.
+ *
+ * NOTE: The threadListLock must be held by the caller (needed for
+ * assignThreadId()).
+ */
+static bool prepareThread(Thread* thread)
+{
+    assignThreadId(thread);
+    thread->handle = pthread_self();
+    thread->systemTid = dvmGetSysThreadId();
+
+    //ALOGI("SYSTEM TID IS %d (pid is %d)", (int) thread->systemTid,
+    //    (int) getpid());
+    /*
+     * If we were called by dvmAttachCurrentThread, the self value is
+     * already correctly established as "thread".
+     */
+    setThreadSelf(thread);
+
+    ALOGV("threadid=%d: interp stack at %p",
+        thread->threadId, thread->interpStackStart - thread->interpStackSize);
+
+    /*
+     * Initialize invokeReq.
+     */
+    dvmInitMutex(&thread->invokeReq.lock);
+    pthread_cond_init(&thread->invokeReq.cv, NULL);
+
+    /*
+     * Initialize our reference tracking tables.
+     *
+     * Most threads won't use jniMonitorRefTable, so we clear out the
+     * structure but don't call the init function (which allocs storage).
+     */
+    if (!thread->jniLocalRefTable.init(kJniLocalRefMin,
+            kJniLocalRefMax, kIndirectKindLocal)) {
+        return false;
+    }
+    if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
+            kInternalRefDefault, kInternalRefMax))
+        return false;
+
+    memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
+
+    pthread_cond_init(&thread->waitCond, NULL);
+    dvmInitMutex(&thread->waitMutex);
+
+    /* Initialize safepoint callback mechanism */
+    dvmInitMutex(&thread->callbackMutex);
+
+    return true;
+}
+
+/*
+ * Remove a thread from the internal list.
+ * Clear out the links to make it obvious that the thread is
+ * no longer on the list.  Caller must hold gDvm.threadListLock.
+ */
+static void unlinkThread(Thread* thread)
+{
+    LOG_THREAD("threadid=%d: removing from list", thread->threadId);
+    if (thread == gDvm.threadList) {
+        assert(thread->prev == NULL);
+        gDvm.threadList = thread->next;
+    } else {
+        assert(thread->prev != NULL);
+        thread->prev->next = thread->next;
+    }
+    if (thread->next != NULL)
+        thread->next->prev = thread->prev;
+    thread->prev = thread->next = NULL;
+}
+
+/*
+ * Free a Thread struct, and all the stuff allocated within.
+ */
+static void freeThread(Thread* thread)
+{
+    if (thread == NULL)
+        return;
+
+    /* thread->threadId is zero at this point */
+    LOGVV("threadid=%d: freeing", thread->threadId);
+
+    if (thread->interpStackStart != NULL) {
+        u1* interpStackBottom;
+
+        interpStackBottom = thread->interpStackStart;
+        interpStackBottom -= thread->interpStackSize;
+#ifdef MALLOC_INTERP_STACK
+        free(interpStackBottom);
+#else
+        if (munmap(interpStackBottom, thread->interpStackSize) != 0)
+            ALOGW("munmap(thread stack) failed");
+#endif
+    }
+
+    thread->jniLocalRefTable.destroy();
+    dvmClearReferenceTable(&thread->internalLocalRefTable);
+    if (&thread->jniMonitorRefTable.table != NULL)
+        dvmClearReferenceTable(&thread->jniMonitorRefTable);
+
+#if defined(WITH_SELF_VERIFICATION)
+    dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+    free(thread);
+}
+
+/*
+ * Like pthread_self(), but on a Thread*.
+ */
+Thread* dvmThreadSelf()
+{
+    return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);
+}
+
+/*
+ * Explore our sense of self.  Stuffs the thread pointer into TLS.
+ */
+static void setThreadSelf(Thread* thread)
+{
+    int cc;
+
+    cc = pthread_setspecific(gDvm.pthreadKeySelf, thread);
+    if (cc != 0) {
+        /*
+         * Sometimes this fails under Bionic with EINVAL during shutdown.
+         * This can happen if the timing is just right, e.g. a thread
+         * fails to attach during shutdown, but the "fail" path calls
+         * here to ensure we clean up after ourselves.
+         */
+        if (thread != NULL) {
+            ALOGE("pthread_setspecific(%p) failed, err=%d", thread, cc);
+            dvmAbort();     /* the world is fundamentally hosed */
+        }
+    }
+}
+
+/*
+ * This is associated with the pthreadKeySelf key.  It's called by the
+ * pthread library when a thread is exiting and the "self" pointer in TLS
+ * is non-NULL, meaning the VM hasn't had a chance to clean up.  In normal
+ * operation this will not be called.
+ *
+ * This is mainly of use to ensure that we don't leak resources if, for
+ * example, a thread attaches itself to us with AttachCurrentThread and
+ * then exits without notifying the VM.
+ *
+ * We could do the detach here instead of aborting, but this will lead to
+ * portability problems.  Other implementations do not do this check and
+ * will simply be unaware that the thread has exited, leading to resource
+ * leaks (and, if this is a non-daemon thread, an infinite hang when the
+ * VM tries to shut down).
+ *
+ * Because some implementations may want to use the pthread destructor
+ * to initiate the detach, and the ordering of destructors is not defined,
+ * we want to iterate a couple of times to give those a chance to run.
+ */
+static void threadExitCheck(void* arg)
+{
+    const int kMaxCount = 2;
+
+    Thread* self = (Thread*) arg;
+    assert(self != NULL);
+
+    ALOGV("threadid=%d: threadExitCheck(%p) count=%d",
+        self->threadId, arg, self->threadExitCheckCount);
+
+    if (self->status == THREAD_ZOMBIE) {
+        ALOGW("threadid=%d: Weird -- shouldn't be in threadExitCheck",
+            self->threadId);
+        return;
+    }
+
+    if (self->threadExitCheckCount < kMaxCount) {
+        /*
+         * Spin a couple of times to let other destructors fire.
+         */
+        ALOGD("threadid=%d: thread exiting, not yet detached (count=%d)",
+            self->threadId, self->threadExitCheckCount);
+        self->threadExitCheckCount++;
+        int cc = pthread_setspecific(gDvm.pthreadKeySelf, self);
+        if (cc != 0) {
+            ALOGE("threadid=%d: unable to re-add thread to TLS",
+                self->threadId);
+            dvmAbort();
+        }
+    } else {
+        ALOGE("threadid=%d: native thread exited without detaching",
+            self->threadId);
+        dvmAbort();
+    }
+}
+
+
+/*
+ * Assign the threadId.  This needs to be a small integer so that our
+ * "thin" locks fit in a small number of bits.
+ *
+ * We reserve zero for use as an invalid ID.
+ *
+ * This must be called with threadListLock held.
+ */
+static void assignThreadId(Thread* thread)
+{
+    /*
+     * Find a small unique integer.  threadIdMap is a vector of
+     * kMaxThreadId bits;  dvmAllocBit() returns the index of a
+     * bit, meaning that it will always be < kMaxThreadId.
+     */
+    int num = dvmAllocBit(gDvm.threadIdMap);
+    if (num < 0) {
+        ALOGE("Ran out of thread IDs");
+        dvmAbort();     // TODO: make this a non-fatal error result
+    }
+
+    thread->threadId = num + 1;
+
+    assert(thread->threadId != 0);
+}
+
+/*
+ * Give back the thread ID.
+ */
+static void releaseThreadId(Thread* thread)
+{
+    assert(thread->threadId > 0);
+    dvmClearBit(gDvm.threadIdMap, thread->threadId - 1);
+    thread->threadId = 0;
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native code in the main
+ * thread was originally invoked from interpreted code.  This gives us a
+ * place to hang JNI local references.  The VM spec says (v2 5.2) that the
+ * VM begins by executing "main" in a class, so in a way this brings us
+ * closer to the spec.
+ */
+static bool createFakeEntryFrame(Thread* thread)
+{
+    /*
+     * Because we are creating a frame that represents application code, we
+     * want to stuff the application class loader into the method's class
+     * loader field, even though we're using the system class loader to
+     * load it.  This makes life easier over in JNI FindClass (though it
+     * could bite us in other ways).
+     *
+     * Unfortunately this is occurring too early in the initialization,
+     * of necessity coming before JNI is initialized, and we're not quite
+     * ready to set up the application class loader.  Also, overwriting
+     * the class' defining classloader pointer seems unwise.
+     *
+     * Instead, we save a pointer to the method and explicitly check for
+     * it in FindClass.  The method is private so nobody else can call it.
+     */
+
+    assert(thread->threadId == kMainThreadId);      /* main thread only */
+
+    if (!dvmPushJNIFrame(thread, gDvm.methDalvikSystemNativeStart_main))
+        return false;
+
+    /*
+     * Null out the "String[] args" argument.
+     */
+    assert(gDvm.methDalvikSystemNativeStart_main->registersSize == 1);
+    u4* framePtr = (u4*) thread->interpSave.curFrame;
+    framePtr[0] = 0;
+
+    return true;
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native thread has been
+ * executing interpreted code.  This gives us a place to hang JNI local
+ * references.
+ */
+static bool createFakeRunFrame(Thread* thread)
+{
+    return dvmPushJNIFrame(thread, gDvm.methDalvikSystemNativeStart_run);
+}
+
+/*
+ * Helper function to set the name of the current thread
+ */
+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) {
+        ALOGW("Unable to set the name of current thread to '%s': %s",
+            buf, strerror(err));
+    }
+#elif defined(HAVE_PRCTL)
+    prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#else
+    ALOGD("No way to set current thread's name (%s)", s);
+#endif
+}
+
+/*
+ * Create a thread as a result of java.lang.Thread.start().
+ *
+ * We do have to worry about some concurrency problems, e.g. programs
+ * that try to call Thread.start() on the same object from multiple threads.
+ * (This will fail for all but one, but we have to make sure that it succeeds
+ * for exactly one.)
+ *
+ * Some of the complexity here arises from our desire to mimic the
+ * Thread vs. VMThread class decomposition we inherited.  We've been given
+ * a Thread, and now we need to create a VMThread and then populate both
+ * objects.  We also need to create one of our internal Thread objects.
+ *
+ * Pass in a stack size of 0 to get the default.
+ *
+ * The "threadObj" reference must be pinned by the caller to prevent the GC
+ * from moving it around (e.g. added to the tracked allocation list).
+ */
+bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
+{
+    assert(threadObj != NULL);
+
+    Thread* self = dvmThreadSelf();
+    int stackSize;
+    if (reqStackSize == 0)
+        stackSize = gDvm.stackSize;
+    else if (reqStackSize < kMinStackSize)
+        stackSize = kMinStackSize;
+    else if (reqStackSize > kMaxStackSize)
+        stackSize = kMaxStackSize;
+    else
+        stackSize = reqStackSize;
+
+    pthread_attr_t threadAttr;
+    pthread_attr_init(&threadAttr);
+    pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+
+    /*
+     * To minimize the time spent in the critical section, we allocate the
+     * vmThread object here.
+     */
+    Object* vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (vmThreadObj == NULL)
+        return false;
+
+    Thread* newThread = allocThread(stackSize);
+    if (newThread == NULL) {
+        dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+        return false;
+    }
+
+    newThread->threadObj = threadObj;
+
+    assert(newThread->status == THREAD_INITIALIZING);
+
+    /*
+     * We need to lock out other threads while we test and set the
+     * "vmThread" field in java.lang.Thread, because we use that to determine
+     * if this thread has been started before.  We use the thread list lock
+     * because it's handy and we're going to need to grab it again soon
+     * anyway.
+     */
+    dvmLockThreadList(self);
+
+    if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+        dvmUnlockThreadList();
+        dvmThrowIllegalThreadStateException(
+            "thread has already been started");
+        freeThread(newThread);
+        dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+    }
+
+    /*
+     * There are actually three data structures: Thread (object), VMThread
+     * (object), and Thread (C struct).  All of them point to at least one
+     * other.
+     *
+     * As soon as "VMThread.vmData" is assigned, other threads can start
+     * making calls into us (e.g. setPriority).
+     */
+    dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
+    dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+    /*
+     * Thread creation might take a while, so release the lock.
+     */
+    dvmUnlockThreadList();
+
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+    pthread_t threadHandle;
+    int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread);
+    pthread_attr_destroy(&threadAttr);
+    dvmChangeStatus(self, oldStatus);
+
+    if (cc != 0) {
+        /*
+         * Failure generally indicates that we have exceeded system
+         * resource limits.  VirtualMachineError is probably too severe,
+         * so use OutOfMemoryError.
+         */
+        ALOGE("Thread creation failed (err=%s)", strerror(errno));
+
+        dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+        dvmThrowOutOfMemoryError("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", newThread->threadId);
+    newThread->next = gDvm.threadList->next;
+    if (newThread->next != NULL)
+        newThread->next->prev = newThread;
+    newThread->prev = gDvm.threadList;
+    gDvm.threadList->next = newThread;
+
+    /* Add any existing global modes to the interpBreak control */
+    dvmInitializeInterpBreak(newThread);
+
+    if (!dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon))
+        gDvm.nonDaemonThreadCount++;        // guarded by thread list lock
+
+    dvmUnlockThreadList();
+
+    /* change status back to RUNNING, self-suspending if necessary */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Tell the new thread to start.
+     *
+     * We must hold the thread list lock before messing with another thread.
+     * In the general case we would also need to verify that newThread was
+     * still in the thread list, but in our case the thread has not started
+     * executing user code and therefore has not had a chance to exit.
+     *
+     * We move it to VMWAIT, and it then shifts itself to RUNNING, which
+     * comes with a suspend-pending check.
+     */
+    dvmLockThreadList(self);
+
+    assert(newThread->status == THREAD_STARTING);
+    newThread->status = THREAD_VMWAIT;
+    pthread_cond_broadcast(&gDvm.threadStartCond);
+
+    dvmUnlockThreadList();
+
+    dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+    return true;
+
+fail:
+    freeThread(newThread);
+    dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+    return false;
+}
+
+/*
+ * pthread entry function for threads started from interpreted code.
+ */
+static void* interpThreadStart(void* arg)
+{
+    Thread* self = (Thread*) arg;
+
+    std::string threadName(dvmGetThreadName(self));
+    setThreadName(threadName.c_str());
+
+    /*
+     * Finish initializing the Thread struct.
+     */
+    dvmLockThreadList(self);
+    prepareThread(self);
+
+    LOG_THREAD("threadid=%d: created from interp", self->threadId);
+
+    /*
+     * Change our status and wake our parent, who will add us to the
+     * thread list and advance our state to VMWAIT.
+     */
+    self->status = THREAD_STARTING;
+    pthread_cond_broadcast(&gDvm.threadStartCond);
+
+    /*
+     * Wait until the parent says we can go.  Assuming there wasn't a
+     * suspend pending, this will happen immediately.  When it completes,
+     * we're full-fledged citizens of the VM.
+     *
+     * We have to use THREAD_VMWAIT here rather than THREAD_RUNNING
+     * because the pthread_cond_wait below needs to reacquire a lock that
+     * suspend-all is also interested in.  If we get unlucky, the parent could
+     * change us to THREAD_RUNNING, then a GC could start before we get
+     * signaled, and suspend-all will grab the thread list lock and then
+     * wait for us to suspend.  We'll be in the tail end of pthread_cond_wait
+     * trying to get the lock.
+     */
+    while (self->status != THREAD_VMWAIT)
+        pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+    dvmUnlockThreadList();
+
+    /*
+     * Add a JNI context.
+     */
+    self->jniEnv = dvmCreateJNIEnv(self);
+
+    /*
+     * Change our state so the GC will wait for us from now on.  If a GC is
+     * in progress this call will suspend us.
+     */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Notify the debugger & DDM.  The debugger notification may cause
+     * us to suspend ourselves (and others).  The thread state may change
+     * to VMWAIT briefly if network packets are sent.
+     */
+    if (gDvm.debuggerConnected)
+        dvmDbgPostThreadStart(self);
+
+    /*
+     * Set the system thread priority according to the Thread object's
+     * priority level.  We don't usually need to do this, because both the
+     * Thread object and system thread priorities inherit from parents.  The
+     * tricky case is when somebody creates a Thread object, calls
+     * setPriority(), and then starts the thread.  We could manage this with
+     * a "needs priority update" flag to avoid the redundant call.
+     */
+    int priority = dvmGetFieldInt(self->threadObj,
+                        gDvm.offJavaLangThread_priority);
+    dvmChangeThreadPriority(self, priority);
+
+    /*
+     * Execute the "run" method.
+     *
+     * At this point our stack is empty, so somebody who comes looking for
+     * stack traces right now won't have much to look at.  This is normal.
+     */
+    Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
+    JValue unused;
+
+    ALOGV("threadid=%d: calling run()", self->threadId);
+    assert(strcmp(run->name, "run") == 0);
+    dvmCallMethod(self, run, self->threadObj, &unused);
+    ALOGV("threadid=%d: exiting", self->threadId);
+
+    /*
+     * Remove the thread from various lists, report its death, and free
+     * its resources.
+     */
+    dvmDetachCurrentThread();
+
+    return NULL;
+}
+
+/*
+ * The current thread is exiting with an uncaught exception.  The
+ * Java programming language allows the application to provide a
+ * thread-exit-uncaught-exception handler for the VM, for a specific
+ * Thread, and for all threads in a ThreadGroup.
+ *
+ * Version 1.5 added the per-thread handler.  We need to call
+ * "uncaughtException" in the handler object, which is either the
+ * ThreadGroup object or the Thread-specific handler.
+ *
+ * This should only be called when an exception is pending.  Before
+ * returning, the exception will be cleared.
+ */
+static void threadExitUncaughtException(Thread* self, Object* group)
+{
+    Object* exception;
+    Object* handlerObj;
+    Method* uncaughtHandler;
+
+    ALOGW("threadid=%d: thread exiting with uncaught exception (group=%p)",
+        self->threadId, group);
+    assert(group != NULL);
+
+    /*
+     * Get a pointer to the exception, then clear out the one in the
+     * thread.  We don't want to have it set when executing interpreted code.
+     */
+    exception = dvmGetException(self);
+    assert(exception != NULL);
+    dvmAddTrackedAlloc(exception, self);
+    dvmClearException(self);
+
+    /*
+     * Get the Thread's "uncaughtHandler" object.  Use it if non-NULL;
+     * else use "group" (which is an instance of UncaughtExceptionHandler).
+     * The ThreadGroup will handle it directly or call the default
+     * uncaught exception handler.
+     */
+    handlerObj = dvmGetFieldObject(self->threadObj,
+            gDvm.offJavaLangThread_uncaughtHandler);
+    if (handlerObj == NULL)
+        handlerObj = group;
+
+    /*
+     * Find the "uncaughtException" method in this object.  The method
+     * was declared in the Thread.UncaughtExceptionHandler interface.
+     */
+    uncaughtHandler = dvmFindVirtualMethodHierByDescriptor(handlerObj->clazz,
+            "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+    if (uncaughtHandler != NULL) {
+        //ALOGI("+++ calling %s.uncaughtException",
+        //     handlerObj->clazz->descriptor);
+        JValue unused;
+        dvmCallMethod(self, uncaughtHandler, handlerObj, &unused,
+            self->threadObj, exception);
+    } else {
+        /* should be impossible, but handle it anyway */
+        ALOGW("WARNING: no 'uncaughtException' method in class %s",
+            handlerObj->clazz->descriptor);
+        dvmSetException(self, exception);
+        dvmLogExceptionStackTrace();
+    }
+
+    /* if the uncaught handler threw, clear it */
+    dvmClearException(self);
+
+    dvmReleaseTrackedAlloc(exception, self);
+
+    /* Remove this thread's suspendCount from global suspendCount sum */
+    lockThreadSuspendCount();
+    dvmAddToSuspendCounts(self, -self->suspendCount, 0);
+    unlockThreadSuspendCount();
+}
+
+
+/*
+ * Create an internal VM thread, for things like JDWP and finalizers.
+ *
+ * The easiest way to do this is create a new thread and then use the
+ * JNI AttachCurrentThread implementation.
+ *
+ * This does not return until after the new thread has begun executing.
+ */
+bool dvmCreateInternalThread(pthread_t* pHandle, const char* name,
+    InternalThreadStart func, void* funcArg)
+{
+    InternalStartArgs* pArgs;
+    Object* systemGroup;
+    volatile Thread* newThread = NULL;
+    volatile int createStatus = 0;
+
+    systemGroup = dvmGetSystemThreadGroup();
+    if (systemGroup == NULL)
+        return false;
+
+    pArgs = (InternalStartArgs*) malloc(sizeof(*pArgs));
+    pArgs->func = func;
+    pArgs->funcArg = funcArg;
+    pArgs->name = strdup(name);     // storage will be owned by new thread
+    pArgs->group = systemGroup;
+    pArgs->isDaemon = true;
+    pArgs->pThread = &newThread;
+    pArgs->pCreateStatus = &createStatus;
+
+    pthread_attr_t threadAttr;
+    pthread_attr_init(&threadAttr);
+
+    int cc = pthread_create(pHandle, &threadAttr, internalThreadStart, pArgs);
+    pthread_attr_destroy(&threadAttr);
+    if (cc != 0) {
+        ALOGE("internal thread creation failed");
+        free(pArgs->name);
+        free(pArgs);
+        return false;
+    }
+
+    /*
+     * Wait for the child to start.  This gives us an opportunity to make
+     * sure that the thread started correctly, and allows our caller to
+     * assume that the thread has started running.
+     *
+     * Because we aren't holding a lock across the thread creation, it's
+     * possible that the child will already have completed its
+     * initialization.  Because the child only adjusts "createStatus" while
+     * holding the thread list lock, the initial condition on the "while"
+     * loop will correctly avoid the wait if this occurs.
+     *
+     * It's also possible that we'll have to wait for the thread to finish
+     * being created, and as part of allocating a Thread object it might
+     * need to initiate a GC.  We switch to VMWAIT while we pause.
+     */
+    Thread* self = dvmThreadSelf();
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+    dvmLockThreadList(self);
+    while (createStatus == 0)
+        pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+    if (newThread == NULL) {
+        ALOGW("internal thread create failed (createStatus=%d)", createStatus);
+        assert(createStatus < 0);
+        /* don't free pArgs -- if pthread_create succeeded, child owns it */
+        dvmUnlockThreadList();
+        dvmChangeStatus(self, oldStatus);
+        return false;
+    }
+
+    /* thread could be in any state now (except early init states) */
+    //assert(newThread->status == THREAD_RUNNING);
+
+    dvmUnlockThreadList();
+    dvmChangeStatus(self, oldStatus);
+
+    return true;
+}
+
+/*
+ * pthread entry function for internally-created threads.
+ *
+ * We are expected to free "arg" and its contents.  If we're a daemon
+ * thread, and we get cancelled abruptly when the VM shuts down, the
+ * storage won't be freed.  If this becomes a concern we can make a copy
+ * on the stack.
+ */
+static void* internalThreadStart(void* arg)
+{
+    InternalStartArgs* pArgs = (InternalStartArgs*) arg;
+    JavaVMAttachArgs jniArgs;
+
+    jniArgs.version = JNI_VERSION_1_2;
+    jniArgs.name = pArgs->name;
+    jniArgs.group = reinterpret_cast<jobject>(pArgs->group);
+
+    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'",
+            dvmThreadSelf()->threadId, pArgs->name);
+
+        /* execute */
+        (*pArgs->func)(pArgs->funcArg);
+
+        /* detach ourselves */
+        dvmDetachCurrentThread();
+    } else {
+        /*
+         * Tell the parent of our failure.  We don't have a Thread struct,
+         * so we can't be suspended, so we don't need to enter a critical
+         * section.
+         */
+        dvmLockThreadList(dvmThreadSelf());
+        *pArgs->pCreateStatus = -1;
+        assert(*pArgs->pThread == NULL);
+        pthread_cond_broadcast(&gDvm.threadStartCond);
+        dvmUnlockThreadList();
+
+        assert(*pArgs->pThread == NULL);
+    }
+
+    free(pArgs->name);
+    free(pArgs);
+    return NULL;
+}
+
+/*
+ * Attach the current thread to the VM.
+ *
+ * Used for internally-created threads and JNI's AttachCurrentThread.
+ */
+bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon)
+{
+    Thread* self = NULL;
+    Object* threadObj = NULL;
+    Object* vmThreadObj = NULL;
+    StringObject* threadNameStr = NULL;
+    Method* init;
+    bool ok, ret;
+
+    /* allocate thread struct, and establish a basic sense of self */
+    self = allocThread(gDvm.stackSize);
+    if (self == NULL)
+        goto fail;
+    setThreadSelf(self);
+
+    /*
+     * Finish our thread prep.  We need to do this before adding ourselves
+     * to the thread list or invoking any interpreted code.  prepareThread()
+     * requires that we hold the thread list lock.
+     */
+    dvmLockThreadList(self);
+    ok = prepareThread(self);
+    dvmUnlockThreadList();
+    if (!ok)
+        goto fail;
+
+    self->jniEnv = dvmCreateJNIEnv(self);
+    if (self->jniEnv == NULL)
+        goto fail;
+
+    /*
+     * Create a "fake" JNI frame at the top of the main thread interp stack.
+     * It isn't really necessary for the internal threads, but it gives
+     * the debugger something to show.  It is essential for the JNI-attached
+     * threads.
+     */
+    if (!createFakeRunFrame(self))
+        goto fail;
+
+    /*
+     * The native side of the thread is ready; add it to the list.  Once
+     * it's on the list the thread is visible to the JDWP code and the GC.
+     */
+    LOG_THREAD("threadid=%d: adding to list (attached)", self->threadId);
+
+    dvmLockThreadList(self);
+
+    self->next = gDvm.threadList->next;
+    if (self->next != NULL)
+        self->next->prev = self;
+    self->prev = gDvm.threadList;
+    gDvm.threadList->next = self;
+    if (!isDaemon)
+        gDvm.nonDaemonThreadCount++;
+
+    dvmUnlockThreadList();
+
+    /*
+     * Switch state from initializing to running.
+     *
+     * It's possible that a GC began right before we added ourselves
+     * to the thread list, and is still going.  That means our thread
+     * suspend count won't reflect the fact that we should be suspended.
+     * To deal with this, we transition to VMWAIT, pulse the heap lock,
+     * and then advance to RUNNING.  That will ensure that we stall until
+     * the GC completes.
+     *
+     * Once we're in RUNNING, we're like any other thread in the VM (except
+     * for the lack of an initialized threadObj).  We're then free to
+     * allocate and initialize objects.
+     */
+    assert(self->status == THREAD_INITIALIZING);
+    dvmChangeStatus(self, THREAD_VMWAIT);
+    dvmLockMutex(&gDvm.gcHeapLock);
+    dvmUnlockMutex(&gDvm.gcHeapLock);
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Create Thread and VMThread objects.
+     */
+    threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
+    vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (threadObj == NULL || vmThreadObj == NULL)
+        goto fail_unlink;
+
+    /*
+     * This makes threadObj visible to the GC.  We still have it in the
+     * tracked allocation table, so it can't move around on us.
+     */
+    self->threadObj = threadObj;
+    dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)self);
+
+    /*
+     * Create a string for the thread name.
+     */
+    if (pArgs->name != NULL) {
+        threadNameStr = dvmCreateStringFromCstr(pArgs->name);
+        if (threadNameStr == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            goto fail_unlink;
+        }
+    }
+
+    init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+            "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+    if (init == NULL) {
+        assert(dvmCheckException(self));
+        goto fail_unlink;
+    }
+
+    /*
+     * Now we're ready to run some interpreted code.
+     *
+     * We need to construct the Thread object and set the VMThread field.
+     * Setting VMThread tells interpreted code that we're alive.
+     *
+     * Call the (group, name, priority, daemon) constructor on the Thread.
+     * This sets the thread's name and adds it to the specified group, and
+     * provides values for priority and daemon (which are normally inherited
+     * from the current thread).
+     */
+    JValue unused;
+    dvmCallMethod(self, init, threadObj, &unused, (Object*)pArgs->group,
+            threadNameStr, os_getThreadPriorityFromSystem(), isDaemon);
+    if (dvmCheckException(self)) {
+        ALOGE("exception thrown while constructing attached thread object");
+        goto fail_unlink;
+    }
+
+    /*
+     * Set the VMThread field, which tells interpreted code that we're alive.
+     *
+     * The risk of a thread start collision here is very low; somebody
+     * would have to be deliberately polling the ThreadGroup list and
+     * trying to start threads against anything it sees, which would
+     * generally cause problems for all thread creation.  However, for
+     * correctness we test "vmThread" before setting it.
+     *
+     * TODO: this still has a race, it's just smaller.  Not sure this is
+     * worth putting effort into fixing.  Need to hold a lock while
+     * fiddling with the field, or maybe initialize the Thread object in a
+     * way that ensures another thread can't call start() on it.
+     */
+    if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+        ALOGW("WOW: thread start hijack");
+        dvmThrowIllegalThreadStateException(
+            "thread has already been started");
+        /* We don't want to free anything associated with the thread
+         * because someone is obviously interested in it.  Just let
+         * it go and hope it will clean itself up when its finished.
+         * This case should never happen anyway.
+         *
+         * Since we're letting it live, we need to finish setting it up.
+         * We just have to let the caller know that the intended operation
+         * has failed.
+         *
+         * [ This seems strange -- stepping on the vmThread object that's
+         * already present seems like a bad idea.  TODO: figure this out. ]
+         */
+        ret = false;
+    } else {
+        ret = true;
+    }
+    dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+    /* we can now safely un-pin these */
+    dvmReleaseTrackedAlloc(threadObj, self);
+    dvmReleaseTrackedAlloc(vmThreadObj, self);
+    dvmReleaseTrackedAlloc((Object*)threadNameStr, self);
+
+    LOG_THREAD("threadid=%d: attached from native, name=%s",
+        self->threadId, pArgs->name);
+
+    /* tell the debugger & DDM */
+    if (gDvm.debuggerConnected)
+        dvmDbgPostThreadStart(self);
+
+    return ret;
+
+fail_unlink:
+    dvmLockThreadList(self);
+    unlinkThread(self);
+    if (!isDaemon)
+        gDvm.nonDaemonThreadCount--;
+    dvmUnlockThreadList();
+    /* fall through to "fail" */
+fail:
+    dvmReleaseTrackedAlloc(threadObj, self);
+    dvmReleaseTrackedAlloc(vmThreadObj, self);
+    dvmReleaseTrackedAlloc((Object*)threadNameStr, self);
+    if (self != NULL) {
+        if (self->jniEnv != NULL) {
+            dvmDestroyJNIEnv(self->jniEnv);
+            self->jniEnv = NULL;
+        }
+        freeThread(self);
+    }
+    setThreadSelf(NULL);
+    return false;
+}
+
+/*
+ * Detach the thread from the various data structures, notify other threads
+ * that are waiting to "join" it, and free up all heap-allocated storage.
+ *
+ * Used for all threads.
+ *
+ * When we get here the interpreted stack should be empty.  The JNI 1.6 spec
+ * requires us to enforce this for the DetachCurrentThread call, probably
+ * because it also says that DetachCurrentThread causes all monitors
+ * associated with the thread to be released.  (Because the stack is empty,
+ * we only have to worry about explicit JNI calls to MonitorEnter.)
+ *
+ * THOUGHT:
+ * We might want to avoid freeing our internal Thread structure until the
+ * associated Thread/VMThread objects get GCed.  Our Thread is impossible to
+ * get to once the thread shuts down, but there is a small possibility of
+ * an operation starting in another thread before this thread halts, and
+ * finishing much later (perhaps the thread got stalled by a weird OS bug).
+ * We don't want something like Thread.isInterrupted() crawling through
+ * freed storage.  Can do with a Thread finalizer, or by creating a
+ * dedicated ThreadObject class for java/lang/Thread and moving all of our
+ * state into that.
+ */
+void dvmDetachCurrentThread()
+{
+    Thread* self = dvmThreadSelf();
+    Object* vmThread;
+    Object* group;
+
+    /*
+     * Make sure we're not detaching a thread that's still running.  (This
+     * could happen with an explicit JNI detach call.)
+     *
+     * A thread created by interpreted code will finish with a depth of
+     * zero, while a JNI-attached thread will have the synthetic "stack
+     * starter" native method at the top.
+     */
+    int curDepth = dvmComputeExactFrameDepth(self->interpSave.curFrame);
+    if (curDepth != 0) {
+        bool topIsNative = false;
+
+        if (curDepth == 1) {
+            /* not expecting a lingering break frame; just look at curFrame */
+            assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));
+            StackSaveArea* ssa = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+            if (dvmIsNativeMethod(ssa->method))
+                topIsNative = true;
+        }
+
+        if (!topIsNative) {
+            ALOGE("ERROR: detaching thread with interp frames (count=%d)",
+                curDepth);
+            dvmDumpThread(self, false);
+            dvmAbort();
+        }
+    }
+
+    group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group);
+    LOG_THREAD("threadid=%d: detach (group=%p)", self->threadId, group);
+
+    /*
+     * Release any held monitors.  Since there are no interpreted stack
+     * frames, the only thing left are the monitors held by JNI MonitorEnter
+     * calls.
+     */
+    dvmReleaseJniMonitors(self);
+
+    /*
+     * Do some thread-exit uncaught exception processing if necessary.
+     */
+    if (dvmCheckException(self))
+        threadExitUncaughtException(self, group);
+
+    /*
+     * Remove the thread from the thread group.
+     */
+    if (group != NULL) {
+        Method* removeThread =
+            group->clazz->vtable[gDvm.voffJavaLangThreadGroup_removeThread];
+        JValue unused;
+        dvmCallMethod(self, removeThread, group, &unused, self->threadObj);
+    }
+
+    /*
+     * Clear the vmThread reference in the Thread object.  Interpreted code
+     * will now see that this Thread is not running.  As this may be the
+     * only reference to the VMThread object that the VM knows about, we
+     * have to create an internal reference to it first.
+     */
+    vmThread = dvmGetFieldObject(self->threadObj,
+                    gDvm.offJavaLangThread_vmThread);
+    dvmAddTrackedAlloc(vmThread, self);
+    dvmSetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+    /* clear out our struct Thread pointer, since it's going away */
+    dvmSetFieldObject(vmThread, gDvm.offJavaLangVMThread_vmData, NULL);
+
+    /*
+     * Tell the debugger & DDM.  This may cause the current thread or all
+     * threads to suspend.
+     *
+     * The JDWP spec is somewhat vague about when this happens, other than
+     * that it's issued by the dying thread, which may still appear in
+     * an "all threads" listing.
+     */
+    if (gDvm.debuggerConnected)
+        dvmDbgPostThreadDeath(self);
+
+    /*
+     * Thread.join() is implemented as an Object.wait() on the VMThread
+     * object.  Signal anyone who is waiting.
+     */
+    dvmLockObject(self, vmThread);
+    dvmObjectNotifyAll(self, vmThread);
+    dvmUnlockObject(self, vmThread);
+
+    dvmReleaseTrackedAlloc(vmThread, self);
+    vmThread = NULL;
+
+    /*
+     * We're done manipulating objects, so it's okay if the GC runs in
+     * parallel with us from here out.  It's important to do this if
+     * profiling is enabled, since we can wait indefinitely.
+     */
+    volatile void* raw = reinterpret_cast<volatile void*>(&self->status);
+    volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
+    android_atomic_release_store(THREAD_VMWAIT, addr);
+
+    /*
+     * If we're doing method trace profiling, we don't want threads to exit,
+     * because if they do we'll end up reusing thread IDs.  This complicates
+     * analysis and makes it impossible to have reasonable output in the
+     * "threads" section of the "key" file.
+     *
+     * We need to do this after Thread.join() completes, or other threads
+     * could get wedged.  Since self->threadObj is still valid, the Thread
+     * object will not get GCed even though we're no longer in the ThreadGroup
+     * list (which is important since the profiling thread needs to get
+     * the thread's name).
+     */
+    MethodTraceState* traceState = &gDvm.methodTrace;
+
+    dvmLockMutex(&traceState->startStopLock);
+    if (traceState->traceEnabled) {
+        ALOGI("threadid=%d: waiting for method trace to finish",
+            self->threadId);
+        while (traceState->traceEnabled) {
+            dvmWaitCond(&traceState->threadExitCond,
+                        &traceState->startStopLock);
+        }
+    }
+    dvmUnlockMutex(&traceState->startStopLock);
+
+    dvmLockThreadList(self);
+
+    /*
+     * Lose the JNI context.
+     */
+    dvmDestroyJNIEnv(self->jniEnv);
+    self->jniEnv = NULL;
+
+    self->status = THREAD_ZOMBIE;
+
+    /*
+     * Remove ourselves from the internal thread list.
+     */
+    unlinkThread(self);
+
+    /*
+     * If we're the last one standing, signal anybody waiting in
+     * DestroyJavaVM that it's okay to exit.
+     */
+    if (!dvmGetFieldBoolean(self->threadObj, gDvm.offJavaLangThread_daemon)) {
+        gDvm.nonDaemonThreadCount--;        // guarded by thread list lock
+
+        if (gDvm.nonDaemonThreadCount == 0) {
+            int cc;
+
+            ALOGV("threadid=%d: last non-daemon thread", self->threadId);
+            //dvmDumpAllThreads(false);
+            // cond var guarded by threadListLock, which we already hold
+            cc = pthread_cond_signal(&gDvm.vmExitCond);
+            assert(cc == 0);
+        }
+    }
+
+    ALOGV("threadid=%d: bye!", self->threadId);
+    releaseThreadId(self);
+    dvmUnlockThreadList();
+
+    setThreadSelf(NULL);
+
+    freeThread(self);
+}
+
+
+/*
+ * Suspend a single thread.  Do not use to suspend yourself.
+ *
+ * This is used primarily for debugger/DDMS activity.  Does not return
+ * until the thread has suspended or is in a "safe" state (e.g. executing
+ * native code outside the VM).
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmSuspendThread(Thread* thread)
+{
+    assert(thread != NULL);
+    assert(thread != dvmThreadSelf());
+    //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+    lockThreadSuspendCount();
+    dvmAddToSuspendCounts(thread, 1, 1);
+
+    LOG_THREAD("threadid=%d: suspend++, now=%d",
+        thread->threadId, thread->suspendCount);
+    unlockThreadSuspendCount();
+
+    waitForThreadSuspend(dvmThreadSelf(), thread);
+}
+
+/*
+ * Reduce the suspend count of a thread.  If it hits zero, tell it to
+ * resume.
+ *
+ * Used primarily for debugger/DDMS activity.  The thread in question
+ * might have been suspended singly or as part of a suspend-all operation.
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmResumeThread(Thread* thread)
+{
+    assert(thread != NULL);
+    assert(thread != dvmThreadSelf());
+    //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+    lockThreadSuspendCount();
+    if (thread->suspendCount > 0) {
+        dvmAddToSuspendCounts(thread, -1, -1);
+    } else {
+        LOG_THREAD("threadid=%d:  suspendCount already zero",
+            thread->threadId);
+    }
+
+    LOG_THREAD("threadid=%d: suspend--, now=%d",
+        thread->threadId, thread->suspendCount);
+
+    if (thread->suspendCount == 0) {
+        dvmBroadcastCond(&gDvm.threadSuspendCountCond);
+    }
+
+    unlockThreadSuspendCount();
+}
+
+/*
+ * Suspend yourself, as a result of debugger activity.
+ */
+void dvmSuspendSelf(bool jdwpActivity)
+{
+    Thread* self = dvmThreadSelf();
+
+    /* debugger thread must not suspend itself due to debugger activity! */
+    assert(gDvm.jdwpState != NULL);
+    if (self->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+        assert(false);
+        return;
+    }
+
+    /*
+     * Collisions with other suspends aren't really interesting.  We want
+     * to ensure that we're the only one fiddling with the suspend count
+     * though.
+     */
+    lockThreadSuspendCount();
+    dvmAddToSuspendCounts(self, 1, 1);
+
+    /*
+     * Suspend ourselves.
+     */
+    assert(self->suspendCount > 0);
+    self->status = THREAD_SUSPENDED;
+    LOG_THREAD("threadid=%d: self-suspending (dbg)", self->threadId);
+
+    /*
+     * Tell JDWP that we've completed suspension.  The JDWP thread can't
+     * tell us to resume before we're fully asleep because we hold the
+     * suspend count lock.
+     *
+     * If we got here via waitForDebugger(), don't do this part.
+     */
+    if (jdwpActivity) {
+        //ALOGI("threadid=%d: clearing wait-for-event (my handle=%08x)",
+        //    self->threadId, (int) self->handle);
+        dvmJdwpClearWaitForEventThread(gDvm.jdwpState);
+    }
+
+    while (self->suspendCount != 0) {
+        dvmWaitCond(&gDvm.threadSuspendCountCond,
+                    &gDvm.threadSuspendCountLock);
+        if (self->suspendCount != 0) {
+            /*
+             * The condition was signaled but we're still suspended.  This
+             * can happen if the debugger lets go while a SIGQUIT thread
+             * dump event is pending (assuming SignalCatcher was resumed for
+             * just long enough to try to grab the thread-suspend lock).
+             */
+            ALOGD("threadid=%d: still suspended after undo (sc=%d dc=%d)",
+                self->threadId, self->suspendCount, self->dbgSuspendCount);
+        }
+    }
+    assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+    self->status = THREAD_RUNNING;
+    LOG_THREAD("threadid=%d: self-reviving (dbg), status=%d",
+        self->threadId, self->status);
+
+    unlockThreadSuspendCount();
+}
+
+/*
+ * Dump the state of the current thread and that of another thread that
+ * we think is wedged.
+ */
+static void dumpWedgedThread(Thread* thread)
+{
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmPrintNativeBackTrace();
+
+    // dumping a running thread is risky, but could be useful
+    dvmDumpThread(thread, true);
+
+    // stop now and get a core dump
+    //abort();
+}
+
+/*
+ * If the thread is running at below-normal priority, temporarily elevate
+ * it to "normal".
+ *
+ * Returns zero if no changes were made.  Otherwise, returns bit flags
+ * indicating what was changed, storing the previous values in the
+ * provided locations.
+ */
+int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio,
+    SchedPolicy* pSavedThreadPolicy)
+{
+    errno = 0;
+    *pSavedThreadPrio = getpriority(PRIO_PROCESS, thread->systemTid);
+    if (errno != 0) {
+        ALOGW("Unable to get priority for threadid=%d sysTid=%d",
+            thread->threadId, thread->systemTid);
+        return 0;
+    }
+    if (get_sched_policy(thread->systemTid, pSavedThreadPolicy) != 0) {
+        ALOGW("Unable to get policy for threadid=%d sysTid=%d",
+            thread->threadId, thread->systemTid);
+        return 0;
+    }
+
+    int changeFlags = 0;
+
+    /*
+     * Change the priority if we're in the background group.
+     */
+    if (*pSavedThreadPolicy == SP_BACKGROUND) {
+        if (set_sched_policy(thread->systemTid, SP_FOREGROUND) != 0) {
+            ALOGW("Couldn't set fg policy on tid %d", thread->systemTid);
+        } else {
+            changeFlags |= kChangedPolicy;
+            ALOGD("Temporarily moving tid %d to fg (was %d)",
+                thread->systemTid, *pSavedThreadPolicy);
+        }
+    }
+
+    /*
+     * getpriority() returns the "nice" value, so larger numbers indicate
+     * lower priority, with 0 being normal.
+     */
+    if (*pSavedThreadPrio > 0) {
+        const int kHigher = 0;
+        if (setpriority(PRIO_PROCESS, thread->systemTid, kHigher) != 0) {
+            ALOGW("Couldn't raise priority on tid %d to %d",
+                thread->systemTid, kHigher);
+        } else {
+            changeFlags |= kChangedPriority;
+            ALOGD("Temporarily raised priority on tid %d (%d -> %d)",
+                thread->systemTid, *pSavedThreadPrio, kHigher);
+        }
+    }
+
+    return changeFlags;
+}
+
+/*
+ * Reset the priority values for the thread in question.
+ */
+void dvmResetThreadPriority(Thread* thread, int changeFlags,
+    int savedThreadPrio, SchedPolicy savedThreadPolicy)
+{
+    if ((changeFlags & kChangedPolicy) != 0) {
+        if (set_sched_policy(thread->systemTid, savedThreadPolicy) != 0) {
+            ALOGW("NOTE: couldn't reset tid %d to (%d)",
+                thread->systemTid, savedThreadPolicy);
+        } else {
+            ALOGD("Restored policy of %d to %d",
+                thread->systemTid, savedThreadPolicy);
+        }
+    }
+
+    if ((changeFlags & kChangedPriority) != 0) {
+        if (setpriority(PRIO_PROCESS, thread->systemTid, savedThreadPrio) != 0)
+        {
+            ALOGW("NOTE: couldn't reset priority on thread %d to %d",
+                thread->systemTid, savedThreadPrio);
+        } else {
+            ALOGD("Restored priority on %d to %d",
+                thread->systemTid, savedThreadPrio);
+        }
+    }
+}
+
+/*
+ * Wait for another thread to see the pending suspension and stop running.
+ * It can either suspend itself or go into a non-running state such as
+ * VMWAIT or NATIVE in which it cannot interact with the GC.
+ *
+ * If we're running at a higher priority, sched_yield() may not do anything,
+ * so we need to sleep for "long enough" to guarantee that the other
+ * thread has a chance to finish what it's doing.  Sleeping for too short
+ * a period (e.g. less than the resolution of the sleep clock) might cause
+ * the scheduler to return immediately, so we want to start with a
+ * "reasonable" value and expand.
+ *
+ * This does not return until the other thread has stopped running.
+ * Eventually we time out and the VM aborts.
+ *
+ * This does not try to detect the situation where two threads are
+ * waiting for each other to suspend.  In normal use this is part of a
+ * suspend-all, which implies that the suspend-all lock is held, or as
+ * part of a debugger action in which the JDWP thread is always the one
+ * doing the suspending.  (We may need to re-evaluate this now that
+ * getThreadStackTrace is implemented as suspend-snapshot-resume.)
+ *
+ * TODO: track basic stats about time required to suspend VM.
+ */
+#define FIRST_SLEEP (250*1000)    /* 0.25s */
+#define MORE_SLEEP  (750*1000)    /* 0.75s */
+static void waitForThreadSuspend(Thread* self, Thread* thread)
+{
+    const int kMaxRetries = 10;
+    int spinSleepTime = FIRST_SLEEP;
+    bool complained = false;
+    int priChangeFlags = 0;
+    int savedThreadPrio = -500;
+    SchedPolicy savedThreadPolicy = SP_FOREGROUND;
+
+    int sleepIter = 0;
+    int retryCount = 0;
+    u8 startWhen = 0;       // init req'd to placate gcc
+    u8 firstStartWhen = 0;
+
+    while (thread->status == THREAD_RUNNING) {
+        if (sleepIter == 0) {           // get current time on first iteration
+            startWhen = dvmGetRelativeTimeUsec();
+            if (firstStartWhen == 0)    // first iteration of first attempt
+                firstStartWhen = startWhen;
+
+            /*
+             * After waiting for a bit, check to see if the target thread is
+             * running at a reduced priority.  If so, bump it up temporarily
+             * to give it more CPU time.
+             */
+            if (retryCount == 2) {
+                assert(thread->systemTid != 0);
+                priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
+                    &savedThreadPrio, &savedThreadPolicy);
+            }
+        }
+
+#if defined (WITH_JIT)
+        /*
+         * If we're still waiting after the first timeout, unchain all
+         * translations iff:
+         *   1) There are new chains formed since the last unchain
+         *   2) The top VM frame of the running thread is running JIT'ed code
+         */
+        if (gDvmJit.pJitEntryTable && retryCount > 0 &&
+            gDvmJit.hasNewChain && thread->inJitCodeCache) {
+            ALOGD("JIT unchain all for threadid=%d", thread->threadId);
+            dvmJitUnchainAll();
+        }
+#endif
+
+        /*
+         * Sleep briefly.  The iterative sleep call returns false if we've
+         * exceeded the total time limit for this round of sleeping.
+         */
+        if (!dvmIterativeSleep(sleepIter++, spinSleepTime, startWhen)) {
+            if (spinSleepTime != FIRST_SLEEP) {
+                ALOGW("threadid=%d: spin on suspend #%d threadid=%d (pcf=%d)",
+                    self->threadId, retryCount,
+                    thread->threadId, priChangeFlags);
+                if (retryCount > 1) {
+                    /* stack trace logging is slow; skip on first iter */
+                    dumpWedgedThread(thread);
+                }
+                complained = true;
+            }
+
+            // keep going; could be slow due to valgrind
+            sleepIter = 0;
+            spinSleepTime = MORE_SLEEP;
+
+            if (retryCount++ == kMaxRetries) {
+                ALOGE("Fatal spin-on-suspend, dumping threads");
+                dvmDumpAllThreads(false);
+
+                /* log this after -- long traces will scroll off log */
+                ALOGE("threadid=%d: stuck on threadid=%d, giving up",
+                    self->threadId, thread->threadId);
+
+                /* try to get a debuggerd dump from the spinning thread */
+                dvmNukeThread(thread);
+                /* abort the VM */
+                dvmAbort();
+            }
+        }
+    }
+
+    if (complained) {
+        ALOGW("threadid=%d: spin on suspend resolved in %lld msec",
+            self->threadId,
+            (dvmGetRelativeTimeUsec() - firstStartWhen) / 1000);
+        //dvmDumpThread(thread, false);   /* suspended, so dump is safe */
+    }
+    if (priChangeFlags != 0) {
+        dvmResetThreadPriority(thread, priChangeFlags, savedThreadPrio,
+            savedThreadPolicy);
+    }
+}
+
+/*
+ * Suspend all threads except the current one.  This is used by the GC,
+ * the debugger, and by any thread that hits a "suspend all threads"
+ * debugger event (e.g. breakpoint or exception).
+ *
+ * If thread N hits a "suspend all threads" breakpoint, we don't want it
+ * to suspend the JDWP thread.  For the GC, we do, because the debugger can
+ * create objects and even execute arbitrary code.  The "why" argument
+ * allows the caller to say why the suspension is taking place.
+ *
+ * This can be called when a global suspend has already happened, due to
+ * various debugger gymnastics, so keeping an "everybody is suspended" flag
+ * doesn't work.
+ *
+ * DO NOT grab any locks before calling here.  We grab & release the thread
+ * lock and suspend lock here (and we're not using recursive threads), and
+ * we might have to self-suspend if somebody else beats us here.
+ *
+ * We know the current thread is in the thread list, because we attach the
+ * thread before doing anything that could cause VM suspension (like object
+ * allocation).
+ */
+void dvmSuspendAllThreads(SuspendCause why)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    assert(why != 0);
+
+    /*
+     * Start by grabbing the thread suspend lock.  If we can't get it, most
+     * likely somebody else is in the process of performing a suspend or
+     * resume, so lockThreadSuspend() will cause us to self-suspend.
+     *
+     * We keep the lock until all other threads are suspended.
+     */
+    lockThreadSuspend("susp-all", why);
+
+    LOG_THREAD("threadid=%d: SuspendAll starting", self->threadId);
+
+    /*
+     * This is possible if the current thread was in VMWAIT mode when a
+     * suspend-all happened, and then decided to do its own suspend-all.
+     * This can happen when a couple of threads have simultaneous events
+     * of interest to the debugger.
+     */
+    //assert(self->suspendCount == 0);
+
+    /*
+     * Increment everybody's suspend count (except our own).
+     */
+    dvmLockThreadList(self);
+
+    lockThreadSuspendCount();
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+            thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+            continue;
+
+        dvmAddToSuspendCounts(thread, 1,
+                              (why == SUSPEND_FOR_DEBUG ||
+                              why == SUSPEND_FOR_DEBUG_EVENT)
+                              ? 1 : 0);
+    }
+    unlockThreadSuspendCount();
+
+    /*
+     * Wait for everybody in THREAD_RUNNING state to stop.  Other states
+     * indicate the code is either running natively or sleeping quietly.
+     * Any attempt to transition back to THREAD_RUNNING will cause a check
+     * for suspension, so it should be impossible for anything to execute
+     * interpreted code or modify objects (assuming native code plays nicely).
+     *
+     * It's also okay if the thread transitions to a non-RUNNING state.
+     *
+     * Note we released the threadSuspendCountLock before getting here,
+     * so if another thread is fiddling with its suspend count (perhaps
+     * self-suspending for the debugger) it won't block while we're waiting
+     * in here.
+     */
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+            thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+            continue;
+
+        /* wait for the other thread to see the pending suspend */
+        waitForThreadSuspend(self, thread);
+
+        LOG_THREAD("threadid=%d:   threadid=%d status=%d sc=%d dc=%d",
+            self->threadId, thread->threadId, thread->status,
+            thread->suspendCount, thread->dbgSuspendCount);
+    }
+
+    dvmUnlockThreadList();
+    unlockThreadSuspend();
+
+    LOG_THREAD("threadid=%d: SuspendAll complete", self->threadId);
+}
+
+/*
+ * Resume all threads that are currently suspended.
+ *
+ * The "why" must match with the previous suspend.
+ */
+void dvmResumeAllThreads(SuspendCause why)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    int cc;
+
+    lockThreadSuspend("res-all", why);  /* one suspend/resume at a time */
+    LOG_THREAD("threadid=%d: ResumeAll starting", self->threadId);
+
+    /*
+     * Decrement the suspend counts for all threads.  No need for atomic
+     * writes, since nobody should be moving until we decrement the count.
+     * We do need to hold the thread list because of JNI attaches.
+     */
+    dvmLockThreadList(self);
+    lockThreadSuspendCount();
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+            thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+        {
+            continue;
+        }
+
+        if (thread->suspendCount > 0) {
+            dvmAddToSuspendCounts(thread, -1,
+                                  (why == SUSPEND_FOR_DEBUG ||
+                                  why == SUSPEND_FOR_DEBUG_EVENT)
+                                  ? -1 : 0);
+        } else {
+            LOG_THREAD("threadid=%d:  suspendCount already zero",
+                thread->threadId);
+        }
+    }
+    unlockThreadSuspendCount();
+    dvmUnlockThreadList();
+
+    /*
+     * In some ways it makes sense to continue to hold the thread-suspend
+     * lock while we issue the wakeup broadcast.  It allows us to complete
+     * one operation before moving on to the next, which simplifies the
+     * thread activity debug traces.
+     *
+     * This approach caused us some difficulty under Linux, because the
+     * condition variable broadcast not only made the threads runnable,
+     * but actually caused them to execute, and it was a while before
+     * the thread performing the wakeup had an opportunity to release the
+     * thread-suspend lock.
+     *
+     * This is a problem because, when a thread tries to acquire that
+     * lock, it times out after 3 seconds.  If at some point the thread
+     * is told to suspend, the clock resets; but since the VM is still
+     * theoretically mid-resume, there's no suspend pending.  If, for
+     * example, the GC was waking threads up while the SIGQUIT handler
+     * was trying to acquire the lock, we would occasionally time out on
+     * a busy system and SignalCatcher would abort.
+     *
+     * We now perform the unlock before the wakeup broadcast.  The next
+     * suspend can't actually start until the broadcast completes and
+     * returns, because we're holding the thread-suspend-count lock, but the
+     * suspending thread is now able to make progress and we avoid the abort.
+     *
+     * (Technically there is a narrow window between when we release
+     * the thread-suspend lock and grab the thread-suspend-count lock.
+     * This could cause us to send a broadcast to threads with nonzero
+     * suspend counts, but this is expected and they'll all just fall
+     * right back to sleep.  It's probably safe to grab the suspend-count
+     * lock before releasing thread-suspend, since we're still following
+     * the correct order of acquisition, but it feels weird.)
+     */
+
+    LOG_THREAD("threadid=%d: ResumeAll waking others", self->threadId);
+    unlockThreadSuspend();
+
+    /*
+     * Broadcast a notification to all suspended threads, some or all of
+     * which may choose to wake up.  No need to wait for them.
+     */
+    lockThreadSuspendCount();
+    cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+    assert(cc == 0);
+    unlockThreadSuspendCount();
+
+    LOG_THREAD("threadid=%d: ResumeAll complete", self->threadId);
+}
+
+/*
+ * Undo any debugger suspensions.  This is called when the debugger
+ * disconnects.
+ */
+void dvmUndoDebuggerSuspensions()
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    int cc;
+
+    lockThreadSuspend("undo", SUSPEND_FOR_DEBUG);
+    LOG_THREAD("threadid=%d: UndoDebuggerSusp starting", self->threadId);
+
+    /*
+     * Decrement the suspend counts for all threads.  No need for atomic
+     * writes, since nobody should be moving until we decrement the count.
+     * We do need to hold the thread list because of JNI attaches.
+     */
+    dvmLockThreadList(self);
+    lockThreadSuspendCount();
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+            assert(thread->dbgSuspendCount == 0);
+            continue;
+        }
+
+        assert(thread->suspendCount >= thread->dbgSuspendCount);
+        dvmAddToSuspendCounts(thread, -thread->dbgSuspendCount,
+                              -thread->dbgSuspendCount);
+    }
+    unlockThreadSuspendCount();
+    dvmUnlockThreadList();
+
+    /*
+     * Broadcast a notification to all suspended threads, some or all of
+     * which may choose to wake up.  No need to wait for them.
+     */
+    lockThreadSuspendCount();
+    cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+    assert(cc == 0);
+    unlockThreadSuspendCount();
+
+    unlockThreadSuspend();
+
+    LOG_THREAD("threadid=%d: UndoDebuggerSusp complete", self->threadId);
+}
+
+/*
+ * Determine if a thread is suspended.
+ *
+ * As with all operations on foreign threads, the caller should hold
+ * the thread list lock before calling.
+ *
+ * If the thread is suspending or waking, these fields could be changing
+ * out from under us (or the thread could change state right after we
+ * examine it), making this generally unreliable.  This is chiefly
+ * intended for use by the debugger.
+ */
+bool dvmIsSuspended(const Thread* thread)
+{
+    /*
+     * The thread could be:
+     *  (1) Running happily.  status is RUNNING, suspendCount is zero.
+     *      Return "false".
+     *  (2) Pending suspend.  status is RUNNING, suspendCount is nonzero.
+     *      Return "false".
+     *  (3) Suspended.  suspendCount is nonzero, and status is !RUNNING.
+     *      Return "true".
+     *  (4) Waking up.  suspendCount is zero, status is SUSPENDED
+     *      Return "false" (since it could change out from under us, unless
+     *      we hold suspendCountLock).
+     */
+
+    return (thread->suspendCount != 0 &&
+            thread->status != THREAD_RUNNING);
+}
+
+/*
+ * Wait until another thread self-suspends.  This is specifically for
+ * synchronization between the JDWP thread and a thread that has decided
+ * to suspend itself after sending an event to the debugger.
+ *
+ * Threads that encounter "suspend all" events work as well -- the thread
+ * in question suspends everybody else and then itself.
+ *
+ * We can't hold a thread lock here or in the caller, because we could
+ * get here just before the to-be-waited-for-thread issues a "suspend all".
+ * There's an opportunity for badness if the thread we're waiting for exits
+ * and gets cleaned up, but since the thread in question is processing a
+ * debugger event, that's not really a possibility.  (To avoid deadlock,
+ * it's important that we not be in THREAD_RUNNING while we wait.)
+ */
+void dvmWaitForSuspend(Thread* thread)
+{
+    Thread* self = dvmThreadSelf();
+
+    LOG_THREAD("threadid=%d: waiting for threadid=%d to sleep",
+        self->threadId, thread->threadId);
+
+    assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+    assert(thread != self);
+    assert(self->status != THREAD_RUNNING);
+
+    waitForThreadSuspend(self, thread);
+
+    LOG_THREAD("threadid=%d: threadid=%d is now asleep",
+        self->threadId, thread->threadId);
+}
+
+/*
+ * Check to see if we need to suspend ourselves.  If so, go to sleep on
+ * a condition variable.
+ *
+ * Returns "true" if we suspended ourselves.
+ */
+static bool fullSuspendCheck(Thread* self)
+{
+    assert(self != NULL);
+    assert(self->suspendCount >= 0);
+
+    /*
+     * Grab gDvm.threadSuspendCountLock.  This gives us exclusive write
+     * access to self->suspendCount.
+     */
+    lockThreadSuspendCount();   /* grab gDvm.threadSuspendCountLock */
+
+    bool needSuspend = (self->suspendCount != 0);
+    if (needSuspend) {
+        LOG_THREAD("threadid=%d: self-suspending", self->threadId);
+        ThreadStatus oldStatus = self->status;      /* should be RUNNING */
+        self->status = THREAD_SUSPENDED;
+
+        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",
+            self->threadId, self->status);
+    }
+
+    unlockThreadSuspendCount();
+
+    return needSuspend;
+}
+
+/*
+ * Check to see if a suspend is pending.  If so, suspend the current
+ * thread, and return "true" after we have been resumed.
+ */
+bool dvmCheckSuspendPending(Thread* self)
+{
+    assert(self != NULL);
+    if (self->suspendCount == 0) {
+        return false;
+    } else {
+        return fullSuspendCheck(self);
+    }
+}
+
+/*
+ * Update our status.
+ *
+ * The "self" argument, which may be NULL, is accepted as an optimization.
+ *
+ * Returns the old status.
+ */
+ThreadStatus dvmChangeStatus(Thread* self, ThreadStatus newStatus)
+{
+    ThreadStatus oldStatus;
+
+    if (self == NULL)
+        self = dvmThreadSelf();
+
+    LOGVV("threadid=%d: (status %d -> %d)",
+        self->threadId, self->status, newStatus);
+
+    oldStatus = self->status;
+    if (oldStatus == newStatus)
+        return oldStatus;
+
+    if (newStatus == THREAD_RUNNING) {
+        /*
+         * Change our status to THREAD_RUNNING.  The transition requires
+         * that we check for pending suspension, because the VM considers
+         * us to be "asleep" in all other states, and another thread could
+         * be performing a GC now.
+         *
+         * The order of operations is very significant here.  One way to
+         * do this wrong is:
+         *
+         *   GCing thread                   Our thread (in NATIVE)
+         *   ------------                   ----------------------
+         *                                  check suspend count (== 0)
+         *   dvmSuspendAllThreads()
+         *   grab suspend-count lock
+         *   increment all suspend counts
+         *   release suspend-count lock
+         *   check thread state (== NATIVE)
+         *   all are suspended, begin GC
+         *                                  set state to RUNNING
+         *                                  (continue executing)
+         *
+         * We can correct this by grabbing the suspend-count lock and
+         * performing both of our operations (check suspend count, set
+         * state) while holding it, now we need to grab a mutex on every
+         * transition to RUNNING.
+         *
+         * What we do instead is change the order of operations so that
+         * the transition to RUNNING happens first.  If we then detect
+         * that the suspend count is nonzero, we switch to SUSPENDED.
+         *
+         * Appropriate compiler and memory barriers are required to ensure
+         * that the operations are observed in the expected order.
+         *
+         * This does create a small window of opportunity where a GC in
+         * progress could observe what appears to be a running thread (if
+         * it happens to look between when we set to RUNNING and when we
+         * switch to SUSPENDED).  At worst this only affects assertions
+         * and thread logging.  (We could work around it with some sort
+         * of intermediate "pre-running" state that is generally treated
+         * as equivalent to running, but that doesn't seem worthwhile.)
+         *
+         * We can also solve this by combining the "status" and "suspend
+         * count" fields into a single 32-bit value.  This trades the
+         * store/load barrier on transition to RUNNING for an atomic RMW
+         * op on all transitions and all suspend count updates (also, all
+         * accesses to status or the thread count require bit-fiddling).
+         * It also eliminates the brief transition through RUNNING when
+         * the thread is supposed to be suspended.  This is possibly faster
+         * on SMP and slightly more correct, but less convenient.
+         */
+        volatile void* raw = reinterpret_cast<volatile void*>(&self->status);
+        volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
+        android_atomic_acquire_store(newStatus, addr);
+        if (self->suspendCount != 0) {
+            fullSuspendCheck(self);
+        }
+    } else {
+        /*
+         * Not changing to THREAD_RUNNING.  No additional work required.
+         *
+         * We use a releasing store to ensure that, if we were RUNNING,
+         * any updates we previously made to objects on the managed heap
+         * will be observed before the state change.
+         */
+        assert(newStatus != THREAD_SUSPENDED);
+        volatile void* raw = reinterpret_cast<volatile void*>(&self->status);
+        volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
+        android_atomic_release_store(newStatus, addr);
+    }
+
+    return oldStatus;
+}
+
+/*
+ * Get a statically defined thread group from a field in the ThreadGroup
+ * Class object.  Expected arguments are "mMain" and "mSystem".
+ */
+static Object* getStaticThreadGroup(const char* fieldName)
+{
+    StaticField* groupField;
+    Object* groupObj;
+
+    groupField = dvmFindStaticField(gDvm.classJavaLangThreadGroup,
+        fieldName, "Ljava/lang/ThreadGroup;");
+    if (groupField == NULL) {
+        ALOGE("java.lang.ThreadGroup does not have an '%s' field", fieldName);
+        dvmThrowInternalError("bad definition for ThreadGroup");
+        return NULL;
+    }
+    groupObj = dvmGetStaticFieldObject(groupField);
+    if (groupObj == NULL) {
+        ALOGE("java.lang.ThreadGroup.%s not initialized", fieldName);
+        dvmThrowInternalError(NULL);
+        return NULL;
+    }
+
+    return groupObj;
+}
+Object* dvmGetSystemThreadGroup()
+{
+    return getStaticThreadGroup("mSystem");
+}
+Object* dvmGetMainThreadGroup()
+{
+    return getStaticThreadGroup("mMain");
+}
+
+/*
+ * Given a VMThread object, return the associated Thread*.
+ *
+ * NOTE: if the thread detaches, the struct Thread will disappear, and
+ * we will be touching invalid data.  For safety, lock the thread list
+ * before calling this.
+ */
+Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj)
+{
+    int vmData;
+
+    vmData = dvmGetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData);
+
+    if (false) {
+        Thread* thread = gDvm.threadList;
+        while (thread != NULL) {
+            if ((Thread*)vmData == thread)
+                break;
+
+            thread = thread->next;
+        }
+
+        if (thread == NULL) {
+            ALOGW("WARNING: vmThreadObj=%p has thread=%p, not in thread list",
+                vmThreadObj, (Thread*)vmData);
+            vmData = 0;
+        }
+    }
+
+    return (Thread*) vmData;
+}
+
+/*
+ * Given a pthread handle, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByHandle(pthread_t handle)
+{
+    Thread* thread;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->handle == handle)
+            break;
+    }
+    return thread;
+}
+
+/*
+ * Given a threadId, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByThreadId(u4 threadId)
+{
+    Thread* thread;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadId == threadId)
+            break;
+    }
+    return thread;
+}
+
+void dvmChangeThreadPriority(Thread* thread, int newPriority)
+{
+    os_changeThreadPriority(thread, newPriority);
+}
+
+/*
+ * Return true if the thread is on gDvm.threadList.
+ * Caller should not hold gDvm.threadListLock.
+ */
+bool dvmIsOnThreadList(const Thread* thread)
+{
+    bool ret = false;
+
+    dvmLockThreadList(NULL);
+    if (thread == gDvm.threadList) {
+        ret = true;
+    } else {
+        ret = thread->prev != NULL || thread->next != NULL;
+    }
+    dvmUnlockThreadList();
+
+    return ret;
+}
+
+/*
+ * Dump a thread to the log file -- just calls dvmDumpThreadEx() with an
+ * output target.
+ */
+void dvmDumpThread(Thread* thread, bool isRunning)
+{
+    DebugOutputTarget target;
+
+    dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+    dvmDumpThreadEx(&target, thread, isRunning);
+}
+
+/*
+ * Try to get the scheduler group.
+ *
+ * The data from /proc/<pid>/cgroup looks (something) like:
+ *  2:cpu:/bg_non_interactive
+ *  1:cpuacct:/
+ *
+ * We return the part on the "cpu" line after the '/', which will be an
+ * empty string for the default cgroup.  If the string is longer than
+ * "bufLen", the string will be truncated.
+ *
+ * On error, -1 is returned, and an error description will be stored in
+ * the buffer.
+ */
+static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
+{
+#ifdef HAVE_ANDROID_OS
+    char pathBuf[32];
+    char lineBuf[256];
+    FILE *fp;
+
+    snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
+    if ((fp = fopen(pathBuf, "r")) == NULL) {
+        snprintf(buf, bufLen, "[fopen-error:%d]", errno);
+        return -1;
+    }
+
+    while (fgets(lineBuf, sizeof(lineBuf) -1, fp) != NULL) {
+        char* subsys;
+        char* grp;
+        size_t len;
+
+        /* Junk the first field */
+        subsys = strchr(lineBuf, ':');
+        if (subsys == NULL) {
+            goto out_bad_data;
+        }
+
+        if (strncmp(subsys, ":cpu:", 5) != 0) {
+            /* Not the subsys we're looking for */
+            continue;
+        }
+
+        grp = strchr(subsys, '/');
+        if (grp == NULL) {
+            goto out_bad_data;
+        }
+        grp++; /* Drop the leading '/' */
+
+        len = strlen(grp);
+        grp[len-1] = '\0'; /* Drop the trailing '\n' */
+
+        if (bufLen <= len) {
+            len = bufLen - 1;
+        }
+        strncpy(buf, grp, len);
+        buf[len] = '\0';
+        fclose(fp);
+        return 0;
+    }
+
+    snprintf(buf, bufLen, "[no-cpu-subsys]");
+    fclose(fp);
+    return -1;
+
+out_bad_data:
+    ALOGE("Bad cgroup data {%s}", lineBuf);
+    snprintf(buf, bufLen, "[data-parse-failed]");
+    fclose(fp);
+    return -1;
+
+#else
+    snprintf(buf, bufLen, "[n/a]");
+    return -1;
+#endif
+}
+
+/*
+ * Convert ThreadStatus to a string.
+ */
+const char* dvmGetThreadStatusStr(ThreadStatus status)
+{
+    switch (status) {
+    case THREAD_ZOMBIE:         return "ZOMBIE";
+    case THREAD_RUNNING:        return "RUNNABLE";
+    case THREAD_TIMED_WAIT:     return "TIMED_WAIT";
+    case THREAD_MONITOR:        return "MONITOR";
+    case THREAD_WAIT:           return "WAIT";
+    case THREAD_INITIALIZING:   return "INITIALIZING";
+    case THREAD_STARTING:       return "STARTING";
+    case THREAD_NATIVE:         return "NATIVE";
+    case THREAD_VMWAIT:         return "VMWAIT";
+    case THREAD_SUSPENDED:      return "SUSPENDED";
+    default:                    return "UNKNOWN";
+    }
+}
+
+static void dumpSchedStat(const DebugOutputTarget* target, pid_t tid) {
+#ifdef HAVE_ANDROID_OS
+    /* get some bits from /proc/self/stat */
+    ProcStatData procStatData;
+    if (!dvmGetThreadStats(&procStatData, tid)) {
+        /* failed, use zeroed values */
+        memset(&procStatData, 0, sizeof(procStatData));
+    }
+
+    /* grab the scheduler stats for this thread */
+    char schedstatBuf[64];
+    snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/self/task/%d/schedstat", tid);
+    int schedstatFd = open(schedstatBuf, O_RDONLY);
+    strcpy(schedstatBuf, "0 0 0");          /* show this if open/read fails */
+    if (schedstatFd >= 0) {
+        ssize_t bytes;
+        bytes = read(schedstatFd, schedstatBuf, sizeof(schedstatBuf) - 1);
+        close(schedstatFd);
+        if (bytes >= 1) {
+            schedstatBuf[bytes - 1] = '\0';   /* remove trailing newline */
+        }
+    }
+
+    /* show what we got */
+    dvmPrintDebugMessage(target,
+        "  | state=%c schedstat=( %s ) utm=%lu stm=%lu core=%d\n",
+        procStatData.state, schedstatBuf, procStatData.utime,
+        procStatData.stime, procStatData.processor);
+#endif
+}
+
+struct SchedulerStats {
+    int policy;
+    int priority;
+    char group[32];
+};
+
+/*
+ * Get scheduler statistics.
+ */
+static void getSchedulerStats(SchedulerStats* stats, pid_t tid) {
+    struct sched_param sp;
+    if (pthread_getschedparam(pthread_self(), &stats->policy, &sp) != 0) {
+        ALOGW("Warning: pthread_getschedparam failed");
+        stats->policy = -1;
+        stats->priority = -1;
+    } else {
+        stats->priority = sp.sched_priority;
+    }
+    if (getSchedulerGroup(tid, stats->group, sizeof(stats->group)) == 0 &&
+            stats->group[0] == '\0') {
+        strcpy(stats->group, "default");
+    }
+}
+
+/*
+ * Print information about the specified thread.
+ *
+ * Works best when the thread in question is "self" or has been suspended.
+ * When dumping a separate thread that's still running, set "isRunning" to
+ * use a more cautious thread dump function.
+ */
+void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread,
+    bool isRunning)
+{
+    Object* threadObj;
+    Object* groupObj;
+    StringObject* nameStr;
+    char* threadName = NULL;
+    char* groupName = NULL;
+    bool isDaemon;
+    int priority;               // java.lang.Thread priority
+
+    /*
+     * Get the java.lang.Thread object.  This function gets called from
+     * some weird debug contexts, so it's possible that there's a GC in
+     * progress on some other thread.  To decrease the chances of the
+     * thread object being moved out from under us, we add the reference
+     * to the tracked allocation list, which pins it in place.
+     *
+     * If threadObj is NULL, the thread is still in the process of being
+     * attached to the VM, and there's really nothing interesting to
+     * say about it yet.
+     */
+    threadObj = thread->threadObj;
+    if (threadObj == NULL) {
+        ALOGI("Can't dump thread %d: threadObj not set", thread->threadId);
+        return;
+    }
+    dvmAddTrackedAlloc(threadObj, NULL);
+
+    nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+                gDvm.offJavaLangThread_name);
+    threadName = dvmCreateCstrFromString(nameStr);
+
+    priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
+    isDaemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
+
+    /* a null value for group is not expected, but deal with it anyway */
+    groupObj = (Object*) dvmGetFieldObject(threadObj,
+                gDvm.offJavaLangThread_group);
+    if (groupObj != NULL) {
+        nameStr = (StringObject*)
+            dvmGetFieldObject(groupObj, gDvm.offJavaLangThreadGroup_name);
+        groupName = dvmCreateCstrFromString(nameStr);
+    }
+    if (groupName == NULL)
+        groupName = strdup("(null; initializing?)");
+
+    SchedulerStats schedStats;
+    getSchedulerStats(&schedStats, thread->systemTid);
+
+    dvmPrintDebugMessage(target,
+        "\"%s\"%s prio=%d tid=%d %s%s\n",
+        threadName, isDaemon ? " daemon" : "",
+        priority, thread->threadId, dvmGetThreadStatusStr(thread->status),
+#if defined(WITH_JIT)
+        thread->inJitCodeCache ? " JIT" : ""
+#else
+        ""
+#endif
+        );
+    dvmPrintDebugMessage(target,
+        "  | group=\"%s\" sCount=%d dsCount=%d obj=%p self=%p\n",
+        groupName, thread->suspendCount, thread->dbgSuspendCount,
+        thread->threadObj, thread);
+    dvmPrintDebugMessage(target,
+        "  | sysTid=%d nice=%d sched=%d/%d cgrp=%s handle=%d\n",
+        thread->systemTid, getpriority(PRIO_PROCESS, thread->systemTid),
+        schedStats.policy, schedStats.priority, schedStats.group, (int)thread->handle);
+
+    dumpSchedStat(target, thread->systemTid);
+
+    /*
+     * Grab the native stack, if possible.
+     *
+     * The native thread is still running, even if the Dalvik side is
+     * suspended.  This means the thread can move itself out of NATIVE state
+     * while we're in here, shifting to SUSPENDED after a brief moment at
+     * RUNNING.  At that point the native stack isn't all that interesting,
+     * though, so if we fail to dump it there's little lost.
+     */
+    if (thread->status == THREAD_NATIVE || thread->status == THREAD_VMWAIT) {
+        dvmDumpNativeStack(target, thread->systemTid);
+    }
+
+    if (isRunning)
+        dvmDumpRunningThreadStack(target, thread);
+    else
+        dvmDumpThreadStack(target, thread);
+
+    dvmPrintDebugMessage(target, "\n");
+
+    dvmReleaseTrackedAlloc(threadObj, NULL);
+    free(threadName);
+    free(groupName);
+}
+
+std::string dvmGetThreadName(Thread* thread) {
+    if (thread->threadObj == NULL) {
+        ALOGW("threadObj is NULL, name not available");
+        return "-unknown-";
+    }
+
+    StringObject* nameObj = (StringObject*)
+        dvmGetFieldObject(thread->threadObj, gDvm.offJavaLangThread_name);
+    char* name = dvmCreateCstrFromString(nameObj);
+    std::string result(name);
+    free(name);
+    return result;
+}
+
+#ifdef HAVE_ANDROID_OS
+/*
+ * Dumps information about a non-Dalvik thread.
+ */
+static void dumpNativeThread(const DebugOutputTarget* target, pid_t tid) {
+    char path[64];
+    snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+
+    int fd = open(path, O_RDONLY);
+    char name[64];
+    ssize_t n = 0;
+    if (fd >= 0) {
+        n = read(fd, name, sizeof(name) - 1);
+        close(fd);
+    }
+    if (n > 0 && name[n - 1] == '\n') {
+        n -= 1;
+    }
+    if (n <= 0) {
+        strcpy(name, "<no name>");
+    } else {
+        name[n] = '\0';
+    }
+
+    SchedulerStats schedStats;
+    getSchedulerStats(&schedStats, tid);
+
+    dvmPrintDebugMessage(target,
+        "\"%s\" sysTid=%d nice=%d sched=%d/%d cgrp=%s\n",
+        name, tid, getpriority(PRIO_PROCESS, tid),
+        schedStats.policy, schedStats.priority, schedStats.group);
+    dumpSchedStat(target, tid);
+    // Temporarily disabled collecting native stacks from non-Dalvik
+    // threads because sometimes they misbehave.
+    //dvmDumpNativeStack(target, tid);
+
+    dvmPrintDebugMessage(target, "\n");
+}
+
+/*
+ * Returns true if the specified tid is a Dalvik thread.
+ * Assumes the thread list lock is held.
+ */
+static bool isDalvikThread(pid_t tid) {
+    for (Thread* thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->systemTid == tid) {
+            return true;
+        }
+    }
+    return false;
+}
+#endif
+
+/*
+ * Dump all threads to the log file -- just calls dvmDumpAllThreadsEx() with
+ * an output target.
+ */
+void dvmDumpAllThreads(bool grabLock)
+{
+    DebugOutputTarget target;
+
+    dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+    dvmDumpAllThreadsEx(&target, grabLock);
+}
+
+/*
+ * Print information about all known threads.  Assumes they have been
+ * suspended (or are in a non-interpreting state, e.g. WAIT or NATIVE).
+ *
+ * If "grabLock" is true, we grab the thread lock list.  This is important
+ * to do unless the caller already holds the lock.
+ */
+void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock)
+{
+    Thread* thread;
+
+    dvmPrintDebugMessage(target, "DALVIK THREADS:\n");
+
+#ifdef HAVE_ANDROID_OS
+    dvmPrintDebugMessage(target,
+        "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x)\n\n",
+        gDvm.threadListLock.value,
+        gDvm._threadSuspendLock.value,
+        gDvm.threadSuspendCountLock.value,
+        gDvm.gcHeapLock.value);
+#endif
+
+    if (grabLock)
+        dvmLockThreadList(dvmThreadSelf());
+
+    thread = gDvm.threadList;
+    while (thread != NULL) {
+        dvmDumpThreadEx(target, thread, false);
+
+        /* verify link */
+        assert(thread->next == NULL || thread->next->prev == thread);
+
+        thread = thread->next;
+    }
+
+#ifdef HAVE_ANDROID_OS
+    char path[64];
+    snprintf(path, sizeof(path), "/proc/%d/task", getpid());
+
+    DIR* d = opendir(path);
+    if (d != NULL) {
+        dirent* entry = NULL;
+        bool first = true;
+        while ((entry = readdir(d)) != NULL) {
+            char* end;
+            pid_t tid = strtol(entry->d_name, &end, 10);
+            if (!*end && !isDalvikThread(tid)) {
+                if (first) {
+                    dvmPrintDebugMessage(target, "NATIVE THREADS:\n");
+                    first = false;
+                }
+                dumpNativeThread(target, tid);
+            }
+        }
+        closedir(d);
+    }
+#endif
+
+    if (grabLock)
+        dvmUnlockThreadList();
+}
+
+/*
+ * Nuke the target thread from orbit.
+ *
+ * The idea is to send a "crash" signal to the target thread so that
+ * debuggerd will take notice and dump an appropriate stack trace.
+ * Because of the way debuggerd works, we have to throw the same signal
+ * at it twice.
+ *
+ * This does not necessarily cause the entire process to stop, but once a
+ * thread has been nuked the rest of the system is likely to be unstable.
+ * This returns so that some limited set of additional operations may be
+ * performed, but it's advisable (and expected) to call dvmAbort soon.
+ * (This is NOT a way to simply cancel a thread.)
+ */
+void dvmNukeThread(Thread* thread)
+{
+    int killResult;
+
+    /* suppress the heapworker watchdog to assist anyone using a debugger */
+    gDvm.nativeDebuggerActive = true;
+
+    /*
+     * Send the signals, separated by a brief interval to allow debuggerd
+     * to work its magic.  An uncommon signal like SIGFPE or SIGSTKFLT
+     * can be used instead of SIGSEGV to avoid making it look like the
+     * code actually crashed at the current point of execution.
+     *
+     * (Observed behavior: with SIGFPE, debuggerd will dump the target
+     * thread and then the thread that calls dvmAbort.  With SIGSEGV,
+     * you don't get the second stack trace; possibly something in the
+     * kernel decides that a signal has already been sent and it's time
+     * to just kill the process.  The position in the current thread is
+     * generally known, so the second dump is not useful.)
+     *
+     * The target thread can continue to execute between the two signals.
+     * (The first just causes debuggerd to attach to it.)
+     */
+#ifdef SIGSTKFLT
+#define SIG SIGSTKFLT
+#define SIGNAME "SIGSTKFLT"
+#elif defined(SIGEMT)
+#define SIG SIGEMT
+#define SIGNAME "SIGEMT"
+#else
+#error No signal available for dvmNukeThread
+#endif
+
+    ALOGD("threadid=%d: sending two " SIGNAME "s to threadid=%d (tid=%d) to"
+          " cause debuggerd dump",
+          dvmThreadSelf()->threadId, thread->threadId, thread->systemTid);
+    killResult = pthread_kill(thread->handle, SIG);
+    if (killResult != 0) {
+        ALOGD("NOTE: pthread_kill #1 failed: %s", strerror(killResult));
+    }
+    usleep(2 * 1000 * 1000);    // TODO: timed-wait until debuggerd attaches
+    killResult = pthread_kill(thread->handle, SIG);
+    if (killResult != 0) {
+        ALOGD("NOTE: pthread_kill #2 failed: %s", strerror(killResult));
+    }
+    ALOGD("Sent, pausing to let debuggerd run");
+    usleep(8 * 1000 * 1000);    // TODO: timed-wait until debuggerd finishes
+
+    /* ignore SIGSEGV so the eventual dmvAbort() doesn't notify debuggerd */
+    signal(SIGSEGV, SIG_IGN);
+    ALOGD("Continuing");
+}
diff --git a/vm/Thread.h b/vm/Thread.h
new file mode 100644
index 0000000..8deef6e
--- /dev/null
+++ b/vm/Thread.h
@@ -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.
+ */
+
+/*
+ * VM thread support.
+ */
+#ifndef DALVIK_THREAD_H_
+#define DALVIK_THREAD_H_
+
+#include "jni.h"
+#include "interp/InterpState.h"
+
+#include <errno.h>
+#include <cutils/sched_policy.h>
+
+#if defined(CHECK_MUTEX) && !defined(__USE_UNIX98)
+/* glibc lacks this unless you #define __USE_UNIX98 */
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
+enum { PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP };
+#endif
+
+/*
+ * Current status; these map to JDWP constants, so don't rearrange them.
+ * (If you do alter this, update the strings in dvmDumpThread and the
+ * conversion table in VMThread.java.)
+ *
+ * Note that "suspended" is orthogonal to these values (so says JDWP).
+ */
+enum ThreadStatus {
+    THREAD_UNDEFINED    = -1,       /* makes enum compatible with int32_t */
+
+    /* these match up with JDWP values */
+    THREAD_ZOMBIE       = 0,        /* TERMINATED */
+    THREAD_RUNNING      = 1,        /* RUNNABLE or running now */
+    THREAD_TIMED_WAIT   = 2,        /* TIMED_WAITING in Object.wait() */
+    THREAD_MONITOR      = 3,        /* BLOCKED on a monitor */
+    THREAD_WAIT         = 4,        /* WAITING in Object.wait() */
+    /* non-JDWP states */
+    THREAD_INITIALIZING = 5,        /* allocated, not yet running */
+    THREAD_STARTING     = 6,        /* started, not yet on thread list */
+    THREAD_NATIVE       = 7,        /* off in a JNI native method */
+    THREAD_VMWAIT       = 8,        /* waiting on a VM resource */
+    THREAD_SUSPENDED    = 9,        /* suspended, usually by GC or debugger */
+};
+
+/* thread priorities, from java.lang.Thread */
+enum {
+    THREAD_MIN_PRIORITY     = 1,
+    THREAD_NORM_PRIORITY    = 5,
+    THREAD_MAX_PRIORITY     = 10,
+};
+
+
+/* initialization */
+bool dvmThreadStartup(void);
+void dvmThreadShutdown(void);
+void dvmSlayDaemons(void);
+
+
+#define kJniLocalRefMin         64
+#define kJniLocalRefMax         512     /* arbitrary; should be plenty */
+#define kInternalRefDefault     32      /* equally arbitrary */
+#define kInternalRefMax         4096    /* mainly a sanity check */
+
+#define kMinStackSize       (512 + STACK_OVERFLOW_RESERVE)
+#define kDefaultStackSize   (16*1024)   /* four 4K pages */
+#define kMaxStackSize       (256*1024 + STACK_OVERFLOW_RESERVE)
+
+/*
+ * Interpreter control struction.  Packed into a long long to enable
+ * atomic updates.
+ */
+union InterpBreak {
+    volatile int64_t   all;
+    struct {
+        uint16_t   subMode;
+        uint8_t    breakFlags;
+        int8_t     unused;   /* for future expansion */
+#ifndef DVM_NO_ASM_INTERP
+        void* curHandlerTable;
+#else
+        int32_t    unused1;
+#endif
+    } ctl;
+};
+
+/*
+ * Our per-thread data.
+ *
+ * These are allocated on the system heap.
+ */
+struct Thread {
+    /*
+     * Interpreter state which must be preserved across nested
+     * interpreter invocations (via JNI callbacks).  Must be the first
+     * element in Thread.
+     */
+    InterpSaveState interpSave;
+
+    /* small unique integer; useful for "thin" locks and debug messages */
+    u4          threadId;
+
+    /*
+     * Begin interpreter state which does not need to be preserved, but should
+     * be located towards the beginning of the Thread structure for
+     * efficiency.
+     */
+
+    /*
+     * interpBreak contains info about the interpreter mode, as well as
+     * a count of the number of times the thread has been suspended.  When
+     * the count drops to zero, the thread resumes.
+     */
+    InterpBreak interpBreak;
+
+    /*
+     * "dbgSuspendCount" is the portion of the suspend count that the
+     * debugger is responsible for.  This has to be tracked separately so
+     * that we can recover correctly if the debugger abruptly disconnects
+     * (suspendCount -= dbgSuspendCount).  The debugger should not be able
+     * to resume GC-suspended threads, because we ignore the debugger while
+     * a GC is in progress.
+     *
+     * Both of these are guarded by gDvm.threadSuspendCountLock.
+     *
+     * Note the non-debug component will rarely be other than 1 or 0 -- (not
+     * sure it's even possible with the way mutexes are currently used.)
+     */
+
+    int suspendCount;
+    int dbgSuspendCount;
+
+    u1*         cardTable;
+
+    /* current limit of stack; flexes for StackOverflowError */
+    const u1*   interpStackEnd;
+
+    /* FP of bottom-most (currently executing) stack frame on interp stack */
+    void*       XcurFrame;
+    /* current exception, or NULL if nothing pending */
+    Object*     exception;
+
+    bool        debugIsMethodEntry;
+    /* interpreter stack size; our stacks are fixed-length */
+    int         interpStackSize;
+    bool        stackOverflowed;
+
+    /* thread handle, as reported by pthread_self() */
+    pthread_t   handle;
+
+    /* Assembly interpreter handler tables */
+#ifndef DVM_NO_ASM_INTERP
+    void*       mainHandlerTable;   // Table of actual instruction handler
+    void*       altHandlerTable;    // Table of breakout handlers
+#else
+    void*       unused0;            // Consume space to keep offsets
+    void*       unused1;            //   the same between builds with
+#endif
+
+    /*
+     * singleStepCount is a countdown timer used with the breakFlag
+     * kInterpSingleStep.  If kInterpSingleStep is set in breakFlags,
+     * singleStepCount will decremented each instruction execution.
+     * Once it reaches zero, the kInterpSingleStep flag in breakFlags
+     * will be cleared.  This can be used to temporarily prevent
+     * execution from re-entering JIT'd code or force inter-instruction
+     * checks by delaying the reset of curHandlerTable to mainHandlerTable.
+     */
+    int         singleStepCount;
+
+#ifdef WITH_JIT
+    struct JitToInterpEntries jitToInterpEntries;
+    /*
+     * Whether the current top VM frame is in the interpreter or JIT cache:
+     *   NULL    : in the interpreter
+     *   non-NULL: entry address of the JIT'ed code (the actual value doesn't
+     *             matter)
+     */
+    void*             inJitCodeCache;
+    unsigned char*    pJitProfTable;
+    int               jitThreshold;
+    const void*       jitResumeNPC;     // Translation return point
+    const u4*         jitResumeNSP;     // Native SP at return point
+    const u2*         jitResumeDPC;     // Dalvik inst following single-step
+    JitState    jitState;
+    int         icRechainCount;
+    const void* pProfileCountdown;
+    const ClassObject* callsiteClass;
+    const Method*     methodToCall;
+#endif
+
+    /* JNI local reference tracking */
+    IndirectRefTable jniLocalRefTable;
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    /* Buffer for register state during self verification */
+    struct ShadowSpace* shadowSpace;
+#endif
+    int         currTraceRun;
+    int         totalTraceLen;  // Number of Dalvik insts in trace
+    const u2*   currTraceHead;  // Start of the trace we're building
+    const u2*   currRunHead;    // Start of run we're building
+    int         currRunLen;     // Length of run in 16-bit words
+    const u2*   lastPC;         // Stage the PC for the threaded interpreter
+    const Method*  traceMethod; // Starting method of current trace
+    intptr_t    threshFilter[JIT_TRACE_THRESH_FILTER_SIZE];
+    JitTraceRun trace[MAX_JIT_RUN_LEN];
+#endif
+
+    /*
+     * Thread's current status.  Can only be changed by the thread itself
+     * (i.e. don't mess with this from other threads).
+     */
+    volatile ThreadStatus status;
+
+    /* thread ID, only useful under Linux */
+    pid_t       systemTid;
+
+    /* start (high addr) of interp stack (subtract size to get malloc addr) */
+    u1*         interpStackStart;
+
+    /* the java/lang/Thread that we are associated with */
+    Object*     threadObj;
+
+    /* the JNIEnv pointer associated with this thread */
+    JNIEnv*     jniEnv;
+
+    /* internal reference tracking */
+    ReferenceTable  internalLocalRefTable;
+
+
+    /* JNI native monitor reference tracking (initialized on first use) */
+    ReferenceTable  jniMonitorRefTable;
+
+    /* hack to make JNI_OnLoad work right */
+    Object*     classLoaderOverride;
+
+    /* mutex to guard the interrupted and the waitMonitor members */
+    pthread_mutex_t    waitMutex;
+
+    /* pointer to the monitor lock we're currently waiting on */
+    /* guarded by waitMutex */
+    /* TODO: consider changing this to Object* for better JDWP interaction */
+    Monitor*    waitMonitor;
+
+    /* thread "interrupted" status; stays raised until queried or thrown */
+    /* guarded by waitMutex */
+    bool        interrupted;
+
+    /* links to the next thread in the wait set this thread is part of */
+    struct Thread*     waitNext;
+
+    /* object to sleep on while we are waiting for a monitor */
+    pthread_cond_t     waitCond;
+
+    /*
+     * Set to true when the thread is in the process of throwing an
+     * OutOfMemoryError.
+     */
+    bool        throwingOOME;
+
+    /* links to rest of thread list; grab global lock before traversing */
+    struct Thread* prev;
+    struct Thread* next;
+
+    /* used by threadExitCheck when a thread exits without detaching */
+    int         threadExitCheckCount;
+
+    /* JDWP invoke-during-breakpoint support */
+    DebugInvokeReq  invokeReq;
+
+    /* base time for per-thread CPU timing (used by method profiling) */
+    bool        cpuClockBaseSet;
+    u8          cpuClockBase;
+
+    /* memory allocation profiling state */
+    AllocProfState allocProf;
+
+#ifdef WITH_JNI_STACK_CHECK
+    u4          stackCrc;
+#endif
+
+#if WITH_EXTRA_GC_CHECKS > 1
+    /* PC, saved on every instruction; redundant with StackSaveArea */
+    const u2*   currentPc2;
+#endif
+
+    /* Safepoint callback state */
+    pthread_mutex_t   callbackMutex;
+    SafePointCallback callback;
+    void*             callbackArg;
+
+#if defined(ARCH_IA32) && defined(WITH_JIT)
+    u4 spillRegion[MAX_SPILL_JIT_IA];
+#endif
+};
+
+/* start point for an internal thread; mimics pthread args */
+typedef void* (*InternalThreadStart)(void* arg);
+
+/* args for internal thread creation */
+struct InternalStartArgs {
+    /* inputs */
+    InternalThreadStart func;
+    void*       funcArg;
+    char*       name;
+    Object*     group;
+    bool        isDaemon;
+    /* result */
+    volatile Thread** pThread;
+    volatile int*     pCreateStatus;
+};
+
+/* finish init */
+bool dvmPrepMainForJni(JNIEnv* pEnv);
+bool dvmPrepMainThread(void);
+
+/* utility function to get the tid */
+pid_t dvmGetSysThreadId(void);
+
+/*
+ * Get our Thread* from TLS.
+ *
+ * Returns NULL if this isn't a thread that the VM is aware of.
+ */
+Thread* dvmThreadSelf(void);
+
+/* grab the thread list global lock */
+void dvmLockThreadList(Thread* self);
+/* try to grab the thread list global lock */
+bool dvmTryLockThreadList(void);
+/* release the thread list global lock */
+void dvmUnlockThreadList(void);
+
+/*
+ * Thread suspend/resume, used by the GC and debugger.
+ */
+enum SuspendCause {
+    SUSPEND_NOT = 0,
+    SUSPEND_FOR_GC,
+    SUSPEND_FOR_DEBUG,
+    SUSPEND_FOR_DEBUG_EVENT,
+    SUSPEND_FOR_STACK_DUMP,
+    SUSPEND_FOR_DEX_OPT,
+    SUSPEND_FOR_VERIFY,
+    SUSPEND_FOR_HPROF,
+#if defined(WITH_JIT)
+    SUSPEND_FOR_TBL_RESIZE,  // jit-table resize
+    SUSPEND_FOR_IC_PATCH,    // polymorphic callsite inline-cache patch
+    SUSPEND_FOR_CC_RESET,    // code-cache reset
+    SUSPEND_FOR_REFRESH,     // Reload data cached in interpState
+#endif
+};
+void dvmSuspendThread(Thread* thread);
+void dvmSuspendSelf(bool jdwpActivity);
+void dvmResumeThread(Thread* thread);
+void dvmSuspendAllThreads(SuspendCause why);
+void dvmResumeAllThreads(SuspendCause why);
+void dvmUndoDebuggerSuspensions(void);
+
+/*
+ * Check suspend state.  Grab threadListLock before calling.
+ */
+bool dvmIsSuspended(const Thread* thread);
+
+/*
+ * Wait until a thread has suspended.  (Used by debugger support.)
+ */
+void dvmWaitForSuspend(Thread* thread);
+
+/*
+ * Check to see if we should be suspended now.  If so, suspend ourselves
+ * by sleeping on a condition variable.
+ */
+extern "C" bool dvmCheckSuspendPending(Thread* self);
+
+/*
+ * Fast test for use in the interpreter.  Returns "true" if our suspend
+ * count is nonzero.
+ */
+INLINE bool dvmCheckSuspendQuick(Thread* self) {
+    return (self->interpBreak.ctl.subMode & kSubModeSuspendPending);
+}
+
+/*
+ * Used when changing thread state.  Threads may only change their own.
+ * The "self" argument, which may be NULL, is accepted as an optimization.
+ *
+ * If you're calling this before waiting on a resource (e.g. THREAD_WAIT
+ * or THREAD_MONITOR), do so in the same function as the wait -- this records
+ * the current stack depth for the GC.
+ *
+ * If you're changing to THREAD_RUNNING, this will check for suspension.
+ *
+ * Returns the old status.
+ */
+ThreadStatus dvmChangeStatus(Thread* self, ThreadStatus newStatus);
+
+/*
+ * Initialize a mutex.
+ */
+INLINE void dvmInitMutex(pthread_mutex_t* pMutex)
+{
+#ifdef CHECK_MUTEX
+    pthread_mutexattr_t attr;
+    int cc;
+
+    pthread_mutexattr_init(&attr);
+    cc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+    assert(cc == 0);
+    pthread_mutex_init(pMutex, &attr);
+    pthread_mutexattr_destroy(&attr);
+#else
+    pthread_mutex_init(pMutex, NULL);       // default=PTHREAD_MUTEX_FAST_NP
+#endif
+}
+
+/*
+ * Grab a plain mutex.
+ */
+INLINE void dvmLockMutex(pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_mutex_lock(pMutex);
+    assert(cc == 0);
+}
+
+/*
+ * Try grabbing a plain mutex.  Returns 0 if successful.
+ */
+INLINE int dvmTryLockMutex(pthread_mutex_t* pMutex)
+{
+    int cc = pthread_mutex_trylock(pMutex);
+    assert(cc == 0 || cc == EBUSY);
+    return cc;
+}
+
+/*
+ * Unlock pthread mutex.
+ */
+INLINE void dvmUnlockMutex(pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_mutex_unlock(pMutex);
+    assert(cc == 0);
+}
+
+/*
+ * Destroy a mutex.
+ */
+INLINE void dvmDestroyMutex(pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_mutex_destroy(pMutex);
+    assert(cc == 0);
+}
+
+INLINE void dvmBroadcastCond(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
+    assert(cc == 0);
+}
+
+INLINE void dvmSignalCond(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
+    assert(cc == 0);
+}
+
+INLINE void dvmWaitCond(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
+    assert(cc == 0);
+}
+
+/*
+ * Create a thread as a result of java.lang.Thread.start().
+ */
+bool dvmCreateInterpThread(Object* threadObj, int reqStackSize);
+
+/*
+ * Create a thread internal to the VM.  It's visible to interpreted code,
+ * but found in the "system" thread group rather than "main".
+ */
+bool dvmCreateInternalThread(pthread_t* pHandle, const char* name,
+    InternalThreadStart func, void* funcArg);
+
+/*
+ * Attach or detach the current thread from the VM.
+ */
+bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon);
+void dvmDetachCurrentThread(void);
+
+/*
+ * Get the "main" or "system" thread group.
+ */
+Object* dvmGetMainThreadGroup(void);
+Object* dvmGetSystemThreadGroup(void);
+
+/*
+ * Given a java/lang/VMThread object, return our Thread.
+ */
+Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj);
+
+/*
+ * Given a pthread handle, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByHandle(pthread_t handle);
+
+/*
+ * Given a thread ID, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByThreadId(u4 threadId);
+
+/*
+ * Sleep in a thread.  Returns when the sleep timer returns or the thread
+ * is interrupted.
+ */
+void dvmThreadSleep(u8 msec, u4 nsec);
+
+/*
+ * Get the name of a thread.
+ *
+ * For correctness, the caller should hold the thread list lock to ensure
+ * that the thread doesn't go away mid-call.
+ */
+std::string dvmGetThreadName(Thread* thread);
+
+/*
+ * Convert ThreadStatus to a string.
+ */
+const char* dvmGetThreadStatusStr(ThreadStatus status);
+
+/*
+ * Return true if a thread is on the internal list.  If it is, the
+ * thread is part of the GC's root set.
+ */
+bool dvmIsOnThreadList(const Thread* thread);
+
+/*
+ * Get/set the JNIEnv field.
+ */
+INLINE JNIEnv* dvmGetThreadJNIEnv(Thread* self) { return self->jniEnv; }
+INLINE void dvmSetThreadJNIEnv(Thread* self, JNIEnv* env) { self->jniEnv = env;}
+
+/*
+ * Update the priority value of the underlying pthread.
+ */
+void dvmChangeThreadPriority(Thread* thread, int newPriority);
+
+/* "change flags" values for raise/reset thread priority calls */
+#define kChangedPriority    0x01
+#define kChangedPolicy      0x02
+
+/*
+ * If necessary, raise the thread's priority to nice=0 cgroup=fg.
+ *
+ * Returns bit flags indicating changes made (zero if nothing was done).
+ */
+int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio,
+    SchedPolicy* pSavedThreadPolicy);
+
+/*
+ * Drop the thread priority to what it was before an earlier call to
+ * dvmRaiseThreadPriorityIfNeeded().
+ */
+void dvmResetThreadPriority(Thread* thread, int changeFlags,
+    int savedThreadPrio, SchedPolicy savedThreadPolicy);
+
+/*
+ * Debug: dump information about a single thread.
+ */
+void dvmDumpThread(Thread* thread, bool isRunning);
+void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread,
+    bool isRunning);
+
+/*
+ * Debug: dump information about all threads.
+ */
+void dvmDumpAllThreads(bool grabLock);
+void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock);
+
+/*
+ * Debug: kill a thread to get a debuggerd stack trace.  Leaves the VM
+ * in an uncertain state.
+ */
+void dvmNukeThread(Thread* thread);
+
+#endif  // DALVIK_THREAD_H_
diff --git a/vm/UtfString.cpp b/vm/UtfString.cpp
new file mode 100644
index 0000000..2307fb4
--- /dev/null
+++ b/vm/UtfString.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * UTF-8 and Unicode string manipulation, plus java/lang/String convenience
+ * functions.
+ *
+ * In most cases we populate the fields in the String object directly,
+ * rather than going through an instance field lookup.
+ */
+#include "Dalvik.h"
+#include <stdlib.h>
+
+/*
+ * Allocate a new instance of the class String, performing first-use
+ * initialization of the class if necessary. Upon success, the
+ * returned value will have all its fields except hashCode already
+ * filled in, including a reference to a newly-allocated char[] for
+ * the contents, sized as given. Additionally, a reference to the
+ * chars array is stored to the pChars pointer. Callers must
+ * subsequently call dvmReleaseTrackedAlloc() on the result pointer.
+ * This function returns NULL on failure.
+ */
+static StringObject* makeStringObject(u4 charsLength, ArrayObject** pChars)
+{
+    /*
+     * The String class should have already gotten found (but not
+     * necessarily initialized) before making it here. We assert it
+     * explicitly, since historically speaking, we have had bugs with
+     * regard to when the class String gets set up. The assert helps
+     * make any regressions easier to diagnose.
+     */
+    assert(gDvm.classJavaLangString != NULL);
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangString)) {
+        /* Perform first-time use initialization of the class. */
+        if (!dvmInitClass(gDvm.classJavaLangString)) {
+            ALOGE("FATAL: Could not initialize class String");
+            dvmAbort();
+        }
+    }
+
+    Object* result = dvmAllocObject(gDvm.classJavaLangString, ALLOC_DEFAULT);
+    if (result == NULL) {
+        return NULL;
+    }
+
+    ArrayObject* chars = dvmAllocPrimitiveArray('C', charsLength, ALLOC_DEFAULT);
+    if (chars == NULL) {
+        dvmReleaseTrackedAlloc(result, NULL);
+        return NULL;
+    }
+
+    dvmSetFieldInt(result, STRING_FIELDOFF_COUNT, charsLength);
+    dvmSetFieldObject(result, STRING_FIELDOFF_VALUE, (Object*) chars);
+    dvmReleaseTrackedAlloc((Object*) chars, NULL);
+    /* Leave offset and hashCode set to zero. */
+
+    *pChars = chars;
+    return (StringObject*) result;
+}
+
+/*
+ * Compute a hash code on a UTF-8 string, for use with internal hash tables.
+ *
+ * This may or may not yield the same results as the java/lang/String
+ * computeHashCode() function.  (To make sure this doesn't get abused,
+ * I'm initializing the hash code to 1 so they *don't* match up.)
+ *
+ * It would be more correct to invoke dexGetUtf16FromUtf8() here and compute
+ * the hash with the result.  That way, if something encoded the same
+ * character in two different ways, the hash value would be the same.  For
+ * our purposes that isn't necessary.
+ */
+u4 dvmComputeUtf8Hash(const char* utf8Str)
+{
+    u4 hash = 1;
+
+    while (*utf8Str != '\0')
+        hash = hash * 31 + *utf8Str++;
+
+    return hash;
+}
+
+/*
+ * Like "strlen", but for strings encoded with "modified" UTF-8.
+ *
+ * The value returned is the number of characters, which may or may not
+ * be the same as the number of bytes.
+ *
+ * (If this needs optimizing, try: mask against 0xa0, shift right 5,
+ * get increment {1-3} from table of 8 values.)
+ */
+size_t dvmUtf8Len(const char* utf8Str)
+{
+    size_t len = 0;
+    int ic;
+
+    while ((ic = *utf8Str++) != '\0') {
+        len++;
+        if ((ic & 0x80) != 0) {
+            /* two- or three-byte encoding */
+            utf8Str++;
+            if ((ic & 0x20) != 0) {
+                /* three-byte encoding */
+                utf8Str++;
+            }
+        }
+    }
+
+    return len;
+}
+
+/*
+ * Convert a "modified" UTF-8 string to UTF-16.
+ */
+void dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str)
+{
+    while (*utf8Str != '\0')
+        *utf16Str++ = dexGetUtf16FromUtf8(&utf8Str);
+}
+
+/*
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+static int utf16_utf8ByteLen(const u2* utf16Str, int len)
+{
+    int utf8Len = 0;
+
+    while (len--) {
+        unsigned int uic = *utf16Str++;
+
+        /*
+         * The most common case is (uic > 0 && uic <= 0x7f).
+         */
+        if (uic == 0 || uic > 0x7f) {
+            if (uic > 0x07ff)
+                utf8Len += 3;
+            else /*(uic > 0x7f || uic == 0) */
+                utf8Len += 2;
+        } else
+            utf8Len++;
+    }
+    return utf8Len;
+}
+
+/*
+ * Convert a UTF-16 string to UTF-8.
+ *
+ * Make sure you allocate "utf8Str" with the result of utf16_utf8ByteLen(),
+ * not just "len".
+ */
+static void convertUtf16ToUtf8(char* utf8Str, const u2* utf16Str, int len)
+{
+    assert(len >= 0);
+
+    while (len--) {
+        unsigned int uic = *utf16Str++;
+
+        /*
+         * The most common case is (uic > 0 && uic <= 0x7f).
+         */
+        if (uic == 0 || uic > 0x7f) {
+            if (uic > 0x07ff) {
+                *utf8Str++ = (uic >> 12) | 0xe0;
+                *utf8Str++ = ((uic >> 6) & 0x3f) | 0x80;
+                *utf8Str++ = (uic & 0x3f) | 0x80;
+            } else /*(uic > 0x7f || uic == 0)*/ {
+                *utf8Str++ = (uic >> 6) | 0xc0;
+                *utf8Str++ = (uic & 0x3f) | 0x80;
+            }
+        } else {
+            *utf8Str++ = uic;
+        }
+    }
+
+    *utf8Str = '\0';
+}
+
+/*
+ * Use the java/lang/String.computeHashCode() algorithm.
+ */
+static inline u4 computeUtf16Hash(const u2* utf16Str, size_t len)
+{
+    u4 hash = 0;
+
+    while (len--)
+        hash = hash * 31 + *utf16Str++;
+
+    return hash;
+}
+
+u4 dvmComputeStringHash(StringObject* strObj) {
+    int hashCode = dvmGetFieldInt(strObj, STRING_FIELDOFF_HASHCODE);
+    if (hashCode != 0) {
+      return hashCode;
+    }
+    int len = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
+    int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE);
+    hashCode = computeUtf16Hash((u2*)(void*)chars->contents + offset, len);
+    dvmSetFieldInt(strObj, STRING_FIELDOFF_HASHCODE, hashCode);
+    return hashCode;
+}
+
+StringObject* dvmCreateStringFromCstr(const char* utf8Str) {
+    assert(utf8Str != NULL);
+    return dvmCreateStringFromCstrAndLength(utf8Str, dvmUtf8Len(utf8Str));
+}
+
+StringObject* dvmCreateStringFromCstr(const std::string& utf8Str) {
+    return dvmCreateStringFromCstr(utf8Str.c_str());
+}
+
+/*
+ * Create a java/lang/String from a C string, given its UTF-16 length
+ * (number of UTF-16 code points).
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
+    size_t utf16Length)
+{
+    assert(utf8Str != NULL);
+
+    ArrayObject* chars;
+    StringObject* newObj = makeStringObject(utf16Length, &chars);
+    if (newObj == NULL) {
+        return NULL;
+    }
+
+    dvmConvertUtf8ToUtf16((u2*)(void*)chars->contents, utf8Str);
+
+    u4 hashCode = computeUtf16Hash((u2*)(void*)chars->contents, utf16Length);
+    dvmSetFieldInt((Object*) newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+
+    return newObj;
+}
+
+/*
+ * Create a new java/lang/String object, using the given Unicode data.
+ */
+StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len)
+{
+    /* We allow a NULL pointer if the length is zero. */
+    assert(len == 0 || unichars != NULL);
+
+    ArrayObject* chars;
+    StringObject* newObj = makeStringObject(len, &chars);
+    if (newObj == NULL) {
+        return NULL;
+    }
+
+    if (len > 0) memcpy(chars->contents, unichars, len * sizeof(u2));
+
+    u4 hashCode = computeUtf16Hash((u2*)(void*)chars->contents, len);
+    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+
+    return newObj;
+}
+
+/*
+ * Create a new C string from a java/lang/String object.
+ *
+ * Returns NULL if the object is NULL.
+ */
+char* dvmCreateCstrFromString(const StringObject* jstr)
+{
+    assert(gDvm.classJavaLangString != NULL);
+    if (jstr == NULL) {
+        return NULL;
+    }
+
+    int len = dvmGetFieldInt(jstr, STRING_FIELDOFF_COUNT);
+    int offset = dvmGetFieldInt(jstr, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(jstr, STRING_FIELDOFF_VALUE);
+    const u2* data = (const u2*)(void*)chars->contents + offset;
+    assert(offset + len <= (int) chars->length);
+
+    int byteLen = utf16_utf8ByteLen(data, len);
+    char* newStr = (char*) malloc(byteLen+1);
+    if (newStr == NULL) {
+        return NULL;
+    }
+    convertUtf16ToUtf8(newStr, data, len);
+
+    return newStr;
+}
+
+void dvmGetStringUtfRegion(const StringObject* jstr,
+        int start, int len, char* buf)
+{
+    const u2* data = jstr->chars() + start;
+    convertUtf16ToUtf8(buf, data, len);
+}
+
+int StringObject::utfLength() const
+{
+    assert(gDvm.classJavaLangString != NULL);
+
+    int len = dvmGetFieldInt(this, STRING_FIELDOFF_COUNT);
+    int offset = dvmGetFieldInt(this, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE);
+    const u2* data = (const u2*)(void*)chars->contents + offset;
+    assert(offset + len <= (int) chars->length);
+
+    return utf16_utf8ByteLen(data, len);
+}
+
+int StringObject::length() const
+{
+    return dvmGetFieldInt(this, STRING_FIELDOFF_COUNT);
+}
+
+ArrayObject* StringObject::array() const
+{
+    return (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE);
+}
+
+const u2* StringObject::chars() const
+{
+    int offset = dvmGetFieldInt(this, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE);
+    return (const u2*)(void*)chars->contents + offset;
+}
+
+
+/*
+ * Compare two String objects.
+ *
+ * This is a dvmHashTableLookup() callback.  The function has already
+ * compared their hash values; we need to do a full compare to ensure
+ * that the strings really match.
+ */
+int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2)
+{
+    const StringObject* strObj1 = (const StringObject*) vstrObj1;
+    const StringObject* strObj2 = (const StringObject*) vstrObj2;
+
+    assert(gDvm.classJavaLangString != NULL);
+
+    /* get offset and length into char array; all values are in 16-bit units */
+    int len1 = dvmGetFieldInt(strObj1, STRING_FIELDOFF_COUNT);
+    int offset1 = dvmGetFieldInt(strObj1, STRING_FIELDOFF_OFFSET);
+    int len2 = dvmGetFieldInt(strObj2, STRING_FIELDOFF_COUNT);
+    int offset2 = dvmGetFieldInt(strObj2, STRING_FIELDOFF_OFFSET);
+    if (len1 != len2) {
+        return len1 - len2;
+    }
+
+    ArrayObject* chars1 =
+            (ArrayObject*) dvmGetFieldObject(strObj1, STRING_FIELDOFF_VALUE);
+    ArrayObject* chars2 =
+            (ArrayObject*) dvmGetFieldObject(strObj2, STRING_FIELDOFF_VALUE);
+
+    /* damage here actually indicates a broken java/lang/String */
+    assert(offset1 + len1 <= (int) chars1->length);
+    assert(offset2 + len2 <= (int) chars2->length);
+
+    return memcmp((const u2*)(void*)chars1->contents + offset1,
+                  (const u2*)(void*)chars2->contents + offset2,
+                  len1 * sizeof(u2));
+}
+
+ArrayObject* dvmCreateStringArray(const std::vector<std::string>& strings) {
+    Thread* self = dvmThreadSelf();
+
+    // Allocate an array to hold the String objects.
+    ClassObject* elementClass = dvmFindArrayClassForElement(gDvm.classJavaLangString);
+    ArrayObject* stringArray = dvmAllocArrayByClass(elementClass, strings.size(), ALLOC_DEFAULT);
+    if (stringArray == NULL) {
+        // Probably OOM.
+        assert(dvmCheckException(self));
+        return NULL;
+    }
+
+    // Create the individual String objects and add them to the array.
+    for (size_t i = 0; i < strings.size(); i++) {
+        Object* str = (Object*) dvmCreateStringFromCstr(strings[i]);
+        if (str == NULL) {
+            // Probably OOM; drop out now.
+            assert(dvmCheckException(self));
+            dvmReleaseTrackedAlloc((Object*) stringArray, self);
+            return NULL;
+        }
+        dvmSetObjectArrayElement(stringArray, i, str);
+        /* stored in tracked array, okay to release */
+        dvmReleaseTrackedAlloc(str, self);
+    }
+
+    return stringArray;
+}
diff --git a/vm/UtfString.h b/vm/UtfString.h
new file mode 100644
index 0000000..352948c
--- /dev/null
+++ b/vm/UtfString.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * UTF-8 and Unicode string manipulation functions, plus convenience
+ * functions for working with java/lang/String.
+ */
+#ifndef DALVIK_STRING_H_
+#define DALVIK_STRING_H_
+
+#include <string>
+#include <vector>
+
+/*
+ * (This is private to UtfString.c, but we cheat a bit and also use it
+ * for InlineNative.c.  Not really worth creating a separate header.)
+ *
+ * We can avoid poking around in gDvm by hard-coding the expected values of
+ * the String field offsets.  This will be annoying if String is in flux
+ * or the VM field layout is changing, so we use defines here to make it
+ * easy to switch back to the gDvm version.
+ *
+ * The values are checked for correctness during startup.
+ */
+//#define USE_GLOBAL_STRING_DEFS
+#ifdef USE_GLOBAL_STRING_DEFS
+# define STRING_FIELDOFF_VALUE      gDvm.offJavaLangString_value
+# define STRING_FIELDOFF_OFFSET     gDvm.offJavaLangString_offset
+# define STRING_FIELDOFF_COUNT      gDvm.offJavaLangString_count
+# define STRING_FIELDOFF_HASHCODE   gDvm.offJavaLangString_hashCode
+#else
+# define STRING_FIELDOFF_VALUE      8
+# define STRING_FIELDOFF_HASHCODE   12
+# define STRING_FIELDOFF_OFFSET     16
+# define STRING_FIELDOFF_COUNT      20
+#endif
+
+/*
+ * Hash function for modified UTF-8 strings.
+ */
+u4 dvmComputeUtf8Hash(const char* str);
+
+/*
+ * Hash function for string objects. Ensures the hash code field is
+ * populated and returns its value.
+ */
+u4 dvmComputeStringHash(StringObject* strObj);
+
+/*
+ * Create a java.lang.String[] from a vector of C++ strings.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the returned array,
+ * but not on the individual elements.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+ArrayObject* dvmCreateStringArray(const std::vector<std::string>& strings);
+
+/*
+ * Create a java/lang/String from a C string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstr(const char* utf8Str);
+
+/*
+ * Create a java/lang/String from a C++ string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstr(const std::string& utf8Str);
+
+/*
+ * Create a java/lang/String from a C string, given its UTF-16 length
+ * (number of UTF-16 code points).
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
+    u4 utf16Length);
+
+/*
+ * Compute the number of characters in a "modified UTF-8" string.  This will
+ * match the result from strlen() so long as there are no multi-byte chars.
+ */
+size_t dvmUtf8Len(const char* utf8Str);
+
+/*
+ * Convert a UTF-8 string to UTF-16.  "utf16Str" must have enough room
+ * to hold the output.
+ */
+void dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str);
+
+/*
+ * Create a java/lang/String from a Unicode string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ */
+StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len);
+
+/*
+ * Create a UTF-8 C string from a java/lang/String.  Caller must free
+ * the result.
+ *
+ * Returns NULL if "jstr" is NULL.
+ */
+char* dvmCreateCstrFromString(const StringObject* jstr);
+
+/*
+ * Create a UTF-8 C string from a region of a java/lang/String.  (Used by
+ * the JNI GetStringUTFRegion call.)
+ */
+void dvmGetStringUtfRegion(const StringObject* jstr,
+        int start, int len, char* buf);
+
+/*
+ * Compare two string objects.  (This is a dvmHashTableLookup() callback.)
+ */
+int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2);
+
+#endif  // DALVIK_STRING_H_
diff --git a/vm/alloc/Alloc.cpp b/vm/alloc/Alloc.cpp
new file mode 100644
index 0000000..cfcb968
--- /dev/null
+++ b/vm/alloc/Alloc.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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"
+
+/*
+ * Initialize the GC universe.
+ *
+ * We're currently using a memory-mapped arena to keep things off of the
+ * main heap.  This needs to be replaced with something real.
+ */
+bool dvmGcStartup()
+{
+    dvmInitMutex(&gDvm.gcHeapLock);
+    pthread_cond_init(&gDvm.gcHeapCond, NULL);
+    return dvmHeapStartup();
+}
+
+/*
+ * Post-zygote heap initialization, including starting
+ * the HeapWorker thread.
+ */
+bool dvmGcStartupAfterZygote()
+{
+    return dvmHeapStartupAfterZygote();
+}
+
+/*
+ * Shutdown the threads internal to the garbage collector.
+ */
+void dvmGcThreadShutdown()
+{
+    dvmHeapThreadShutdown();
+}
+
+/*
+ * Shut the GC down.
+ */
+void dvmGcShutdown()
+{
+    //TODO: grab and destroy the lock
+    dvmHeapShutdown();
+}
+
+/*
+ * Do any last-minute preparation before we call fork() for the first time.
+ */
+bool dvmGcPreZygoteFork()
+{
+    return dvmHeapSourceStartupBeforeFork();
+}
+
+bool dvmGcStartupClasses()
+{
+    ClassObject *klass = dvmFindSystemClass("Ljava/lang/Daemons;");
+    if (klass == NULL) {
+        return false;
+    }
+    Method *method = dvmFindDirectMethodByDescriptor(klass, "start", "()V");
+    if (method == NULL) {
+        return false;
+    }
+    Thread *self = dvmThreadSelf();
+    assert(self != NULL);
+    JValue unusedResult;
+    dvmCallMethod(self, method, NULL, &unusedResult);
+    return true;
+}
+
+/*
+ * Create a "stock instance" of an exception class.
+ */
+static Object* createStockException(const char* descriptor, const char* msg)
+{
+    Thread* self = dvmThreadSelf();
+    StringObject* msgStr = NULL;
+    ClassObject* clazz;
+    Method* init;
+    Object* obj;
+
+    /* find class, initialize if necessary */
+    clazz = dvmFindSystemClass(descriptor);
+    if (clazz == NULL) {
+        ALOGE("Unable to find %s", descriptor);
+        return NULL;
+    }
+
+    init = dvmFindDirectMethodByDescriptor(clazz, "<init>",
+            "(Ljava/lang/String;)V");
+    if (init == NULL) {
+        ALOGE("Unable to find String-arg constructor for %s", descriptor);
+        return NULL;
+    }
+
+    obj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    if (obj == NULL)
+        return NULL;
+
+    if (msg == NULL) {
+        msgStr = NULL;
+    } else {
+        msgStr = dvmCreateStringFromCstr(msg);
+        if (msgStr == NULL) {
+            ALOGW("Could not allocate message string \"%s\"", msg);
+            dvmReleaseTrackedAlloc(obj, self);
+            return NULL;
+        }
+    }
+
+    JValue unused;
+    dvmCallMethod(self, init, obj, &unused, msgStr);
+    if (dvmCheckException(self)) {
+        dvmReleaseTrackedAlloc((Object*) msgStr, self);
+        dvmReleaseTrackedAlloc(obj, self);
+        return NULL;
+    }
+
+    dvmReleaseTrackedAlloc((Object*) msgStr, self);     // okay if msgStr NULL
+    return obj;
+}
+
+/*
+ * Create some "stock" exceptions.  These can be thrown when the system is
+ * too screwed up to allocate and initialize anything, or when we don't
+ * need a meaningful stack trace.
+ *
+ * We can't do this during the initial startup because we need to execute
+ * the constructors.
+ */
+bool dvmCreateStockExceptions()
+{
+    /*
+     * Pre-allocate some throwables.  These need to be explicitly added
+     * to the GC's root set (see dvmHeapMarkRootSet()).
+     */
+    gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;",
+        "[memory exhausted]");
+    dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
+    gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;",
+        "[pre-allocated]");
+    dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
+    gDvm.noClassDefFoundErrorObj =
+        createStockException("Ljava/lang/NoClassDefFoundError;",
+            "[generic]");
+    dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL);
+
+    if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL ||
+        gDvm.noClassDefFoundErrorObj == NULL)
+    {
+        ALOGW("Unable to create stock exceptions");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Create an instance of the specified class.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+Object* dvmAllocObject(ClassObject* clazz, int flags)
+{
+    Object* newObj;
+
+    assert(clazz != NULL);
+    assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz));
+
+    /* allocate on GC heap; memory is zeroed out */
+    newObj = (Object*)dvmMalloc(clazz->objectSize, flags);
+    if (newObj != NULL) {
+        DVM_OBJECT_INIT(newObj, clazz);
+        dvmTrackAllocation(clazz, clazz->objectSize);   /* notify DDMS */
+    }
+
+    return newObj;
+}
+
+/*
+ * Create a copy of an object, for Object.clone().
+ *
+ * We use the size actually allocated, rather than obj->clazz->objectSize,
+ * because the latter doesn't work for array objects.
+ */
+Object* dvmCloneObject(Object* obj, int flags)
+{
+    assert(dvmIsValidObject(obj));
+    ClassObject* clazz = obj->clazz;
+
+    /* Class.java shouldn't let us get here (java.lang.Class is final
+     * and does not implement Clonable), but make extra sure.
+     * A memcpy() clone will wreak havoc on a ClassObject's "innards".
+     */
+    assert(!dvmIsTheClassClass(clazz));
+
+    size_t size;
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+        size = dvmArrayObjectSize((ArrayObject *)obj);
+    } else {
+        size = clazz->objectSize;
+    }
+
+    Object* copy = (Object*)dvmMalloc(size, flags);
+    if (copy == NULL)
+        return NULL;
+
+    DVM_OBJECT_INIT(copy, clazz);
+    size_t offset = sizeof(Object);
+    /* Copy instance data.  We assume memcpy copies by words. */
+    memcpy((char*)copy + offset, (char*)obj + offset, size - offset);
+
+    /* Mark the clone as finalizable if appropriate. */
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) {
+        dvmSetFinalizable(copy);
+    }
+
+    dvmTrackAllocation(clazz, size);    /* notify DDMS */
+
+    return copy;
+}
+
+
+/*
+ * Track an object that was allocated internally and isn't yet part of the
+ * VM root set.
+ *
+ * We could do this per-thread or globally.  If it's global we don't have
+ * to do the thread lookup but we do have to synchronize access to the list.
+ *
+ * "obj" must not be NULL.
+ *
+ * NOTE: "obj" is not a fully-formed object; in particular, obj->clazz will
+ * usually be NULL since we're being called from dvmMalloc().
+ */
+void dvmAddTrackedAlloc(Object* obj, Thread* self)
+{
+    if (self == NULL)
+        self = dvmThreadSelf();
+
+    assert(obj != NULL);
+    assert(self != NULL);
+    if (!dvmAddToReferenceTable(&self->internalLocalRefTable, obj)) {
+        ALOGE("threadid=%d: unable to add %p to internal ref table",
+            self->threadId, obj);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+}
+
+/*
+ * Stop tracking an object.
+ *
+ * We allow attempts to delete NULL "obj" so that callers don't have to wrap
+ * calls with "if != NULL".
+ */
+void dvmReleaseTrackedAlloc(Object* obj, Thread* self)
+{
+    if (obj == NULL)
+        return;
+
+    if (self == NULL)
+        self = dvmThreadSelf();
+    assert(self != NULL);
+
+    if (!dvmRemoveFromReferenceTable(&self->internalLocalRefTable,
+            self->internalLocalRefTable.table, obj))
+    {
+        ALOGE("threadid=%d: failed to remove %p from internal ref table",
+            self->threadId, obj);
+        dvmAbort();
+    }
+}
+
+
+/*
+ * Explicitly initiate garbage collection.
+ */
+void dvmCollectGarbage()
+{
+    if (gDvm.disableExplicitGc) {
+        return;
+    }
+    dvmLockHeap();
+    dvmWaitForConcurrentGcToComplete();
+    dvmCollectGarbageInternal(GC_EXPLICIT);
+    dvmUnlockHeap();
+}
+
+struct CountContext {
+    const ClassObject *clazz;
+    size_t count;
+};
+
+static void countInstancesOfClassCallback(Object *obj, void *arg)
+{
+    CountContext *ctx = (CountContext *)arg;
+    assert(ctx != NULL);
+    if (obj->clazz == ctx->clazz) {
+        ctx->count += 1;
+    }
+}
+
+size_t dvmCountInstancesOfClass(const ClassObject *clazz)
+{
+    CountContext ctx = { clazz, 0 };
+    dvmLockHeap();
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    dvmHeapBitmapWalk(bitmap, countInstancesOfClassCallback, &ctx);
+    dvmUnlockHeap();
+    return ctx.count;
+}
+
+static void countAssignableInstancesOfClassCallback(Object *obj, void *arg)
+{
+    CountContext *ctx = (CountContext *)arg;
+    assert(ctx != NULL);
+    if (obj->clazz != NULL && dvmInstanceof(obj->clazz, ctx->clazz)) {
+        ctx->count += 1;
+    }
+}
+
+size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz)
+{
+    CountContext ctx = { clazz, 0 };
+    dvmLockHeap();
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    dvmHeapBitmapWalk(bitmap, countAssignableInstancesOfClassCallback, &ctx);
+    dvmUnlockHeap();
+    return ctx.count;
+}
+
+bool dvmIsHeapAddress(void *address)
+{
+    return address != NULL && (((uintptr_t) address & (8-1)) == 0);
+}
+
+bool dvmIsNonMovingObject(const Object* object)
+{
+    return true;
+}
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
new file mode 100644
index 0000000..efee1bd
--- /dev/null
+++ b/vm/alloc/Alloc.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Garbage-collecting allocator.
+ */
+#ifndef DALVIK_ALLOC_ALLOC_H_
+#define DALVIK_ALLOC_ALLOC_H_
+
+#include <stddef.h>
+
+/* flags for dvmMalloc */
+enum {
+    ALLOC_DEFAULT = 0x00,
+    ALLOC_DONT_TRACK = 0x01,  /* don't add to internal tracking list */
+    ALLOC_NON_MOVING = 0x02,
+};
+
+/*
+ * Initialization.
+ */
+bool dvmGcStartup(void);
+bool dvmCreateStockExceptions(void);
+bool dvmGcStartupAfterZygote(void);
+void dvmGcShutdown(void);
+void dvmGcThreadShutdown(void);
+bool dvmGcStartupClasses(void);
+
+/*
+ * Do any last-minute preparation before we call fork() for the first time.
+ */
+bool dvmGcPreZygoteFork(void);
+
+/*
+ * Basic allocation function.
+ *
+ * The new object will be added to the "tracked alloc" table unless
+ * flags is ALLOC_DONT_TRACK.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+void* dvmMalloc(size_t size, int flags);
+
+/*
+ * Allocate a new object.
+ *
+ * The new object will be added to the "tracked alloc" table unless
+ * flags is ALLOC_DONT_TRACK.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+extern "C" Object* dvmAllocObject(ClassObject* clazz, int flags);
+
+/*
+ * Track an object reference that is currently only visible internally.
+ * This is called automatically by dvmMalloc() unless ALLOC_DONT_TRACK
+ * is set.
+ *
+ * The "self" argument is allowed as an optimization; it may be NULL.
+ */
+extern "C" void dvmAddTrackedAlloc(Object* obj, Thread* self);
+
+/*
+ * Remove an object from the internal tracking list.
+ *
+ * Does nothing if "obj" is NULL.
+ *
+ * The "self" argument is allowed as an optimization; it may be NULL.
+ */
+extern "C" void dvmReleaseTrackedAlloc(Object* obj, Thread* self);
+
+/*
+ * Returns true iff <obj> points to a zygote allocated object.
+ */
+bool dvmIsZygoteObject(const Object* obj);
+
+/*
+ * Create a copy of an object.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+Object* dvmCloneObject(Object* obj, int flags);
+
+/*
+ * Make the object finalizable.
+ */
+extern "C" void dvmSetFinalizable(Object* obj);
+
+/*
+ * Determine the exact number of GC heap bytes used by an object.  (Internal
+ * to heap code except for debugging.)
+ */
+size_t dvmObjectSizeInHeap(const Object* obj);
+
+/*
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+float dvmGetTargetHeapUtilization(void);
+
+/*
+ * Sets the new ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+void dvmSetTargetHeapUtilization(float newTarget);
+
+/*
+ * Initiate garbage collection.
+ *
+ * This usually happens automatically, but can also be caused by
+ * Runtime.gc().
+ */
+void dvmCollectGarbage(void);
+
+/*
+ * Returns a count of the direct instances of a class.
+ */
+size_t dvmCountInstancesOfClass(const ClassObject *clazz);
+
+/*
+ * Returns a count of the instances of a class and its subclasses.
+ */
+size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz);
+
+/*
+ * Removes any growth limits from the heap.
+ */
+void dvmClearGrowthLimit(void);
+
+/*
+ * Returns true if the address is aligned appropriately for a heap object.
+ * Does not require the caller to hold the heap lock, and does not take the
+ * heap lock internally.
+ */
+bool dvmIsHeapAddress(void *address);
+
+bool dvmIsNonMovingObject(const Object* object);
+
+#endif  // DALVIK_ALLOC_ALLOC_H_
diff --git a/vm/alloc/CardTable.cpp b/vm/alloc/CardTable.cpp
new file mode 100644
index 0000000..2c81fd1
--- /dev/null
+++ b/vm/alloc/CardTable.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>  /* for PROT_* */
+
+#include "Dalvik.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapBitmapInlines.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Visit.h"
+
+/*
+ * Maintain a card table from the the write barrier. All writes of
+ * non-NULL values to heap addresses should go through an entry in
+ * WriteBarrier, and from there to here.
+ *
+ * The heap is divided into "cards" of GC_CARD_SIZE bytes, as
+ * determined by GC_CARD_SHIFT. The card table contains one byte of
+ * data per card, to be used by the GC. The value of the byte will be
+ * one of GC_CARD_CLEAN or GC_CARD_DIRTY.
+ *
+ * After any store of a non-NULL object pointer into a heap object,
+ * code is obliged to mark the card dirty. The setters in
+ * ObjectInlines.h [such as dvmSetFieldObject] do this for you. The
+ * JIT and fast interpreters also contain code to mark cards as dirty.
+ *
+ * The card table's base [the "biased card table"] gets set to a
+ * rather strange value.  In order to keep the JIT from having to
+ * fabricate or load GC_DIRTY_CARD to store into the card table,
+ * biased base is within the mmap allocation at a point where it's low
+ * byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details.
+ */
+
+/*
+ * Initializes the card table; must be called before any other
+ * dvmCardTable*() functions.
+ */
+bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit)
+{
+    size_t length;
+    void *allocBase;
+    u1 *biasedBase;
+    GcHeap *gcHeap = gDvm.gcHeap;
+    void *heapBase = dvmHeapSourceGetBase();
+    assert(gcHeap != NULL);
+    assert(heapBase != NULL);
+
+    /* Set up the card table */
+    length = heapMaximumSize / GC_CARD_SIZE;
+    /* Allocate an extra 256 bytes to allow fixed low-byte of base */
+    allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
+                            "dalvik-card-table");
+    if (allocBase == NULL) {
+        return false;
+    }
+    gcHeap->cardTableBase = (u1*)allocBase;
+    gcHeap->cardTableLength = growthLimit / GC_CARD_SIZE;
+    gcHeap->cardTableMaxLength = length;
+    gcHeap->cardTableOffset = 0;
+    /* 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);
+        gcHeap->cardTableOffset = offset + (offset < 0 ? 0x100 : 0);
+        biasedBase += gcHeap->cardTableOffset;
+    }
+    assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY);
+    gDvm.biasedCardTableBase = biasedBase;
+
+    return true;
+}
+
+/*
+ * Tears down the entire CardTable.
+ */
+void dvmCardTableShutdown()
+{
+    gDvm.biasedCardTableBase = NULL;
+    munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength);
+}
+
+void dvmClearCardTable()
+{
+    /*
+     * The goal is to zero out some mmap-allocated pages.  We can accomplish
+     * this with memset() or madvise(MADV_DONTNEED).  The latter has some
+     * useful properties, notably that the pages are returned to the system,
+     * so cards for parts of the heap we haven't expanded into won't be
+     * allocated physical pages.  On the other hand, if we un-map the card
+     * area, we'll have to fault it back in as we resume dirtying objects,
+     * which reduces performance.
+     *
+     * We don't cause any correctness issues by failing to clear cards; we
+     * just take a performance hit during the second pause of the concurrent
+     * collection.  The "advisory" nature of madvise() isn't a big problem.
+     *
+     * What we really want to do is:
+     * (1) zero out all cards that were touched
+     * (2) use madvise() to release any pages that won't be used in the near
+     *     future
+     *
+     * For #1, we don't really know which cards were touched, but we can
+     * approximate it with the "live bits max" value, which tells us the
+     * highest start address at which an object was allocated.  This may
+     * leave vestigial nonzero entries at the end if temporary objects are
+     * created during a concurrent GC, but that should be harmless.  (We
+     * can round up to the end of the card table page to reduce this.)
+     *
+     * For #2, we don't know which pages will be used in the future.  Some
+     * simple experiments suggested that a "typical" app will touch about
+     * 60KB of pages while initializing, but drops down to 20-24KB while
+     * idle.  We can save a few hundred KB system-wide with aggressive
+     * use of madvise().  The cost of mapping those pages back in is paid
+     * outside of the GC pause, which reduces the impact.  (We might be
+     * able to get the benefits by only doing this occasionally, e.g. if
+     * the heap shrinks a lot or we somehow notice that we've been idle.)
+     *
+     * Note that cardTableLength is initially set to the growth limit, and
+     * on request will be expanded to the heap maximum.
+     */
+    assert(gDvm.gcHeap->cardTableBase != NULL);
+
+#if 1
+    // zero out cards with memset(), using liveBits as an estimate
+    const HeapBitmap* liveBits = dvmHeapSourceGetLiveBits();
+    size_t maxLiveCard = (liveBits->max - liveBits->base) / GC_CARD_SIZE;
+    maxLiveCard = ALIGN_UP_TO_PAGE_SIZE(maxLiveCard);
+    if (maxLiveCard > gDvm.gcHeap->cardTableLength) {
+        maxLiveCard = gDvm.gcHeap->cardTableLength;
+    }
+
+    memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, maxLiveCard);
+#else
+    // zero out cards with madvise(), discarding all pages in the card table
+    madvise(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength,
+        MADV_DONTNEED);
+#endif
+}
+
+/*
+ * Returns true iff the address is within the bounds of the card table.
+ */
+bool dvmIsValidCard(const u1 *cardAddr)
+{
+    GcHeap *h = gDvm.gcHeap;
+    u1* begin = h->cardTableBase + h->cardTableOffset;
+    u1* end = &begin[h->cardTableLength];
+    return cardAddr >= begin && cardAddr < end;
+}
+
+/*
+ * Returns the address of the 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.
+ */
+struct WhiteReferenceCounter {
+    HeapBitmap *markBits;
+    size_t whiteRefs;
+};
+
+/*
+ * Visitor that counts white referents.
+ */
+static void countWhiteReferenceVisitor(void *addr, void *arg)
+{
+    WhiteReferenceCounter *ctx;
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        return;
+    }
+    assert(dvmIsValidObject(obj));
+    ctx = (WhiteReferenceCounter *)arg;
+    if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
+        return;
+    }
+    ctx->whiteRefs += 1;
+}
+
+/*
+ * Visitor that logs white references.
+ */
+static void dumpWhiteReferenceVisitor(void *addr, void *arg)
+{
+    WhiteReferenceCounter *ctx;
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        return;
+    }
+    assert(dvmIsValidObject(obj));
+    ctx = (WhiteReferenceCounter*)arg;
+    if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
+        return;
+    }
+    ALOGE("object %p is white", obj);
+}
+
+/*
+ * Visitor that signals the caller when a matching reference is found.
+ */
+static void dumpReferencesVisitor(void *pObj, void *arg)
+{
+    Object *obj = *(Object **)pObj;
+    Object *lookingFor = *(Object **)arg;
+    if (lookingFor != NULL && lookingFor == obj) {
+        *(Object **)arg = NULL;
+    }
+}
+
+static void dumpReferencesCallback(Object *obj, void *arg)
+{
+    if (obj == (Object *)arg) {
+        return;
+    }
+    dvmVisitObject(dumpReferencesVisitor, obj, &arg);
+    if (arg == NULL) {
+        ALOGD("Found %p in the heap @ %p", arg, obj);
+        dvmDumpObject(obj);
+    }
+}
+
+/*
+ * Root visitor that looks for matching references.
+ */
+static void dumpReferencesRootVisitor(void *ptr, u4 threadId,
+                                      RootType type, void *arg)
+{
+    Object *obj = *(Object **)ptr;
+    Object *lookingFor = *(Object **)arg;
+    if (obj == lookingFor) {
+        ALOGD("Found %p in a root @ %p", arg, ptr);
+    }
+}
+
+/*
+ * Invokes visitors to search for references to an object.
+ */
+static void dumpReferences(const Object *obj)
+{
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    void *arg = (void *)obj;
+    dvmVisitRoots(dumpReferencesRootVisitor, arg);
+    dvmHeapBitmapWalk(bitmap, dumpReferencesCallback, arg);
+}
+
+/*
+ * Returns true if the given object is a reference object and the
+ * just the referent is unmarked.
+ */
+static bool isReferentUnmarked(const Object *obj,
+                               const WhiteReferenceCounter* ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    if (ctx->whiteRefs != 1) {
+        return false;
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+        size_t offset = gDvm.offJavaLangRefReference_referent;
+        const Object *referent = dvmGetFieldObject(obj, offset);
+        return !dvmHeapBitmapIsObjectBitSet(ctx->markBits, referent);
+    } else {
+        return false;
+    }
+}
+
+/*
+ * Returns true if the given object is a string and has been interned
+ * by the user.
+ */
+static bool isWeakInternedString(const Object *obj)
+{
+    assert(obj != NULL);
+    if (obj->clazz == gDvm.classJavaLangString) {
+        return dvmIsWeakInternedString((StringObject *)obj);
+    } else {
+        return false;
+    }
+}
+
+/*
+ * Returns true if the given object has been pushed on the mark stack
+ * by root marking.
+ */
+static bool isPushedOnMarkStack(const Object *obj)
+{
+    GcMarkStack *stack = &gDvm.gcHeap->markContext.stack;
+    for (const Object **ptr = stack->base; ptr < stack->top; ++ptr) {
+        if (*ptr == obj) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Callback applied to marked objects.  If the object is gray and on
+ * an unmarked card an error is logged and the VM is aborted.  Card
+ * table verification occurs between root marking and weak reference
+ * processing.  We treat objects marked from the roots and weak
+ * references specially as it is permissible for these objects to be
+ * gray and on an unmarked card.
+ */
+static void verifyCardTableCallback(Object *obj, void *arg)
+{
+    WhiteReferenceCounter ctx = { (HeapBitmap *)arg, 0 };
+
+    dvmVisitObject(countWhiteReferenceVisitor, obj, &ctx);
+    if (ctx.whiteRefs == 0) {
+        return;
+    } else if (isObjectDirty(obj)) {
+        return;
+    } else if (isReferentUnmarked(obj, &ctx)) {
+        return;
+    } else if (isWeakInternedString(obj)) {
+        return;
+    } else if (isPushedOnMarkStack(obj)) {
+        return;
+    } else {
+        ALOGE("Verify failed, object %p is gray and on an unmarked card", obj);
+        dvmDumpObject(obj);
+        dvmVisitObject(dumpWhiteReferenceVisitor, obj, &ctx);
+        dumpReferences(obj);
+        dvmAbort();
+    }
+}
+
+/*
+ * Verifies that gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable()
+{
+    HeapBitmap *markBits = gDvm.gcHeap->markContext.bitmap;
+    dvmHeapBitmapWalk(markBits, verifyCardTableCallback, markBits);
+}
diff --git a/vm/alloc/CardTable.h b/vm/alloc/CardTable.h
new file mode 100644
index 0000000..43b0563
--- /dev/null
+++ b/vm/alloc/CardTable.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Maintain a card table from the the write barrier. All writes of
+ * non-NULL values to heap addresses should go through an entry in
+ * WriteBarrier, and from there to here.
+ */
+
+#ifndef DALVIK_ALLOC_CARDTABLE_H_
+#define DALVIK_ALLOC_CARDTABLE_H_
+
+#define GC_CARD_SHIFT 7
+#define GC_CARD_SIZE (1 << GC_CARD_SHIFT)
+#define GC_CARD_CLEAN 0
+#define GC_CARD_DIRTY 0x70
+
+/*
+ * Initializes the card table; must be called before any other
+ * dvmCardTable*() functions.
+ */
+bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit);
+
+/*
+ * Tears down the entire CardTable structure.
+ */
+void dvmCardTableShutdown(void);
+
+/*
+ * Resets all of the bytes in the card table to clean.
+ */
+void dvmClearCardTable(void);
+
+/*
+ * Returns the address of the relevent byte in the card table, given
+ * an address on the heap.
+ */
+u1 *dvmCardFromAddr(const void *addr);
+
+/*
+ * Returns the first address in the heap which maps to this card.
+ */
+void *dvmAddrFromCard(const u1 *card);
+
+/*
+ * Returns true if addr points to a valid card.
+ */
+bool dvmIsValidCard(const u1 *card);
+
+/*
+ * Set the card associated with the given address to GC_CARD_DIRTY.
+ */
+void dvmMarkCard(const void *addr);
+
+/*
+ * Verifies that all gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable(void);
+
+#endif  // DALVIK_ALLOC_CARDTABLE_H_
diff --git a/vm/alloc/Copying.cpp b/vm/alloc/Copying.cpp
new file mode 100644
index 0000000..77cdac3
--- /dev/null
+++ b/vm/alloc/Copying.cpp
@@ -0,0 +1,2235 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Verify.h"
+
+/*
+ * A "mostly copying", generational, garbage collector.
+ *
+ * TODO: we allocate our own contiguous tract of page frames to back
+ * object allocations.  To cooperate with other heaps active in the
+ * virtual machine we need to move the responsibility of allocating
+ * pages someplace outside of this code.
+ *
+ * The other major data structures that maintain the state of the heap
+ * are the block space table and the block queue.
+ *
+ * The block space table records the state of a block.  We must track
+ * whether a block is:
+ *
+ * - Free or allocated in some space.
+ *
+ * - If the block holds part of a large object allocation, whether the
+ *   block is the initial or a continued block of the allocation.
+ *
+ * - Whether the block is pinned, that is to say whether at least one
+ *   object in the block must remain stationary.  Only needed during a
+ *   GC.
+ *
+ * - Which space the object belongs to.  At present this means
+ *   from-space or to-space.
+ *
+ * The block queue is used during garbage collection.  Unlike Cheney's
+ * algorithm, from-space and to-space are not contiguous.  Therefore,
+ * one cannot maintain the state of the copy with just two pointers.
+ * The block queue exists to thread lists of blocks from the various
+ * spaces together.
+ *
+ * Additionally, we record the free space frontier of the heap, as
+ * well as the address of the first object within a block, which is
+ * required to copy objects following a large object (not currently
+ * implemented).  This is stored in the heap source structure.  This
+ * should be moved elsewhere to support in-line allocations from Java
+ * threads.
+ *
+ * Allocation requests are satisfied by reserving storage from one or
+ * more contiguous blocks.  Objects that are small enough to fit
+ * inside a block are packed together within a block.  Objects that
+ * are larger than a block are allocated from contiguous sequences of
+ * blocks.  When half the available blocks are filled, a garbage
+ * collection occurs.  We "flip" spaces (exchange from- and to-space),
+ * copy live objects into to space, and perform pointer adjustment.
+ *
+ * Copying is made more complicated by the requirement that some
+ * objects must not be moved.  This property is known as "pinning".
+ * These objects must be dealt with specially.  We use Bartlett's
+ * scheme; blocks containing such objects are grayed (promoted) at the
+ * start of a garbage collection.  By virtue of this trick, tracing
+ * from the roots proceeds as usual but all objects on those pages are
+ * considered promoted and therefore not moved.
+ *
+ * TODO: there is sufficient information within the garbage collector
+ * to implement Attardi's scheme for evacuating unpinned objects from
+ * a page that is otherwise pinned.  This would eliminate false
+ * retention caused by the large pinning granularity.
+ *
+ * We need a scheme for medium and large objects.  Ignore that for
+ * now, we can return to this later.
+ *
+ * Eventually we need to worry about promoting objects out of the
+ * copy-collected heap (tenuring) into a less volatile space.  Copying
+ * may not always be the best policy for such spaces.  We should
+ * consider a variant of mark, sweep, compact.
+ *
+ * The block scheme allows us to use VM page faults to maintain a
+ * write barrier.  Consider having a special leaf state for a page.
+ *
+ * Bibliography:
+ *
+ * C. J. Cheney. 1970. A non-recursive list compacting
+ * algorithm. CACM. 13-11 pp677--678.
+ *
+ * Joel F. Bartlett. 1988. Compacting Garbage Collection with
+ * Ambiguous Roots. Digital Equipment Corporation.
+ *
+ * Joel F. Bartlett. 1989. Mostly-Copying Garbage Collection Picks Up
+ * Generations and C++. Digital Equipment Corporation.
+ *
+ * G. May Yip. 1991. Incremental, Generational Mostly-Copying Garbage
+ * Collection in Uncooperative Environments. Digital Equipment
+ * Corporation.
+ *
+ * Giuseppe Attardi, Tito Flagella. 1994. A Customisable Memory
+ * Management Framework. TR-94-010
+ *
+ * Giuseppe Attardi, Tito Flagella, Pietro Iglio. 1998. A customisable
+ * memory management framework for C++. Software -- Practice and
+ * Experience. 28(11), 1143-1183.
+ *
+ */
+
+#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
+
+#if 0
+#define LOG_ALLOC ALOGI
+#define LOG_PIN ALOGI
+#define LOG_PROM ALOGI
+#define LOG_REF ALOGI
+#define LOG_SCAV ALOGI
+#define LOG_TRAN ALOGI
+#define LOG_VER ALOGI
+#else
+#define LOG_ALLOC(...) ((void)0)
+#define LOG_PIN(...) ((void)0)
+#define LOG_PROM(...) ((void)0)
+#define LOG_REF(...) ((void)0)
+#define LOG_SCAV(...) ((void)0)
+#define LOG_TRAN(...) ((void)0)
+#define LOG_VER(...) ((void)0)
+#endif
+
+static void enqueueBlock(HeapSource *heapSource, size_t block);
+static void scavengeReference(Object **obj);
+static bool toSpaceContains(const void *addr);
+static bool fromSpaceContains(const void *addr);
+static size_t sumHeapBitmap(const HeapBitmap *bitmap);
+static size_t objectSize(const Object *obj);
+static void scavengeDataObject(Object *obj);
+static void scavengeBlockQueue();
+
+/*
+ * We use 512-byte blocks.
+ */
+enum { BLOCK_SHIFT = 9 };
+enum { BLOCK_SIZE = 1 << BLOCK_SHIFT };
+
+/*
+ * Space identifiers, stored into the blockSpace array.
+ */
+enum {
+    BLOCK_FREE = 0,
+    BLOCK_FROM_SPACE = 1,
+    BLOCK_TO_SPACE = 2,
+    BLOCK_CONTINUED = 7
+};
+
+/*
+ * Alignment for all allocations, in bytes.
+ */
+enum { ALLOC_ALIGNMENT = 8 };
+
+/*
+ * Sentinel value for the queue end.
+ */
+#define QUEUE_TAIL (~(size_t)0)
+
+struct HeapSource {
+
+    /* The base address of backing store. */
+    u1 *blockBase;
+
+    /* Total number of blocks available for allocation. */
+    size_t totalBlocks;
+    size_t allocBlocks;
+
+    /*
+     * The scavenger work queue.  Implemented as an array of index
+     * values into the queue.
+     */
+    size_t *blockQueue;
+
+    /*
+     * Base and limit blocks.  Basically the shifted start address of
+     * the block.  We convert blocks to a relative number when
+     * indexing in the block queue.  TODO: make the block queue base
+     * relative rather than the index into the block queue.
+     */
+    size_t baseBlock, limitBlock;
+
+    size_t queueHead;
+    size_t queueTail;
+    size_t queueSize;
+
+    /* The space of the current block 0 (free), 1 or 2. */
+    char *blockSpace;
+
+    /* Start of free space in the current block. */
+    u1 *allocPtr;
+    /* Exclusive limit of free space in the current block. */
+    u1 *allocLimit;
+
+    HeapBitmap allocBits;
+
+    /*
+     * The starting size of the heap.  This value is the same as the
+     * value provided to the -Xms flag.
+     */
+    size_t minimumSize;
+
+    /*
+     * The maximum size of the heap.  This value is the same as the
+     * -Xmx flag.
+     */
+    size_t maximumSize;
+
+    /*
+     * The current, committed size of the heap.  At present, this is
+     * equivalent to the maximumSize.
+     */
+    size_t currentSize;
+
+    size_t bytesAllocated;
+};
+
+static unsigned long alignDown(unsigned long x, unsigned long n)
+{
+    return x & -n;
+}
+
+static unsigned long alignUp(unsigned long x, unsigned long n)
+{
+    return alignDown(x + (n - 1), n);
+}
+
+static void describeBlocks(const HeapSource *heapSource)
+{
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        if ((i % 32) == 0) putchar('\n');
+        printf("%d ", heapSource->blockSpace[i]);
+    }
+    putchar('\n');
+}
+
+/*
+ * Virtual memory interface.
+ */
+
+static void *virtualAlloc(size_t length)
+{
+    int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+    int prot = PROT_READ | PROT_WRITE;
+    void *addr = mmap(NULL, length, prot, flags, -1, 0);
+    if (addr == MAP_FAILED) {
+        LOGE_HEAP("mmap: %s", strerror(errno));
+        addr = NULL;
+    }
+    return addr;
+}
+
+static void virtualFree(void *addr, size_t length)
+{
+    assert(addr != NULL);
+    assert((uintptr_t)addr % SYSTEM_PAGE_SIZE == 0);
+    int res = munmap(addr, length);
+    if (res == -1) {
+        LOGE_HEAP("munmap: %s", strerror(errno));
+    }
+}
+
+#ifndef NDEBUG
+static int isValidAddress(const HeapSource *heapSource, const u1 *addr)
+{
+    size_t block;
+
+    block = (uintptr_t)addr >> BLOCK_SHIFT;
+    return heapSource->baseBlock <= block &&
+           heapSource->limitBlock > block;
+}
+#endif
+
+/*
+ * Iterate over the block map looking for a contiguous run of free
+ * blocks.
+ */
+static void *allocateBlocks(HeapSource *heapSource, size_t blocks)
+{
+    size_t allocBlocks = heapSource->allocBlocks;
+    size_t totalBlocks = heapSource->totalBlocks;
+    /* Check underflow. */
+    assert(blocks != 0);
+    /* Check overflow. */
+    if (allocBlocks + blocks > totalBlocks / 2) {
+        return NULL;
+    }
+    /* Scan block map. */
+    for (size_t i = 0; i < totalBlocks; ++i) {
+        /* Check fit. */
+        for (size_t j = 0; j < blocks; ++j) { /* runs over totalBlocks */
+            if (heapSource->blockSpace[i+j] != BLOCK_FREE) {
+                break;
+            }
+        }
+        /* No fit? */
+        if (j != blocks) {
+            i += j;
+            continue;
+        }
+        /* Fit, allocate. */
+        heapSource->blockSpace[i] = BLOCK_TO_SPACE; /* why to-space? */
+        for (size_t j = 1; j < blocks; ++j) {
+            heapSource->blockSpace[i+j] = BLOCK_CONTINUED;
+        }
+        heapSource->allocBlocks += blocks;
+        void *addr = &heapSource->blockBase[i*BLOCK_SIZE];
+        memset(addr, 0, blocks*BLOCK_SIZE);
+        /* Collecting? */
+        if (heapSource->queueHead != QUEUE_TAIL) {
+            LOG_ALLOC("allocateBlocks allocBlocks=%zu,block#=%zu", heapSource->allocBlocks, i);
+            /*
+             * This allocated was on behalf of the transporter when it
+             * shaded a white object gray.  We enqueue the block so
+             * the scavenger can further shade the gray objects black.
+             */
+            enqueueBlock(heapSource, i);
+        }
+
+        return addr;
+    }
+    /* Insufficient space, fail. */
+    ALOGE("Insufficient space, %zu blocks, %zu blocks allocated and %zu bytes allocated",
+         heapSource->totalBlocks,
+         heapSource->allocBlocks,
+         heapSource->bytesAllocated);
+    return NULL;
+}
+
+/* Converts an absolute address to a relative block number. */
+static size_t addressToBlock(const HeapSource *heapSource, const void *addr)
+{
+    assert(heapSource != NULL);
+    assert(isValidAddress(heapSource, addr));
+    return (((uintptr_t)addr) >> BLOCK_SHIFT) - heapSource->baseBlock;
+}
+
+/* Converts a relative block number to an absolute address. */
+static u1 *blockToAddress(const HeapSource *heapSource, size_t block)
+{
+    u1 *addr;
+
+    addr = (u1 *) (((uintptr_t) heapSource->baseBlock + block) * BLOCK_SIZE);
+    assert(isValidAddress(heapSource, addr));
+    return addr;
+}
+
+static void clearBlock(HeapSource *heapSource, size_t block)
+{
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    u1 *addr = heapSource->blockBase + block*BLOCK_SIZE;
+    memset(addr, 0xCC, BLOCK_SIZE);
+    for (size_t i = 0; i < BLOCK_SIZE; i += 8) {
+        dvmHeapBitmapClearObjectBit(&heapSource->allocBits, addr + i);
+    }
+}
+
+static void clearFromSpace(HeapSource *heapSource)
+{
+    assert(heapSource != NULL);
+    size_t i = 0;
+    size_t count = 0;
+    while (i < heapSource->totalBlocks) {
+        if (heapSource->blockSpace[i] != BLOCK_FROM_SPACE) {
+            ++i;
+            continue;
+        }
+        heapSource->blockSpace[i] = BLOCK_FREE;
+        clearBlock(heapSource, i);
+        ++i;
+        ++count;
+        while (i < heapSource->totalBlocks &&
+               heapSource->blockSpace[i] == BLOCK_CONTINUED) {
+            heapSource->blockSpace[i] = BLOCK_FREE;
+            clearBlock(heapSource, i);
+            ++i;
+            ++count;
+        }
+    }
+    LOG_SCAV("freed %zu blocks (%zu bytes)", count, count*BLOCK_SIZE);
+}
+
+/*
+ * Appends the given block to the block queue.  The block queue is
+ * processed in-order by the scavenger.
+ */
+static void enqueueBlock(HeapSource *heapSource, size_t block)
+{
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    if (heapSource->queueHead != QUEUE_TAIL) {
+        heapSource->blockQueue[heapSource->queueTail] = block;
+    } else {
+        heapSource->queueHead = block;
+    }
+    heapSource->blockQueue[block] = QUEUE_TAIL;
+    heapSource->queueTail = block;
+    ++heapSource->queueSize;
+}
+
+/*
+ * Grays all objects within the block corresponding to the given
+ * address.
+ */
+static void promoteBlockByAddr(HeapSource *heapSource, const void *addr)
+{
+    size_t block;
+
+    block = addressToBlock(heapSource, (const u1 *)addr);
+    if (heapSource->blockSpace[block] != BLOCK_TO_SPACE) {
+        // LOG_PROM("promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
+        heapSource->blockSpace[block] = BLOCK_TO_SPACE;
+        enqueueBlock(heapSource, block);
+        /* TODO(cshapiro): count continued blocks?*/
+        heapSource->allocBlocks += 1;
+    } else {
+        // LOG_PROM("NOT promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
+    }
+}
+
+GcHeap *dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
+{
+    GcHeap* gcHeap;
+    HeapSource *heapSource;
+
+    assert(startSize <= absoluteMaxSize);
+
+    heapSource = calloc(1, sizeof(*heapSource));
+    assert(heapSource != NULL);
+
+    heapSource->minimumSize = alignUp(startSize, BLOCK_SIZE);
+    heapSource->maximumSize = alignUp(absoluteMaxSize, BLOCK_SIZE);
+
+    heapSource->currentSize = heapSource->maximumSize;
+
+    /* Allocate underlying storage for blocks. */
+    heapSource->blockBase = virtualAlloc(heapSource->maximumSize);
+    assert(heapSource->blockBase != NULL);
+    heapSource->baseBlock = (uintptr_t) heapSource->blockBase >> BLOCK_SHIFT;
+    heapSource->limitBlock = ((uintptr_t) heapSource->blockBase + heapSource->maximumSize) >> BLOCK_SHIFT;
+
+    heapSource->allocBlocks = 0;
+    heapSource->totalBlocks = (heapSource->limitBlock - heapSource->baseBlock);
+
+    assert(heapSource->totalBlocks = heapSource->maximumSize / BLOCK_SIZE);
+
+    {
+        size_t size = sizeof(heapSource->blockQueue[0]);
+        heapSource->blockQueue = malloc(heapSource->totalBlocks*size);
+        assert(heapSource->blockQueue != NULL);
+        memset(heapSource->blockQueue, 0xCC, heapSource->totalBlocks*size);
+        heapSource->queueHead = QUEUE_TAIL;
+    }
+
+    /* Byte indicating space residence or free status of block. */
+    {
+        size_t size = sizeof(heapSource->blockSpace[0]);
+        heapSource->blockSpace = calloc(1, heapSource->totalBlocks*size);
+        assert(heapSource->blockSpace != NULL);
+    }
+
+    dvmHeapBitmapInit(&heapSource->allocBits,
+                      heapSource->blockBase,
+                      heapSource->maximumSize,
+                      "blockBase");
+
+    /* Initialize allocation pointers. */
+    heapSource->allocPtr = allocateBlocks(heapSource, 1);
+    heapSource->allocLimit = heapSource->allocPtr + BLOCK_SIZE;
+
+    gcHeap = calloc(1, sizeof(*gcHeap));
+    assert(gcHeap != NULL);
+    gcHeap->heapSource = heapSource;
+
+    return gcHeap;
+}
+
+/*
+ * Perform any required heap initializations after forking from the
+ * zygote process.  This is a no-op for the time being.  Eventually
+ * this will demarcate the shared region of the heap.
+ */
+bool dvmHeapSourceStartupAfterZygote()
+{
+    return true;
+}
+
+bool dvmHeapSourceStartupBeforeFork()
+{
+    assert(!"implemented");
+    return false;
+}
+
+void dvmHeapSourceShutdown(GcHeap **gcHeap)
+{
+    if (*gcHeap == NULL || (*gcHeap)->heapSource == NULL)
+        return;
+    free((*gcHeap)->heapSource->blockQueue);
+    free((*gcHeap)->heapSource->blockSpace);
+    virtualFree((*gcHeap)->heapSource->blockBase,
+                (*gcHeap)->heapSource->maximumSize);
+    free((*gcHeap)->heapSource);
+    (*gcHeap)->heapSource = NULL;
+    free(*gcHeap);
+    *gcHeap = NULL;
+}
+
+size_t dvmHeapSourceGetValue(HeapSourceValueSpec spec,
+                             size_t perHeapStats[],
+                             size_t arrayLen)
+{
+    HeapSource *heapSource;
+    size_t value;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    switch (spec) {
+    case HS_FOOTPRINT:
+        value = heapSource->maximumSize;
+        break;
+    case HS_ALLOWED_FOOTPRINT:
+        value = heapSource->maximumSize;
+        break;
+    case HS_BYTES_ALLOCATED:
+        value = heapSource->bytesAllocated;
+        break;
+    case HS_OBJECTS_ALLOCATED:
+        value = sumHeapBitmap(&heapSource->allocBits);
+        break;
+    default:
+        assert(!"implemented");
+        value = 0;
+    }
+    if (perHeapStats) {
+        *perHeapStats = value;
+    }
+    return value;
+}
+
+/*
+ * Performs a shallow copy of the allocation bitmap into the given
+ * vector of heap bitmaps.
+ */
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap objBits[], HeapBitmap markBits[],
+                                   size_t numHeaps)
+{
+    assert(!"implemented");
+}
+
+HeapBitmap *dvmHeapSourceGetLiveBits()
+{
+    return &gDvm.gcHeap->heapSource->allocBits;
+}
+
+/*
+ * Allocate the specified number of bytes from the heap.  The
+ * allocation cursor points into a block of free storage.  If the
+ * given allocation fits in the remaining space of the block, we
+ * advance the cursor and return a pointer to the free storage.  If
+ * the allocation cannot fit in the current block but is smaller than
+ * a block we request a new block and allocate from it instead.  If
+ * the allocation is larger than a block we must allocate from a span
+ * of contiguous blocks.
+ */
+void *dvmHeapSourceAlloc(size_t length)
+{
+    HeapSource *heapSource;
+    unsigned char *addr;
+    size_t aligned, available, blocks;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    assert(heapSource != NULL);
+    assert(heapSource->allocPtr != NULL);
+    assert(heapSource->allocLimit != NULL);
+
+    aligned = alignUp(length, ALLOC_ALIGNMENT);
+    available = heapSource->allocLimit - heapSource->allocPtr;
+
+    /* Try allocating inside the current block. */
+    if (aligned <= available) {
+        addr = heapSource->allocPtr;
+        heapSource->allocPtr += aligned;
+        heapSource->bytesAllocated += aligned;
+        dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+        return addr;
+    }
+
+    /* Try allocating in a new block. */
+    if (aligned <= BLOCK_SIZE) {
+        addr =  allocateBlocks(heapSource, 1);
+        if (addr != NULL) {
+            heapSource->allocLimit = addr + BLOCK_SIZE;
+            heapSource->allocPtr = addr + aligned;
+            heapSource->bytesAllocated += aligned;
+            dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+            /* TODO(cshapiro): pad out the current block. */
+        }
+        return addr;
+    }
+
+    /* Try allocating in a span of blocks. */
+    blocks = alignUp(aligned, BLOCK_SIZE) / BLOCK_SIZE;
+
+    addr = allocateBlocks(heapSource, blocks);
+    /* Propagate failure upward. */
+    if (addr != NULL) {
+        heapSource->bytesAllocated += aligned;
+        dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+        /* TODO(cshapiro): pad out free space in the last block. */
+    }
+    return addr;
+}
+
+void *dvmHeapSourceAllocAndGrow(size_t size)
+{
+    return dvmHeapSourceAlloc(size);
+}
+
+/* TODO: refactor along with dvmHeapSourceAlloc */
+void *allocateGray(size_t size)
+{
+    HeapSource *heapSource;
+    void *addr;
+    size_t block;
+
+    /* TODO: add a check that we are in a GC. */
+    heapSource = gDvm.gcHeap->heapSource;
+    addr = dvmHeapSourceAlloc(size);
+    assert(addr != NULL);
+    block = addressToBlock(heapSource, (const u1 *)addr);
+    if (heapSource->queueHead == QUEUE_TAIL) {
+        /*
+         * Forcibly append the underlying block to the queue.  This
+         * condition occurs when referents are transported following
+         * the initial trace.
+         */
+        enqueueBlock(heapSource, block);
+        LOG_PROM("forced promoting block %zu %d @ %p", block, heapSource->blockSpace[block], addr);
+    }
+    return addr;
+}
+
+bool dvmHeapSourceContainsAddress(const void *ptr)
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    return dvmHeapBitmapCoversAddress(&heapSource->allocBits, ptr);
+}
+
+/*
+ * Returns true if the given address is within the heap and points to
+ * the header of a live object.
+ */
+bool dvmHeapSourceContains(const void *addr)
+{
+    HeapSource *heapSource;
+    HeapBitmap *bitmap;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    bitmap = &heapSource->allocBits;
+    if (!dvmHeapBitmapCoversAddress(bitmap, addr)) {
+        return false;
+    } else {
+        return dvmHeapBitmapIsObjectBitSet(bitmap, addr);
+    }
+}
+
+bool dvmHeapSourceGetPtrFlag(const void *ptr, HeapSourcePtrFlag flag)
+{
+    assert(!"implemented");
+    return false;
+}
+
+size_t dvmHeapSourceChunkSize(const void *ptr)
+{
+    assert(!"implemented");
+    return 0;
+}
+
+size_t dvmHeapSourceFootprint()
+{
+    assert(!"implemented");
+    return 0;
+}
+
+/*
+ * Returns the "ideal footprint" which appears to be the number of
+ * bytes currently committed to the heap.  This starts out at the
+ * start size of the heap and grows toward the maximum size.
+ */
+size_t dvmHeapSourceGetIdealFootprint()
+{
+    return gDvm.gcHeap->heapSource->currentSize;
+}
+
+float dvmGetTargetHeapUtilization()
+{
+    return 0.5f;
+}
+
+void dvmSetTargetHeapUtilization(float newTarget)
+{
+    assert(newTarget > 0.0f && newTarget < 1.0f);
+}
+
+/*
+ * Expands the size of the heap after a collection.  At present we
+ * commit the pages for maximum size of the heap so this routine is
+ * just a no-op.  Eventually, we will either allocate or commit pages
+ * on an as-need basis.
+ */
+void dvmHeapSourceGrowForUtilization()
+{
+    /* do nothing */
+}
+
+void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
+                                       size_t used_bytes, void* arg),
+                       void *arg)
+{
+    assert(!"implemented");
+}
+
+size_t dvmHeapSourceGetNumHeaps()
+{
+    return 1;
+}
+
+bool dvmTrackExternalAllocation(size_t n)
+{
+    /* do nothing */
+    return true;
+}
+
+void dvmTrackExternalFree(size_t n)
+{
+    /* do nothing */
+}
+
+size_t dvmGetExternalBytesAllocated()
+{
+    assert(!"implemented");
+    return 0;
+}
+
+void dvmHeapSourceFlip()
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+
+    /* Reset the block queue. */
+    heapSource->allocBlocks = 0;
+    heapSource->queueSize = 0;
+    heapSource->queueHead = QUEUE_TAIL;
+
+    /* TODO(cshapiro): pad the current (prev) block. */
+
+    heapSource->allocPtr = NULL;
+    heapSource->allocLimit = NULL;
+
+    /* Whiten all allocated blocks. */
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        if (heapSource->blockSpace[i] == BLOCK_TO_SPACE) {
+            heapSource->blockSpace[i] = BLOCK_FROM_SPACE;
+        }
+    }
+}
+
+static void room(size_t *alloc, size_t *avail, size_t *total)
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    *total = heapSource->totalBlocks*BLOCK_SIZE;
+    *alloc = heapSource->allocBlocks*BLOCK_SIZE;
+    *avail = *total - *alloc;
+}
+
+static bool isSpaceInternal(u1 *addr, int space)
+{
+    HeapSource *heapSource;
+    u1 *base, *limit;
+    size_t offset;
+    char space2;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    base = heapSource->blockBase;
+    assert(addr >= base);
+    limit = heapSource->blockBase + heapSource->maximumSize;
+    assert(addr < limit);
+    offset = addr - base;
+    space2 = heapSource->blockSpace[offset >> BLOCK_SHIFT];
+    return space == space2;
+}
+
+static bool fromSpaceContains(const void *addr)
+{
+    return isSpaceInternal((u1 *)addr, BLOCK_FROM_SPACE);
+}
+
+static bool toSpaceContains(const void *addr)
+{
+    return isSpaceInternal((u1 *)addr, BLOCK_TO_SPACE);
+}
+
+/*
+ * Notifies the collector that the object at the given address must
+ * remain stationary during the current collection.
+ */
+static void pinObject(const Object *obj)
+{
+    promoteBlockByAddr(gDvm.gcHeap->heapSource, obj);
+}
+
+static size_t sumHeapBitmap(const HeapBitmap *bitmap)
+{
+    size_t sum = 0;
+    for (size_t i = 0; i < bitmap->bitsLen >> 2; ++i) {
+        sum += CLZ(bitmap->bits[i]);
+    }
+    return sum;
+}
+
+/*
+ * Miscellaneous functionality.
+ */
+
+static int isForward(const void *addr)
+{
+    return (uintptr_t)addr & 0x1;
+}
+
+static void setForward(const void *toObj, void *fromObj)
+{
+    *(unsigned long *)fromObj = (uintptr_t)toObj | 0x1;
+}
+
+static void* getForward(const void *fromObj)
+{
+    return (void *)((uintptr_t)fromObj & ~0x1);
+}
+
+/* Beware, uses the same encoding as a forwarding pointers! */
+static int isPermanentString(const StringObject *obj) {
+    return (uintptr_t)obj & 0x1;
+}
+
+static void* getPermanentString(const StringObject *obj)
+{
+    return (void *)((uintptr_t)obj & ~0x1);
+}
+
+
+/*
+ * Scavenging and transporting routines follow.  A transporter grays
+ * an object.  A scavenger blackens an object.  We define these
+ * routines for each fundamental object type.  Dispatch is performed
+ * in scavengeObject.
+ */
+
+/*
+ * Class object scavenging.
+ */
+static void scavengeClassObject(ClassObject *obj)
+{
+    LOG_SCAV("scavengeClassObject(obj=%p)", obj);
+    assert(obj != NULL);
+    assert(obj->obj.clazz != NULL);
+    assert(obj->obj.clazz->descriptor != NULL);
+    assert(!strcmp(obj->obj.clazz->descriptor, "Ljava/lang/Class;"));
+    assert(obj->descriptor != NULL);
+    LOG_SCAV("scavengeClassObject: descriptor='%s',vtableCount=%zu",
+             obj->descriptor, obj->vtableCount);
+    /* Delegate class object and instance field scavenging. */
+    scavengeDataObject((Object *)obj);
+    /* Scavenge the array element class object. */
+    if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
+        scavengeReference((Object **)(void *)&obj->elementClass);
+    }
+    /* Scavenge the superclass. */
+    scavengeReference((Object **)(void *)&obj->super);
+    /* Scavenge the class loader. */
+    scavengeReference(&obj->classLoader);
+    /* Scavenge static fields. */
+    for (int i = 0; i < obj->sfieldCount; ++i) {
+        char ch = obj->sfields[i].field.signature[0];
+        if (ch == '[' || ch == 'L') {
+            scavengeReference((Object **)(void *)&obj->sfields[i].value.l);
+        }
+    }
+    /* Scavenge interface class objects. */
+    for (int i = 0; i < obj->interfaceCount; ++i) {
+        scavengeReference((Object **) &obj->interfaces[i]);
+    }
+}
+
+/*
+ * Array object scavenging.
+ */
+static size_t scavengeArrayObject(ArrayObject *array)
+{
+    LOG_SCAV("scavengeArrayObject(array=%p)", array);
+    /* Scavenge the class object. */
+    assert(toSpaceContains(array));
+    assert(array != NULL);
+    assert(array->obj.clazz != NULL);
+    scavengeReference((Object **) array);
+    size_t length = dvmArrayObjectSize(array);
+    /* Scavenge the array contents. */
+    if (IS_CLASS_FLAG_SET(array->obj.clazz, CLASS_ISOBJECTARRAY)) {
+        Object **contents = (Object **)array->contents;
+        for (size_t i = 0; i < array->length; ++i) {
+            scavengeReference(&contents[i]);
+        }
+    }
+    return length;
+}
+
+/*
+ * Reference object scavenging.
+ */
+
+static int getReferenceFlags(const Object *obj)
+{
+    int flags;
+
+    flags = CLASS_ISREFERENCE |
+            CLASS_ISWEAKREFERENCE |
+            CLASS_ISPHANTOMREFERENCE;
+    return GET_CLASS_FLAG_GROUP(obj->clazz, flags);
+}
+
+static int isSoftReference(const Object *obj)
+{
+    return getReferenceFlags(obj) == CLASS_ISREFERENCE;
+}
+
+static int isWeakReference(const Object *obj)
+{
+    return getReferenceFlags(obj) & CLASS_ISWEAKREFERENCE;
+}
+
+#ifndef NDEBUG
+static bool isPhantomReference(const Object *obj)
+{
+    return getReferenceFlags(obj) & CLASS_ISPHANTOMREFERENCE;
+}
+#endif
+
+/*
+ * Returns true if the reference was registered with a reference queue
+ * but has not yet been appended to it.
+ */
+static bool isReferenceEnqueuable(const Object *ref)
+{
+    Object *queue, *queueNext;
+
+    queue = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue);
+    queueNext = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext);
+    if (queue == NULL || queueNext != NULL) {
+        /*
+         * There is no queue, or the reference has already
+         * been enqueued.  The Reference.enqueue() method
+         * will do nothing even if we call it.
+         */
+        return false;
+    }
+
+    /*
+     * We need to call enqueue(), but if we called it from
+     * here we'd probably deadlock.  Schedule a call.
+     */
+    return true;
+}
+
+/*
+ * Schedules a reference to be appended to its reference queue.
+ */
+static void enqueueReference(Object *ref)
+{
+    assert(ref != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+    if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
+        ALOGE("no room for any more reference operations");
+        dvmAbort();
+    }
+}
+
+/*
+ * Sets the referent field of a reference object to NULL.
+ */
+static void clearReference(Object *obj)
+{
+    dvmSetFieldObject(obj, gDvm.offJavaLangRefReference_referent, NULL);
+}
+
+/*
+ * Clears reference objects with white referents.
+ */
+void clearWhiteReferences(Object **list)
+{
+    size_t referentOffset, queueNextOffset;
+    bool doSignal;
+
+    queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    doSignal = false;
+    while (*list != NULL) {
+        Object *ref = *list;
+        JValue *field = dvmFieldPtr(ref, referentOffset);
+        Object *referent = field->l;
+        *list = dvmGetFieldObject(ref, queueNextOffset);
+        dvmSetFieldObject(ref, queueNextOffset, NULL);
+        assert(referent != NULL);
+        if (isForward(referent->clazz)) {
+            field->l = referent = getForward(referent->clazz);
+            continue;
+        }
+        if (fromSpaceContains(referent)) {
+            /* Referent is white, clear it. */
+            clearReference(ref);
+            if (isReferenceEnqueuable(ref)) {
+                enqueueReference(ref);
+                doSignal = true;
+            }
+        }
+    }
+    /*
+     * If we cleared a reference with a reference queue we must notify
+     * the heap worker to append the reference.
+     */
+    if (doSignal) {
+        dvmSignalHeapWorker(false);
+    }
+    assert(*list == NULL);
+}
+
+/*
+ * Blackens referents subject to the soft reference preservation
+ * policy.
+ */
+void preserveSoftReferences(Object **list)
+{
+    Object *ref;
+    Object *prev, *next;
+    size_t referentOffset, queueNextOffset;
+    unsigned counter;
+    bool white;
+
+    queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    counter = 0;
+    prev = next = NULL;
+    ref = *list;
+    while (ref != NULL) {
+        JValue *field = dvmFieldPtr(ref, referentOffset);
+        Object *referent = field->l;
+        next = dvmGetFieldObject(ref, queueNextOffset);
+        assert(referent != NULL);
+        if (isForward(referent->clazz)) {
+            /* Referent is black. */
+            field->l = referent = getForward(referent->clazz);
+            white = false;
+        } else {
+            white = fromSpaceContains(referent);
+        }
+        if (!white && ((++counter) & 1)) {
+            /* Referent is white and biased toward saving, gray it. */
+            scavengeReference((Object **)(void *)&field->l);
+            white = true;
+        }
+        if (white) {
+            /* Referent is black, unlink it. */
+            if (prev != NULL) {
+                dvmSetFieldObject(ref, queueNextOffset, NULL);
+                dvmSetFieldObject(prev, queueNextOffset, next);
+            }
+        } else {
+            /* Referent is white, skip over it. */
+            prev = ref;
+        }
+        ref = next;
+    }
+    /*
+     * Restart the trace with the newly gray references added to the
+     * root set.
+     */
+    scavengeBlockQueue();
+}
+
+void processFinalizableReferences()
+{
+    HeapRefTable newPendingRefs;
+    LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
+    Object **ref;
+    Object **lastRef;
+    size_t totalPendCount;
+
+    /*
+     * All strongly, reachable objects are black.
+     * Any white finalizable objects need to be finalized.
+     */
+
+    /* Create a table that the new pending refs will
+     * be added to.
+     */
+    if (!dvmHeapInitHeapRefTable(&newPendingRefs)) {
+        //TODO: mark all finalizable refs and hope that
+        //      we can schedule them next time.  Watch out,
+        //      because we may be expecting to free up space
+        //      by calling finalizers.
+        LOG_REF("no room for pending finalizations");
+        dvmAbort();
+    }
+
+    /*
+     * Walk through finalizableRefs and move any white references to
+     * the list of new pending refs.
+     */
+    totalPendCount = 0;
+    while (finRefs != NULL) {
+        Object **gapRef;
+        size_t newPendCount = 0;
+
+        gapRef = ref = finRefs->refs.table;
+        lastRef = finRefs->refs.nextEntry;
+        while (ref < lastRef) {
+            if (fromSpaceContains(*ref)) {
+                if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
+                    //TODO: add the current table and allocate
+                    //      a new, smaller one.
+                    LOG_REF("no room for any more pending finalizations: %zd",
+                            dvmHeapNumHeapRefTableEntries(&newPendingRefs));
+                    dvmAbort();
+                }
+                newPendCount++;
+            } else {
+                /* This ref is black, so will remain on finalizableRefs.
+                 */
+                if (newPendCount > 0) {
+                    /* Copy it up to fill the holes.
+                     */
+                    *gapRef++ = *ref;
+                } else {
+                    /* No holes yet; don't bother copying.
+                     */
+                    gapRef++;
+                }
+            }
+            ref++;
+        }
+        finRefs->refs.nextEntry = gapRef;
+        //TODO: if the table is empty when we're done, free it.
+        totalPendCount += newPendCount;
+        finRefs = finRefs->next;
+    }
+    LOG_REF("%zd finalizers triggered.", totalPendCount);
+    if (totalPendCount == 0) {
+        /* No objects required finalization.
+         * Free the empty temporary table.
+         */
+        dvmClearReferenceTable(&newPendingRefs);
+        return;
+    }
+
+    /* Add the new pending refs to the main list.
+     */
+    if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs,
+                &newPendingRefs))
+    {
+        LOG_REF("can't insert new pending finalizations");
+        dvmAbort();
+    }
+
+    //TODO: try compacting the main list with a memcpy loop
+
+    /* Blacken the refs we just moved; we don't want them or their
+     * children to get swept yet.
+     */
+    ref = newPendingRefs.table;
+    lastRef = newPendingRefs.nextEntry;
+    assert(ref < lastRef);
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
+    while (ref < lastRef) {
+        scavengeReference(ref);
+        ref++;
+    }
+    HPROF_CLEAR_GC_SCAN_STATE();
+    scavengeBlockQueue();
+    dvmSignalHeapWorker(false);
+}
+
+/*
+ * If a reference points to from-space and has been forwarded, we snap
+ * the pointer to its new to-space address.  If the reference points
+ * to an unforwarded from-space address we must enqueue the reference
+ * for later processing.  TODO: implement proper reference processing
+ * and move the referent scavenging elsewhere.
+ */
+static void scavengeReferenceObject(Object *obj)
+{
+    Object *referent;
+    Object **queue;
+    size_t referentOffset, queueNextOffset;
+
+    assert(obj != NULL);
+    LOG_SCAV("scavengeReferenceObject(obj=%p),'%s'", obj, obj->clazz->descriptor);
+    scavengeDataObject(obj);
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    referent = dvmGetFieldObject(obj, referentOffset);
+    if (referent == NULL || toSpaceContains(referent)) {
+        return;
+    }
+    if (isSoftReference(obj)) {
+        queue = &gDvm.gcHeap->softReferences;
+    } else if (isWeakReference(obj)) {
+        queue = &gDvm.gcHeap->weakReferences;
+    } else {
+        assert(isPhantomReference(obj));
+        queue = &gDvm.gcHeap->phantomReferences;
+    }
+    queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+    dvmSetFieldObject(obj, queueNextOffset, *queue);
+    *queue = obj;
+    LOG_SCAV("scavengeReferenceObject: enqueueing %p", obj);
+}
+
+/*
+ * Data object scavenging.
+ */
+static void scavengeDataObject(Object *obj)
+{
+    // LOG_SCAV("scavengeDataObject(obj=%p)", obj);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(obj->clazz->objectSize != 0);
+    assert(toSpaceContains(obj));
+    /* Scavenge the class object. */
+    ClassObject *clazz = obj->clazz;
+    scavengeReference((Object **) obj);
+    /* Scavenge instance fields. */
+    if (clazz->refOffsets != CLASS_WALK_SUPER) {
+        size_t refOffsets = clazz->refOffsets;
+        while (refOffsets != 0) {
+            size_t rshift = CLZ(refOffsets);
+            size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+            Object **ref = (Object **)((u1 *)obj + offset);
+            scavengeReference(ref);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+        }
+    } else {
+        for (; clazz != NULL; clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            for (int i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                size_t offset = field->byteOffset;
+                Object **ref = (Object **)((u1 *)obj + offset);
+                scavengeReference(ref);
+            }
+        }
+    }
+}
+
+static Object *transportObject(const Object *fromObj)
+{
+    Object *toObj;
+    size_t allocSize, copySize;
+
+    LOG_TRAN("transportObject(fromObj=%p) allocBlocks=%zu",
+                  fromObj,
+                  gDvm.gcHeap->heapSource->allocBlocks);
+    assert(fromObj != NULL);
+    assert(fromSpaceContains(fromObj));
+    allocSize = copySize = objectSize(fromObj);
+    if (LW_HASH_STATE(fromObj->lock) != LW_HASH_STATE_UNHASHED) {
+        /*
+         * The object has been hashed or hashed and moved.  We must
+         * reserve an additional word for a hash code.
+         */
+        allocSize += sizeof(u4);
+    }
+    if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
+        /*
+         * The object has its hash code allocated.  Ensure the hash
+         * code is copied along with the instance data.
+         */
+        copySize += sizeof(u4);
+    }
+    /* TODO(cshapiro): don't copy, re-map large data objects. */
+    assert(copySize <= allocSize);
+    toObj = allocateGray(allocSize);
+    assert(toObj != NULL);
+    assert(toSpaceContains(toObj));
+    memcpy(toObj, fromObj, copySize);
+    if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED) {
+        /*
+         * The object has had its hash code exposed.  Append it to the
+         * instance and set a bit so we know to look for it there.
+         */
+        *(u4 *)(((char *)toObj) + copySize) = (u4)fromObj >> 3;
+        toObj->lock |= LW_HASH_STATE_HASHED_AND_MOVED << LW_HASH_STATE_SHIFT;
+    }
+    LOG_TRAN("transportObject: from %p/%zu to %p/%zu (%zu,%zu) %s",
+             fromObj, addressToBlock(gDvm.gcHeap->heapSource,fromObj),
+             toObj, addressToBlock(gDvm.gcHeap->heapSource,toObj),
+             copySize, allocSize, copySize < allocSize ? "DIFFERENT" : "");
+    return toObj;
+}
+
+/*
+ * Generic reference scavenging.
+ */
+
+/*
+ * Given a reference to an object, the scavenge routine will gray the
+ * reference.  Any objects pointed to by the scavenger object will be
+ * transported to new space and a forwarding pointer will be installed
+ * in the header of the object.
+ */
+
+/*
+ * Blacken the given pointer.  If the pointer is in from space, it is
+ * transported to new space.  If the object has a forwarding pointer
+ * installed it has already been transported and the referent is
+ * snapped to the new address.
+ */
+static void scavengeReference(Object **obj)
+{
+    ClassObject *clazz;
+    Object *fromObj, *toObj;
+
+    assert(obj);
+
+    if (*obj == NULL) return;
+
+    assert(dvmIsValidObject(*obj));
+
+    /* The entire block is black. */
+    if (toSpaceContains(*obj)) {
+        LOG_SCAV("scavengeReference skipping pinned object @ %p", *obj);
+        return;
+    }
+    LOG_SCAV("scavengeReference(*obj=%p)", *obj);
+
+    assert(fromSpaceContains(*obj));
+
+    clazz = (*obj)->clazz;
+
+    if (isForward(clazz)) {
+        // LOG_SCAV("forwarding %p @ %p to %p", *obj, obj, (void *)((uintptr_t)clazz & ~0x1));
+        *obj = (Object *)getForward(clazz);
+        return;
+    }
+    fromObj = *obj;
+    if (clazz == NULL) {
+        // LOG_SCAV("scavangeReference %p has a NULL class object", fromObj);
+        assert(!"implemented");
+        toObj = NULL;
+    } else {
+        toObj = transportObject(fromObj);
+    }
+    setForward(toObj, fromObj);
+    *obj = (Object *)toObj;
+}
+
+/*
+ * Generic object scavenging.
+ */
+static void scavengeObject(Object *obj)
+{
+    ClassObject *clazz;
+
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(!((uintptr_t)obj->clazz & 0x1));
+    clazz = obj->clazz;
+    if (dvmIsTheClassClass(clazz)) {
+        scavengeClassObject((ClassObject *)obj);
+    } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+        scavengeArrayObject((ArrayObject *)obj);
+    } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) {
+        scavengeReferenceObject(obj);
+    } else {
+        scavengeDataObject(obj);
+    }
+}
+
+/*
+ * External root scavenging routines.
+ */
+
+static void pinHashTableEntries(HashTable *table)
+{
+    LOG_PIN(">>> pinHashTableEntries(table=%p)", table);
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        void *obj = entry->data;
+        if (obj == NULL || obj == HASH_TOMBSTONE) {
+            continue;
+        }
+        pinObject(entry->data);
+    }
+    dvmHashTableUnlock(table);
+    LOG_PIN("<<< pinHashTableEntries(table=%p)", table);
+}
+
+static void pinPrimitiveClasses()
+{
+    size_t length = ARRAYSIZE(gDvm.primitiveClass);
+    for (size_t i = 0; i < length; i++) {
+        if (gDvm.primitiveClass[i] != NULL) {
+            pinObject((Object *)gDvm.primitiveClass[i]);
+        }
+    }
+}
+
+/*
+ * Scavenge interned strings.  Permanent interned strings will have
+ * been pinned and are therefore ignored.  Non-permanent strings that
+ * have been forwarded are snapped.  All other entries are removed.
+ */
+static void scavengeInternedStrings()
+{
+    HashTable *table = gDvm.internedStrings;
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        Object *obj = (Object *)entry->data;
+        if (obj == NULL || obj == HASH_TOMBSTONE) {
+            continue;
+        } else if (!isPermanentString((StringObject *)obj)) {
+            // LOG_SCAV("entry->data=%p", entry->data);
+            LOG_SCAV(">>> string obj=%p", entry->data);
+            /* TODO(cshapiro): detach white string objects */
+            scavengeReference((Object **)(void *)&entry->data);
+            LOG_SCAV("<<< string obj=%p", entry->data);
+        }
+    }
+    dvmHashTableUnlock(table);
+}
+
+static void pinInternedStrings()
+{
+    HashTable *table = gDvm.internedStrings;
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        Object *obj = (Object *)entry->data;
+        if (obj == NULL || obj == HASH_TOMBSTONE) {
+            continue;
+        } else if (isPermanentString((StringObject *)obj)) {
+            obj = (Object *)getPermanentString((StringObject*)obj);
+            LOG_PROM(">>> pin string obj=%p", obj);
+            pinObject(obj);
+            LOG_PROM("<<< pin string obj=%p", obj);
+        }
+     }
+    dvmHashTableUnlock(table);
+}
+
+/*
+ * At present, reference tables contain references that must not be
+ * moved by the collector.  Instead of scavenging each reference in
+ * the table we pin each referenced object.
+ */
+static void pinReferenceTable(const ReferenceTable *table)
+{
+    assert(table != NULL);
+    assert(table->table != NULL);
+    assert(table->nextEntry != NULL);
+    for (Object **entry = table->table; entry < table->nextEntry; ++entry) {
+        assert(entry != NULL);
+        assert(!isForward(*entry));
+        pinObject(*entry);
+    }
+}
+
+static void scavengeLargeHeapRefTable(LargeHeapRefTable *table)
+{
+    for (; table != NULL; table = table->next) {
+        Object **ref = table->refs.table;
+        for (; ref < table->refs.nextEntry; ++ref) {
+            scavengeReference(ref);
+        }
+    }
+}
+
+/* This code was copied from Thread.c */
+static void scavengeThreadStack(Thread *thread)
+{
+    const u4 *framePtr;
+#if WITH_EXTRA_GC_CHECKS > 1
+    bool first = true;
+#endif
+
+    framePtr = (const u4 *)thread->interpSave.curFrame;
+    while (framePtr != NULL) {
+        const StackSaveArea *saveArea;
+        const Method *method;
+
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = saveArea->method;
+        if (method != NULL && !dvmIsNativeMethod(method)) {
+#ifdef COUNT_PRECISE_METHODS
+            /* the GC is running, so no lock required */
+            if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
+                LOG_SCAV("PGC: added %s.%s %p",
+                             method->clazz->descriptor, method->name, method);
+#endif
+#if WITH_EXTRA_GC_CHECKS > 1
+            /*
+             * May also want to enable the memset() in the "invokeMethod"
+             * goto target in the portable interpreter.  That sets the stack
+             * to a pattern that makes referring to uninitialized data
+             * very obvious.
+             */
+
+            if (first) {
+                /*
+                 * First frame, isn't native, check the "alternate" saved PC
+                 * as a sanity check.
+                 *
+                 * It seems like we could check the second frame if the first
+                 * is native, since the PCs should be the same.  It turns out
+                 * this doesn't always work.  The problem is that we could
+                 * have calls in the sequence:
+                 *   interp method #2
+                 *   native method
+                 *   interp method #1
+                 *
+                 * and then GC while in the native method after returning
+                 * from interp method #2.  The currentPc on the stack is
+                 * for interp method #1, but thread->currentPc2 is still
+                 * set for the last thing interp method #2 did.
+                 *
+                 * This can also happen in normal execution:
+                 * - sget-object on not-yet-loaded class
+                 * - class init updates currentPc2
+                 * - static field init is handled by parsing annotations;
+                 *   static String init requires creation of a String object,
+                 *   which can cause a GC
+                 *
+                 * Essentially, any pattern that involves executing
+                 * interpreted code and then causes an allocation without
+                 * executing instructions in the original method will hit
+                 * this.  These are rare enough that the test still has
+                 * some value.
+                 */
+                if (saveArea->xtra.currentPc != thread->currentPc2) {
+                    ALOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p",
+                        saveArea->xtra.currentPc, thread->currentPc2,
+                        method->clazz->descriptor, method->name, method->insns);
+                    if (saveArea->xtra.currentPc != NULL)
+                        ALOGE("  pc inst = 0x%04x", *saveArea->xtra.currentPc);
+                    if (thread->currentPc2 != NULL)
+                        ALOGE("  pc2 inst = 0x%04x", *thread->currentPc2);
+                    dvmDumpThread(thread, false);
+                }
+            } else {
+                /*
+                 * It's unusual, but not impossible, for a non-first frame
+                 * to be at something other than a method invocation.  For
+                 * example, if we do a new-instance on a nonexistent class,
+                 * we'll have a lot of class loader activity on the stack
+                 * above the frame with the "new" operation.  Could also
+                 * happen while we initialize a Throwable when an instruction
+                 * fails.
+                 *
+                 * So there's not much we can do here to verify the PC,
+                 * except to verify that it's a GC point.
+                 */
+            }
+            assert(saveArea->xtra.currentPc != NULL);
+#endif
+
+            const RegisterMap* pMap;
+            const u1* regVector;
+
+            Method* nonConstMethod = (Method*) method;  // quiet gcc
+            pMap = dvmGetExpandedRegisterMap(nonConstMethod);
+
+            //LOG_SCAV("PGC: %s.%s", method->clazz->descriptor, method->name);
+
+            if (pMap != NULL) {
+                /* found map, get registers for this address */
+                int addr = saveArea->xtra.currentPc - method->insns;
+                regVector = dvmRegisterMapGetLine(pMap, addr);
+                /*
+                if (regVector == NULL) {
+                    LOG_SCAV("PGC: map but no entry for %s.%s addr=0x%04x",
+                                 method->clazz->descriptor, method->name, addr);
+                } else {
+                    LOG_SCAV("PGC: found map for %s.%s 0x%04x (t=%d)",
+                                 method->clazz->descriptor, method->name, addr,
+                                 thread->threadId);
+                }
+                */
+            } else {
+                /*
+                 * No map found.  If precise GC is disabled this is
+                 * expected -- we don't create pointers to the map data even
+                 * if it's present -- but if it's enabled it means we're
+                 * unexpectedly falling back on a conservative scan, so it's
+                 * worth yelling a little.
+                 */
+                if (gDvm.preciseGc) {
+                    LOG_SCAV("PGC: no map for %s.%s", method->clazz->descriptor, method->name);
+                }
+                regVector = NULL;
+            }
+            if (regVector == NULL) {
+                /*
+                 * There are no roots to scavenge.  Skip over the entire frame.
+                 */
+                framePtr += method->registersSize;
+            } else {
+                /*
+                 * Precise scan.  v0 is at the lowest address on the
+                 * interpreted stack, and is the first bit in the register
+                 * vector, so we can walk through the register map and
+                 * memory in the same direction.
+                 *
+                 * A '1' bit indicates a live reference.
+                 */
+                u2 bits = 1 << 1;
+                for (int i = method->registersSize - 1; i >= 0; i--) {
+                    u4 rval = *framePtr;
+
+                    bits >>= 1;
+                    if (bits == 1) {
+                        /* set bit 9 so we can tell when we're empty */
+                        bits = *regVector++ | 0x0100;
+                    }
+
+                    if (rval != 0 && (bits & 0x01) != 0) {
+                        /*
+                         * Non-null, register marked as live reference.  This
+                         * should always be a valid object.
+                         */
+#if WITH_EXTRA_GC_CHECKS > 0
+                        if ((rval & 0x3) != 0 || !dvmIsValidObject((Object*) rval)) {
+                            /* this is very bad */
+                            ALOGE("PGC: invalid ref in reg %d: 0x%08x",
+                                method->registersSize-1 - i, rval);
+                        } else
+#endif
+                        {
+
+                            // LOG_SCAV("stack reference %u@%p", *framePtr, framePtr);
+                            /* dvmMarkObjectNonNull((Object *)rval); */
+                            scavengeReference((Object **) framePtr);
+                        }
+                    } else {
+                        /*
+                         * Null or non-reference, do nothing at all.
+                         */
+#if WITH_EXTRA_GC_CHECKS > 1
+                        if (dvmIsValidObject((Object*) rval)) {
+                            /* this is normal, but we feel chatty */
+                            ALOGD("PGC: ignoring valid ref in reg %d: 0x%08x",
+                                 method->registersSize-1 - i, rval);
+                        }
+#endif
+                    }
+                    ++framePtr;
+                }
+                dvmReleaseRegisterMapLine(pMap, regVector);
+            }
+        }
+        /* else this is a break frame and there is nothing to gray, or
+         * this is a native method and the registers are just the "ins",
+         * copied from various registers in the caller's set.
+         */
+
+#if WITH_EXTRA_GC_CHECKS > 1
+        first = false;
+#endif
+
+        /* Don't fall into an infinite loop if things get corrupted.
+         */
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+               saveArea->prevFrame == NULL);
+        framePtr = saveArea->prevFrame;
+    }
+}
+
+static void scavengeThread(Thread *thread)
+{
+    // LOG_SCAV("scavengeThread(thread=%p)", thread);
+
+    // LOG_SCAV("Scavenging threadObj=%p", thread->threadObj);
+    scavengeReference(&thread->threadObj);
+
+    // LOG_SCAV("Scavenging exception=%p", thread->exception);
+    scavengeReference(&thread->exception);
+
+    scavengeThreadStack(thread);
+}
+
+static void scavengeThreadList()
+{
+    Thread *thread;
+
+    dvmLockThreadList(dvmThreadSelf());
+    thread = gDvm.threadList;
+    while (thread) {
+        scavengeThread(thread);
+        thread = thread->next;
+    }
+    dvmUnlockThreadList();
+}
+
+static void pinThreadStack(const Thread *thread)
+{
+    const u4 *framePtr;
+    const StackSaveArea *saveArea;
+    Method *method;
+    const char *shorty;
+    Object *obj;
+
+    saveArea = NULL;
+    framePtr = (const u4 *)thread->interpSave.curFrame;
+    for (; framePtr != NULL; framePtr = saveArea->prevFrame) {
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = (Method *)saveArea->method;
+        if (method != NULL && dvmIsNativeMethod(method)) {
+            /*
+             * This is native method, pin its arguments.
+             *
+             * For purposes of graying references, we don't need to do
+             * anything here, because all of the native "ins" were copied
+             * from registers in the caller's stack frame and won't be
+             * changed (an interpreted method can freely use registers
+             * with parameters like any other register, but natives don't
+             * work that way).
+             *
+             * However, we need to ensure that references visible to
+             * native methods don't move around.  We can do a precise scan
+             * of the arguments by examining the method signature.
+             */
+            LOG_PIN("+++ native scan %s.%s",
+                    method->clazz->descriptor, method->name);
+            assert(method->registersSize == method->insSize);
+            if (!dvmIsStaticMethod(method)) {
+                /* grab the "this" pointer */
+                obj = (Object *)*framePtr++;
+                if (obj == NULL) {
+                    /*
+                     * This can happen for the "fake" entry frame inserted
+                     * for threads created outside the VM.  There's no actual
+                     * call so there's no object.  If we changed the fake
+                     * entry method to be declared "static" then this
+                     * situation should never occur.
+                     */
+                } else {
+                    assert(dvmIsValidObject(obj));
+                    pinObject(obj);
+                }
+            }
+            shorty = method->shorty+1;      // skip return value
+            for (int i = method->registersSize - 1; i >= 0; i--, framePtr++) {
+                switch (*shorty++) {
+                case 'L':
+                    obj = (Object *)*framePtr;
+                    if (obj != NULL) {
+                        assert(dvmIsValidObject(obj));
+                        pinObject(obj);
+                    }
+                    break;
+                case 'D':
+                case 'J':
+                    framePtr++;
+                    break;
+                default:
+                    /* 32-bit non-reference value */
+                    obj = (Object *)*framePtr;          // debug, remove
+                    if (dvmIsValidObject(obj)) {        // debug, remove
+                        /* if we see a lot of these, our scan might be off */
+                        LOG_PIN("+++ did NOT pin obj %p", obj);
+                    }
+                    break;
+                }
+            }
+        } else if (method != NULL && !dvmIsNativeMethod(method)) {
+            const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+            const u1* regVector = NULL;
+
+            ALOGI("conservative : %s.%s", method->clazz->descriptor, method->name);
+
+            if (pMap != NULL) {
+                int addr = saveArea->xtra.currentPc - method->insns;
+                regVector = dvmRegisterMapGetLine(pMap, addr);
+            }
+            if (regVector == NULL) {
+                /*
+                 * No register info for this frame, conservatively pin.
+                 */
+                for (int i = 0; i < method->registersSize; ++i) {
+                    u4 regValue = framePtr[i];
+                    if (regValue != 0 && (regValue & 0x3) == 0 && dvmIsValidObject((Object *)regValue)) {
+                        pinObject((Object *)regValue);
+                    }
+                }
+            }
+        }
+        /*
+         * Don't fall into an infinite loop if things get corrupted.
+         */
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+               saveArea->prevFrame == NULL);
+    }
+}
+
+static void pinThread(const Thread *thread)
+{
+    assert(thread != NULL);
+    LOG_PIN("pinThread(thread=%p)", thread);
+
+    LOG_PIN("Pin native method arguments");
+    pinThreadStack(thread);
+
+    LOG_PIN("Pin internalLocalRefTable");
+    pinReferenceTable(&thread->internalLocalRefTable);
+
+    LOG_PIN("Pin jniLocalRefTable");
+    pinReferenceTable(&thread->jniLocalRefTable);
+
+    /* Can the check be pushed into the promote routine? */
+    if (thread->jniMonitorRefTable.table) {
+        LOG_PIN("Pin jniMonitorRefTable");
+        pinReferenceTable(&thread->jniMonitorRefTable);
+    }
+}
+
+static void pinThreadList()
+{
+    Thread *thread;
+
+    dvmLockThreadList(dvmThreadSelf());
+    thread = gDvm.threadList;
+    while (thread) {
+        pinThread(thread);
+        thread = thread->next;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Heap block scavenging.
+ */
+
+/*
+ * Scavenge objects in the current block.  Scavenging terminates when
+ * the pointer reaches the highest address in the block or when a run
+ * of zero words that continues to the highest address is reached.
+ */
+static void scavengeBlock(HeapSource *heapSource, size_t block)
+{
+    u1 *cursor;
+    u1 *end;
+    size_t size;
+
+    LOG_SCAV("scavengeBlock(heapSource=%p,block=%zu)", heapSource, block);
+
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
+
+    cursor = blockToAddress(heapSource, block);
+    end = cursor + BLOCK_SIZE;
+    LOG_SCAV("scavengeBlock start=%p, end=%p", cursor, end);
+
+    /* Parse and scavenge the current block. */
+    size = 0;
+    while (cursor < end) {
+        u4 word = *(u4 *)cursor;
+        if (word != 0) {
+            scavengeObject((Object *)cursor);
+            size = objectSize((Object *)cursor);
+            size = alignUp(size, ALLOC_ALIGNMENT);
+            cursor += size;
+        } else {
+            /* Check for padding. */
+            while (*(u4 *)cursor == 0) {
+                cursor += 4;
+                if (cursor == end) break;
+            }
+            /* Punt if something went wrong. */
+            assert(cursor == end);
+        }
+    }
+}
+
+static size_t objectSize(const Object *obj)
+{
+    size_t size;
+
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        size = dvmClassObjectSize((ClassObject *)obj);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        size = dvmArrayObjectSize((ArrayObject *)obj);
+    } else {
+        assert(obj->clazz->objectSize != 0);
+        size = obj->clazz->objectSize;
+    }
+    if (LW_HASH_STATE(obj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
+        size += sizeof(u4);
+    }
+    return size;
+}
+
+static void verifyBlock(HeapSource *heapSource, size_t block)
+{
+    u1 *cursor;
+    u1 *end;
+    size_t size;
+
+    // LOG_VER("verifyBlock(heapSource=%p,block=%zu)", heapSource, block);
+
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
+
+    cursor = blockToAddress(heapSource, block);
+    end = cursor + BLOCK_SIZE;
+    // LOG_VER("verifyBlock start=%p, end=%p", cursor, end);
+
+    /* Parse and scavenge the current block. */
+    size = 0;
+    while (cursor < end) {
+        u4 word = *(u4 *)cursor;
+        if (word != 0) {
+            dvmVerifyObject((Object *)cursor);
+            size = objectSize((Object *)cursor);
+            size = alignUp(size, ALLOC_ALIGNMENT);
+            cursor += size;
+        } else {
+            /* Check for padding. */
+            while (*(unsigned long *)cursor == 0) {
+                cursor += 4;
+                if (cursor == end) break;
+            }
+            /* Punt if something went wrong. */
+            assert(cursor == end);
+        }
+    }
+}
+
+static void describeBlockQueue(const HeapSource *heapSource)
+{
+    size_t block, count;
+    char space;
+
+    block = heapSource->queueHead;
+    count = 0;
+    LOG_SCAV(">>> describeBlockQueue(heapSource=%p)", heapSource);
+    /* Count the number of blocks enqueued. */
+    while (block != QUEUE_TAIL) {
+        block = heapSource->blockQueue[block];
+        ++count;
+    }
+    LOG_SCAV("blockQueue %zu elements, enqueued %zu",
+                 count, heapSource->queueSize);
+    block = heapSource->queueHead;
+    while (block != QUEUE_TAIL) {
+        space = heapSource->blockSpace[block];
+        LOG_SCAV("block=%zu@%p,space=%zu", block, blockToAddress(heapSource,block), space);
+        block = heapSource->blockQueue[block];
+    }
+
+    LOG_SCAV("<<< describeBlockQueue(heapSource=%p)", heapSource);
+}
+
+/*
+ * Blackens promoted objects.
+ */
+static void scavengeBlockQueue()
+{
+    HeapSource *heapSource;
+    size_t block;
+
+    LOG_SCAV(">>> scavengeBlockQueue()");
+    heapSource = gDvm.gcHeap->heapSource;
+    describeBlockQueue(heapSource);
+    while (heapSource->queueHead != QUEUE_TAIL) {
+        block = heapSource->queueHead;
+        LOG_SCAV("Dequeueing block %zu", block);
+        scavengeBlock(heapSource, block);
+        heapSource->queueHead = heapSource->blockQueue[block];
+        LOG_SCAV("New queue head is %zu", heapSource->queueHead);
+    }
+    LOG_SCAV("<<< scavengeBlockQueue()");
+}
+
+/*
+ * Scan the block list and verify all blocks that are marked as being
+ * in new space.  This should be parametrized so we can invoke this
+ * routine outside of the context of a collection.
+ */
+static void verifyNewSpace()
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    size_t c0 = 0, c1 = 0, c2 = 0, c7 = 0;
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        switch (heapSource->blockSpace[i]) {
+        case BLOCK_FREE: ++c0; break;
+        case BLOCK_TO_SPACE: ++c1; break;
+        case BLOCK_FROM_SPACE: ++c2; break;
+        case BLOCK_CONTINUED: ++c7; break;
+        default: assert(!"reached");
+        }
+    }
+    LOG_VER("Block Demographics: "
+            "Free=%zu,ToSpace=%zu,FromSpace=%zu,Continued=%zu",
+            c0, c1, c2, c7);
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        if (heapSource->blockSpace[i] != BLOCK_TO_SPACE) {
+            continue;
+        }
+        verifyBlock(heapSource, i);
+    }
+}
+
+void describeHeap()
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    describeBlocks(heapSource);
+}
+
+/*
+ * The collection interface.  Collection has a few distinct phases.
+ * The first is flipping AKA condemning AKA whitening the heap.  The
+ * second is to promote all objects which are pointed to by pinned or
+ * ambiguous references.  The third phase is tracing from the stacks,
+ * registers and various globals.  Lastly, a verification of the heap
+ * is performed.  The last phase should be optional.
+ */
+void dvmScavengeRoots()  /* Needs a new name badly */
+{
+    GcHeap *gcHeap;
+
+    {
+        size_t alloc, unused, total;
+
+        room(&alloc, &unused, &total);
+        LOG_SCAV("BEFORE GC: %zu alloc, %zu free, %zu total.",
+                     alloc, unused, total);
+    }
+
+    gcHeap = gDvm.gcHeap;
+    dvmHeapSourceFlip();
+
+    /*
+     * Promote blocks with stationary objects.
+     */
+    pinThreadList();
+    pinReferenceTable(&gDvm.jniGlobalRefTable);
+    pinReferenceTable(&gDvm.jniPinRefTable);
+    pinHashTableEntries(gDvm.loadedClasses);
+    pinHashTableEntries(gDvm.dbgRegistry);
+    pinPrimitiveClasses();
+    pinInternedStrings();
+
+    // describeBlocks(gcHeap->heapSource);
+
+    /*
+     * Create first, open new-space page right here.
+     */
+
+    /* Reset allocation to an unallocated block. */
+    gDvm.gcHeap->heapSource->allocPtr = allocateBlocks(gDvm.gcHeap->heapSource, 1);
+    gDvm.gcHeap->heapSource->allocLimit = gDvm.gcHeap->heapSource->allocPtr + BLOCK_SIZE;
+    /*
+     * Hack: promote the empty block allocated above.  If the
+     * promotions that occurred above did not actually gray any
+     * objects, the block queue may be empty.  We must force a
+     * promotion to be safe.
+     */
+    promoteBlockByAddr(gDvm.gcHeap->heapSource, gDvm.gcHeap->heapSource->allocPtr);
+
+    /*
+     * Scavenge blocks and relocate movable objects.
+     */
+
+    LOG_SCAV("Scavenging gDvm.threadList");
+    scavengeThreadList();
+
+    LOG_SCAV("Scavenging gDvm.gcHeap->referenceOperations");
+    scavengeLargeHeapRefTable(gcHeap->referenceOperations);
+
+    LOG_SCAV("Scavenging gDvm.gcHeap->pendingFinalizationRefs");
+    scavengeLargeHeapRefTable(gcHeap->pendingFinalizationRefs);
+
+    LOG_SCAV("Scavenging random global stuff");
+    scavengeReference(&gDvm.outOfMemoryObj);
+    scavengeReference(&gDvm.internalErrorObj);
+    scavengeReference(&gDvm.noClassDefFoundErrorObj);
+
+    // LOG_SCAV("Scavenging gDvm.internedString");
+    scavengeInternedStrings();
+
+    LOG_SCAV("Root scavenge has completed.");
+
+    scavengeBlockQueue();
+
+    // LOG_SCAV("Re-snap global class pointers.");
+    // scavengeGlobals();
+
+    LOG_SCAV("New space scavenge has completed.");
+
+    /*
+     * Process reference objects in strength order.
+     */
+
+    LOG_REF("Processing soft references...");
+    preserveSoftReferences(&gDvm.gcHeap->softReferences);
+    clearWhiteReferences(&gDvm.gcHeap->softReferences);
+
+    LOG_REF("Processing weak references...");
+    clearWhiteReferences(&gDvm.gcHeap->weakReferences);
+
+    LOG_REF("Finding finalizations...");
+    processFinalizableReferences();
+
+    LOG_REF("Processing f-reachable soft references...");
+    clearWhiteReferences(&gDvm.gcHeap->softReferences);
+
+    LOG_REF("Processing f-reachable weak references...");
+    clearWhiteReferences(&gDvm.gcHeap->weakReferences);
+
+    LOG_REF("Processing phantom references...");
+    clearWhiteReferences(&gDvm.gcHeap->phantomReferences);
+
+    /*
+     * Verify the stack and heap.
+     */
+    dvmVerifyRoots();
+    verifyNewSpace();
+
+    //describeBlocks(gcHeap->heapSource);
+
+    clearFromSpace(gcHeap->heapSource);
+
+    {
+        size_t alloc, rem, total;
+
+        room(&alloc, &rem, &total);
+        LOG_SCAV("AFTER GC: %zu alloc, %zu free, %zu total.", alloc, rem, total);
+    }
+}
+
+/*
+ * Interface compatibility routines.
+ */
+
+void dvmClearWhiteRefs(Object **list)
+{
+    /* do nothing */
+    assert(*list == NULL);
+}
+
+void dvmHandleSoftRefs(Object **list)
+{
+    /* do nothing */
+    assert(*list == NULL);
+}
+
+bool dvmHeapBeginMarkStep(GcMode mode)
+{
+    /* do nothing */
+    return true;
+}
+
+void dvmHeapFinishMarkStep()
+{
+    /* do nothing */
+}
+
+void dvmHeapMarkRootSet()
+{
+    /* do nothing */
+}
+
+void dvmHeapScanMarkedObjects()
+{
+    dvmScavengeRoots();
+}
+
+void dvmHeapScheduleFinalizations()
+{
+    /* do nothing */
+}
+
+void dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
+{
+    *numFreed = 0;
+    *sizeFreed = 0;
+    /* do nothing */
+}
+
+void dvmMarkDirtyObjects()
+{
+    assert(!"implemented");
+}
+
+void dvmHeapSourceThreadShutdown()
+{
+    /* do nothing */
+}
diff --git a/vm/alloc/DdmHeap.cpp b/vm/alloc/DdmHeap.cpp
new file mode 100644
index 0000000..0f1fc31
--- /dev/null
+++ b/vm/alloc/DdmHeap.cpp
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * DDM-related heap functions
+ */
+#include <sys/time.h>
+#include <time.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/DdmHeap.h"
+#include "alloc/DlMalloc.h"
+#include "alloc/HeapSource.h"
+
+#define DEFAULT_HEAP_ID  1
+
+enum HpifWhen {
+    HPIF_WHEN_NEVER = 0,
+    HPIF_WHEN_NOW = 1,
+    HPIF_WHEN_NEXT_GC = 2,
+    HPIF_WHEN_EVERY_GC = 3
+};
+
+/*
+ * Chunk HPIF (client --> server)
+ *
+ * Heap Info. General information about the heap,
+ * suitable for a summary display.
+ *
+ *   [u4]: number of heaps
+ *
+ *   For each heap:
+ *     [u4]: heap ID
+ *     [u8]: timestamp in ms since Unix epoch
+ *     [u1]: capture reason (same as 'when' value from server)
+ *     [u4]: max heap size in bytes (-Xmx)
+ *     [u4]: current heap size in bytes
+ *     [u4]: current number of bytes allocated
+ *     [u4]: current number of objects allocated
+ */
+#define HPIF_SIZE(numHeaps) \
+        (sizeof(u4) + (numHeaps) * (5 * sizeof(u4) + sizeof(u1) + sizeof(u8)))
+void dvmDdmSendHeapInfo(int reason, bool shouldLock)
+{
+    struct timeval now;
+    u8 nowMs;
+    u1 *buf, *b;
+
+    buf = (u1 *)malloc(HPIF_SIZE(1));
+    if (buf == NULL) {
+        return;
+    }
+    b = buf;
+
+    /* If there's a one-shot 'when', reset it.
+     */
+    if (reason == gDvm.gcHeap->ddmHpifWhen) {
+        if (shouldLock && ! dvmLockHeap()) {
+            ALOGW("%s(): can't lock heap to clear when", __func__);
+            goto skip_when;
+        }
+        if (reason == gDvm.gcHeap->ddmHpifWhen) {
+            if (gDvm.gcHeap->ddmHpifWhen == HPIF_WHEN_NEXT_GC) {
+                gDvm.gcHeap->ddmHpifWhen = HPIF_WHEN_NEVER;
+            }
+        }
+        if (shouldLock) {
+            dvmUnlockHeap();
+        }
+    }
+skip_when:
+
+    /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
+     */
+    if (gettimeofday(&now, NULL) < 0) {
+        nowMs = 0;
+    } else {
+        nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
+    }
+
+    /* number of heaps */
+    set4BE(b, 1); b += 4;
+
+    /* For each heap (of which there is one) */
+    {
+        /* heap ID */
+        set4BE(b, DEFAULT_HEAP_ID); b += 4;
+
+        /* timestamp */
+        set8BE(b, nowMs); b += 8;
+
+        /* 'when' value */
+        *b++ = (u1)reason;
+
+        /* max allowed heap size in bytes */
+        set4BE(b, dvmHeapSourceGetMaximumSize()); b += 4;
+
+        /* current heap size in bytes */
+        set4BE(b, dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0)); b += 4;
+
+        /* number of bytes allocated */
+        set4BE(b, dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0)); b += 4;
+
+        /* number of objects allocated */
+        set4BE(b, dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0)); b += 4;
+    }
+    assert((intptr_t)b == (intptr_t)buf + (intptr_t)HPIF_SIZE(1));
+
+    dvmDbgDdmSendChunk(CHUNK_TYPE("HPIF"), b - buf, buf);
+}
+
+bool dvmDdmHandleHpifChunk(int when)
+{
+    switch (when) {
+    case HPIF_WHEN_NOW:
+        dvmDdmSendHeapInfo(when, true);
+        break;
+    case HPIF_WHEN_NEVER:
+    case HPIF_WHEN_NEXT_GC:
+    case HPIF_WHEN_EVERY_GC:
+        if (dvmLockHeap()) {
+            gDvm.gcHeap->ddmHpifWhen = when;
+            dvmUnlockHeap();
+        } else {
+            ALOGI("%s(): can't lock heap to set when", __func__);
+            return false;
+        }
+        break;
+    default:
+        ALOGI("%s(): bad when value 0x%08x", __func__, when);
+        return false;
+    }
+
+    return true;
+}
+
+enum HpsgSolidity {
+    SOLIDITY_FREE = 0,
+    SOLIDITY_HARD = 1,
+    SOLIDITY_SOFT = 2,
+    SOLIDITY_WEAK = 3,
+    SOLIDITY_PHANTOM = 4,
+    SOLIDITY_FINALIZABLE = 5,
+    SOLIDITY_SWEEP = 6,
+};
+
+enum HpsgKind {
+    KIND_OBJECT = 0,
+    KIND_CLASS_OBJECT = 1,
+    KIND_ARRAY_1 = 2,
+    KIND_ARRAY_2 = 3,
+    KIND_ARRAY_4 = 4,
+    KIND_ARRAY_8 = 5,
+    KIND_UNKNOWN = 6,
+    KIND_NATIVE = 7,
+};
+
+#define HPSG_PARTIAL (1<<7)
+#define HPSG_STATE(solidity, kind) \
+    ((u1)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
+
+struct HeapChunkContext {
+    void* startOfNextMemoryChunk;
+    u1 *buf;
+    u1 *p;
+    u1 *pieceLenField;
+    size_t bufLen;
+    size_t totalAllocationUnits;
+    int type;
+    bool merge;
+    bool needHeader;
+};
+
+#define ALLOCATION_UNIT_SIZE 8
+
+static void flush_hpsg_chunk(HeapChunkContext *ctx)
+{
+    if (ctx->pieceLenField == NULL && ctx->needHeader) {
+        /* Already flushed */
+        return;
+    }
+    /* Patch the "length of piece" field.
+     */
+    assert(ctx->buf <= ctx->pieceLenField &&
+            ctx->pieceLenField <= ctx->p);
+    set4BE(ctx->pieceLenField, ctx->totalAllocationUnits);
+
+    /* Send the chunk.
+     */
+    dvmDbgDdmSendChunk(ctx->type, ctx->p - ctx->buf, ctx->buf);
+
+    /* Reset the context.
+     */
+    ctx->p = ctx->buf;
+    ctx->totalAllocationUnits = 0;
+    ctx->needHeader = true;
+    ctx->pieceLenField = NULL;
+}
+
+static void append_chunk(HeapChunkContext *ctx, u1 state, void* ptr, size_t length) {
+    /* Make sure there's enough room left in the buffer.
+     * We need to use two bytes for every fractional 256
+     * allocation units used by the chunk and 17 bytes for
+     * any header.
+     */
+    {
+        size_t needed = (((length/ALLOCATION_UNIT_SIZE + 255) / 256) * 2) + 17;
+        size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
+        if (bytesLeft < needed) {
+            flush_hpsg_chunk(ctx);
+        }
+        bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
+        if (bytesLeft < needed) {
+            ALOGW("chunk is too big to transmit (length=%zd, %zd bytes)",
+                  length, needed);
+            return;
+        }
+    }
+    if (ctx->needHeader) {
+        /*
+         * Start a new HPSx chunk.
+         */
+
+        /* [u4]: heap ID */
+        set4BE(ctx->p, DEFAULT_HEAP_ID); ctx->p += 4;
+
+        /* [u1]: size of allocation unit, in bytes */
+        *ctx->p++ = 8;
+
+        /* [u4]: virtual address of segment start */
+        set4BE(ctx->p, (uintptr_t)ptr); ctx->p += 4;
+
+        /* [u4]: offset of this piece (relative to the virtual address) */
+        set4BE(ctx->p, 0); ctx->p += 4;
+
+        /* [u4]: length of piece, in allocation units
+         * We won't know this until we're done, so save the offset
+         * and stuff in a dummy value.
+         */
+        ctx->pieceLenField = ctx->p;
+        set4BE(ctx->p, 0x55555555); ctx->p += 4;
+
+        ctx->needHeader = false;
+    }
+    /* Write out the chunk description.
+     */
+    length /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
+    ctx->totalAllocationUnits += length;
+    while (length > 256) {
+        *ctx->p++ = state | HPSG_PARTIAL;
+        *ctx->p++ = 255;     // length - 1
+        length -= 256;
+    }
+    *ctx->p++ = state;
+    *ctx->p++ = length - 1;
+}
+
+/*
+ * Called by dlmalloc_inspect_all. If used_bytes != 0 then start is
+ * the start of a malloc-ed piece of memory of size used_bytes. If
+ * start is 0 then start is the beginning of any free space not
+ * including dlmalloc's book keeping and end the start of the next
+ * dlmalloc chunk. Regions purely containing book keeping don't
+ * callback.
+ */
+static void heap_chunk_callback(void* start, void* end, size_t used_bytes,
+                                void* arg)
+{
+    u1 state;
+    HeapChunkContext *ctx = (HeapChunkContext *)arg;
+    UNUSED_PARAMETER(end);
+
+    if (used_bytes == 0) {
+        if (start == NULL) {
+            // Reset for start of new heap.
+            ctx->startOfNextMemoryChunk = NULL;
+            flush_hpsg_chunk(ctx);
+        }
+        // Only process in use memory so that free region information
+        // also includes dlmalloc book keeping.
+        return;
+    }
+
+    /* If we're looking at the native heap, we'll just return
+     * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
+     */
+    bool native = ctx->type == CHUNK_TYPE("NHSG");
+
+    if (ctx->startOfNextMemoryChunk != NULL) {
+        // Transmit any pending free memory. Native free memory of
+        // over kMaxFreeLen could be because of the use of mmaps, so
+        // don't report. If not free memory then start a new segment.
+        bool flush = true;
+        if (start > ctx->startOfNextMemoryChunk) {
+            const size_t kMaxFreeLen = 2 * SYSTEM_PAGE_SIZE;
+            void* freeStart = ctx->startOfNextMemoryChunk;
+            void* freeEnd = start;
+            size_t freeLen = (char*)freeEnd - (char*)freeStart;
+            if (!native || freeLen < kMaxFreeLen) {
+                append_chunk(ctx, HPSG_STATE(SOLIDITY_FREE, 0),
+                             freeStart, freeLen);
+                flush = false;
+            }
+        }
+        if (flush) {
+            ctx->startOfNextMemoryChunk = NULL;
+            flush_hpsg_chunk(ctx);
+        }
+    }
+    const Object *obj = (const Object *)start;
+
+    /* It's an allocated chunk.  Figure out what it is.
+     */
+//TODO: if ctx.merge, see if this chunk is different from the last chunk.
+//      If it's the same, we should combine them.
+    if (!native && dvmIsValidObject(obj)) {
+        ClassObject *clazz = obj->clazz;
+        if (clazz == NULL) {
+            /* The object was probably just created
+             * but hasn't been initialized yet.
+             */
+            state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+        } else if (dvmIsTheClassClass(clazz)) {
+            state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
+        } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+            if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
+                state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+            } else {
+                switch (clazz->elementClass->primitiveType) {
+                case PRIM_BOOLEAN:
+                case PRIM_BYTE:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
+                    break;
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
+                    break;
+                case PRIM_INT:
+                case PRIM_FLOAT:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+                    break;
+                case PRIM_DOUBLE:
+                case PRIM_LONG:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
+                    break;
+                default:
+                    assert(!"Unknown GC heap object type");
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+                    break;
+                }
+            }
+        } else {
+            state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+        }
+    } else {
+        obj = NULL; // it's not actually an object
+        state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
+    }
+    append_chunk(ctx, state, start, used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD);
+    ctx->startOfNextMemoryChunk =
+        (char*)start + used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD;
+}
+
+enum HpsgWhen {
+    HPSG_WHEN_NEVER = 0,
+    HPSG_WHEN_EVERY_GC = 1,
+};
+enum HpsgWhat {
+    HPSG_WHAT_MERGED_OBJECTS = 0,
+    HPSG_WHAT_DISTINCT_OBJECTS = 1,
+};
+
+/*
+ * Maximum chunk size.  Obtain this from the formula:
+ *
+ * (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
+ */
+#define HPSx_CHUNK_SIZE (16384 - 16)
+
+static void walkHeap(bool merge, bool native)
+{
+    HeapChunkContext ctx;
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.bufLen = HPSx_CHUNK_SIZE;
+    ctx.buf = (u1 *)malloc(ctx.bufLen);
+    if (ctx.buf == NULL) {
+        return;
+    }
+
+    ctx.merge = merge;
+    if (native) {
+        ctx.type = CHUNK_TYPE("NHSG");
+    } else {
+        if (ctx.merge) {
+            ctx.type = CHUNK_TYPE("HPSG");
+        } else {
+            ctx.type = CHUNK_TYPE("HPSO");
+        }
+    }
+
+    ctx.p = ctx.buf;
+    ctx.needHeader = true;
+    if (native) {
+        dlmalloc_inspect_all(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()) {
+        ALOGW("Can't lock heap for DDM HPSx dump");
+        return;
+    }
+
+    /* Figure out what kind of chunks we'll be sending.
+     */
+    if (what == HPSG_WHAT_MERGED_OBJECTS) {
+        merge = true;
+    } else if (what == HPSG_WHAT_DISTINCT_OBJECTS) {
+        merge = false;
+    } else {
+        assert(!"bad HPSG.what value");
+        return;
+    }
+
+    /* First, send a heap start chunk.
+     */
+    set4BE(heapId, DEFAULT_HEAP_ID);
+    dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"),
+        sizeof(u4), heapId);
+
+    /* Send a series of heap segment chunks.
+     */
+    walkHeap(merge, native);
+
+    /* Finally, send a heap end chunk.
+     */
+    dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"),
+        sizeof(u4), heapId);
+
+    if (shouldLock) {
+        dvmUnlockHeap();
+    }
+}
+
+bool dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native)
+{
+    ALOGI("dvmDdmHandleHpsgChunk(when %d, what %d, heap %d)", when, what,
+         native);
+    switch (when) {
+    case HPSG_WHEN_NEVER:
+    case HPSG_WHEN_EVERY_GC:
+        break;
+    default:
+        ALOGI("%s(): bad when value 0x%08x", __func__, when);
+        return false;
+    }
+
+    switch (what) {
+    case HPSG_WHAT_MERGED_OBJECTS:
+    case HPSG_WHAT_DISTINCT_OBJECTS:
+        break;
+    default:
+        ALOGI("%s(): bad what value 0x%08x", __func__, what);
+        return false;
+    }
+
+    if (dvmLockHeap()) {
+        if (!native) {
+            gDvm.gcHeap->ddmHpsgWhen = when;
+            gDvm.gcHeap->ddmHpsgWhat = what;
+        } else {
+            gDvm.gcHeap->ddmNhsgWhen = when;
+            gDvm.gcHeap->ddmNhsgWhat = what;
+        }
+//TODO: if what says we should dump immediately, signal (or do) it from here
+        dvmUnlockHeap();
+    } else {
+        ALOGI("%s(): can't lock heap to set when/what", __func__);
+        return false;
+    }
+
+    return true;
+}
diff --git a/vm/alloc/DdmHeap.h b/vm/alloc/DdmHeap.h
new file mode 100644
index 0000000..32be78d
--- /dev/null
+++ b/vm/alloc/DdmHeap.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * DDM-specific internal heap functions.
+ */
+#ifndef DALVIK_ALLOC_DDMHEAP_H_
+#define DALVIK_ALLOC_DDMHEAP_H_
+
+/*
+ * Sends the current heap info to the DDM server.
+ * Should be called after a GC when gcHeap->ddmHpifWhen
+ * is non-zero.
+ */
+void dvmDdmSendHeapInfo(int reason, bool shouldLock);
+
+/*
+ * Walks through the heap and sends a series of
+ * HPST/NHST, HPSG/HPSO/NHSG, and HPEN/NHEN chunks that describe
+ * the contents of the GC or native heap.
+ *
+ * @param shouldLock If true, grab the heap lock.  If false,
+ *                   the heap lock must already be held.
+ * @param heap       If false, dump the GC heap; if true, dump the
+ *                   native heap.
+ */
+void dvmDdmSendHeapSegments(bool shouldLock, bool native);
+
+#endif  // DALVIK_ALLOC_DDMHEAP_H_
diff --git a/vm/alloc/DlMalloc.cpp b/vm/alloc/DlMalloc.cpp
new file mode 100644
index 0000000..1571801
--- /dev/null
+++ b/vm/alloc/DlMalloc.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DlMalloc.h"
+
+#include <stdint.h>
+#include "Common.h"
+
+/* Dalvik specific morecore implementation defined in HeapSource.cpp. */
+#define MORECORE(x) dvmHeapSourceMorecore(m, x)
+extern void* dvmHeapSourceMorecore(void* mspace, intptr_t increment);
+
+/* Custom heap error handling. */
+#define PROCEED_ON_ERROR 0
+static void heap_error(const char* msg, const char* function, void* p);
+#define CORRUPTION_ERROR_ACTION(m) \
+    heap_error("HEAP MEMORY CORRUPTION", __FUNCTION__, NULL)
+#define USAGE_ERROR_ACTION(m,p) \
+    heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
+
+/*
+ * Ugly inclusion of C file so that Dalvik specific #defines configure
+ * dlmalloc for our use for mspaces (regular dlmalloc is still declared
+ * in bionic).
+ */
+#include "../../../bionic/libc/upstream-dlmalloc/malloc.c"
+
+
+static void heap_error(const char* msg, const char* function, void* p) {
+    ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: DALVIK: %s IN %s addr=%p", msg,
+         function, p);
+    /* So that we can get a memory dump around p */
+    *((int **) 0xdeadbaad) = (int *) p;
+}
diff --git a/vm/alloc/DlMalloc.h b/vm/alloc/DlMalloc.h
new file mode 100644
index 0000000..f076656
--- /dev/null
+++ b/vm/alloc/DlMalloc.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_ALLOC_DLMALLOC_H_
+#define DALVIK_VM_ALLOC_DLMALLOC_H_
+
+/* Configure dlmalloc for mspaces. */
+#define HAVE_MMAP 0
+#define HAVE_MREMAP 0
+#define HAVE_MORECORE 1
+#define MSPACES 1
+#define NO_MALLINFO 1
+#define ONLY_MSPACES 1
+#define MALLOC_INSPECT_ALL 1
+
+/* Include the proper definitions. */
+#include "../../../bionic/libc/upstream-dlmalloc/malloc.h"
+
+/*
+ * Define dlmalloc routines from bionic that cannot be included
+ * directly because of redefining symbols from the include above.
+ */
+extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*),
+                                     void* arg);
+extern "C" int  dlmalloc_trim(size_t);
+extern "C" void* dlmem2chunk(void* mem);
+
+#endif  // DALVIK_VM_ALLOC_DLMALLOC_H_
diff --git a/vm/alloc/Heap.cpp b/vm/alloc/Heap.cpp
new file mode 100644
index 0000000..9bbe8a5
--- /dev/null
+++ b/vm/alloc/Heap.cpp
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/DdmHeap.h"
+#include "alloc/HeapSource.h"
+#include "alloc/MarkSweep.h"
+#include "os/os.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <errno.h>
+
+static const GcSpec kGcForMallocSpec = {
+    true,  /* isPartial */
+    false,  /* isConcurrent */
+    true,  /* doPreserve */
+    "GC_FOR_ALLOC"
+};
+
+const GcSpec *GC_FOR_MALLOC = &kGcForMallocSpec;
+
+static const GcSpec kGcConcurrentSpec  = {
+    true,  /* isPartial */
+    true,  /* isConcurrent */
+    true,  /* doPreserve */
+    "GC_CONCURRENT"
+};
+
+const GcSpec *GC_CONCURRENT = &kGcConcurrentSpec;
+
+static const GcSpec kGcExplicitSpec = {
+    false,  /* isPartial */
+    true,  /* isConcurrent */
+    true,  /* doPreserve */
+    "GC_EXPLICIT"
+};
+
+const GcSpec *GC_EXPLICIT = &kGcExplicitSpec;
+
+static const GcSpec kGcBeforeOomSpec = {
+    false,  /* isPartial */
+    false,  /* isConcurrent */
+    false,  /* doPreserve */
+    "GC_BEFORE_OOM"
+};
+
+const GcSpec *GC_BEFORE_OOM = &kGcBeforeOomSpec;
+
+/*
+ * Initialize the GC heap.
+ *
+ * Returns true if successful, false otherwise.
+ */
+bool dvmHeapStartup()
+{
+    GcHeap *gcHeap;
+
+    if (gDvm.heapGrowthLimit == 0) {
+        gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
+    }
+
+    gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
+                                  gDvm.heapMaximumSize,
+                                  gDvm.heapGrowthLimit);
+    if (gcHeap == NULL) {
+        return false;
+    }
+    gcHeap->ddmHpifWhen = 0;
+    gcHeap->ddmHpsgWhen = 0;
+    gcHeap->ddmHpsgWhat = 0;
+    gcHeap->ddmNhsgWhen = 0;
+    gcHeap->ddmNhsgWhat = 0;
+    gDvm.gcHeap = gcHeap;
+
+    /* Set up the lists we'll use for cleared reference objects.
+     */
+    gcHeap->clearedReferences = NULL;
+
+    if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) {
+        LOGE_HEAP("card table startup failed.");
+        return false;
+    }
+
+    return true;
+}
+
+bool dvmHeapStartupAfterZygote()
+{
+    return dvmHeapSourceStartupAfterZygote();
+}
+
+void dvmHeapShutdown()
+{
+//TODO: make sure we're locked
+    if (gDvm.gcHeap != NULL) {
+        dvmCardTableShutdown();
+        /* Destroy the heap.  Any outstanding pointers will point to
+         * unmapped memory (unless/until someone else maps it).  This
+         * frees gDvm.gcHeap as a side-effect.
+         */
+        dvmHeapSourceShutdown(&gDvm.gcHeap);
+    }
+}
+
+/*
+ * Shutdown any threads internal to the heap.
+ */
+void dvmHeapThreadShutdown()
+{
+    dvmHeapSourceThreadShutdown();
+}
+
+/*
+ * Grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
+ * we're going to have to wait on the mutex.
+ */
+bool dvmLockHeap()
+{
+    if (dvmTryLockMutex(&gDvm.gcHeapLock) != 0) {
+        Thread *self;
+        ThreadStatus oldStatus;
+
+        self = dvmThreadSelf();
+        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmLockMutex(&gDvm.gcHeapLock);
+        dvmChangeStatus(self, oldStatus);
+    }
+
+    return true;
+}
+
+void dvmUnlockHeap()
+{
+    dvmUnlockMutex(&gDvm.gcHeapLock);
+}
+
+/* Do a full garbage collection, which may grow the
+ * heap as a side-effect if the live set is large.
+ */
+static void gcForMalloc(bool clearSoftReferences)
+{
+    if (gDvm.allocProf.enabled) {
+        Thread* self = dvmThreadSelf();
+        gDvm.allocProf.gcCount++;
+        if (self != NULL) {
+            self->allocProf.gcCount++;
+        }
+    }
+    /* This may adjust the soft limit as a side-effect.
+     */
+    const GcSpec *spec = clearSoftReferences ? GC_BEFORE_OOM : GC_FOR_MALLOC;
+    dvmCollectGarbageInternal(spec);
+}
+
+/* Try as hard as possible to allocate some memory.
+ */
+static void *tryMalloc(size_t size)
+{
+    void *ptr;
+
+//TODO: figure out better heuristics
+//    There will be a lot of churn if someone allocates a bunch of
+//    big objects in a row, and we hit the frag case each time.
+//    A full GC for each.
+//    Maybe we grow the heap in bigger leaps
+//    Maybe we skip the GC if the size is large and we did one recently
+//      (number of allocations ago) (watch for thread effects)
+//    DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other
+//      (or, at least, there are only 0-5 objects swept each time)
+
+    ptr = dvmHeapSourceAlloc(size);
+    if (ptr != NULL) {
+        return ptr;
+    }
+
+    /*
+     * The allocation failed.  If the GC is running, block until it
+     * completes and retry.
+     */
+    if (gDvm.gcHeap->gcRunning) {
+        /*
+         * The GC is concurrently tracing the heap.  Release the heap
+         * lock, wait for the GC to complete, and retrying allocating.
+         */
+        dvmWaitForConcurrentGcToComplete();
+    } else {
+      /*
+       * Try a foreground GC since a concurrent GC is not currently running.
+       */
+      gcForMalloc(false);
+    }
+
+    ptr = dvmHeapSourceAlloc(size);
+    if (ptr != NULL) {
+        return ptr;
+    }
+
+    /* Even that didn't work;  this is an exceptional state.
+     * Try harder, growing the heap if necessary.
+     */
+    ptr = dvmHeapSourceAllocAndGrow(size);
+    if (ptr != NULL) {
+        size_t newHeapSize;
+
+        newHeapSize = dvmHeapSourceGetIdealFootprint();
+//TODO: may want to grow a little bit more so that the amount of free
+//      space is equal to the old free space + the utilization slop for
+//      the new allocation.
+        LOGI_HEAP("Grow heap (frag case) to "
+                "%zu.%03zuMB for %zu-byte allocation",
+                FRACTIONAL_MB(newHeapSize), size);
+        return ptr;
+    }
+
+    /* Most allocations should have succeeded by now, so the heap
+     * is really full, really fragmented, or the requested size is
+     * really big.  Do another GC, collecting SoftReferences this
+     * time.  The VM spec requires that all SoftReferences have
+     * been collected and cleared before throwing an OOME.
+     */
+//TODO: wait for the finalizers from the previous GC to finish
+    LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation",
+            size);
+    gcForMalloc(true);
+    ptr = dvmHeapSourceAllocAndGrow(size);
+    if (ptr != NULL) {
+        return ptr;
+    }
+//TODO: maybe wait for finalizers and try one last time
+
+    LOGE_HEAP("Out of memory on a %zd-byte allocation.", size);
+//TODO: tell the HeapSource to dump its state
+    dvmDumpThread(dvmThreadSelf(), false);
+
+    return NULL;
+}
+
+/* Throw an OutOfMemoryError if there's a thread to attach it to.
+ * Avoid recursing.
+ *
+ * The caller must not be holding the heap lock, or else the allocations
+ * in dvmThrowException() will deadlock.
+ */
+static void throwOOME()
+{
+    Thread *self;
+
+    if ((self = dvmThreadSelf()) != NULL) {
+        /* If the current (failing) dvmMalloc() happened as part of thread
+         * creation/attachment before the thread became part of the root set,
+         * we can't rely on the thread-local trackedAlloc table, so
+         * we can't keep track of a real allocated OOME object.  But, since
+         * the thread is in the process of being created, it won't have
+         * a useful stack anyway, so we may as well make things easier
+         * by throwing the (stackless) pre-built OOME.
+         */
+        if (dvmIsOnThreadList(self) && !self->throwingOOME) {
+            /* Let ourselves know that we tried to throw an OOM
+             * error in the normal way in case we run out of
+             * memory trying to allocate it inside dvmThrowException().
+             */
+            self->throwingOOME = true;
+
+            /* Don't include a description string;
+             * one fewer allocation.
+             */
+            dvmThrowOutOfMemoryError(NULL);
+        } else {
+            /*
+             * This thread has already tried to throw an OutOfMemoryError,
+             * which probably means that we're running out of memory
+             * while recursively trying to throw.
+             *
+             * To avoid any more allocation attempts, "throw" a pre-built
+             * OutOfMemoryError object (which won't have a useful stack trace).
+             *
+             * Note that since this call can't possibly allocate anything,
+             * we don't care about the state of self->throwingOOME
+             * (which will usually already be set).
+             */
+            dvmSetException(self, gDvm.outOfMemoryObj);
+        }
+        /* We're done with the possible recursion.
+         */
+        self->throwingOOME = false;
+    }
+}
+
+/*
+ * Allocate storage on the GC heap.  We guarantee 8-byte alignment.
+ *
+ * The new storage is zeroed out.
+ *
+ * Note that, in rare cases, this could get called while a GC is in
+ * progress.  If a non-VM thread tries to attach itself through JNI,
+ * it will need to allocate some objects.  If this becomes annoying to
+ * deal with, we can block it at the source, but holding the allocation
+ * mutex should be enough.
+ *
+ * In rare circumstances (JNI AttachCurrentThread) we can be called
+ * from a non-VM thread.
+ *
+ * Use ALLOC_DONT_TRACK when we either don't want to track an allocation
+ * (because it's being done for the interpreter "new" operation and will
+ * be part of the root set immediately) or we can't (because this allocation
+ * is for a brand new thread).
+ *
+ * Returns NULL and throws an exception on failure.
+ *
+ * TODO: don't do a GC if the debugger thinks all threads are suspended
+ */
+void* dvmMalloc(size_t size, int flags)
+{
+    void *ptr;
+
+    dvmLockHeap();
+
+    /* Try as hard as possible to allocate some memory.
+     */
+    ptr = tryMalloc(size);
+    if (ptr != NULL) {
+        /* We've got the memory.
+         */
+        if (gDvm.allocProf.enabled) {
+            Thread* self = dvmThreadSelf();
+            gDvm.allocProf.allocCount++;
+            gDvm.allocProf.allocSize += size;
+            if (self != NULL) {
+                self->allocProf.allocCount++;
+                self->allocProf.allocSize += size;
+            }
+        }
+    } else {
+        /* The allocation failed.
+         */
+
+        if (gDvm.allocProf.enabled) {
+            Thread* self = dvmThreadSelf();
+            gDvm.allocProf.failedAllocCount++;
+            gDvm.allocProf.failedAllocSize += size;
+            if (self != NULL) {
+                self->allocProf.failedAllocCount++;
+                self->allocProf.failedAllocSize += size;
+            }
+        }
+    }
+
+    dvmUnlockHeap();
+
+    if (ptr != NULL) {
+        /*
+         * If caller hasn't asked us not to track it, add it to the
+         * internal tracking list.
+         */
+        if ((flags & ALLOC_DONT_TRACK) == 0) {
+            dvmAddTrackedAlloc((Object*)ptr, NULL);
+        }
+    } else {
+        /*
+         * The allocation failed; throw an OutOfMemoryError.
+         */
+        throwOOME();
+    }
+
+    return ptr;
+}
+
+/*
+ * Returns true iff <obj> points to a valid allocated object.
+ */
+bool dvmIsValidObject(const Object* obj)
+{
+    /* Don't bother if it's NULL or not 8-byte aligned.
+     */
+    if (obj != NULL && ((uintptr_t)obj & (8-1)) == 0) {
+        /* Even if the heap isn't locked, this shouldn't return
+         * any false negatives.  The only mutation that could
+         * be happening is allocation, which means that another
+         * thread could be in the middle of a read-modify-write
+         * to add a new bit for a new object.  However, that
+         * RMW will have completed by the time any other thread
+         * could possibly see the new pointer, so there is no
+         * danger of dvmIsValidObject() being called on a valid
+         * pointer whose bit isn't set.
+         *
+         * Freeing will only happen during the sweep phase, which
+         * only happens while the heap is locked.
+         */
+        return dvmHeapSourceContains(obj);
+    }
+    return false;
+}
+
+size_t dvmObjectSizeInHeap(const Object *obj)
+{
+    return dvmHeapSourceChunkSize(obj);
+}
+
+static void verifyRootsAndHeap()
+{
+    dvmVerifyRoots();
+    dvmVerifyBitmap(dvmHeapSourceGetLiveBits());
+}
+
+/*
+ * Initiate garbage collection.
+ *
+ * NOTES:
+ * - If we don't hold gDvm.threadListLock, it's possible for a thread to
+ *   be added to the thread list while we work.  The thread should NOT
+ *   start executing, so this is only interesting when we start chasing
+ *   thread stacks.  (Before we do so, grab the lock.)
+ *
+ * We are not allowed to GC when the debugger has suspended the VM, which
+ * is awkward because debugger requests can cause allocations.  The easiest
+ * way to enforce this is to refuse to GC on an allocation made by the
+ * JDWP thread -- we have to expand the heap or fail.
+ */
+void dvmCollectGarbageInternal(const GcSpec* spec)
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    u4 gcEnd = 0;
+    u4 rootStart = 0 , rootEnd = 0;
+    u4 dirtyStart = 0, dirtyEnd = 0;
+    size_t numObjectsFreed, numBytesFreed;
+    size_t currAllocated, currFootprint;
+    size_t percentFree;
+    int oldThreadPriority = INT_MAX;
+
+    /* The heap lock must be held.
+     */
+
+    if (gcHeap->gcRunning) {
+        LOGW_HEAP("Attempted recursive GC");
+        return;
+    }
+
+    gcHeap->gcRunning = true;
+
+    rootStart = dvmGetRelativeTimeMsec();
+    dvmSuspendAllThreads(SUSPEND_FOR_GC);
+
+    /*
+     * If we are not marking concurrently raise the priority of the
+     * thread performing the garbage collection.
+     */
+    if (!spec->isConcurrent) {
+        oldThreadPriority = os_raiseThreadPriority();
+    }
+    if (gDvm.preVerify) {
+        LOGV_HEAP("Verifying roots and heap before GC");
+        verifyRootsAndHeap();
+    }
+
+    dvmMethodTraceGCBegin();
+
+    /* Set up the marking context.
+     */
+    if (!dvmHeapBeginMarkStep(spec->isPartial)) {
+        LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting");
+        dvmAbort();
+    }
+
+    /* Mark the set of objects that are strongly reachable from the roots.
+     */
+    LOGD_HEAP("Marking...");
+    dvmHeapMarkRootSet();
+
+    /* dvmHeapScanMarkedObjects() will build the lists of known
+     * instances of the Reference classes.
+     */
+    assert(gcHeap->softReferences == NULL);
+    assert(gcHeap->weakReferences == NULL);
+    assert(gcHeap->finalizerReferences == NULL);
+    assert(gcHeap->phantomReferences == NULL);
+    assert(gcHeap->clearedReferences == NULL);
+
+    if (spec->isConcurrent) {
+        /*
+         * Resume threads while tracing from the roots.  We unlock the
+         * heap to allow mutator threads to allocate from free space.
+         */
+        dvmClearCardTable();
+        dvmUnlockHeap();
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+        rootEnd = dvmGetRelativeTimeMsec();
+    }
+
+    /* Recursively mark any objects that marked objects point to strongly.
+     * If we're not collecting soft references, soft-reachable
+     * objects will also be marked.
+     */
+    LOGD_HEAP("Recursing...");
+    dvmHeapScanMarkedObjects();
+
+    if (spec->isConcurrent) {
+        /*
+         * Re-acquire the heap lock and perform the final thread
+         * suspension.
+         */
+        dirtyStart = dvmGetRelativeTimeMsec();
+        dvmLockHeap();
+        dvmSuspendAllThreads(SUSPEND_FOR_GC);
+        /*
+         * As no barrier intercepts root updates, we conservatively
+         * assume all roots may be gray and re-mark them.
+         */
+        dvmHeapReMarkRootSet();
+        /*
+         * With the exception of reference objects and weak interned
+         * strings, all gray objects should now be on dirty cards.
+         */
+        if (gDvm.verifyCardTable) {
+            dvmVerifyCardTable();
+        }
+        /*
+         * Recursively mark gray objects pointed to by the roots or by
+         * heap objects dirtied during the concurrent mark.
+         */
+        dvmHeapReScanMarkedObjects();
+    }
+
+    /*
+     * All strongly-reachable objects have now been marked.  Process
+     * weakly-reachable objects discovered while tracing.
+     */
+    dvmHeapProcessReferences(&gcHeap->softReferences,
+                             spec->doPreserve == false,
+                             &gcHeap->weakReferences,
+                             &gcHeap->finalizerReferences,
+                             &gcHeap->phantomReferences);
+
+#if defined(WITH_JIT)
+    /*
+     * Patching a chaining cell is very cheap as it only updates 4 words. It's
+     * the overhead of stopping all threads and synchronizing the I/D cache
+     * that makes it expensive.
+     *
+     * Therefore we batch those work orders in a queue and go through them
+     * when threads are suspended for GC.
+     */
+    dvmCompilerPerformSafePointChecks();
+#endif
+
+    LOGD_HEAP("Sweeping...");
+
+    dvmHeapSweepSystemWeaks();
+
+    /*
+     * Live objects have a bit set in the mark bitmap, swap the mark
+     * and live bitmaps.  The sweep can proceed concurrently viewing
+     * the new live bitmap as the old mark bitmap, and vice versa.
+     */
+    dvmHeapSourceSwapBitmaps();
+
+    if (gDvm.postVerify) {
+        LOGV_HEAP("Verifying roots and heap after GC");
+        verifyRootsAndHeap();
+    }
+
+    if (spec->isConcurrent) {
+        dvmUnlockHeap();
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+        dirtyEnd = dvmGetRelativeTimeMsec();
+    }
+    dvmHeapSweepUnmarkedObjects(spec->isPartial, spec->isConcurrent,
+                                &numObjectsFreed, &numBytesFreed);
+    LOGD_HEAP("Cleaning up...");
+    dvmHeapFinishMarkStep();
+    if (spec->isConcurrent) {
+        dvmLockHeap();
+    }
+
+    LOGD_HEAP("Done.");
+
+    /* Now's a good time to adjust the heap size, since
+     * we know what our utilization is.
+     *
+     * This doesn't actually resize any memory;
+     * it just lets the heap grow more when necessary.
+     */
+    dvmHeapSourceGrowForUtilization();
+
+    currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+    currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+
+    dvmMethodTraceGCEnd();
+    LOGV_HEAP("GC finished");
+
+    gcHeap->gcRunning = false;
+
+    LOGV_HEAP("Resuming threads");
+
+    if (spec->isConcurrent) {
+        /*
+         * Wake-up any threads that blocked after a failed allocation
+         * request.
+         */
+        dvmBroadcastCond(&gDvm.gcHeapCond);
+    }
+
+    if (!spec->isConcurrent) {
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+        dirtyEnd = dvmGetRelativeTimeMsec();
+        /*
+         * Restore the original thread scheduling priority if it was
+         * changed at the start of the current garbage collection.
+         */
+        if (oldThreadPriority != INT_MAX) {
+            os_lowerThreadPriority(oldThreadPriority);
+        }
+    }
+
+    /*
+     * Move queue of pending references back into Java.
+     */
+    dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);
+
+    gcEnd = dvmGetRelativeTimeMsec();
+    percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
+    if (!spec->isConcurrent) {
+        u4 markSweepTime = dirtyEnd - rootStart;
+        u4 gcTime = gcEnd - rootStart;
+        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+        ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums, total %ums",
+             spec->reason,
+             isSmall ? "<" : "",
+             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+             percentFree,
+             currAllocated / 1024, currFootprint / 1024,
+             markSweepTime, gcTime);
+    } else {
+        u4 rootTime = rootEnd - rootStart;
+        u4 dirtyTime = dirtyEnd - dirtyStart;
+        u4 gcTime = gcEnd - rootStart;
+        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+        ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums+%ums, total %ums",
+             spec->reason,
+             isSmall ? "<" : "",
+             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+             percentFree,
+             currAllocated / 1024, currFootprint / 1024,
+             rootTime, dirtyTime, gcTime);
+    }
+    if (gcHeap->ddmHpifWhen != 0) {
+        LOGD_HEAP("Sending VM heap info to DDM");
+        dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false);
+    }
+    if (gcHeap->ddmHpsgWhen != 0) {
+        LOGD_HEAP("Dumping VM heap to DDM");
+        dvmDdmSendHeapSegments(false, false);
+    }
+    if (gcHeap->ddmNhsgWhen != 0) {
+        LOGD_HEAP("Dumping native heap to DDM");
+        dvmDdmSendHeapSegments(false, true);
+    }
+}
+
+/*
+ * If the concurrent GC is running, wait for it to finish.  The caller
+ * must hold the heap lock.
+ *
+ * Note: the second dvmChangeStatus() could stall if we were in RUNNING
+ * on entry, and some other thread has asked us to suspend.  In that
+ * case we will be suspended with the heap lock held, which can lead to
+ * deadlock if the other thread tries to do something with the managed heap.
+ * For example, the debugger might suspend us and then execute a method that
+ * allocates memory.  We can avoid this situation by releasing the lock
+ * before self-suspending.  (The developer can work around this specific
+ * situation by single-stepping the VM.  Alternatively, we could disable
+ * concurrent GC when the debugger is attached, but that might change
+ * behavior more than is desirable.)
+ *
+ * This should not be a problem in production, because any GC-related
+ * activity will grab the lock before issuing a suspend-all.  (We may briefly
+ * suspend when the GC thread calls dvmUnlockHeap before dvmResumeAllThreads,
+ * but there's no risk of deadlock.)
+ */
+bool dvmWaitForConcurrentGcToComplete()
+{
+    bool waited = gDvm.gcHeap->gcRunning;
+    Thread *self = dvmThreadSelf();
+    assert(self != NULL);
+    u4 start = dvmGetRelativeTimeMsec();
+    while (gDvm.gcHeap->gcRunning) {
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
+        dvmChangeStatus(self, oldStatus);
+    }
+    u4 end = dvmGetRelativeTimeMsec();
+    if (end - start > 0) {
+        ALOGD("WAIT_FOR_CONCURRENT_GC blocked %ums", end - start);
+    }
+    return waited;
+}
diff --git a/vm/alloc/Heap.h b/vm/alloc/Heap.h
new file mode 100644
index 0000000..19e48cd
--- /dev/null
+++ b/vm/alloc/Heap.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Internal heap functions
+ */
+#ifndef DALVIK_ALLOC_HEAP_H_
+#define DALVIK_ALLOC_HEAP_H_
+
+struct GcSpec {
+  /* If true, only the application heap is threatened. */
+  bool isPartial;
+  /* If true, the trace is run concurrently with the mutator. */
+  bool isConcurrent;
+  /* Toggles for the soft reference clearing policy. */
+  bool doPreserve;
+  /* A name for this garbage collection mode. */
+  const char *reason;
+};
+
+/* Not enough space for an "ordinary" Object to be allocated. */
+extern const GcSpec *GC_FOR_MALLOC;
+
+/* Automatic GC triggered by exceeding a heap occupancy threshold. */
+extern const GcSpec *GC_CONCURRENT;
+
+/* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */
+extern const GcSpec *GC_EXPLICIT;
+
+/* Final attempt to reclaim memory before throwing an OOM. */
+extern const GcSpec *GC_BEFORE_OOM;
+
+/*
+ * Initialize the GC heap.
+ *
+ * Returns true if successful, false otherwise.
+ */
+bool dvmHeapStartup(void);
+
+/*
+ * Initialization that needs to wait until after leaving zygote mode.
+ * This needs to be called before the first allocation or GC that
+ * happens after forking.
+ */
+bool dvmHeapStartupAfterZygote(void);
+
+/*
+ * Tear down the GC heap.
+ *
+ * Frees all memory allocated via dvmMalloc() as
+ * a side-effect.
+ */
+void dvmHeapShutdown(void);
+
+/*
+ * Stops any threads internal to the garbage collector.  Called before
+ * the heap itself is shutdown.
+ */
+void dvmHeapThreadShutdown(void);
+
+#if 0       // needs to be in Alloc.h so debug code can find it.
+/*
+ * Returns a number of bytes greater than or
+ * equal to the size of the named object in the heap.
+ *
+ * Specifically, it returns the size of the heap
+ * chunk which contains the object.
+ */
+size_t dvmObjectSizeInHeap(const Object *obj);
+#endif
+
+/*
+ * Run the garbage collector without doing any locking.
+ */
+void dvmCollectGarbageInternal(const GcSpec *spec);
+
+/*
+ * Blocks the calling thread until the garbage collector is inactive.
+ * The caller must hold the heap lock as this call releases and
+ * re-acquires the heap lock.  After returning, no garbage collection
+ * will be in progress and the heap lock will be held by the caller.
+ */
+bool dvmWaitForConcurrentGcToComplete(void);
+
+/*
+ * Returns true iff <obj> points to a valid allocated object.
+ */
+bool dvmIsValidObject(const Object* obj);
+
+#endif  // DALVIK_ALLOC_HEAP_H_
diff --git a/vm/alloc/HeapBitmap.cpp b/vm/alloc/HeapBitmap.cpp
new file mode 100644
index 0000000..d6a737b
--- /dev/null
+++ b/vm/alloc/HeapBitmap.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "HeapBitmap.h"
+#include <sys/mman.h>   /* for PROT_* */
+
+/*
+ * Initialize a HeapBitmap so that it points to a bitmap large
+ * enough to cover a heap at <base> of <maxSize> bytes, where
+ * objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
+ */
+bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
+                       const char *name)
+{
+    void *bits;
+    size_t bitsLen;
+
+    assert(hb != NULL);
+    assert(name != NULL);
+    bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits);
+    bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name);
+    if (bits == NULL) {
+        ALOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);
+        return false;
+    }
+    hb->bits = (unsigned long *)bits;
+    hb->bitsLen = hb->allocLen = bitsLen;
+    hb->base = (uintptr_t)base;
+    hb->max = hb->base - 1;
+    return true;
+}
+
+/*
+ * Clean up any resources associated with the bitmap.
+ */
+void dvmHeapBitmapDelete(HeapBitmap *hb)
+{
+    assert(hb != NULL);
+
+    if (hb->bits != NULL) {
+        munmap((char *)hb->bits, hb->allocLen);
+    }
+    memset(hb, 0, sizeof(*hb));
+}
+
+/*
+ * Fill the bitmap with zeroes.  Returns the bitmap's memory to
+ * the system as a side-effect.
+ */
+void dvmHeapBitmapZero(HeapBitmap *hb)
+{
+    assert(hb != NULL);
+
+    if (hb->bits != NULL) {
+        /* This returns the memory to the system.
+         * Successive page faults will return zeroed memory.
+         */
+        madvise(hb->bits, hb->bitsLen, MADV_DONTNEED);
+        hb->max = hb->base - 1;
+    }
+}
+
+/*
+ * Return true iff <obj> is within the range of pointers that this
+ * bitmap could potentially cover, even if a bit has not been set
+ * for it.
+ */
+bool dvmHeapBitmapCoversAddress(const HeapBitmap *hb, const void *obj)
+{
+    assert(hb != NULL);
+    if (obj != NULL) {
+        const uintptr_t offset = (uintptr_t)obj - hb->base;
+        const size_t index = HB_OFFSET_TO_INDEX(offset);
+        return index < hb->bitsLen / sizeof(*hb->bits);
+    }
+    return false;
+}
+
+/*
+ * Visits set bits in address order.  The callback is not permitted to
+ * change the bitmap bits or max during the traversal.
+ */
+void dvmHeapBitmapWalk(const HeapBitmap *bitmap, BitmapCallback *callback,
+                       void *arg)
+{
+    assert(bitmap != NULL);
+    assert(bitmap->bits != NULL);
+    assert(callback != NULL);
+    uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+    for (uintptr_t i = 0; i <= end; ++i) {
+        unsigned long word = bitmap->bits[i];
+        if (UNLIKELY(word != 0)) {
+            unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+            uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
+            while (word != 0) {
+                const int shift = CLZ(word);
+                Object* obj = (Object *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+                (*callback)(obj, arg);
+                word &= ~(highBit >> shift);
+            }
+        }
+    }
+}
+
+/*
+ * Similar to dvmHeapBitmapWalk but the callback routine is permitted
+ * to change the bitmap bits and max during traversal.  Used by the
+ * the root marking scan exclusively.
+ *
+ * The callback is invoked with a finger argument.  The finger is a
+ * pointer to an address not yet visited by the traversal.  If the
+ * callback sets a bit for an address at or above the finger, this
+ * address will be visited by the traversal.  If the callback sets a
+ * bit for an address below the finger, this address will not be
+ * visited.
+ */
+void dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
+                           BitmapScanCallback *callback, void *arg)
+{
+    assert(bitmap != NULL);
+    assert(bitmap->bits != NULL);
+    assert(callback != NULL);
+    uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+    uintptr_t i;
+    for (i = 0; i <= end; ++i) {
+        unsigned long word = bitmap->bits[i];
+        if (UNLIKELY(word != 0)) {
+            unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+            uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
+            void *finger = (void *)(HB_INDEX_TO_OFFSET(i + 1) + bitmap->base);
+            while (word != 0) {
+                const int shift = CLZ(word);
+                Object *obj = (Object *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+                (*callback)(obj, finger, arg);
+                word &= ~(highBit >> shift);
+            }
+            end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+        }
+    }
+}
+
+/*
+ * Walk through the bitmaps in increasing address order, and find the
+ * object pointers that correspond to garbage objects.  Call
+ * <callback> zero or more times with lists of these object pointers.
+ *
+ * The callback is not permitted to increase the max of either bitmap.
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+                            uintptr_t base, uintptr_t max,
+                            BitmapSweepCallback *callback, void *callbackArg)
+{
+    assert(liveHb != NULL);
+    assert(liveHb->bits != NULL);
+    assert(markHb != NULL);
+    assert(markHb->bits != NULL);
+    assert(liveHb->base == markHb->base);
+    assert(liveHb->bitsLen == markHb->bitsLen);
+    assert(callback != NULL);
+    assert(base <= max);
+    assert(base >= liveHb->base);
+    assert(max <= liveHb->max);
+    if (liveHb->max < liveHb->base) {
+        /* Easy case; both are obviously empty.
+         */
+        return;
+    }
+    void *pointerBuf[4 * HB_BITS_PER_WORD];
+    void **pb = pointerBuf;
+    size_t start = HB_OFFSET_TO_INDEX(base - liveHb->base);
+    size_t end = HB_OFFSET_TO_INDEX(max - liveHb->base);
+    unsigned long *live = liveHb->bits;
+    unsigned long *mark = markHb->bits;
+    for (size_t i = start; i <= end; i++) {
+        unsigned long garbage = live[i] & ~mark[i];
+        if (UNLIKELY(garbage != 0)) {
+            unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+            uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + liveHb->base;
+            while (garbage != 0) {
+                int shift = CLZ(garbage);
+                garbage &= ~(highBit >> shift);
+                *pb++ = (void *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+            }
+            /* Make sure that there are always enough slots available */
+            /* for an entire word of 1s. */
+            if (pb >= &pointerBuf[NELEM(pointerBuf) - HB_BITS_PER_WORD]) {
+                (*callback)(pb - pointerBuf, pointerBuf, callbackArg);
+                pb = pointerBuf;
+            }
+        }
+    }
+    if (pb > pointerBuf) {
+        (*callback)(pb - pointerBuf, pointerBuf, callbackArg);
+    }
+}
diff --git a/vm/alloc/HeapBitmap.h b/vm/alloc/HeapBitmap.h
new file mode 100644
index 0000000..9f2a0a1
--- /dev/null
+++ b/vm/alloc/HeapBitmap.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DALVIK_HEAP_BITMAP_H_
+#define DALVIK_HEAP_BITMAP_H_
+
+#include <limits.h>
+#include <stdint.h>
+
+#define HB_OBJECT_ALIGNMENT 8
+#define HB_BITS_PER_WORD (sizeof(unsigned long) * CHAR_BIT)
+
+/* <offset> is the difference from .base to a pointer address.
+ * <index> is the index of .bits that contains the bit representing
+ *         <offset>.
+ */
+#define HB_OFFSET_TO_INDEX(offset_) \
+    ((uintptr_t)(offset_) / HB_OBJECT_ALIGNMENT / HB_BITS_PER_WORD)
+#define HB_INDEX_TO_OFFSET(index_) \
+    ((uintptr_t)(index_) * HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD)
+
+#define HB_OFFSET_TO_BYTE_INDEX(offset_) \
+  (HB_OFFSET_TO_INDEX(offset_) * sizeof(*((HeapBitmap *)0)->bits))
+
+/* Pack the bits in backwards so they come out in address order
+ * when using CLZ.
+ */
+#define HB_OFFSET_TO_MASK(offset_) \
+    (1 << \
+        (31-(((uintptr_t)(offset_) / HB_OBJECT_ALIGNMENT) % HB_BITS_PER_WORD)))
+
+struct HeapBitmap {
+    /* The bitmap data, which points to an mmap()ed area of zeroed
+     * anonymous memory.
+     */
+    unsigned long *bits;
+
+    /* The size of the used memory pointed to by bits, in bytes.  This
+     * value changes when the bitmap is shrunk.
+     */
+    size_t bitsLen;
+
+    /* The real size of the memory pointed to by bits.  This is the
+     * number of bytes we requested from the allocator and does not
+     * change.
+     */
+    size_t allocLen;
+
+    /* The base address, which corresponds to the first bit in
+     * the bitmap.
+     */
+    uintptr_t base;
+
+    /* The highest pointer value ever returned by an allocation
+     * from this heap.  I.e., the highest address that may correspond
+     * to a set bit.  If there are no bits set, (max < base).
+     */
+    uintptr_t max;
+};
+
+/*
+ * Callback types used by the walking routines.
+ */
+typedef void BitmapCallback(Object *obj, void *arg);
+typedef void BitmapScanCallback(Object *obj, void *finger, void *arg);
+typedef void BitmapSweepCallback(size_t numPtrs, void **ptrs, void *arg);
+
+/*
+ * Initialize a HeapBitmap so that it points to a bitmap large
+ * enough to cover a heap at <base> of <maxSize> bytes, where
+ * objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
+ */
+bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
+        const char *name);
+
+/*
+ * Clean up any resources associated with the bitmap.
+ */
+void dvmHeapBitmapDelete(HeapBitmap *hb);
+
+/*
+ * Fill the bitmap with zeroes.  Returns the bitmap's memory to
+ * the system as a side-effect.
+ */
+void dvmHeapBitmapZero(HeapBitmap *hb);
+
+/*
+ * Returns true if the address range of the bitmap covers the object
+ * address.
+ */
+bool dvmHeapBitmapCoversAddress(const HeapBitmap *hb, const void *obj);
+
+/*
+ * Applies the callback function to each set address in the bitmap.
+ */
+void dvmHeapBitmapWalk(const HeapBitmap *bitmap,
+                       BitmapCallback *callback, void *callbackArg);
+
+/*
+ * Like dvmHeapBitmapWalk but takes a callback function with a finger
+ * address.
+ */
+void dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
+                           BitmapScanCallback *callback, void *arg);
+
+/*
+ * Walk through the bitmaps in increasing address order, and find the
+ * object pointers that correspond to garbage objects.  Call
+ * <callback> zero or more times with lists of these object pointers.
+ *
+ * The callback is not permitted to increase the max of either bitmap.
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+                            uintptr_t base, uintptr_t max,
+                            BitmapSweepCallback *callback, void *callbackArg);
+
+#endif  // DALVIK_HEAP_BITMAP_H_
diff --git a/vm/alloc/HeapBitmapInlines.h b/vm/alloc/HeapBitmapInlines.h
new file mode 100644
index 0000000..a5e9be4
--- /dev/null
+++ b/vm/alloc/HeapBitmapInlines.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_HEAP_BITMAPINLINES_H_
+#define DALVIK_HEAP_BITMAPINLINES_H_
+
+static unsigned long dvmHeapBitmapSetAndReturnObjectBit(HeapBitmap *hb, const void *obj) __attribute__((used));
+static void dvmHeapBitmapSetObjectBit(HeapBitmap *hb, const void *obj) __attribute__((used));
+static void dvmHeapBitmapClearObjectBit(HeapBitmap *hb, const void *obj) __attribute__((used));
+
+/*
+ * Internal function; do not call directly.
+ */
+static unsigned long _heapBitmapModifyObjectBit(HeapBitmap *hb, const void *obj,
+                                                bool setBit, bool returnOld)
+{
+    const uintptr_t offset = (uintptr_t)obj - hb->base;
+    const size_t index = HB_OFFSET_TO_INDEX(offset);
+    const unsigned long mask = HB_OFFSET_TO_MASK(offset);
+
+    assert(hb->bits != NULL);
+    assert((uintptr_t)obj >= hb->base);
+    assert(index < hb->bitsLen / sizeof(*hb->bits));
+    if (setBit) {
+        if ((uintptr_t)obj > hb->max) {
+            hb->max = (uintptr_t)obj;
+        }
+        if (returnOld) {
+            unsigned long *p = hb->bits + index;
+            const unsigned long word = *p;
+            *p |= mask;
+            return word & mask;
+        } else {
+            hb->bits[index] |= mask;
+        }
+    } else {
+        hb->bits[index] &= ~mask;
+    }
+    return false;
+}
+
+/*
+ * Sets the bit corresponding to <obj>, and returns the previous value
+ * of that bit (as zero or non-zero). Does no range checking to see if
+ * <obj> is outside of the coverage of the bitmap.
+ *
+ * NOTE: casting this value to a bool is dangerous, because higher
+ * set bits will be lost.
+ */
+static unsigned long dvmHeapBitmapSetAndReturnObjectBit(HeapBitmap *hb,
+                                                        const void *obj)
+{
+    return _heapBitmapModifyObjectBit(hb, obj, true, true);
+}
+
+/*
+ * Sets the bit corresponding to <obj>, and widens the range of seen
+ * pointers if necessary.  Does no range checking.
+ */
+static void dvmHeapBitmapSetObjectBit(HeapBitmap *hb, const void *obj)
+{
+    _heapBitmapModifyObjectBit(hb, obj, true, false);
+}
+
+/*
+ * Clears the bit corresponding to <obj>.  Does no range checking.
+ */
+static void dvmHeapBitmapClearObjectBit(HeapBitmap *hb, const void *obj)
+{
+    _heapBitmapModifyObjectBit(hb, obj, false, false);
+}
+
+/*
+ * Returns the current value of the bit corresponding to <obj>,
+ * as zero or non-zero.  Does no range checking.
+ *
+ * NOTE: casting this value to a bool is dangerous, because higher
+ * set bits will be lost.
+ */
+static unsigned long dvmHeapBitmapIsObjectBitSet(const HeapBitmap *hb,
+                                                 const void *obj)
+{
+    assert(dvmHeapBitmapCoversAddress(hb, obj));
+    assert(hb->bits != NULL);
+    assert((uintptr_t)obj >= hb->base);
+    if ((uintptr_t)obj <= hb->max) {
+        const uintptr_t offset = (uintptr_t)obj - hb->base;
+        return hb->bits[HB_OFFSET_TO_INDEX(offset)] & HB_OFFSET_TO_MASK(offset);
+    } else {
+        return 0;
+    }
+}
+
+#endif  // DALVIK_HEAP_BITMAPINLINES_H_
diff --git a/vm/alloc/HeapDebug.cpp b/vm/alloc/HeapDebug.cpp
new file mode 100644
index 0000000..9bd6799
--- /dev/null
+++ b/vm/alloc/HeapDebug.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "HeapSource.h"
+
+int dvmGetHeapDebugInfo(HeapDebugInfoType info)
+{
+    switch (info) {
+    case kVirtualHeapSize:
+        return (int)dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+    case kVirtualHeapAllocated:
+        return (int)dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+    case kVirtualHeapMaximumSize:
+        return dvmHeapSourceGetMaximumSize();
+    default:
+        return -1;
+    }
+}
diff --git a/vm/alloc/HeapDebug.h b/vm/alloc/HeapDebug.h
new file mode 100644
index 0000000..de4d65b
--- /dev/null
+++ b/vm/alloc/HeapDebug.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DALVIK_HEAPDEBUG_H_
+#define DALVIK_HEAPDEBUG_H_
+
+enum HeapDebugInfoType {
+    kVirtualHeapSize = 0,
+    kNativeHeapSize = 1,
+    kVirtualHeapAllocated = 2,
+    kNativeHeapAllocated = 3,
+    kVirtualHeapMaximumSize = 4
+};
+
+/* Return the specified value.
+ * Returns -1 if the type is unknown.
+ */
+int dvmGetHeapDebugInfo(HeapDebugInfoType info);
+
+#endif  // DALVIK_HEAPDEBUG_H_
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
new file mode 100644
index 0000000..576bf7d
--- /dev/null
+++ b/vm/alloc/HeapInternal.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Types and macros used internally by the heap.
+ */
+#ifndef DALVIK_ALLOC_HEAP_INTERNAL_H_
+#define DALVIK_ALLOC_HEAP_INTERNAL_H_
+
+#include "MarkSweep.h"
+
+struct HeapSource;
+
+struct GcHeap {
+    HeapSource *heapSource;
+
+    /* Linked lists of subclass instances of java/lang/ref/Reference
+     * that we find while recursing.  The "next" pointers are hidden
+     * in the Reference objects' pendingNext fields.  These lists are
+     * cleared and rebuilt each time the GC runs.
+     */
+    Object *softReferences;
+    Object *weakReferences;
+    Object *finalizerReferences;
+    Object *phantomReferences;
+
+    /* The list of Reference objects that need to be enqueued.
+     */
+    Object *clearedReferences;
+
+    /* The current state of the mark step.
+     * Only valid during a GC.
+     */
+    GcMarkContext markContext;
+
+    /* GC's card table */
+    u1* cardTableBase;
+    size_t cardTableLength;
+    size_t cardTableMaxLength;
+    size_t cardTableOffset;
+
+    /* Is the GC running?  Used to avoid recursive calls to GC.
+     */
+    bool gcRunning;
+
+    /*
+     * Debug control values
+     */
+    int ddmHpifWhen;
+    int ddmHpsgWhen;
+    int ddmHpsgWhat;
+    int ddmNhsgWhen;
+    int ddmNhsgWhat;
+};
+
+bool dvmLockHeap(void);
+void dvmUnlockHeap(void);
+
+/*
+ * Logging helpers
+ */
+
+#define HEAP_LOG_TAG      LOG_TAG "-heap"
+
+#if LOG_NDEBUG
+#define LOGV_HEAP(...)    ((void)0)
+#define LOGD_HEAP(...)    ((void)0)
+#else
+#define LOGV_HEAP(...)    ALOG(LOG_VERBOSE, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGD_HEAP(...)    ALOG(LOG_DEBUG, HEAP_LOG_TAG, __VA_ARGS__)
+#endif
+#define LOGI_HEAP(...) \
+    do { \
+        if (!gDvm.zygote) { ALOG(LOG_INFO, HEAP_LOG_TAG, __VA_ARGS__); } \
+    } while (0)
+
+#define LOGW_HEAP(...)    ALOG(LOG_WARN, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGE_HEAP(...)    ALOG(LOG_ERROR, HEAP_LOG_TAG, __VA_ARGS__)
+
+#define FRACTIONAL_MB(n)    (n) / (1024 * 1024), \
+                            ((((n) % (1024 * 1024)) / 1024) * 1000) / 1024
+#define FRACTIONAL_PCT(n,max)    ((n) * 100) / (max), \
+                                 (((n) * 1000) / (max)) % 10
+
+#endif  // DALVIK_ALLOC_HEAP_INTERNAL_H_
diff --git a/vm/alloc/HeapSource.cpp b/vm/alloc/HeapSource.cpp
new file mode 100644
index 0000000..ee40af8
--- /dev/null
+++ b/vm/alloc/HeapSource.cpp
@@ -0,0 +1,1417 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#define SIZE_MAX UINT_MAX  // TODO: get SIZE_MAX from stdint.h
+
+#include "Dalvik.h"
+#include "alloc/DlMalloc.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapBitmapInlines.h"
+
+static void snapIdealFootprint();
+static void setIdealFootprint(size_t max);
+static size_t getMaximumSize(const HeapSource *hs);
+static void trimHeaps();
+
+#define HEAP_UTILIZATION_MAX        1024
+
+/* How long to wait after a GC before performing a heap trim
+ * operation to reclaim unused pages.
+ */
+#define HEAP_TRIM_IDLE_TIME_MS (5 * 1000)
+
+/* Start a concurrent collection when free memory falls under this
+ * many bytes.
+ */
+#define CONCURRENT_START (128 << 10)
+
+/* The next GC will not be concurrent when free memory after a GC is
+ * under this many bytes.
+ */
+#define CONCURRENT_MIN_FREE (CONCURRENT_START + (128 << 10))
+
+#define HS_BOILERPLATE() \
+    do { \
+        assert(gDvm.gcHeap != NULL); \
+        assert(gDvm.gcHeap->heapSource != NULL); \
+        assert(gHs == gDvm.gcHeap->heapSource); \
+    } while (0)
+
+struct Heap {
+    /* The mspace to allocate from.
+     */
+    mspace msp;
+
+    /* The largest size that this heap is allowed to grow to.
+     */
+    size_t maximumSize;
+
+    /* Number of bytes allocated from this mspace for objects,
+     * including any overhead.  This value is NOT exact, and
+     * should only be used as an input for certain heuristics.
+     */
+    size_t bytesAllocated;
+
+    /* Number of bytes allocated from this mspace at which a
+     * concurrent garbage collection will be started.
+     */
+    size_t concurrentStartBytes;
+
+    /* Number of objects currently allocated from this mspace.
+     */
+    size_t objectsAllocated;
+
+    /*
+     * The lowest address of this heap, inclusive.
+     */
+    char *base;
+
+    /*
+     * The highest address of this heap, exclusive.
+     */
+    char *limit;
+
+    /*
+     * If the heap has an mspace, the current high water mark in
+     * allocations requested via dvmHeapSourceMorecore.
+     */
+    char *brk;
+};
+
+struct HeapSource {
+    /* Target ideal heap utilization ratio; range 1..HEAP_UTILIZATION_MAX
+     */
+    size_t targetUtilization;
+
+    /* The starting heap size.
+     */
+    size_t startSize;
+
+    /* The largest that the heap source as a whole is allowed to grow.
+     */
+    size_t maximumSize;
+
+    /*
+     * The largest size we permit the heap to grow.  This value allows
+     * the user to limit the heap growth below the maximum size.  This
+     * is a work around until we can dynamically set the maximum size.
+     * This value can range between the starting size and the maximum
+     * size but should never be set below the current footprint of the
+     * heap.
+     */
+    size_t growthLimit;
+
+    /* The desired max size of the heap source as a whole.
+     */
+    size_t idealSize;
+
+    /* The maximum number of bytes allowed to be allocated from the
+     * active heap before a GC is forced.  This is used to "shrink" the
+     * heap in lieu of actual compaction.
+     */
+    size_t softLimit;
+
+    /* Minimum number of free bytes. Used with the target utilization when
+     * setting the softLimit. Never allows less bytes than this to be free
+     * when the heap size is below the maximum size or growth limit.
+     */
+    size_t minFree;
+
+    /* Maximum number of free bytes. Used with the target utilization when
+     * setting the softLimit. Never allows more bytes than this to be free
+     * when the heap size is below the maximum size or growth limit.
+     */
+    size_t maxFree;
+
+    /* The heaps; heaps[0] is always the active heap,
+     * which new objects should be allocated from.
+     */
+    Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];
+
+    /* The current number of heaps.
+     */
+    size_t numHeaps;
+
+    /* True if zygote mode was active when the HeapSource was created.
+     */
+    bool sawZygote;
+
+    /*
+     * The base address of the virtual memory reservation.
+     */
+    char *heapBase;
+
+    /*
+     * The length in bytes of the virtual memory reservation.
+     */
+    size_t heapLength;
+
+    /*
+     * The live object bitmap.
+     */
+    HeapBitmap liveBits;
+
+    /*
+     * The mark bitmap.
+     */
+    HeapBitmap markBits;
+
+    /*
+     * State for the GC daemon.
+     */
+    bool hasGcThread;
+    pthread_t gcThread;
+    bool gcThreadShutdown;
+    pthread_mutex_t gcThreadMutex;
+    pthread_cond_t gcThreadCond;
+    bool gcThreadTrimNeeded;
+};
+
+#define hs2heap(hs_) (&((hs_)->heaps[0]))
+
+/*
+ * Returns true iff a soft limit is in effect for the active heap.
+ */
+static bool isSoftLimited(const HeapSource *hs)
+{
+    /* softLimit will be either SIZE_MAX or the limit for the
+     * active mspace.  idealSize can be greater than softLimit
+     * if there is more than one heap.  If there is only one
+     * heap, a non-SIZE_MAX softLimit should always be the same
+     * as idealSize.
+     */
+    return hs->softLimit <= hs->idealSize;
+}
+
+/*
+ * Returns approximately the maximum number of bytes allowed to be
+ * allocated from the active heap before a GC is forced.
+ */
+static size_t getAllocLimit(const HeapSource *hs)
+{
+    if (isSoftLimited(hs)) {
+        return hs->softLimit;
+    } else {
+        return mspace_footprint_limit(hs2heap(hs)->msp);
+    }
+}
+
+/*
+ * Returns the current footprint of all heaps.  If includeActive
+ * is false, don't count the heap at index 0.
+ */
+static size_t oldHeapOverhead(const HeapSource *hs, bool includeActive)
+{
+    size_t footprint = 0;
+    size_t i;
+
+    if (includeActive) {
+        i = 0;
+    } else {
+        i = 1;
+    }
+    for (/* i = i */; i < hs->numHeaps; i++) {
+//TODO: include size of bitmaps?  If so, don't use bitsLen, listen to .max
+        footprint += mspace_footprint(hs->heaps[i].msp);
+    }
+    return footprint;
+}
+
+/*
+ * Returns the heap that <ptr> could have come from, or NULL
+ * if it could not have come from any heap.
+ */
+static Heap *ptr2heap(const HeapSource *hs, const void *ptr)
+{
+    const size_t numHeaps = hs->numHeaps;
+
+    if (ptr != NULL) {
+        for (size_t i = 0; i < numHeaps; i++) {
+            const Heap *const heap = &hs->heaps[i];
+
+            if ((const char *)ptr >= heap->base && (const char *)ptr < heap->limit) {
+                return (Heap *)heap;
+            }
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Functions to update heapSource->bytesAllocated when an object
+ * is allocated or freed.  mspace_usable_size() will give
+ * us a much more accurate picture of heap utilization than
+ * the requested byte sizes would.
+ *
+ * These aren't exact, and should not be treated as such.
+ */
+static void countAllocation(Heap *heap, const void *ptr)
+{
+    assert(heap->bytesAllocated < mspace_footprint(heap->msp));
+
+    heap->bytesAllocated += mspace_usable_size(ptr) +
+            HEAP_SOURCE_CHUNK_OVERHEAD;
+    heap->objectsAllocated++;
+    HeapSource* hs = gDvm.gcHeap->heapSource;
+    dvmHeapBitmapSetObjectBit(&hs->liveBits, ptr);
+
+    assert(heap->bytesAllocated < mspace_footprint(heap->msp));
+}
+
+static void countFree(Heap *heap, const void *ptr, size_t *numBytes)
+{
+    size_t delta = mspace_usable_size(ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
+    assert(delta > 0);
+    if (delta < heap->bytesAllocated) {
+        heap->bytesAllocated -= delta;
+    } else {
+        heap->bytesAllocated = 0;
+    }
+    HeapSource* hs = gDvm.gcHeap->heapSource;
+    dvmHeapBitmapClearObjectBit(&hs->liveBits, ptr);
+    if (heap->objectsAllocated > 0) {
+        heap->objectsAllocated--;
+    }
+    *numBytes += delta;
+}
+
+static HeapSource *gHs = NULL;
+
+static mspace createMspace(void* begin, size_t morecoreStart, size_t startingSize)
+{
+    // Clear errno to allow strerror on error.
+    errno = 0;
+    // Allow access to inital pages that will hold mspace.
+    mprotect(begin, morecoreStart, PROT_READ | PROT_WRITE);
+    // Create mspace using our backing storage starting at begin and with a footprint of
+    // morecoreStart. Don't use an internal dlmalloc lock. When morecoreStart bytes of memory are
+    // exhausted morecore will be called.
+    mspace msp = create_mspace_with_base(begin, morecoreStart, false /*locked*/);
+    if (msp != NULL) {
+        // Do not allow morecore requests to succeed beyond the starting size of the heap.
+        mspace_set_footprint_limit(msp, startingSize);
+    } else {
+        ALOGE("create_mspace_with_base failed %s", strerror(errno));
+    }
+    return msp;
+}
+
+/*
+ * Service request from DlMalloc to increase heap size.
+ */
+void* dvmHeapSourceMorecore(void* mspace, intptr_t increment)
+{
+    Heap* heap = NULL;
+    for (size_t i = 0; i < gHs->numHeaps; i++) {
+        if (gHs->heaps[i].msp == mspace) {
+            heap = &gHs->heaps[i];
+            break;
+        }
+    }
+    if (heap == NULL) {
+        ALOGE("Failed to find heap for mspace %p", mspace);
+        dvmAbort();
+    }
+    char* original_brk = heap->brk;
+    if (increment != 0) {
+        char* new_brk = original_brk + increment;
+        if (increment > 0) {
+            // Should never be asked to increase the allocation beyond the capacity of the space.
+            // Enforced by mspace_set_footprint_limit.
+            assert(new_brk <= heap->limit);
+            mprotect(original_brk, increment, PROT_READ | PROT_WRITE);
+        } else {
+            // Should never be asked for negative footprint (ie before base).
+            assert(original_brk + increment > heap->base);
+            // Advise we don't need the pages and protect them.
+            size_t size = -increment;
+            madvise(new_brk, size, MADV_DONTNEED);
+            mprotect(new_brk, size, PROT_NONE);
+        }
+        // Update brk.
+        heap->brk = new_brk;
+    }
+    return original_brk;
+}
+
+const size_t kInitialMorecoreStart = SYSTEM_PAGE_SIZE;
+/*
+ * Add the initial heap.  Returns false if the initial heap was
+ * already added to the heap source.
+ */
+static bool addInitialHeap(HeapSource *hs, mspace msp, size_t maximumSize)
+{
+    assert(hs != NULL);
+    assert(msp != NULL);
+    if (hs->numHeaps != 0) {
+        return false;
+    }
+    hs->heaps[0].msp = msp;
+    hs->heaps[0].maximumSize = maximumSize;
+    hs->heaps[0].concurrentStartBytes = SIZE_MAX;
+    hs->heaps[0].base = hs->heapBase;
+    hs->heaps[0].limit = hs->heapBase + maximumSize;
+    hs->heaps[0].brk = hs->heapBase + kInitialMorecoreStart;
+    hs->numHeaps = 1;
+    return true;
+}
+
+/*
+ * Adds an additional heap to the heap source.  Returns false if there
+ * are too many heaps or insufficient free space to add another heap.
+ */
+static bool addNewHeap(HeapSource *hs)
+{
+    Heap heap;
+
+    assert(hs != NULL);
+    if (hs->numHeaps >= HEAP_SOURCE_MAX_HEAP_COUNT) {
+        ALOGE("Attempt to create too many heaps (%zd >= %zd)",
+                hs->numHeaps, HEAP_SOURCE_MAX_HEAP_COUNT);
+        dvmAbort();
+        return false;
+    }
+
+    memset(&heap, 0, sizeof(heap));
+
+    /*
+     * Heap storage comes from a common virtual memory reservation.
+     * The new heap will start on the page after the old heap.
+     */
+    char *base = hs->heaps[0].brk;
+    size_t overhead = base - hs->heaps[0].base;
+    assert(((size_t)hs->heaps[0].base & (SYSTEM_PAGE_SIZE - 1)) == 0);
+
+    if (overhead + hs->minFree >= hs->maximumSize) {
+        LOGE_HEAP("No room to create any more heaps "
+                  "(%zd overhead, %zd max)",
+                  overhead, hs->maximumSize);
+        return false;
+    }
+    size_t morecoreStart = SYSTEM_PAGE_SIZE;
+    heap.maximumSize = hs->growthLimit - overhead;
+    heap.concurrentStartBytes = hs->minFree - CONCURRENT_START;
+    heap.base = base;
+    heap.limit = heap.base + heap.maximumSize;
+    heap.brk = heap.base + morecoreStart;
+    heap.msp = createMspace(base, morecoreStart, hs->minFree);
+    if (heap.msp == NULL) {
+        return false;
+    }
+
+    /* Don't let the soon-to-be-old heap grow any further.
+     */
+    hs->heaps[0].maximumSize = overhead;
+    hs->heaps[0].limit = base;
+    mspace_set_footprint_limit(hs->heaps[0].msp, overhead);
+
+    /* Put the new heap in the list, at heaps[0].
+     * Shift existing heaps down.
+     */
+    memmove(&hs->heaps[1], &hs->heaps[0], hs->numHeaps * sizeof(hs->heaps[0]));
+    hs->heaps[0] = heap;
+    hs->numHeaps++;
+
+    return true;
+}
+
+/*
+ * The garbage collection daemon.  Initiates a concurrent collection
+ * when signaled.  Also periodically trims the heaps when a few seconds
+ * have elapsed since the last concurrent GC.
+ */
+static void *gcDaemonThread(void* arg)
+{
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+    dvmLockMutex(&gHs->gcThreadMutex);
+    while (gHs->gcThreadShutdown != true) {
+        bool trim = false;
+        if (gHs->gcThreadTrimNeeded) {
+            int result = dvmRelativeCondWait(&gHs->gcThreadCond, &gHs->gcThreadMutex,
+                    HEAP_TRIM_IDLE_TIME_MS, 0);
+            if (result == ETIMEDOUT) {
+                /* Timed out waiting for a GC request, schedule a heap trim. */
+                trim = true;
+            }
+        } else {
+            dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
+        }
+
+        dvmLockHeap();
+        /*
+         * Another thread may have started a concurrent garbage
+         * collection before we were scheduled.  Check for this
+         * condition before proceeding.
+         */
+        if (!gDvm.gcHeap->gcRunning) {
+            dvmChangeStatus(NULL, THREAD_RUNNING);
+            if (trim) {
+                trimHeaps();
+                gHs->gcThreadTrimNeeded = false;
+            } else {
+                dvmCollectGarbageInternal(GC_CONCURRENT);
+                gHs->gcThreadTrimNeeded = true;
+            }
+            dvmChangeStatus(NULL, THREAD_VMWAIT);
+        }
+        dvmUnlockHeap();
+    }
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+    return NULL;
+}
+
+static bool gcDaemonStartup()
+{
+    dvmInitMutex(&gHs->gcThreadMutex);
+    pthread_cond_init(&gHs->gcThreadCond, NULL);
+    gHs->gcThreadShutdown = false;
+    gHs->hasGcThread = dvmCreateInternalThread(&gHs->gcThread, "GC",
+                                               gcDaemonThread, NULL);
+    return gHs->hasGcThread;
+}
+
+static void gcDaemonShutdown()
+{
+    if (gHs->hasGcThread) {
+        dvmLockMutex(&gHs->gcThreadMutex);
+        gHs->gcThreadShutdown = true;
+        dvmSignalCond(&gHs->gcThreadCond);
+        dvmUnlockMutex(&gHs->gcThreadMutex);
+        pthread_join(gHs->gcThread, NULL);
+    }
+}
+
+/*
+ * Create a stack big enough for the worst possible case, where the
+ * heap is perfectly full of the smallest object.
+ * TODO: be better about memory usage; use a smaller stack with
+ *       overflow detection and recovery.
+ */
+static bool allocMarkStack(GcMarkStack *stack, size_t maximumSize)
+{
+    const char *name = "dalvik-mark-stack";
+    void *addr;
+
+    assert(stack != NULL);
+    stack->length = maximumSize * sizeof(Object*) /
+        (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
+    addr = dvmAllocRegion(stack->length, PROT_READ | PROT_WRITE, name);
+    if (addr == NULL) {
+        return false;
+    }
+    stack->base = (const Object **)addr;
+    stack->limit = (const Object **)((char *)addr + stack->length);
+    stack->top = NULL;
+    madvise(stack->base, stack->length, MADV_DONTNEED);
+    return true;
+}
+
+static void freeMarkStack(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    munmap(stack->base, stack->length);
+    memset(stack, 0, sizeof(*stack));
+}
+
+/*
+ * Initializes the heap source; must be called before any other
+ * dvmHeapSource*() functions.  Returns a GcHeap structure
+ * allocated from the heap source.
+ */
+GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize,
+                             size_t growthLimit)
+{
+    GcHeap *gcHeap;
+    HeapSource *hs;
+    mspace msp;
+    size_t length;
+    void *base;
+
+    assert(gHs == NULL);
+
+    if (!(startSize <= growthLimit && growthLimit <= maximumSize)) {
+        ALOGE("Bad heap size parameters (start=%zd, max=%zd, limit=%zd)",
+             startSize, maximumSize, growthLimit);
+        return NULL;
+    }
+
+    /*
+     * Allocate a contiguous region of virtual memory to subdivided
+     * among the heaps managed by the garbage collector.
+     */
+    length = ALIGN_UP_TO_PAGE_SIZE(maximumSize);
+    base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap");
+    if (base == NULL) {
+        return NULL;
+    }
+
+    /* Create an unlocked dlmalloc mspace to use as
+     * a heap source.
+     */
+    msp = createMspace(base, kInitialMorecoreStart, startSize);
+    if (msp == NULL) {
+        goto fail;
+    }
+
+    gcHeap = (GcHeap *)calloc(1, sizeof(*gcHeap));
+    if (gcHeap == NULL) {
+        LOGE_HEAP("Can't allocate heap descriptor");
+        goto fail;
+    }
+
+    hs = (HeapSource *)calloc(1, sizeof(*hs));
+    if (hs == NULL) {
+        LOGE_HEAP("Can't allocate heap source");
+        free(gcHeap);
+        goto fail;
+    }
+
+    hs->targetUtilization = gDvm.heapTargetUtilization * HEAP_UTILIZATION_MAX;
+    hs->minFree = gDvm.heapMinFree;
+    hs->maxFree = gDvm.heapMaxFree;
+    hs->startSize = startSize;
+    hs->maximumSize = maximumSize;
+    hs->growthLimit = growthLimit;
+    hs->idealSize = startSize;
+    hs->softLimit = SIZE_MAX;    // no soft limit at first
+    hs->numHeaps = 0;
+    hs->sawZygote = gDvm.zygote;
+    hs->hasGcThread = false;
+    hs->heapBase = (char *)base;
+    hs->heapLength = length;
+
+    if (hs->maxFree > hs->maximumSize) {
+      hs->maxFree = hs->maximumSize;
+    }
+    if (hs->minFree < CONCURRENT_START) {
+      hs->minFree = CONCURRENT_START;
+    } else if (hs->minFree > hs->maxFree) {
+      hs->minFree = hs->maxFree;
+    }
+
+    if (!addInitialHeap(hs, msp, growthLimit)) {
+        LOGE_HEAP("Can't add initial heap");
+        goto fail;
+    }
+    if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
+        LOGE_HEAP("Can't create liveBits");
+        goto fail;
+    }
+    if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
+        LOGE_HEAP("Can't create markBits");
+        dvmHeapBitmapDelete(&hs->liveBits);
+        goto fail;
+    }
+    if (!allocMarkStack(&gcHeap->markContext.stack, hs->maximumSize)) {
+        ALOGE("Can't create markStack");
+        dvmHeapBitmapDelete(&hs->markBits);
+        dvmHeapBitmapDelete(&hs->liveBits);
+        goto fail;
+    }
+    gcHeap->markContext.bitmap = &hs->markBits;
+    gcHeap->heapSource = hs;
+
+    gHs = hs;
+    return gcHeap;
+
+fail:
+    munmap(base, length);
+    return NULL;
+}
+
+bool dvmHeapSourceStartupAfterZygote()
+{
+    return gDvm.concurrentMarkSweep ? gcDaemonStartup() : true;
+}
+
+/*
+ * This is called while in zygote mode, right before we fork() for the
+ * first time.  We create a heap for all future zygote process allocations,
+ * in an attempt to avoid touching pages in the zygote heap.  (This would
+ * probably be unnecessary if we had a compacting GC -- the source of our
+ * troubles is small allocations filling in the gaps from larger ones.)
+ */
+bool dvmHeapSourceStartupBeforeFork()
+{
+    HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"
+
+    HS_BOILERPLATE();
+
+    assert(gDvm.zygote);
+
+    if (!gDvm.newZygoteHeapAllocated) {
+       /* Ensure heaps are trimmed to minimize footprint pre-fork.
+        */
+        trimHeaps();
+        /* Create a new heap for post-fork zygote allocations.  We only
+         * try once, even if it fails.
+         */
+        ALOGV("Splitting out new zygote heap");
+        gDvm.newZygoteHeapAllocated = true;
+        return addNewHeap(hs);
+    }
+    return true;
+}
+
+void dvmHeapSourceThreadShutdown()
+{
+    if (gDvm.gcHeap != NULL && gDvm.concurrentMarkSweep) {
+        gcDaemonShutdown();
+    }
+}
+
+/*
+ * Tears down the entire GcHeap structure and all of the substructures
+ * attached to it.  This call has the side effect of setting the given
+ * gcHeap pointer and gHs to NULL.
+ */
+void dvmHeapSourceShutdown(GcHeap **gcHeap)
+{
+    assert(gcHeap != NULL);
+    if (*gcHeap != NULL && (*gcHeap)->heapSource != NULL) {
+        HeapSource *hs = (*gcHeap)->heapSource;
+        dvmHeapBitmapDelete(&hs->liveBits);
+        dvmHeapBitmapDelete(&hs->markBits);
+        freeMarkStack(&(*gcHeap)->markContext.stack);
+        munmap(hs->heapBase, hs->heapLength);
+        free(hs);
+        gHs = NULL;
+        free(*gcHeap);
+        *gcHeap = NULL;
+    }
+}
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase()
+{
+    return gHs->heapBase;
+}
+
+/*
+ * Returns a high water mark, between base and limit all objects must have been
+ * allocated.
+ */
+void *dvmHeapSourceGetLimit()
+{
+    HeapSource *hs = gHs;
+    void *max_brk = hs->heaps[0].brk;
+
+#ifndef NDEBUG
+    for (size_t i = 1; i < hs->numHeaps; i++) {
+        Heap *const heap = &hs->heaps[i];
+        void *heap_brk = heap->brk;
+        assert (max_brk > heap_brk);
+    }
+#endif
+    return max_brk;
+}
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ *
+ * Caller must hold the heap lock.
+ */
+size_t dvmHeapSourceGetValue(HeapSourceValueSpec spec, size_t perHeapStats[],
+                             size_t arrayLen)
+{
+    HeapSource *hs = gHs;
+    size_t value = 0;
+    size_t total = 0;
+
+    HS_BOILERPLATE();
+
+    assert(arrayLen >= hs->numHeaps || perHeapStats == NULL);
+    for (size_t i = 0; i < hs->numHeaps; i++) {
+        Heap *const heap = &hs->heaps[i];
+
+        switch (spec) {
+        case HS_FOOTPRINT:
+            value = heap->brk - heap->base;
+            assert(value == mspace_footprint(heap->msp));
+            break;
+        case HS_ALLOWED_FOOTPRINT:
+            value = mspace_footprint_limit(heap->msp);
+            break;
+        case HS_BYTES_ALLOCATED:
+            value = heap->bytesAllocated;
+            break;
+        case HS_OBJECTS_ALLOCATED:
+            value = heap->objectsAllocated;
+            break;
+        default:
+            // quiet gcc
+            break;
+        }
+        if (perHeapStats) {
+            perHeapStats[i] = value;
+        }
+        total += value;
+    }
+    return total;
+}
+
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps)
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    assert(numHeaps <= hs->numHeaps);
+    for (size_t i = 0; i < numHeaps; ++i) {
+        base[i] = (uintptr_t)hs->heaps[i].base;
+        max[i] = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
+    }
+}
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits()
+{
+    HS_BOILERPLATE();
+
+    return &gHs->liveBits;
+}
+
+/*
+ * Get the bitmap representing all marked objects.
+ */
+HeapBitmap *dvmHeapSourceGetMarkBits()
+{
+    HS_BOILERPLATE();
+
+    return &gHs->markBits;
+}
+
+void dvmHeapSourceSwapBitmaps()
+{
+    HeapBitmap tmp = gHs->liveBits;
+    gHs->liveBits = gHs->markBits;
+    gHs->markBits = tmp;
+}
+
+void dvmHeapSourceZeroMarkBitmap()
+{
+    HS_BOILERPLATE();
+
+    dvmHeapBitmapZero(&gHs->markBits);
+}
+
+void dvmMarkImmuneObjects(const char *immuneLimit)
+{
+    /*
+     * Copy the contents of the live bit vector for immune object
+     * range into the mark bit vector.
+     */
+    /* The only values generated by dvmHeapSourceGetImmuneLimit() */
+    assert(immuneLimit == gHs->heaps[0].base ||
+           immuneLimit == NULL);
+    assert(gHs->liveBits.base == gHs->markBits.base);
+    assert(gHs->liveBits.bitsLen == gHs->markBits.bitsLen);
+    /* heap[0] is never immune */
+    assert(gHs->heaps[0].base >= immuneLimit);
+    assert(gHs->heaps[0].limit > immuneLimit);
+
+    for (size_t i = 1; i < gHs->numHeaps; ++i) {
+        if (gHs->heaps[i].base < immuneLimit) {
+            assert(gHs->heaps[i].limit <= immuneLimit);
+            /* Compute the number of words to copy in the bitmap. */
+            size_t index = HB_OFFSET_TO_INDEX(
+                (uintptr_t)gHs->heaps[i].base - gHs->liveBits.base);
+            /* Compute the starting offset in the live and mark bits. */
+            char *src = (char *)(gHs->liveBits.bits + index);
+            char *dst = (char *)(gHs->markBits.bits + index);
+            /* Compute the number of bytes of the live bitmap to copy. */
+            size_t length = HB_OFFSET_TO_BYTE_INDEX(
+                gHs->heaps[i].limit - gHs->heaps[i].base);
+            /* Do the copy. */
+            memcpy(dst, src, length);
+            /* Make sure max points to the address of the highest set bit. */
+            if (gHs->markBits.max < (uintptr_t)gHs->heaps[i].limit) {
+                gHs->markBits.max = (uintptr_t)gHs->heaps[i].limit;
+            }
+        }
+    }
+}
+
+/*
+ * Allocates <n> bytes of zeroed data.
+ */
+void* dvmHeapSourceAlloc(size_t n)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    Heap* heap = hs2heap(hs);
+    if (heap->bytesAllocated + n > hs->softLimit) {
+        /*
+         * This allocation would push us over the soft limit; act as
+         * if the heap is full.
+         */
+        LOGV_HEAP("softLimit of %zd.%03zdMB hit for %zd-byte allocation",
+                  FRACTIONAL_MB(hs->softLimit), n);
+        return NULL;
+    }
+    void* ptr = mspace_calloc(heap->msp, 1, n);
+    if (ptr == NULL) {
+        return NULL;
+    }
+    countAllocation(heap, ptr);
+    /*
+     * Check to see if a concurrent GC should be initiated.
+     */
+    if (gDvm.gcHeap->gcRunning || !hs->hasGcThread) {
+        /*
+         * The garbage collector thread is already running or has yet
+         * to be started.  Do nothing.
+         */
+        return ptr;
+    }
+    if (heap->bytesAllocated > heap->concurrentStartBytes) {
+        /*
+         * We have exceeded the allocation threshold.  Wake up the
+         * garbage collector.
+         */
+        dvmSignalCond(&gHs->gcThreadCond);
+    }
+    return ptr;
+}
+
+/* Remove any hard limits, try to allocate, and shrink back down.
+ * Last resort when trying to allocate an object.
+ */
+static void* heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n)
+{
+    /* Grow as much as possible, but don't let the real footprint
+     * go over the absolute max.
+     */
+    size_t max = heap->maximumSize;
+
+    mspace_set_footprint_limit(heap->msp, max);
+    void* ptr = dvmHeapSourceAlloc(n);
+
+    /* Shrink back down as small as possible.  Our caller may
+     * readjust max_allowed to a more appropriate value.
+     */
+    mspace_set_footprint_limit(heap->msp,
+                               mspace_footprint(heap->msp));
+    return ptr;
+}
+
+/*
+ * Allocates <n> bytes of zeroed data, growing as much as possible
+ * if necessary.
+ */
+void* dvmHeapSourceAllocAndGrow(size_t n)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    Heap* heap = hs2heap(hs);
+    void* ptr = dvmHeapSourceAlloc(n);
+    if (ptr != NULL) {
+        return ptr;
+    }
+
+    size_t oldIdealSize = hs->idealSize;
+    if (isSoftLimited(hs)) {
+        /* We're soft-limited.  Try removing the soft limit to
+         * see if we can allocate without actually growing.
+         */
+        hs->softLimit = SIZE_MAX;
+        ptr = dvmHeapSourceAlloc(n);
+        if (ptr != NULL) {
+            /* Removing the soft limit worked;  fix things up to
+             * reflect the new effective ideal size.
+             */
+            snapIdealFootprint();
+            return ptr;
+        }
+        // softLimit intentionally left at SIZE_MAX.
+    }
+
+    /* We're not soft-limited.  Grow the heap to satisfy the request.
+     * If this call fails, no footprints will have changed.
+     */
+    ptr = heapAllocAndGrow(hs, heap, n);
+    if (ptr != NULL) {
+        /* The allocation succeeded.  Fix up the ideal size to
+         * reflect any footprint modifications that had to happen.
+         */
+        snapIdealFootprint();
+    } else {
+        /* We just couldn't do it.  Restore the original ideal size,
+         * fixing up softLimit if necessary.
+         */
+        setIdealFootprint(oldIdealSize);
+    }
+    return ptr;
+}
+
+/*
+ * Frees the first numPtrs objects in the ptrs list and returns the
+ * amount of reclaimed storage. The list must contain addresses all in
+ * the same mspace, and must be in increasing order. This implies that
+ * there are no duplicates, and no entries are NULL.
+ */
+size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs)
+{
+    HS_BOILERPLATE();
+
+    if (numPtrs == 0) {
+        return 0;
+    }
+
+    assert(ptrs != NULL);
+    assert(*ptrs != NULL);
+    Heap* heap = ptr2heap(gHs, *ptrs);
+    size_t numBytes = 0;
+    if (heap != NULL) {
+        mspace msp = heap->msp;
+        // Calling mspace_free on shared heaps disrupts sharing too
+        // much. For heap[0] -- the 'active heap' -- we call
+        // mspace_free, but on the other heaps we only do some
+        // accounting.
+        if (heap == gHs->heaps) {
+            // Count freed objects.
+            for (size_t i = 0; i < numPtrs; i++) {
+                assert(ptrs[i] != NULL);
+                assert(ptr2heap(gHs, ptrs[i]) == heap);
+                countFree(heap, ptrs[i], &numBytes);
+            }
+            // Bulk free ptrs.
+            mspace_bulk_free(msp, ptrs, numPtrs);
+        } else {
+            // This is not an 'active heap'. Only do the accounting.
+            for (size_t i = 0; i < numPtrs; i++) {
+                assert(ptrs[i] != NULL);
+                assert(ptr2heap(gHs, ptrs[i]) == heap);
+                countFree(heap, ptrs[i], &numBytes);
+            }
+        }
+    }
+    return numBytes;
+}
+
+/*
+ * Returns true iff <ptr> is in the heap source.
+ */
+bool dvmHeapSourceContainsAddress(const void *ptr)
+{
+    HS_BOILERPLATE();
+
+    return (dvmHeapSourceGetBase() <= ptr) && (ptr <= dvmHeapSourceGetLimit());
+}
+
+/*
+ * Returns true iff <ptr> was allocated from the heap source.
+ */
+bool dvmHeapSourceContains(const void *ptr)
+{
+    HS_BOILERPLATE();
+
+    if (dvmHeapSourceContainsAddress(ptr)) {
+        return dvmHeapBitmapIsObjectBitSet(&gHs->liveBits, ptr) != 0;
+    }
+    return false;
+}
+
+bool dvmIsZygoteObject(const Object* obj)
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    if (dvmHeapSourceContains(obj) && hs->sawZygote) {
+        Heap *heap = ptr2heap(hs, obj);
+        if (heap != NULL) {
+            /* If the object is not in the active heap, we assume that
+             * it was allocated as part of zygote.
+             */
+            return heap != hs->heaps;
+        }
+    }
+    /* The pointer is outside of any known heap, or we are not
+     * running in zygote mode.
+     */
+    return false;
+}
+
+/*
+ * Returns the number of usable bytes in an allocated chunk; the size
+ * may be larger than the size passed to dvmHeapSourceAlloc().
+ */
+size_t dvmHeapSourceChunkSize(const void *ptr)
+{
+    HS_BOILERPLATE();
+
+    Heap* heap = ptr2heap(gHs, ptr);
+    if (heap != NULL) {
+        return mspace_usable_size(ptr);
+    }
+    return 0;
+}
+
+/*
+ * Returns the number of bytes that the heap source has allocated
+ * from the system using sbrk/mmap, etc.
+ *
+ * Caller must hold the heap lock.
+ */
+size_t dvmHeapSourceFootprint()
+{
+    HS_BOILERPLATE();
+
+//TODO: include size of bitmaps?
+    return oldHeapOverhead(gHs, true);
+}
+
+static size_t getMaximumSize(const HeapSource *hs)
+{
+    return hs->growthLimit;
+}
+
+/*
+ * Returns the current maximum size of the heap source respecting any
+ * growth limits.
+ */
+size_t dvmHeapSourceGetMaximumSize()
+{
+    HS_BOILERPLATE();
+    return getMaximumSize(gHs);
+}
+
+/*
+ * Removes any growth limits.  Allows the user to allocate up to the
+ * maximum heap size.
+ */
+void dvmClearGrowthLimit()
+{
+    HS_BOILERPLATE();
+    dvmLockHeap();
+    dvmWaitForConcurrentGcToComplete();
+    gDvm.gcHeap->cardTableLength = gDvm.gcHeap->cardTableMaxLength;
+    gHs->growthLimit = gHs->maximumSize;
+    size_t overhead = oldHeapOverhead(gHs, false);
+    gHs->heaps[0].maximumSize = gHs->maximumSize - overhead;
+    gHs->heaps[0].limit = gHs->heaps[0].base + gHs->heaps[0].maximumSize;
+    dvmUnlockHeap();
+}
+
+/*
+ * Return the real bytes used by old heaps plus the soft usage of the
+ * current heap.  When a soft limit is in effect, this is effectively
+ * what it's compared against (though, in practice, it only looks at
+ * the current heap).
+ */
+static size_t getSoftFootprint(bool includeActive)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    size_t ret = oldHeapOverhead(hs, false);
+    if (includeActive) {
+        ret += hs->heaps[0].bytesAllocated;
+    }
+
+    return ret;
+}
+
+/*
+ * Gets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.
+ */
+size_t dvmHeapSourceGetIdealFootprint()
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    return hs->idealSize;
+}
+
+/*
+ * Sets the soft limit, handling any necessary changes to the allowed
+ * footprint of the active heap.
+ */
+static void setSoftLimit(HeapSource *hs, size_t softLimit)
+{
+    /* Compare against the actual footprint, rather than the
+     * max_allowed, because the heap may not have grown all the
+     * way to the allowed size yet.
+     */
+    mspace msp = hs->heaps[0].msp;
+    size_t currentHeapSize = mspace_footprint(msp);
+    if (softLimit < currentHeapSize) {
+        /* Don't let the heap grow any more, and impose a soft limit.
+         */
+        mspace_set_footprint_limit(msp, currentHeapSize);
+        hs->softLimit = softLimit;
+    } else {
+        /* Let the heap grow to the requested max, and remove any
+         * soft limit, if set.
+         */
+        mspace_set_footprint_limit(msp, softLimit);
+        hs->softLimit = SIZE_MAX;
+    }
+}
+
+/*
+ * Sets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.  Clamps to the appropriate maximum
+ * value.
+ */
+static void setIdealFootprint(size_t max)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    size_t maximumSize = getMaximumSize(hs);
+    if (max > maximumSize) {
+        LOGI_HEAP("Clamp target GC heap from %zd.%03zdMB to %u.%03uMB",
+                FRACTIONAL_MB(max),
+                FRACTIONAL_MB(maximumSize));
+        max = maximumSize;
+    }
+
+    /* Convert max into a size that applies to the active heap.
+     * Old heaps will count against the ideal size.
+     */
+    size_t overhead = getSoftFootprint(false);
+    size_t activeMax;
+    if (overhead < max) {
+        activeMax = max - overhead;
+    } else {
+        activeMax = 0;
+    }
+
+    setSoftLimit(hs, activeMax);
+    hs->idealSize = max;
+}
+
+/*
+ * Make the ideal footprint equal to the current footprint.
+ */
+static void snapIdealFootprint()
+{
+    HS_BOILERPLATE();
+
+    setIdealFootprint(getSoftFootprint(true));
+}
+
+/*
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+float dvmGetTargetHeapUtilization()
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    return (float)hs->targetUtilization / (float)HEAP_UTILIZATION_MAX;
+}
+
+/*
+ * Sets the new ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+void dvmSetTargetHeapUtilization(float newTarget)
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    /* Clamp it to a reasonable range.
+     */
+    // TODO: This may need some tuning.
+    if (newTarget < 0.2) {
+        newTarget = 0.2;
+    } else if (newTarget > 0.8) {
+        newTarget = 0.8;
+    }
+
+    hs->targetUtilization =
+            (size_t)(newTarget * (float)HEAP_UTILIZATION_MAX);
+    ALOGV("Set heap target utilization to %zd/%d (%f)",
+            hs->targetUtilization, HEAP_UTILIZATION_MAX, newTarget);
+}
+
+/*
+ * Given the size of a live set, returns the ideal heap size given
+ * the current target utilization and MIN/MAX values.
+ */
+static size_t getUtilizationTarget(const HeapSource* hs, size_t liveSize)
+{
+    /* Use the current target utilization ratio to determine the
+     * ideal heap size based on the size of the live set.
+     */
+    size_t targetSize = (liveSize / hs->targetUtilization) * HEAP_UTILIZATION_MAX;
+
+    /* Cap the amount of free space, though, so we don't end up
+     * with, e.g., 8MB of free space when the live set size hits 8MB.
+     */
+    if (targetSize > liveSize + hs->maxFree) {
+        targetSize = liveSize + hs->maxFree;
+    } else if (targetSize < liveSize + hs->minFree) {
+        targetSize = liveSize + hs->minFree;
+    }
+    return targetSize;
+}
+
+/*
+ * Given the current contents of the active heap, increase the allowed
+ * heap footprint to match the target utilization ratio.  This
+ * should only be called immediately after a full mark/sweep.
+ */
+void dvmHeapSourceGrowForUtilization()
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    Heap* heap = hs2heap(hs);
+
+    /* Use the current target utilization ratio to determine the
+     * ideal heap size based on the size of the live set.
+     * Note that only the active heap plays any part in this.
+     *
+     * Avoid letting the old heaps influence the target free size,
+     * because they may be full of objects that aren't actually
+     * in the working set.  Just look at the allocated size of
+     * the current heap.
+     */
+    size_t currentHeapUsed = heap->bytesAllocated;
+    size_t targetHeapSize = getUtilizationTarget(hs, currentHeapUsed);
+
+    /* The ideal size includes the old heaps; add overhead so that
+     * it can be immediately subtracted again in setIdealFootprint().
+     * If the target heap size would exceed the max, setIdealFootprint()
+     * will clamp it to a legal value.
+     */
+    size_t overhead = getSoftFootprint(false);
+    setIdealFootprint(targetHeapSize + overhead);
+
+    size_t freeBytes = getAllocLimit(hs);
+    if (freeBytes < CONCURRENT_MIN_FREE) {
+        /* Not enough free memory to allow a concurrent GC. */
+        heap->concurrentStartBytes = SIZE_MAX;
+    } else {
+        heap->concurrentStartBytes = freeBytes - CONCURRENT_START;
+    }
+}
+
+/*
+ * Return free pages to the system.
+ * TODO: move this somewhere else, especially the native heap part.
+ */
+static void releasePagesInRange(void* start, void* end, size_t used_bytes,
+                                void* releasedBytes)
+{
+    if (used_bytes == 0) {
+        /*
+         * We have a range of memory we can try to madvise()
+         * back. Linux requires that the madvise() start address is
+         * page-aligned.  We also align the end address.
+         */
+        start = (void *)ALIGN_UP_TO_PAGE_SIZE(start);
+        end = (void *)((size_t)end & ~(SYSTEM_PAGE_SIZE - 1));
+        if (end > start) {
+            size_t length = (char *)end - (char *)start;
+            madvise(start, length, MADV_DONTNEED);
+            *(size_t *)releasedBytes += length;
+        }
+    }
+}
+
+/*
+ * Return unused memory to the system if possible.
+ */
+static void trimHeaps()
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    size_t heapBytes = 0;
+    for (size_t i = 0; i < hs->numHeaps; i++) {
+        Heap *heap = &hs->heaps[i];
+
+        /* Return the wilderness chunk to the system. */
+        mspace_trim(heap->msp, 0);
+
+        /* Return any whole free pages to the system. */
+        mspace_inspect_all(heap->msp, releasePagesInRange, &heapBytes);
+    }
+
+    /* Same for the native heap. */
+    dlmalloc_trim(0);
+    size_t nativeBytes = 0;
+    dlmalloc_inspect_all(releasePagesInRange, &nativeBytes);
+
+    LOGD_HEAP("madvised %zd (GC) + %zd (native) = %zd total bytes",
+            heapBytes, nativeBytes, heapBytes + nativeBytes);
+}
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
+                                       size_t used_bytes, void* arg),
+                       void *arg)
+{
+    HS_BOILERPLATE();
+
+    /* Walk the heaps from oldest to newest.
+     */
+//TODO: do this in address order
+    HeapSource *hs = gHs;
+    for (size_t i = hs->numHeaps; i > 0; --i) {
+        mspace_inspect_all(hs->heaps[i-1].msp, callback, arg);
+        callback(NULL, NULL, 0, arg);  // Indicate end of a heap.
+    }
+}
+
+/*
+ * Gets the number of heaps available in the heap source.
+ *
+ * Caller must hold the heap lock, because gHs caches a field
+ * in gDvm.gcHeap.
+ */
+size_t dvmHeapSourceGetNumHeaps()
+{
+    HS_BOILERPLATE();
+
+    return gHs->numHeaps;
+}
+
+void *dvmHeapSourceGetImmuneLimit(bool isPartial)
+{
+    if (isPartial) {
+        return hs2heap(gHs)->base;
+    } else {
+        return NULL;
+    }
+}
diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h
new file mode 100644
index 0000000..e1f6820
--- /dev/null
+++ b/vm/alloc/HeapSource.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.
+ */
+#ifndef DALVIK_HEAP_SOURCE_H_
+#define DALVIK_HEAP_SOURCE_H_
+
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h" // for GcHeap
+
+/* dlmalloc uses one size_t per allocated chunk.
+ */
+#define HEAP_SOURCE_CHUNK_OVERHEAD         (1 * sizeof (size_t))
+
+/* The largest number of separate heaps we can handle.
+ */
+#define HEAP_SOURCE_MAX_HEAP_COUNT 2
+
+enum HeapSourceValueSpec {
+    HS_FOOTPRINT,
+    HS_ALLOWED_FOOTPRINT,
+    HS_BYTES_ALLOCATED,
+    HS_OBJECTS_ALLOCATED
+};
+
+/*
+ * Initializes the heap source; must be called before any other
+ * dvmHeapSource*() functions.
+ */
+GcHeap *dvmHeapSourceStartup(size_t startingSize,
+                             size_t maximumSize,
+                             size_t growthLimit);
+
+/*
+ * If the HeapSource was created while in zygote mode, this
+ * will create a new heap for post-zygote allocations.
+ * Having a separate heap should maximize the number of pages
+ * that a given app_process shares with the zygote process.
+ */
+bool dvmHeapSourceStartupAfterZygote(void);
+
+/*
+ * If the HeapSource was created while in zygote mode, this
+ * will create an additional zygote heap before the first fork().
+ * Having a separate heap should reduce the number of shared
+ * pages subsequently touched by the zygote process.
+ */
+bool dvmHeapSourceStartupBeforeFork(void);
+
+/*
+ * Shutdown any threads internal to the heap source.  This should be
+ * called before the heap source itself is shutdown.
+ */
+void dvmHeapSourceThreadShutdown(void);
+
+/*
+ * Tears down the heap source and frees any resources associated with it.
+ */
+void dvmHeapSourceShutdown(GcHeap **gcHeap);
+
+/*
+ * Returns the base and inclusive max addresses of the heap source
+ * heaps.  The base and max values are suitable for passing directly
+ * to the bitmap sweeping routine.
+ */
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps);
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits(void);
+
+/*
+ * Get the bitmap representing all marked objects.
+ */
+HeapBitmap *dvmHeapSourceGetMarkBits(void);
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase(void);
+
+/*
+ * Returns a high water mark, between base and limit all objects must have been
+ * allocated.
+ */
+void *dvmHeapSourceGetLimit(void);
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ */
+size_t dvmHeapSourceGetValue(HeapSourceValueSpec spec,
+                             size_t perHeapStats[], size_t arrayLen);
+
+/*
+ * Allocates <n> bytes of zeroed data.
+ */
+void *dvmHeapSourceAlloc(size_t n);
+
+/*
+ * Allocates <n> bytes of zeroed data, growing up to absoluteMaxSize
+ * if necessary.
+ */
+void *dvmHeapSourceAllocAndGrow(size_t n);
+
+/*
+ * Frees the first numPtrs objects in the ptrs list and returns the
+ * amount of reclaimed storage.  The list must contain addresses all
+ * in the same mspace, and must be in increasing order. This implies
+ * that there are no duplicates, and no entries are NULL.
+ */
+size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs);
+
+/*
+ * Returns true iff <ptr> was allocated from the heap source.
+ */
+bool dvmHeapSourceContains(const void *ptr);
+
+/*
+ * Returns true iff <ptr> is within the address space managed by heap source.
+ */
+bool dvmHeapSourceContainsAddress(const void *ptr);
+
+/*
+ * Returns the number of usable bytes in an allocated chunk; the size
+ * may be larger than the size passed to dvmHeapSourceAlloc().
+ */
+size_t dvmHeapSourceChunkSize(const void *ptr);
+
+/*
+ * Returns the number of bytes that the heap source has allocated
+ * from the system using sbrk/mmap, etc.
+ */
+size_t dvmHeapSourceFootprint(void);
+
+/*
+ * Gets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.
+ */
+size_t dvmHeapSourceGetIdealFootprint(void);
+
+/*
+ * Given the current contents of the heap, increase the allowed
+ * heap footprint to match the target utilization ratio.  This
+ * should only be called immediately after a full mark/sweep.
+ */
+void dvmHeapSourceGrowForUtilization(void);
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
+                                       size_t used_bytes, void* arg),
+                       void *arg);
+/*
+ * Gets the number of heaps available in the heap source.
+ */
+size_t dvmHeapSourceGetNumHeaps(void);
+
+/*
+ * Exchanges the mark and object bitmaps.
+ */
+void dvmHeapSourceSwapBitmaps(void);
+
+/*
+ * Zeroes the mark bitmap.
+ */
+void dvmHeapSourceZeroMarkBitmap(void);
+
+/*
+ * Marks all objects inside the immune region of the heap. Addresses
+ * at or above this pointer are threatened, addresses below this
+ * pointer are immune.
+ */
+void dvmMarkImmuneObjects(const char *immuneLimit);
+
+/*
+ * Returns a pointer that demarcates the threatened region of the
+ * heap.  Addresses at or above this pointer are threatened, addresses
+ * below this pointer are immune.
+ */
+void *dvmHeapSourceGetImmuneLimit(bool isPartial);
+
+/*
+ * Returns the maximum size of the heap.  This value will be either
+ * the value of -Xmx or a user supplied growth limit.
+ */
+size_t dvmHeapSourceGetMaximumSize(void);
+
+#endif  // DALVIK_HEAP_SOURCE_H_
diff --git a/vm/alloc/MarkSweep.cpp b/vm/alloc/MarkSweep.cpp
new file mode 100644
index 0000000..eb739e5
--- /dev/null
+++ b/vm/alloc/MarkSweep.cpp
@@ -0,0 +1,944 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "alloc/CardTable.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapBitmapInlines.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/MarkSweep.h"
+#include "alloc/Visit.h"
+#include <limits.h>     // for ULONG_MAX
+#include <sys/mman.h>   // for madvise(), mmap()
+#include <errno.h>
+
+typedef unsigned long Word;
+const size_t kWordSize = sizeof(Word);
+
+/*
+ * Returns true if the given object is marked.
+ */
+static bool isMarked(const Object *obj, const GcMarkContext *ctx)
+{
+    return dvmHeapBitmapIsObjectBitSet(ctx->bitmap, obj);
+}
+
+/*
+ * Initializes the stack top and advises the mark stack pages as needed.
+ */
+static bool createMarkStack(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    size_t length = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) /
+        (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
+    madvise(stack->base, length, MADV_NORMAL);
+    stack->top = stack->base;
+    return true;
+}
+
+/*
+ * Assigns NULL to the stack top and advises the mark stack pages as
+ * not needed.
+ */
+static void destroyMarkStack(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    madvise(stack->base, stack->length, MADV_DONTNEED);
+    stack->top = NULL;
+}
+
+/*
+ * Pops an object from the mark stack.
+ */
+static void markStackPush(GcMarkStack *stack, const Object *obj)
+{
+    assert(stack != NULL);
+    assert(stack->base <= stack->top);
+    assert(stack->limit > stack->top);
+    assert(obj != NULL);
+    *stack->top = obj;
+    ++stack->top;
+}
+
+/*
+ * Pushes an object on the mark stack.
+ */
+static const Object *markStackPop(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    assert(stack->base < stack->top);
+    assert(stack->limit > stack->top);
+    --stack->top;
+    return *stack->top;
+}
+
+bool dvmHeapBeginMarkStep(bool isPartial)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    if (!createMarkStack(&ctx->stack)) {
+        return false;
+    }
+    ctx->finger = NULL;
+    ctx->immuneLimit = (char*)dvmHeapSourceGetImmuneLimit(isPartial);
+    return true;
+}
+
+static long setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
+{
+    return dvmHeapBitmapSetAndReturnObjectBit(ctx->bitmap, obj);
+}
+
+static void markObjectNonNull(const Object *obj, GcMarkContext *ctx,
+                              bool checkFinger)
+{
+    assert(ctx != NULL);
+    assert(obj != NULL);
+    assert(dvmIsValidObject(obj));
+    if (obj < (Object *)ctx->immuneLimit) {
+        assert(isMarked(obj, ctx));
+        return;
+    }
+    if (!setAndReturnMarkBit(ctx, obj)) {
+        /* This object was not previously marked.
+         */
+        if (checkFinger && (void *)obj < ctx->finger) {
+            /* This object will need to go on the mark stack.
+             */
+            markStackPush(&ctx->stack, obj);
+        }
+    }
+}
+
+/* Used to mark objects when recursing.  Recursion is done by moving
+ * the finger across the bitmaps in address order and marking child
+ * objects.  Any newly-marked objects whose addresses are lower than
+ * the finger won't be visited by the bitmap scan, so those objects
+ * need to be added to the mark stack.
+ */
+static void markObject(const Object *obj, GcMarkContext *ctx)
+{
+    if (obj != NULL) {
+        markObjectNonNull(obj, ctx, true);
+    }
+}
+
+/*
+ * Callback applied to root references during the initial root
+ * marking.  Marks white objects but does not push them on the mark
+ * stack.
+ */
+static void rootMarkObjectVisitor(void *addr, u4 thread, RootType type,
+                                  void *arg)
+{
+    assert(addr != NULL);
+    assert(arg != NULL);
+    Object *obj = *(Object **)addr;
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    if (obj != NULL) {
+        markObjectNonNull(obj, ctx, false);
+    }
+}
+
+/* Mark the set of root objects.
+ *
+ * Things we need to scan:
+ * - System classes defined by root classloader
+ * - For each thread:
+ *   - Interpreted stack, from top to "curFrame"
+ *     - Dalvik registers (args + local vars)
+ *   - JNI local references
+ *   - Automatic VM local references (TrackedAlloc)
+ *   - Associated Thread/VMThread object
+ *   - ThreadGroups (could track & start with these instead of working
+ *     upward from Threads)
+ *   - Exception currently being thrown, if present
+ * - JNI global references
+ * - Interned string table
+ * - Primitive classes
+ * - Special objects
+ *   - gDvm.outOfMemoryObj
+ * - Objects in debugger object registry
+ *
+ * Don't need:
+ * - Native stack (for in-progress stuff in the VM)
+ *   - The TrackedAlloc stuff watches all native VM references.
+ */
+void dvmHeapMarkRootSet()
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
+    dvmVisitRoots(rootMarkObjectVisitor, &gcHeap->markContext);
+}
+
+/*
+ * Callback applied to root references during root remarking.  Marks
+ * white objects and pushes them on the mark stack.
+ */
+static void rootReMarkObjectVisitor(void *addr, u4 thread, RootType type,
+                                    void *arg)
+{
+    assert(addr != NULL);
+    assert(arg != NULL);
+    Object *obj = *(Object **)addr;
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    if (obj != NULL) {
+        markObjectNonNull(obj, ctx, true);
+    }
+}
+
+/*
+ * Grays all references in the roots.
+ */
+void dvmHeapReMarkRootSet()
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    assert(ctx->finger == (void *)ULONG_MAX);
+    dvmVisitRoots(rootReMarkObjectVisitor, ctx);
+}
+
+/*
+ * Scans instance fields.
+ */
+static void scanFields(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
+        unsigned int refOffsets = obj->clazz->refOffsets;
+        while (refOffsets != 0) {
+            size_t rshift = CLZ(refOffsets);
+            size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+            Object *ref = dvmGetFieldObject(obj, offset);
+            markObject(ref, ctx);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+        }
+    } else {
+        for (ClassObject *clazz = obj->clazz;
+             clazz != NULL;
+             clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            for (int i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                void *addr = BYTE_OFFSET(obj, field->byteOffset);
+                Object *ref = ((JValue *)addr)->l;
+                markObject(ref, ctx);
+            }
+        }
+    }
+}
+
+/*
+ * Scans the static fields of a class object.
+ */
+static void scanStaticFields(const ClassObject *clazz, GcMarkContext *ctx)
+{
+    assert(clazz != NULL);
+    assert(ctx != NULL);
+    for (int i = 0; i < clazz->sfieldCount; ++i) {
+        char ch = clazz->sfields[i].signature[0];
+        if (ch == '[' || ch == 'L') {
+            Object *obj = clazz->sfields[i].value.l;
+            markObject(obj, ctx);
+        }
+    }
+}
+
+/*
+ * Visit the interfaces of a class object.
+ */
+static void scanInterfaces(const ClassObject *clazz, GcMarkContext *ctx)
+{
+    assert(clazz != NULL);
+    assert(ctx != NULL);
+    for (int i = 0; i < clazz->interfaceCount; ++i) {
+        markObject((const Object *)clazz->interfaces[i], ctx);
+    }
+}
+
+/*
+ * Scans the header, static field references, and interface
+ * pointers of a class object.
+ */
+static void scanClassObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(dvmIsClassObject(obj));
+    assert(ctx != NULL);
+    markObject((const Object *)obj->clazz, ctx);
+    const ClassObject *asClass = (const ClassObject *)obj;
+    if (IS_CLASS_FLAG_SET(asClass, CLASS_ISARRAY)) {
+        markObject((const Object *)asClass->elementClass, ctx);
+    }
+    /* Do super and the interfaces contain Objects and not dex idx values? */
+    if (asClass->status > CLASS_IDX) {
+        markObject((const Object *)asClass->super, ctx);
+    }
+    markObject((const Object *)asClass->classLoader, ctx);
+    scanFields(obj, ctx);
+    scanStaticFields(asClass, ctx);
+    if (asClass->status > CLASS_IDX) {
+        scanInterfaces(asClass, ctx);
+    }
+}
+
+/*
+ * Scans the header of all array objects.  If the array object is
+ * specialized to a reference type, scans the array data as well.
+ */
+static void scanArrayObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    markObject((const Object *)obj->clazz, ctx);
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) {
+        const ArrayObject *array = (const ArrayObject *)obj;
+        const Object **contents = (const Object **)(void *)array->contents;
+        for (size_t i = 0; i < array->length; ++i) {
+            markObject(contents[i], ctx);
+        }
+    }
+}
+
+/*
+ * Returns class flags relating to Reference subclasses.
+ */
+static int referenceClassFlags(const Object *obj)
+{
+    int flags = CLASS_ISREFERENCE |
+                CLASS_ISWEAKREFERENCE |
+                CLASS_ISFINALIZERREFERENCE |
+                CLASS_ISPHANTOMREFERENCE;
+    return GET_CLASS_FLAG_GROUP(obj->clazz, flags);
+}
+
+/*
+ * Returns true if the object derives from SoftReference.
+ */
+static bool isSoftReference(const Object *obj)
+{
+    return referenceClassFlags(obj) == CLASS_ISREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from WeakReference.
+ */
+static bool isWeakReference(const Object *obj)
+{
+    return referenceClassFlags(obj) & CLASS_ISWEAKREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from FinalizerReference.
+ */
+static bool isFinalizerReference(const Object *obj)
+{
+    return referenceClassFlags(obj) & CLASS_ISFINALIZERREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from PhantomReference.
+ */
+static bool isPhantomReference(const Object *obj)
+{
+    return referenceClassFlags(obj) & CLASS_ISPHANTOMREFERENCE;
+}
+
+/*
+ * Adds a reference to the tail of a circular queue of references.
+ */
+static void enqueuePendingReference(Object *ref, Object **list)
+{
+    assert(ref != NULL);
+    assert(list != NULL);
+    size_t offset = gDvm.offJavaLangRefReference_pendingNext;
+    if (*list == NULL) {
+        dvmSetFieldObject(ref, offset, ref);
+        *list = ref;
+    } else {
+        Object *head = dvmGetFieldObject(*list, offset);
+        dvmSetFieldObject(ref, offset, head);
+        dvmSetFieldObject(*list, offset, ref);
+    }
+}
+
+/*
+ * Removes the reference at the head of a circular queue of
+ * references.
+ */
+static Object *dequeuePendingReference(Object **list)
+{
+    assert(list != NULL);
+    assert(*list != NULL);
+    size_t offset = gDvm.offJavaLangRefReference_pendingNext;
+    Object *head = dvmGetFieldObject(*list, offset);
+    Object *ref;
+    if (*list == head) {
+        ref = *list;
+        *list = NULL;
+    } else {
+        Object *next = dvmGetFieldObject(head, offset);
+        dvmSetFieldObject(*list, offset, next);
+        ref = head;
+    }
+    dvmSetFieldObject(ref, offset, NULL);
+    return ref;
+}
+
+/*
+ * Process the "referent" field in a java.lang.ref.Reference.  If the
+ * referent has not yet been marked, put it on the appropriate list in
+ * the gcHeap for later processing.
+ */
+static void delayReferenceReferent(Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE));
+    assert(ctx != NULL);
+    GcHeap *gcHeap = gDvm.gcHeap;
+    size_t pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    Object *pending = dvmGetFieldObject(obj, pendingNextOffset);
+    Object *referent = dvmGetFieldObject(obj, referentOffset);
+    if (pending == NULL && referent != NULL && !isMarked(referent, ctx)) {
+        Object **list = NULL;
+        if (isSoftReference(obj)) {
+            list = &gcHeap->softReferences;
+        } else if (isWeakReference(obj)) {
+            list = &gcHeap->weakReferences;
+        } else if (isFinalizerReference(obj)) {
+            list = &gcHeap->finalizerReferences;
+        } else if (isPhantomReference(obj)) {
+            list = &gcHeap->phantomReferences;
+        }
+        assert(list != NULL);
+        enqueuePendingReference(obj, list);
+    }
+}
+
+/*
+ * Scans the header and field references of a data object.
+ */
+static void scanDataObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    markObject((const Object *)obj->clazz, ctx);
+    scanFields(obj, ctx);
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+        delayReferenceReferent((Object *)obj, ctx);
+    }
+}
+
+/*
+ * Scans an object reference.  Determines the type of the reference
+ * and dispatches to a specialized scanning routine.
+ */
+static void scanObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        scanClassObject(obj, ctx);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        scanArrayObject(obj, ctx);
+    } else {
+        scanDataObject(obj, ctx);
+    }
+}
+
+/*
+ * Scan anything that's on the mark stack.  We can't use the bitmaps
+ * anymore, so use a finger that points past the end of them.
+ */
+static void processMarkStack(GcMarkContext *ctx)
+{
+    assert(ctx != NULL);
+    assert(ctx->finger == (void *)ULONG_MAX);
+    assert(ctx->stack.top >= ctx->stack.base);
+    GcMarkStack *stack = &ctx->stack;
+    while (stack->top > stack->base) {
+        const Object *obj = markStackPop(stack);
+        scanObject(obj, ctx);
+    }
+}
+
+static size_t objectSize(const Object *obj)
+{
+    assert(dvmIsValidObject(obj));
+    assert(dvmIsValidObject((Object *)obj->clazz));
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        return dvmArrayObjectSize((ArrayObject *)obj);
+    } else if (obj->clazz == gDvm.classJavaLangClass) {
+        return dvmClassObjectSize((ClassObject *)obj);
+    } else {
+        return obj->clazz->objectSize;
+    }
+}
+
+/*
+ * Scans forward to the header of the next marked object between start
+ * and limit.  Returns NULL if no marked objects are in that region.
+ */
+static Object *nextGrayObject(const u1 *base, const u1 *limit,
+                              const HeapBitmap *markBits)
+{
+    const u1 *ptr;
+
+    assert(base < limit);
+    assert(limit - base <= GC_CARD_SIZE);
+    for (ptr = base; ptr < limit; ptr += HB_OBJECT_ALIGNMENT) {
+        if (dvmHeapBitmapIsObjectBitSet(markBits, ptr))
+            return (Object *)ptr;
+    }
+    return NULL;
+}
+
+/*
+ * Scans range of dirty cards between start and end.  A range of dirty
+ * cards is composed consecutively dirty cards or dirty cards spanned
+ * by a gray object.  Returns the address of a clean card if the scan
+ * reached a clean card or NULL if the scan reached the end.
+ */
+const u1 *scanDirtyCards(const u1 *start, const u1 *end,
+                         GcMarkContext *ctx)
+{
+    const HeapBitmap *markBits = ctx->bitmap;
+    const u1 *card = start, *prevAddr = NULL;
+    while (card < end) {
+        if (*card != GC_CARD_DIRTY) {
+            return card;
+        }
+        const u1 *ptr = prevAddr ? prevAddr : (u1*)dvmAddrFromCard(card);
+        const u1 *limit = ptr + GC_CARD_SIZE;
+        while (ptr < limit) {
+            Object *obj = nextGrayObject(ptr, limit, markBits);
+            if (obj == NULL) {
+                break;
+            }
+            scanObject(obj, ctx);
+            ptr = (u1*)obj + ALIGN_UP(objectSize(obj), HB_OBJECT_ALIGNMENT);
+        }
+        if (ptr < limit) {
+            /* Ended within the current card, advance to the next card. */
+            ++card;
+            prevAddr = NULL;
+        } else {
+            /* Ended past the current card, skip ahead. */
+            card = dvmCardFromAddr(ptr);
+            prevAddr = ptr;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Blackens gray objects found on dirty cards.
+ */
+static void scanGrayObjects(GcMarkContext *ctx)
+{
+    GcHeap *h = gDvm.gcHeap;
+    const u1 *base, *limit, *ptr, *dirty;
+
+    base = &h->cardTableBase[0];
+    limit = dvmCardFromAddr((u1 *)dvmHeapSourceGetLimit());
+    assert(limit <= &h->cardTableBase[h->cardTableLength]);
+
+    ptr = base;
+    for (;;) {
+        dirty = (const u1 *)memchr(ptr, GC_CARD_DIRTY, limit - ptr);
+        if (dirty == NULL) {
+            break;
+        }
+        assert((dirty > ptr) && (dirty < limit));
+        ptr = scanDirtyCards(dirty, limit, ctx);
+        if (ptr == NULL) {
+            break;
+        }
+        assert((ptr > dirty) && (ptr < limit));
+    }
+}
+
+/*
+ * Callback for scanning each object in the bitmap.  The finger is set
+ * to the address corresponding to the lowest address in the next word
+ * of bits in the bitmap.
+ */
+static void scanBitmapCallback(Object *obj, void *finger, void *arg)
+{
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    ctx->finger = (void *)finger;
+    scanObject(obj, ctx);
+}
+
+/* Given bitmaps with the root set marked, find and mark all
+ * reachable objects.  When this returns, the entire set of
+ * live objects will be marked and the mark stack will be empty.
+ */
+void dvmHeapScanMarkedObjects(void)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    assert(ctx->finger == NULL);
+
+    /* The bitmaps currently have bits set for the root set.
+     * Walk across the bitmaps and scan each object.
+     */
+    dvmHeapBitmapScanWalk(ctx->bitmap, scanBitmapCallback, ctx);
+
+    ctx->finger = (void *)ULONG_MAX;
+
+    /* We've walked the mark bitmaps.  Scan anything that's
+     * left on the mark stack.
+     */
+    processMarkStack(ctx);
+}
+
+void dvmHeapReScanMarkedObjects()
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    /*
+     * The finger must have been set to the maximum value to ensure
+     * that gray objects will be pushed onto the mark stack.
+     */
+    assert(ctx->finger == (void *)ULONG_MAX);
+    scanGrayObjects(ctx);
+    processMarkStack(ctx);
+}
+
+/*
+ * Clear the referent field.
+ */
+static void clearReference(Object *reference)
+{
+    size_t offset = gDvm.offJavaLangRefReference_referent;
+    dvmSetFieldObject(reference, offset, NULL);
+}
+
+/*
+ * Returns true if the reference was registered with a reference queue
+ * and has not yet been enqueued.
+ */
+static bool isEnqueuable(const Object *reference)
+{
+    assert(reference != NULL);
+    Object *queue = dvmGetFieldObject(reference,
+            gDvm.offJavaLangRefReference_queue);
+    Object *queueNext = dvmGetFieldObject(reference,
+            gDvm.offJavaLangRefReference_queueNext);
+    return queue != NULL && queueNext == NULL;
+}
+
+/*
+ * Schedules a reference to be appended to its reference queue.
+ */
+static void enqueueReference(Object *ref)
+{
+    assert(ref != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+    enqueuePendingReference(ref, &gDvm.gcHeap->clearedReferences);
+}
+
+/*
+ * Walks the reference list marking any references subject to the
+ * reference clearing policy.  References with a black referent are
+ * removed from the list.  References with white referents biased
+ * toward saving are blackened and also removed from the list.
+ */
+static void preserveSomeSoftReferences(Object **list)
+{
+    assert(list != NULL);
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    Object *clear = NULL;
+    size_t counter = 0;
+    while (*list != NULL) {
+        Object *ref = dequeuePendingReference(list);
+        Object *referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent == NULL) {
+            /* Referent was cleared by the user during marking. */
+            continue;
+        }
+        bool marked = isMarked(referent, ctx);
+        if (!marked && ((++counter) & 1)) {
+            /* Referent is white and biased toward saving, mark it. */
+            markObject(referent, ctx);
+            marked = true;
+        }
+        if (!marked) {
+            /* Referent is white, queue it for clearing. */
+            enqueuePendingReference(ref, &clear);
+        }
+    }
+    *list = clear;
+    /*
+     * Restart the mark with the newly black references added to the
+     * root set.
+     */
+    processMarkStack(ctx);
+}
+
+/*
+ * Unlink the reference list clearing references objects with white
+ * referents.  Cleared references registered to a reference queue are
+ * scheduled for appending by the heap worker thread.
+ */
+static void clearWhiteReferences(Object **list)
+{
+    assert(list != NULL);
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    while (*list != NULL) {
+        Object *ref = dequeuePendingReference(list);
+        Object *referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent != NULL && !isMarked(referent, ctx)) {
+            /* Referent is white, clear it. */
+            clearReference(ref);
+            if (isEnqueuable(ref)) {
+                enqueueReference(ref);
+            }
+        }
+    }
+    assert(*list == NULL);
+}
+
+/*
+ * Enqueues finalizer references with white referents.  White
+ * referents are blackened, moved to the zombie field, and the
+ * referent field is cleared.
+ */
+static void enqueueFinalizerReferences(Object **list)
+{
+    assert(list != NULL);
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    size_t zombieOffset = gDvm.offJavaLangRefFinalizerReference_zombie;
+    bool hasEnqueued = false;
+    while (*list != NULL) {
+        Object *ref = dequeuePendingReference(list);
+        Object *referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent != NULL && !isMarked(referent, ctx)) {
+            markObject(referent, ctx);
+            /* If the referent is non-null the reference must queuable. */
+            assert(isEnqueuable(ref));
+            dvmSetFieldObject(ref, zombieOffset, referent);
+            clearReference(ref);
+            enqueueReference(ref);
+            hasEnqueued = true;
+        }
+    }
+    if (hasEnqueued) {
+        processMarkStack(ctx);
+    }
+    assert(*list == NULL);
+}
+
+/*
+ * This object is an instance of a class that overrides finalize().  Mark
+ * it as finalizable.
+ *
+ * This is called when Object.<init> completes normally.  It's also
+ * called for clones of finalizable objects.
+ */
+void dvmSetFinalizable(Object *obj)
+{
+    assert(obj != NULL);
+    Thread *self = dvmThreadSelf();
+    assert(self != NULL);
+    Method *meth = gDvm.methJavaLangRefFinalizerReferenceAdd;
+    assert(meth != NULL);
+    JValue unusedResult;
+    dvmCallMethod(self, meth, NULL, &unusedResult, obj);
+}
+
+/*
+ * Process reference class instances and schedule finalizations.
+ */
+void dvmHeapProcessReferences(Object **softReferences, bool clearSoftRefs,
+                              Object **weakReferences,
+                              Object **finalizerReferences,
+                              Object **phantomReferences)
+{
+    assert(softReferences != NULL);
+    assert(weakReferences != NULL);
+    assert(finalizerReferences != NULL);
+    assert(phantomReferences != NULL);
+    /*
+     * Unless we are in the zygote or required to clear soft
+     * references with white references, preserve some white
+     * referents.
+     */
+    if (!gDvm.zygote && !clearSoftRefs) {
+        preserveSomeSoftReferences(softReferences);
+    }
+    /*
+     * Clear all remaining soft and weak references with white
+     * referents.
+     */
+    clearWhiteReferences(softReferences);
+    clearWhiteReferences(weakReferences);
+    /*
+     * Preserve all white objects with finalize methods and schedule
+     * them for finalization.
+     */
+    enqueueFinalizerReferences(finalizerReferences);
+    /*
+     * Clear all f-reachable soft and weak references with white
+     * referents.
+     */
+    clearWhiteReferences(softReferences);
+    clearWhiteReferences(weakReferences);
+    /*
+     * Clear all phantom references with white referents.
+     */
+    clearWhiteReferences(phantomReferences);
+    /*
+     * At this point all reference lists should be empty.
+     */
+    assert(*softReferences == NULL);
+    assert(*weakReferences == NULL);
+    assert(*finalizerReferences == NULL);
+    assert(*phantomReferences == NULL);
+}
+
+/*
+ * Pushes a list of cleared references out to the managed heap.
+ */
+void dvmEnqueueClearedReferences(Object **cleared)
+{
+    assert(cleared != NULL);
+    if (*cleared != NULL) {
+        Thread *self = dvmThreadSelf();
+        assert(self != NULL);
+        Method *meth = gDvm.methJavaLangRefReferenceQueueAdd;
+        assert(meth != NULL);
+        JValue unused;
+        Object *reference = *cleared;
+        dvmCallMethod(self, meth, NULL, &unused, reference);
+        *cleared = NULL;
+    }
+}
+
+void dvmHeapFinishMarkStep()
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    /* The mark bits are now not needed.
+     */
+    dvmHeapSourceZeroMarkBitmap();
+
+    /* Clean up everything else associated with the marking process.
+     */
+    destroyMarkStack(&ctx->stack);
+
+    ctx->finger = NULL;
+}
+
+struct SweepContext {
+    size_t numObjects;
+    size_t numBytes;
+    bool isConcurrent;
+};
+
+static void sweepBitmapCallback(size_t numPtrs, void **ptrs, void *arg)
+{
+    assert(arg != NULL);
+    SweepContext *ctx = (SweepContext *)arg;
+    if (ctx->isConcurrent) {
+        dvmLockHeap();
+    }
+    ctx->numBytes += dvmHeapSourceFreeList(numPtrs, ptrs);
+    ctx->numObjects += numPtrs;
+    if (ctx->isConcurrent) {
+        dvmUnlockHeap();
+    }
+}
+
+/*
+ * Returns true if the given object is unmarked.  This assumes that
+ * the bitmaps have not yet been swapped.
+ */
+static int isUnmarkedObject(void *obj)
+{
+    return !isMarked((Object *)obj, &gDvm.gcHeap->markContext);
+}
+
+static void sweepWeakJniGlobals()
+{
+    IndirectRefTable* table = &gDvm.jniWeakGlobalRefTable;
+    GcMarkContext* ctx = &gDvm.gcHeap->markContext;
+    typedef IndirectRefTable::iterator It; // TODO: C++0x auto
+    for (It it = table->begin(), end = table->end(); it != end; ++it) {
+        Object** entry = *it;
+        if (!isMarked(*entry, ctx)) {
+            *entry = kClearedJniWeakGlobal;
+        }
+    }
+}
+
+/*
+ * Process all the internal system structures that behave like
+ * weakly-held objects.
+ */
+void dvmHeapSweepSystemWeaks()
+{
+    dvmGcDetachDeadInternedStrings(isUnmarkedObject);
+    dvmSweepMonitorList(&gDvm.monitorList, isUnmarkedObject);
+    sweepWeakJniGlobals();
+}
+
+/*
+ * Walk through the list of objects that haven't been marked and free
+ * them.  Assumes the bitmaps have been swapped.
+ */
+void dvmHeapSweepUnmarkedObjects(bool isPartial, bool isConcurrent,
+                                 size_t *numObjects, size_t *numBytes)
+{
+    uintptr_t base[HEAP_SOURCE_MAX_HEAP_COUNT];
+    uintptr_t max[HEAP_SOURCE_MAX_HEAP_COUNT];
+    SweepContext ctx;
+    HeapBitmap *prevLive, *prevMark;
+    size_t numHeaps, numSweepHeaps;
+
+    numHeaps = dvmHeapSourceGetNumHeaps();
+    dvmHeapSourceGetRegions(base, max, numHeaps);
+    if (isPartial) {
+        assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == base[0]);
+        numSweepHeaps = 1;
+    } else {
+        numSweepHeaps = numHeaps;
+    }
+    ctx.numObjects = ctx.numBytes = 0;
+    ctx.isConcurrent = isConcurrent;
+    prevLive = dvmHeapSourceGetMarkBits();
+    prevMark = dvmHeapSourceGetLiveBits();
+    for (size_t i = 0; i < numSweepHeaps; ++i) {
+        dvmHeapBitmapSweepWalk(prevLive, prevMark, base[i], max[i],
+                               sweepBitmapCallback, &ctx);
+    }
+    *numObjects = ctx.numObjects;
+    *numBytes = ctx.numBytes;
+    if (gDvm.allocProf.enabled) {
+        gDvm.allocProf.freeCount += ctx.numObjects;
+        gDvm.allocProf.freeSize += ctx.numBytes;
+    }
+}
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
new file mode 100644
index 0000000..b14333a
--- /dev/null
+++ b/vm/alloc/MarkSweep.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DALVIK_ALLOC_MARK_SWEEP_H_
+#define DALVIK_ALLOC_MARK_SWEEP_H_
+
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapSource.h"
+
+struct GcMarkStack {
+    /* Highest address (exclusive)
+     */
+    const Object **limit;
+
+    /* Current top of the stack (exclusive)
+     */
+    const Object **top;
+
+    /* Lowest address (inclusive)
+     */
+    const Object **base;
+
+    /* Maximum stack size, in bytes.
+     */
+    size_t length;
+};
+
+/* This is declared publicly so that it can be included in gDvm.gcHeap.
+ */
+struct GcMarkContext {
+    HeapBitmap *bitmap;
+    GcMarkStack stack;
+    const char *immuneLimit;
+    const void *finger;   // only used while scanning/recursing.
+};
+
+bool dvmHeapBeginMarkStep(bool isPartial);
+void dvmHeapMarkRootSet(void);
+void dvmHeapReMarkRootSet(void);
+void dvmHeapScanMarkedObjects(void);
+void dvmHeapReScanMarkedObjects(void);
+void dvmHeapProcessReferences(Object **softReferences, bool clearSoftRefs,
+                              Object **weakReferences,
+                              Object **finalizerReferences,
+                              Object **phantomReferences);
+void dvmHeapFinishMarkStep(void);
+void dvmHeapSweepSystemWeaks(void);
+void dvmHeapSweepUnmarkedObjects(bool isPartial, bool isConcurrent,
+                                 size_t *numObjects, size_t *numBytes);
+void dvmEnqueueClearedReferences(Object **references);
+
+#endif  // DALVIK_ALLOC_MARK_SWEEP_H_
diff --git a/vm/alloc/TEST/HeapBitmapTest/Makefile b/vm/alloc/TEST/HeapBitmapTest/Makefile
new file mode 100644
index 0000000..969eb63
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/Makefile
@@ -0,0 +1,25 @@
+.PHONY: all
+all: runtest
+
+$(shell mkdir -p out)
+
+CC := gcc
+CFLAGS := -g -Wall -Werror
+#CFLAGS += -O2
+
+out/main.o: main.c ../../HeapBitmap.h
+	$(CC) $(CFLAGS) -c $< -o $@ -I ../..
+
+out/HeapBitmap.o: ../../HeapBitmap.c ../../HeapBitmap.h include/cutils/ashmem.h include/Dalvik.h
+	$(CC) $(CFLAGS) -c $< -o $@ -I ../.. -I include
+
+out/hbtest: out/main.o out/HeapBitmap.o out/clz.o
+	$(CC) $^ -o $@
+
+.PHONY: runtest
+runtest: out/hbtest
+	out/hbtest
+
+.PHONY: clean
+clean:
+	rm -rf out
diff --git a/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h b/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h
new file mode 100644
index 0000000..89492d5
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h
@@ -0,0 +1,18 @@
+#ifndef DALVIK_H_
+#define DALVIK_H_
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#define ALOGW(...) printf("W/" __VA_ARGS__)
+#define ALOGE(...) printf("E/" __VA_ARGS__)
+
+inline void dvmAbort(void) {
+    exit(1);
+}
+
+#endif  // DALVIK_H_
diff --git a/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h b/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h
new file mode 100644
index 0000000..8680c77
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h
@@ -0,0 +1,14 @@
+#ifndef ASHMEM_H_
+#define ASHMEM_H_
+
+#include <fcntl.h>
+
+#define ASHMEM_NAME_LEN 128
+
+inline int
+ashmem_create_region(const char *name, size_t len)
+{
+    return open("/dev/zero", O_RDWR);
+}
+
+#endif
diff --git a/vm/alloc/TEST/HeapBitmapTest/main.c b/vm/alloc/TEST/HeapBitmapTest/main.c
new file mode 100644
index 0000000..10fa7f8
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/main.c
@@ -0,0 +1,496 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#define __attribute(x) /* disable inlining */
+#include "HeapBitmap.h"
+#undef __attribute
+
+#define PAGE_SIZE 4096
+#define HEAP_BASE ((void *)0x10000)
+#define HEAP_SIZE (5 * PAGE_SIZE + 888)
+
+#define VERBOSE 1
+#if VERBOSE
+#define TRACE(...) printf(__VA_ARGS__)
+#else
+#define TRACE(...) /**/
+#endif
+
+void
+test_init()
+{
+    HeapBitmap hb;
+    bool ok;
+
+    memset(&hb, 0x55, sizeof(hb));
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+
+    assert(hb.bits != NULL);
+    assert(hb.bitsLen >= HB_OFFSET_TO_INDEX(HEAP_SIZE));
+    assert(hb.base == (uintptr_t)HEAP_BASE);
+    assert(hb.max < hb.base);
+
+    /* Make sure hb.bits is mapped.
+     */
+    *hb.bits = 0x55;
+    assert(*hb.bits = 0x55);
+    *hb.bits = 0;
+
+#define TEST_UNMAP 0
+#if TEST_UNMAP
+    /* Hold onto this to make sure it's unmapped later.
+     */
+    unsigned long int *bits = hb.bits;
+#endif
+
+    dvmHeapBitmapDelete(&hb);
+
+    assert(hb.bits == NULL);
+    assert(hb.bitsLen == 0);
+    assert(hb.base == 0);
+    assert(hb.max == 0);
+
+#if TEST_UNMAP
+    /* This pointer shouldn't be mapped anymore.
+     */
+    *bits = 0x55;
+    assert(!"Should have segfaulted");
+#endif
+}
+
+bool is_zeroed(const HeapBitmap *hb)
+{
+    int i;
+
+    for (i = 0; i < hb->bitsLen / sizeof (*hb->bits); i++) {
+        if (hb->bits[i] != 0L) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void assert_empty(const HeapBitmap *hb)
+{
+    assert(hb->bits != NULL);
+    assert(hb->bitsLen >= HB_OFFSET_TO_INDEX(HEAP_SIZE));
+    assert(hb->base == (uintptr_t)HEAP_BASE);
+    assert(hb->max < hb->base);
+
+    assert(is_zeroed(hb));
+
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(!dvmHeapBitmapIsObjectBitSet(hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+}
+
+void
+test_bits()
+{
+    HeapBitmap hb;
+    bool ok;
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+
+    assert_empty(&hb);
+
+    /* Set the lowest address.
+     */
+    dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the highest address.
+     */
+    dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Clear the lowest address.
+     */
+    dvmHeapBitmapClearObjectBit(&hb, HEAP_BASE);
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!is_zeroed(&hb));
+
+    /* Clear the highest address.
+     */
+    dvmHeapBitmapClearObjectBit(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(is_zeroed(&hb));
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb);
+}
+
+void
+test_clear()
+{
+    HeapBitmap hb;
+    bool ok;
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+    assert_empty(&hb);
+
+    /* Set the highest address.
+     */
+    dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(!is_zeroed(&hb));
+
+    /* Clear the bitmap.
+     */
+    dvmHeapBitmapZero(&hb);
+    assert_empty(&hb);
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb);
+}
+
+void
+test_modify()
+{
+    HeapBitmap hb;
+    bool ok;
+    unsigned long bit;
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+    assert_empty(&hb);
+
+    /* Set the lowest address.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb, HEAP_BASE);
+    assert(bit == 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the lowest address again.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb, HEAP_BASE);
+    assert(bit != 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the highest address.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(bit == 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the highest address again.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(bit != 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb);
+}
+
+/*
+ * xor test support functions
+ */
+
+static void *gCallbackArg = NULL;
+
+#define NUM_XOR_PTRS  128
+static size_t gNumPtrs;
+static void *gXorPtrs[NUM_XOR_PTRS];
+static bool gClearedPtrs[NUM_XOR_PTRS];
+static bool gSeenPtrs[NUM_XOR_PTRS];
+
+bool
+xorCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
+{
+    assert(numPtrs > 0);
+    assert(ptrs != NULL);
+    assert(arg == gCallbackArg);
+
+size_t i;
+    for (i = 0; i < numPtrs; i++) {
+        assert(ptrs[i] < finger);
+        printf("callback: 0x%08x ( < 0x%08x )\n",
+                (uintptr_t)ptrs[i], (uintptr_t)finger);
+    }
+
+    return true;
+}
+
+bool
+seenAndClearedMatch()
+{
+    size_t i;
+    for (i = 0; i < gNumPtrs; i++) {
+        if (gClearedPtrs[i] != gSeenPtrs[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void
+run_xor(ssize_t offset, size_t step)
+{
+    assert(step != 0);
+    assert(step < HEAP_SIZE);
+
+    /* Figure out the range.
+     */
+uintptr_t base;
+uintptr_t top;
+    if (offset >= 0) {
+        base = (uintptr_t)HEAP_BASE + offset;
+    } else {
+        base = (uintptr_t)HEAP_BASE + (uintptr_t)HEAP_SIZE + offset;
+    }
+    if (base < (uintptr_t)HEAP_BASE) {
+        base = (uintptr_t)HEAP_BASE;
+    } else if (base > (uintptr_t)(HEAP_BASE + HEAP_SIZE)) {
+        base = (uintptr_t)(HEAP_BASE + HEAP_SIZE);
+    } else {
+        base = (base + HB_OBJECT_ALIGNMENT - 1) & ~(HB_OBJECT_ALIGNMENT - 1);
+    }
+    step *= HB_OBJECT_ALIGNMENT;
+    top = base + step * NUM_XOR_PTRS;
+    if (top > (uintptr_t)(HEAP_BASE + HEAP_SIZE)) {
+        top = (uintptr_t)(HEAP_BASE + HEAP_SIZE);
+    }
+
+    /* Create the pointers.
+     */
+    gNumPtrs = 0;
+    memset(gXorPtrs, 0, sizeof(gXorPtrs));
+    memset(gClearedPtrs, 0, sizeof(gClearedPtrs));
+    memset(gSeenPtrs, 0, sizeof(gSeenPtrs));
+
+uintptr_t addr;
+void **p = gXorPtrs;
+    for (addr = base; addr < top; addr += step) {
+        *p++ = (void *)addr;
+        gNumPtrs++;
+    }
+    assert(seenAndClearedMatch());
+
+    /* Set up the bitmaps.
+     */
+HeapBitmap hb1, hb2;
+bool ok;
+
+    ok = dvmHeapBitmapInit(&hb1, HEAP_BASE, HEAP_SIZE, "test1");
+    assert(ok);
+    ok = dvmHeapBitmapInitFromTemplate(&hb2, &hb1, "test2");
+    assert(ok);
+
+    /* Walk two empty bitmaps.
+     */
+TRACE("walk 0\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+    assert(seenAndClearedMatch());
+
+    /* Walk one empty bitmap.
+     */
+TRACE("walk 1\n");
+    dvmHeapBitmapSetObjectBit(&hb1, (void *)base);
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Make the bitmaps match.
+     */
+TRACE("walk 2\n");
+    dvmHeapBitmapSetObjectBit(&hb2, (void *)base);
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Clear the bitmaps.
+     */
+    dvmHeapBitmapZero(&hb1);
+    assert_empty(&hb1);
+    dvmHeapBitmapZero(&hb2);
+    assert_empty(&hb2);
+
+    /* Set the pointers we created in one of the bitmaps,
+     * then visit them.
+     */
+size_t i;
+    for (i = 0; i < gNumPtrs; i++) {
+        dvmHeapBitmapSetObjectBit(&hb1, gXorPtrs[i]);
+    }
+TRACE("walk 3\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Set every third pointer in the other bitmap, and visit again.
+     */
+    for (i = 0; i < gNumPtrs; i += 3) {
+        dvmHeapBitmapSetObjectBit(&hb2, gXorPtrs[i]);
+    }
+TRACE("walk 4\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Set every other pointer in the other bitmap, and visit again.
+     */
+    for (i = 0; i < gNumPtrs; i += 2) {
+        dvmHeapBitmapSetObjectBit(&hb2, gXorPtrs[i]);
+    }
+TRACE("walk 5\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Walk just one bitmap.
+     */
+TRACE("walk 6\n");
+    ok = dvmHeapBitmapWalk(&hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+//xxx build an expect list for the callback
+//xxx test where max points to beginning, middle, and end of a word
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb1);
+    dvmHeapBitmapDelete(&hb2);
+}
+
+void
+test_xor()
+{
+    run_xor(0, 1);
+    run_xor(100, 34);
+}
+
+int main(int argc, char *argv[])
+{
+    printf("test_init...\n");
+    test_init();
+
+    printf("test_bits...\n");
+    test_bits();
+
+    printf("test_clear...\n");
+    test_clear();
+
+    printf("test_modify...\n");
+    test_modify();
+
+    printf("test_xor...\n");
+    test_xor();
+
+    printf("done.\n");
+    return 0;
+}
diff --git a/vm/alloc/Verify.cpp b/vm/alloc/Verify.cpp
new file mode 100644
index 0000000..93a090a
--- /dev/null
+++ b/vm/alloc/Verify.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Verify.h"
+#include "alloc/Visit.h"
+
+/*
+ * Visitor applied to each reference field when searching for things
+ * that point to an object.  Sets the argument to NULL when a match is
+ * found.
+ */
+static void dumpReferencesVisitor(void *pObj, void *arg)
+{
+    Object *obj = *(Object **)pObj;
+    Object *lookingFor = *(Object **)arg;
+    if (lookingFor != NULL && lookingFor == obj) {
+        *(Object **)arg = NULL;
+    }
+}
+
+/*
+ * Visitor applied to each bitmap element to search for things that
+ * point to an object.  Logs a message when a match is found.
+ */
+static void dumpReferencesCallback(Object *obj, void *arg)
+{
+    if (obj == (Object *)arg) {
+        return;
+    }
+    dvmVisitObject(dumpReferencesVisitor, obj, &arg);
+    if (arg == NULL) {
+        ALOGD("Found %p in the heap @ %p", arg, obj);
+        dvmDumpObject(obj);
+    }
+}
+
+/*
+ * Visitor applied to each root to search for things that point to an
+ * object.  Logs a message when a match is found.
+ */
+static void dumpReferencesRootVisitor(void *ptr, u4 threadId,
+                                      RootType type, void *arg)
+{
+    Object *obj = *(Object **)ptr;
+    Object *lookingFor = *(Object **)arg;
+    if (obj == lookingFor) {
+        ALOGD("Found %p in a root @ %p", arg, ptr);
+    }
+}
+
+/*
+ * Searches the roots and heap for object references.
+ */
+static void dumpReferences(const Object *obj)
+{
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    void *arg = (void *)obj;
+    dvmVisitRoots(dumpReferencesRootVisitor, arg);
+    dvmHeapBitmapWalk(bitmap, dumpReferencesCallback, arg);
+}
+
+/*
+ * Checks that the given reference points to a valid object.
+ */
+static void verifyReference(void *addr, void *arg)
+{
+    Object *obj;
+    bool isValid;
+
+    assert(addr != NULL);
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        isValid = true;
+    } else {
+        isValid = dvmIsValidObject(obj);
+    }
+    if (!isValid) {
+        Object **parent = (Object **)arg;
+        if (*parent != NULL) {
+            ALOGE("Verify of object %p failed", *parent);
+            dvmDumpObject(*parent);
+            *parent = NULL;
+        }
+        ALOGE("Verify of reference %p @ %p failed", obj, addr);
+        dvmDumpObject(obj);
+    }
+}
+
+/*
+ * Verifies an object reference.
+ */
+void dvmVerifyObject(const Object *obj)
+{
+    Object *arg = const_cast<Object*>(obj);
+    dvmVisitObject(verifyReference, arg, &arg);
+    if (arg == NULL) {
+        dumpReferences(obj);
+        dvmAbort();
+    }
+}
+
+/*
+ * Helper function to call dvmVerifyObject from a bitmap walker.
+ */
+static void verifyBitmapCallback(Object *obj, void *arg)
+{
+    dvmVerifyObject(obj);
+}
+
+/*
+ * Verifies the object references in a heap bitmap. Assumes the VM is
+ * suspended.
+ */
+void dvmVerifyBitmap(const HeapBitmap *bitmap)
+{
+    dvmHeapBitmapWalk(bitmap, verifyBitmapCallback, NULL);
+}
+
+/*
+ * Helper function to call verifyReference from the root verifier.
+ */
+static void verifyRootReference(void *addr, u4 threadId,
+                                RootType type, void *arg)
+{
+    verifyReference(addr, arg);
+}
+
+/*
+ * Verifies references in the roots.
+ */
+void dvmVerifyRoots()
+{
+    dvmVisitRoots(verifyRootReference, NULL);
+}
diff --git a/vm/alloc/Verify.h b/vm/alloc/Verify.h
new file mode 100644
index 0000000..b9370ff
--- /dev/null
+++ b/vm/alloc/Verify.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_ALLOC_VERIFY_H_
+#define DALVIK_ALLOC_VERIFY_H_
+
+/*
+ * Verifies an object reference.
+ */
+void dvmVerifyObject(const Object *obj);
+
+/*
+ * Verifies the object references in a heap bitmap. Assumes the VM is
+ * suspended.
+ */
+void dvmVerifyBitmap(const HeapBitmap *bitmap);
+
+/*
+ * Verifies the contents of various global roots.
+ */
+void dvmVerifyRoots(void);
+
+#endif  // DALVIK_ALLOC_VERIFY_H_
diff --git a/vm/alloc/Visit.cpp b/vm/alloc/Visit.cpp
new file mode 100644
index 0000000..410b66e
--- /dev/null
+++ b/vm/alloc/Visit.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/Visit.h"
+#include "alloc/VisitInlines.h"
+
+/*
+ * Visits all of the reference locations in an object.
+ */
+void dvmVisitObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    visitObject(visitor, obj, arg);
+}
+
+/*
+ * Applies a verification function to all present values in the hash table.
+ */
+static void visitHashTable(RootVisitor *visitor, HashTable *table,
+                           RootType type, void *arg)
+{
+    assert(visitor != NULL);
+    assert(table != NULL);
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
+            (*visitor)(&entry->data, 0, type, arg);
+        }
+    }
+    dvmHashTableUnlock(table);
+}
+
+/*
+ * Visits all entries in the reference table.
+ */
+static void visitReferenceTable(RootVisitor *visitor, ReferenceTable *table,
+                                u4 threadId, RootType type, void *arg)
+{
+    assert(visitor != NULL);
+    assert(table != NULL);
+    for (Object **entry = table->table; entry < table->nextEntry; ++entry) {
+        assert(entry != NULL);
+        (*visitor)(entry, threadId, type, arg);
+    }
+}
+
+/*
+ * Visits all entries in the indirect reference table.
+ */
+static void visitIndirectRefTable(RootVisitor *visitor, IndirectRefTable *table,
+                                  u4 threadId, RootType type, void *arg)
+{
+    assert(visitor != NULL);
+    assert(table != NULL);
+    typedef IndirectRefTable::iterator It; // TODO: C++0x auto
+    for (It it = table->begin(), end = table->end(); it != end; ++it) {
+        (*visitor)(*it, threadId, type, arg);
+    }
+}
+
+/*
+ * Visits all stack slots except those belonging to native method
+ * arguments.
+ */
+static void visitThreadStack(RootVisitor *visitor, Thread *thread, void *arg)
+{
+    assert(visitor != NULL);
+    assert(thread != NULL);
+    u4 threadId = thread->threadId;
+    const StackSaveArea *saveArea;
+    for (u4 *fp = (u4 *)thread->interpSave.curFrame;
+         fp != NULL;
+         fp = (u4 *)saveArea->prevFrame) {
+        Method *method;
+        saveArea = SAVEAREA_FROM_FP(fp);
+        method = (Method *)saveArea->method;
+        if (method != NULL && !dvmIsNativeMethod(method)) {
+            const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+            const u1* regVector = NULL;
+            if (pMap != NULL) {
+                /* found map, get registers for this address */
+                int addr = saveArea->xtra.currentPc - method->insns;
+                regVector = dvmRegisterMapGetLine(pMap, addr);
+            }
+            if (regVector == NULL) {
+                /*
+                 * Either there was no register map or there is no
+                 * info for the current PC.  Perform a conservative
+                 * scan.
+                 */
+                for (size_t i = 0; i < method->registersSize; ++i) {
+                    if (dvmIsValidObject((Object *)fp[i])) {
+                        (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
+                    }
+                }
+            } else {
+                /*
+                 * Precise scan.  v0 is at the lowest address on the
+                 * interpreted stack, and is the first bit in the
+                 * register vector, so we can walk through the
+                 * register map and memory in the same direction.
+                 *
+                 * A '1' bit indicates a live reference.
+                 */
+                u2 bits = 1 << 1;
+                for (size_t i = 0; i < method->registersSize; ++i) {
+                    bits >>= 1;
+                    if (bits == 1) {
+                        /* set bit 9 so we can tell when we're empty */
+                        bits = *regVector++ | 0x0100;
+                    }
+                    if ((bits & 0x1) != 0) {
+                        /*
+                         * Register is marked as live, it's a valid root.
+                         */
+#if WITH_EXTRA_GC_CHECKS
+                        if (fp[i] != 0 && !dvmIsValidObject((Object *)fp[i])) {
+                            /* this is very bad */
+                            ALOGE("PGC: invalid ref in reg %d: %#x",
+                                 method->registersSize - 1 - i, fp[i]);
+                            ALOGE("PGC: %s.%s addr %#x",
+                                 method->clazz->descriptor, method->name,
+                                 saveArea->xtra.currentPc - method->insns);
+                            continue;
+                        }
+#endif
+                        (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
+                    }
+                }
+                dvmReleaseRegisterMapLine(pMap, regVector);
+            }
+        }
+        /*
+         * Don't fall into an infinite loop if things get corrupted.
+         */
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)fp ||
+               saveArea->prevFrame == NULL);
+    }
+}
+
+/*
+ * Visits all roots associated with a thread.
+ */
+static void visitThread(RootVisitor *visitor, Thread *thread, void *arg)
+{
+    u4 threadId;
+
+    assert(visitor != NULL);
+    assert(thread != NULL);
+    threadId = thread->threadId;
+    (*visitor)(&thread->threadObj, threadId, ROOT_THREAD_OBJECT, arg);
+    (*visitor)(&thread->exception, threadId, ROOT_NATIVE_STACK, arg);
+    visitReferenceTable(visitor, &thread->internalLocalRefTable, threadId, ROOT_NATIVE_STACK, arg);
+    visitIndirectRefTable(visitor, &thread->jniLocalRefTable, threadId, ROOT_JNI_LOCAL, arg);
+    if (thread->jniMonitorRefTable.table != NULL) {
+        visitReferenceTable(visitor, &thread->jniMonitorRefTable, threadId, ROOT_JNI_MONITOR, arg);
+    }
+    visitThreadStack(visitor, thread, arg);
+}
+
+/*
+ * Visits all threads on the thread list.
+ */
+static void visitThreads(RootVisitor *visitor, void *arg)
+{
+    Thread *thread;
+
+    assert(visitor != NULL);
+    dvmLockThreadList(dvmThreadSelf());
+    thread = gDvm.threadList;
+    while (thread) {
+        visitThread(visitor, thread, arg);
+        thread = thread->next;
+    }
+    dvmUnlockThreadList();
+}
+
+static void visitPrimitiveTypes(RootVisitor *visitor, void *arg)
+{
+    (*visitor)(&gDvm.typeVoid, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeBoolean, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeByte, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeShort, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeChar, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeInt, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeLong, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeFloat, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeDouble, 0, ROOT_STICKY_CLASS, arg);
+}
+
+/*
+ * Visits roots.  TODO: visit cached global references.
+ */
+void dvmVisitRoots(RootVisitor *visitor, void *arg)
+{
+    assert(visitor != NULL);
+    visitHashTable(visitor, gDvm.loadedClasses, ROOT_STICKY_CLASS, arg);
+    visitPrimitiveTypes(visitor, arg);
+    if (gDvm.dbgRegistry != NULL) {
+        visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg);
+    }
+    if (gDvm.literalStrings != NULL) {
+        visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg);
+    }
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
+    visitIndirectRefTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg);
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+    dvmLockMutex(&gDvm.jniPinRefLock);
+    visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg);
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
+    visitThreads(visitor, arg);
+    (*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
+    (*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
+    (*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg);
+}
diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h
new file mode 100644
index 0000000..ff69a63
--- /dev/null
+++ b/vm/alloc/Visit.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_ALLOC_VISIT_H_
+#define DALVIK_ALLOC_VISIT_H_
+
+#include "Dalvik.h"
+
+enum RootType {
+  ROOT_UNKNOWN = 0,
+  ROOT_JNI_GLOBAL,
+  ROOT_JNI_LOCAL,
+  ROOT_JAVA_FRAME,
+  ROOT_NATIVE_STACK,
+  ROOT_STICKY_CLASS,
+  ROOT_THREAD_BLOCK,
+  ROOT_MONITOR_USED,
+  ROOT_THREAD_OBJECT,
+  ROOT_INTERNED_STRING,
+  ROOT_DEBUGGER,
+  ROOT_VM_INTERNAL,
+  ROOT_JNI_MONITOR,
+};
+
+/*
+ * Callback invoked with the address of a reference and a user
+ * supplied context argument.
+ */
+typedef void Visitor(void *addr, void *arg);
+
+/*
+ * Like a Visitor, but passes root specific information such as the
+ * containing thread id and the root type.  In cases where a root is
+ * not specific to a thread, 0, an invalid thread id is provided.
+ */
+typedef void RootVisitor(void *addr, u4 threadId, RootType type, void *arg);
+
+/*
+ * Visits references in an object.
+ */
+void dvmVisitObject(Visitor *visitor, Object *obj, void *arg);
+
+/*
+ * Visits references in the root set.
+ */
+void dvmVisitRoots(RootVisitor *visitor, void *arg);
+
+#endif  // DALVIK_ALLOC_VISIT_H_
diff --git a/vm/alloc/VisitInlines.h b/vm/alloc/VisitInlines.h
new file mode 100644
index 0000000..c6204ac
--- /dev/null
+++ b/vm/alloc/VisitInlines.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_ALLOC_VISITINLINES_H_
+#define DALVIK_ALLOC_VISITINLINES_H_
+
+/*
+ * Visits the instance fields of a class or data object.
+ */
+static void visitFields(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
+        size_t refOffsets = obj->clazz->refOffsets;
+        while (refOffsets != 0) {
+            size_t rshift = CLZ(refOffsets);
+            size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+            Object **ref = (Object **)BYTE_OFFSET(obj, offset);
+            (*visitor)(ref, arg);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+        }
+    } else {
+        for (ClassObject *clazz = obj->clazz;
+             clazz != NULL;
+             clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            for (int i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                size_t offset = field->byteOffset;
+                Object **ref = (Object **)BYTE_OFFSET(obj, offset);
+                (*visitor)(ref, arg);
+            }
+        }
+    }
+}
+
+/*
+ * Visits the static fields of a class object.
+ */
+static void visitStaticFields(Visitor *visitor, ClassObject *clazz,
+                              void *arg)
+{
+    assert(visitor != NULL);
+    assert(clazz != NULL);
+    for (int i = 0; i < clazz->sfieldCount; ++i) {
+        char ch = clazz->sfields[i].signature[0];
+        if (ch == '[' || ch == 'L') {
+            (*visitor)(&clazz->sfields[i].value.l, arg);
+        }
+    }
+}
+
+/*
+ * Visit the interfaces of a class object.
+ */
+static void visitInterfaces(Visitor *visitor, ClassObject *clazz,
+                            void *arg)
+{
+    assert(visitor != NULL);
+    assert(clazz != NULL);
+    for (int i = 0; i < clazz->interfaceCount; ++i) {
+        (*visitor)(&clazz->interfaces[i], arg);
+    }
+}
+
+/*
+ * Visits all the references stored in a class object instance.
+ */
+static void visitClassObject(Visitor *visitor, Object *obj, void *arg)
+{
+    ClassObject *asClass;
+
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(!strcmp(obj->clazz->descriptor, "Ljava/lang/Class;"));
+    (*visitor)(&obj->clazz, arg);
+    asClass = (ClassObject *)obj;
+    if (IS_CLASS_FLAG_SET(asClass, CLASS_ISARRAY)) {
+        (*visitor)(&asClass->elementClass, arg);
+    }
+    if (asClass->status > CLASS_IDX) {
+        (*visitor)(&asClass->super, arg);
+    }
+    (*visitor)(&asClass->classLoader, arg);
+    visitFields(visitor, obj, arg);
+    visitStaticFields(visitor, asClass, arg);
+    if (asClass->status > CLASS_IDX) {
+      visitInterfaces(visitor, asClass, arg);
+    }
+}
+
+/*
+ * Visits the class object and, if the array is typed as an object
+ * array, all of the array elements.
+ */
+static void visitArrayObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    (*visitor)(&obj->clazz, arg);
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) {
+        ArrayObject *array = (ArrayObject *)obj;
+        Object **contents = (Object **)(void *)array->contents;
+        for (size_t i = 0; i < array->length; ++i) {
+            (*visitor)(&contents[i], arg);
+        }
+    }
+}
+
+/*
+ * Visits the class object and reference typed instance fields of a
+ * data object.
+ */
+static void visitDataObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    (*visitor)(&obj->clazz, arg);
+    visitFields(visitor, obj, arg);
+}
+
+/*
+ * Like visitDataObject, but visits the hidden referent field that
+ * belongings to the subclasses of java.lang.Reference.
+ */
+static void visitReferenceObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    visitDataObject(visitor, obj, arg);
+    size_t offset = gDvm.offJavaLangRefReference_referent;
+    Object **ref = (Object **)BYTE_OFFSET(obj, offset);
+    (*visitor)(ref, arg);
+}
+
+/*
+ * Visits all of the reference stored in an object.
+ */
+static void visitObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (dvmIsClassObject(obj)) {
+        visitClassObject(visitor, obj, arg);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        visitArrayObject(visitor, obj, arg);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+        visitReferenceObject(visitor, obj, arg);
+    } else {
+        visitDataObject(visitor, obj, arg);
+    }
+}
+
+#endif  // DALVIK_ALLOC_VISITINLINES_H_
diff --git a/vm/alloc/WriteBarrier.h b/vm/alloc/WriteBarrier.h
new file mode 100644
index 0000000..31b3482
--- /dev/null
+++ b/vm/alloc/WriteBarrier.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_ALLOC_WRITEBARRIER_H_
+#define DALVIK_ALLOC_WRITEBARRIER_H_
+
+/*
+ * Note writes to the heap. These functions must be called if a field
+ * of an Object in the heap changes, and before any GC safe-point. The
+ * call is not needed if NULL is stored in the field.
+ */
+
+/*
+ * The address within the Object has been written, and perhaps changed.
+ */
+INLINE void dvmWriteBarrierField(const Object *obj, void *addr)
+{
+    dvmMarkCard(obj);
+}
+
+/*
+ * All of the Object may have changed.
+ */
+INLINE void dvmWriteBarrierObject(const Object *obj)
+{
+    dvmMarkCard(obj);
+}
+
+/*
+ * Some or perhaps all of the array indexes in the Array, greater than
+ * or equal to start and strictly less than end, have been written,
+ * and perhaps changed.
+ */
+INLINE void dvmWriteBarrierArray(const ArrayObject *obj,
+                                 size_t start, size_t end)
+{
+    dvmMarkCard((Object *)obj);
+}
+
+#endif  // DALVIK_ALLOC_WRITEBARRIER_H_
diff --git a/vm/analysis/CodeVerify.cpp b/vm/analysis/CodeVerify.cpp
new file mode 100644
index 0000000..56a9fbd
--- /dev/null
+++ b/vm/analysis/CodeVerify.cpp
@@ -0,0 +1,6206 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik bytecode structural verifier.  The only public entry point
+ * (except for a few shared utility functions) is dvmVerifyCodeFlow().
+ *
+ * TODO: might benefit from a signature-->class lookup cache.  Could avoid
+ * some string-peeling and wouldn't need to compute hashes.
+ */
+#include "Dalvik.h"
+#include "analysis/Liveness.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/Optimize.h"
+#include "analysis/RegisterMap.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+#include <stddef.h>
+
+
+/*
+ * We don't need to store the register data for many instructions, because
+ * we either only need it at branch points (for verification) or GC points
+ * and branches (for verification + type-precise register analysis).
+ */
+enum RegisterTrackingMode {
+    kTrackRegsBranches,
+    kTrackRegsGcPoints,
+    kTrackRegsAll
+};
+
+/*
+ * Set this to enable dead code scanning.  This is not required, but it's
+ * very useful when testing changes to the verifier (to make sure we're not
+ * skipping over stuff) and for checking the optimized output from "dx".
+ * The only reason not to do it is that it slightly increases the time
+ * required to perform verification.
+ */
+#ifndef NDEBUG
+# define DEAD_CODE_SCAN  true
+#else
+# define DEAD_CODE_SCAN  false
+#endif
+
+static bool gDebugVerbose = false;
+
+#define SHOW_REG_DETAILS \
+    (0 | DRT_SHOW_LIVENESS /*| DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS*/)
+
+/*
+ * We need an extra "pseudo register" to hold the return type briefly.  It
+ * can be category 1 or 2, so we need two slots.
+ */
+#define kExtraRegs  2
+#define RESULT_REGISTER(_insnRegCount)  (_insnRegCount)
+
+/*
+ * Big fat collection of register data.
+ */
+typedef struct RegisterTable {
+    /*
+     * Array of RegisterLine structs, one per address in the method.  We only
+     * set the pointers for certain addresses, based on instruction widths
+     * and what we're trying to accomplish.
+     */
+    RegisterLine* registerLines;
+
+    /*
+     * Number of registers we track for each instruction.  This is equal
+     * to the method's declared "registersSize" plus kExtraRegs.
+     */
+    size_t      insnRegCountPlus;
+
+    /*
+     * Storage for a register line we're currently working on.
+     */
+    RegisterLine workLine;
+
+    /*
+     * Storage for a register line we're saving for later.
+     */
+    RegisterLine savedLine;
+
+    /*
+     * A single large alloc, with all of the storage needed for RegisterLine
+     * data (RegType array, MonitorEntries array, monitor stack).
+     */
+    void*       lineAlloc;
+} RegisterTable;
+
+
+/* fwd */
+#ifndef NDEBUG
+static void checkMergeTab();
+#endif
+static bool isInitMethod(const Method* meth);
+static RegType getInvocationThis(const RegisterLine* registerLine,\
+    const DecodedInstruction* pDecInsn, VerifyError* pFailure);
+static void verifyRegisterType(RegisterLine* registerLine, \
+    u4 vsrc, RegType checkType, VerifyError* pFailure);
+static bool doCodeVerification(VerifierData* vdata, RegisterTable* regTable);
+static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,\
+    RegisterTable* regTable, int insnIdx, UninitInstanceMap* uninitMap,
+    int* pStartGuess);
+static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2);
+static void dumpRegTypes(const VerifierData* vdata, \
+    const RegisterLine* registerLine, int addr, const char* addrName,
+    const UninitInstanceMap* uninitMap, int displayFlags);
+
+/* bit values for dumpRegTypes() "displayFlags" */
+enum {
+    DRT_SIMPLE          = 0,
+    DRT_SHOW_REF_TYPES  = 0x01,
+    DRT_SHOW_LOCALS     = 0x02,
+    DRT_SHOW_LIVENESS   = 0x04,
+};
+
+
+/*
+ * ===========================================================================
+ *      RegType and UninitInstanceMap utility functions
+ * ===========================================================================
+ */
+
+#define __  kRegTypeUnknown
+#define _U  kRegTypeUninit
+#define _X  kRegTypeConflict
+#define _0  kRegTypeZero
+#define _1  kRegTypeOne
+#define _Z  kRegTypeBoolean
+#define _y  kRegTypeConstPosByte
+#define _Y  kRegTypeConstByte
+#define _h  kRegTypeConstPosShort
+#define _H  kRegTypeConstShort
+#define _c  kRegTypeConstChar
+#define _i  kRegTypeConstInteger
+#define _b  kRegTypePosByte
+#define _B  kRegTypeByte
+#define _s  kRegTypePosShort
+#define _S  kRegTypeShort
+#define _C  kRegTypeChar
+#define _I  kRegTypeInteger
+#define _F  kRegTypeFloat
+#define _N  kRegTypeConstLo
+#define _n  kRegTypeConstHi
+#define _J  kRegTypeLongLo
+#define _j  kRegTypeLongHi
+#define _D  kRegTypeDoubleLo
+#define _d  kRegTypeDoubleHi
+
+/*
+ * Merge result table for primitive values.  The table is symmetric along
+ * the diagonal.
+ *
+ * Note that 32-bit int/float do not merge into 64-bit long/double.  This
+ * is a register merge, not a widening conversion.  Only the "implicit"
+ * widening within a category, e.g. byte to short, is allowed.
+ *
+ * Dalvik does not draw a distinction between int and float, but we enforce
+ * that once a value is used as int, it can't be used as float, and vice
+ * versa. We do not allow free exchange between 32-bit int/float and 64-bit
+ * long/double.
+ *
+ * Note that Uninit+Uninit=Uninit.  This holds true because we only
+ * use this when the RegType value is exactly equal to kRegTypeUninit, which
+ * can only happen for the zeroeth entry in the table.
+ *
+ * "Unknown" never merges with anything known.  The only time a register
+ * transitions from "unknown" to "known" is when we're executing code
+ * for the first time, and we handle that with a simple copy.
+ */
+const char gDvmMergeTab[kRegTypeMAX][kRegTypeMAX] =
+{
+    /* chk:  _  U  X  0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F  N  n  J  j  D  d */
+    { /*_*/ __,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+    { /*U*/ _X,_U,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+    { /*X*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+    { /*0*/ _X,_X,_X,_0,_Z,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*1*/ _X,_X,_X,_Z,_1,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*Z*/ _X,_X,_X,_Z,_Z,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*y*/ _X,_X,_X,_y,_y,_y,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*Y*/ _X,_X,_X,_Y,_Y,_Y,_Y,_Y,_h,_H,_c,_i,_B,_B,_S,_S,_I,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*h*/ _X,_X,_X,_h,_h,_h,_h,_h,_h,_H,_c,_i,_s,_S,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*H*/ _X,_X,_X,_H,_H,_H,_H,_H,_H,_H,_c,_i,_S,_S,_S,_S,_I,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*c*/ _X,_X,_X,_c,_c,_c,_c,_c,_c,_c,_c,_i,_C,_I,_C,_I,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*i*/ _X,_X,_X,_i,_i,_i,_i,_i,_i,_i,_i,_i,_I,_I,_I,_I,_I,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*b*/ _X,_X,_X,_b,_b,_b,_b,_B,_s,_S,_C,_I,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*B*/ _X,_X,_X,_B,_B,_B,_B,_B,_S,_S,_I,_I,_B,_B,_S,_S,_I,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*s*/ _X,_X,_X,_s,_s,_s,_s,_S,_s,_S,_C,_I,_s,_S,_s,_S,_C,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*S*/ _X,_X,_X,_S,_S,_S,_S,_S,_S,_S,_I,_I,_S,_S,_S,_S,_I,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*C*/ _X,_X,_X,_C,_C,_C,_C,_I,_C,_I,_C,_I,_C,_I,_C,_I,_C,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*I*/ _X,_X,_X,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*F*/ _X,_X,_X,_F,_F,_F,_F,_F,_F,_F,_F,_F,_X,_X,_X,_X,_X,_X,_F,_X,_X,_X,_X,_X,_X },
+    { /*N*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_N,_X,_J,_X,_D,_X },
+    { /*n*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_n,_X,_j,_X,_d },
+    { /*J*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_J,_X,_J,_X,_X,_X },
+    { /*j*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_j,_X,_j,_X,_X },
+    { /*D*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_D,_X,_X,_X,_D,_X },
+    { /*d*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_d,_X,_X,_X,_d },
+};
+
+#undef __
+#undef _U
+#undef _X
+#undef _0
+#undef _1
+#undef _Z
+#undef _y
+#undef _Y
+#undef _h
+#undef _H
+#undef _c
+#undef _i
+#undef _b
+#undef _B
+#undef _s
+#undef _S
+#undef _C
+#undef _I
+#undef _F
+#undef _N
+#undef _n
+#undef _J
+#undef _j
+#undef _D
+#undef _d
+
+#ifndef NDEBUG
+/*
+ * Verify symmetry in the conversion table.
+ */
+static void checkMergeTab()
+{
+    int i, j;
+
+    for (i = 0; i < kRegTypeMAX; i++) {
+        for (j = i; j < kRegTypeMAX; j++) {
+            if (gDvmMergeTab[i][j] != gDvmMergeTab[j][i]) {
+                ALOGE("Symmetry violation: %d,%d vs %d,%d", i, j, j, i);
+                dvmAbort();
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * Determine whether we can convert "srcType" to "checkType", where
+ * "checkType" is one of the category-1 non-reference types.
+ *
+ * Constant derived types may become floats, but other values may not.
+ */
+static bool canConvertTo1nr(RegType srcType, RegType checkType)
+{
+    static const char convTab
+        [kRegType1nrEND-kRegType1nrSTART+1][kRegType1nrEND-kRegType1nrSTART+1] =
+    {
+        /* chk: 0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F */
+        { /*0*/ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*1*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*Z*/ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*y*/ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*Y*/ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 },
+        { /*h*/ 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1 },
+        { /*H*/ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1 },
+        { /*c*/ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1 },
+        { /*i*/ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1 },
+        { /*b*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 },
+        { /*B*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0 },
+        { /*s*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 },
+        { /*S*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
+        { /*C*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
+        { /*I*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
+        { /*F*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+    };
+
+    assert(checkType >= kRegType1nrSTART && checkType <= kRegType1nrEND);
+#if 0
+    if (checkType < kRegType1nrSTART || checkType > kRegType1nrEND) {
+        LOG_VFY("Unexpected checkType %d (srcType=%d)", checkType, srcType);
+        assert(false);
+        return false;
+    }
+#endif
+
+    //printf("convTab[%d][%d] = %d\n", srcType, checkType,
+    //    convTab[srcType-kRegType1nrSTART][checkType-kRegType1nrSTART]);
+    if (srcType >= kRegType1nrSTART && srcType <= kRegType1nrEND)
+        return (bool) convTab[srcType-kRegType1nrSTART][checkType-kRegType1nrSTART];
+
+    return false;
+}
+
+/*
+ * Determine whether the category-2 types are compatible.
+ */
+static bool canConvertTo2(RegType srcType, RegType checkType)
+{
+    return ((srcType == kRegTypeConstLo || srcType == checkType) &&
+            (checkType == kRegTypeLongLo || checkType == kRegTypeDoubleLo));
+}
+
+/*
+ * Determine whether or not "instrType" and "targetType" are compatible,
+ * for purposes of getting or setting a value in a field or array.  The
+ * idea is that an instruction with a category 1nr type (say, aget-short
+ * or iput-boolean) is accessing a static field, instance field, or array
+ * entry, and we want to make sure sure that the operation is legal.
+ *
+ * At a minimum, source and destination must have the same width.  We
+ * further refine this to assert that "short" and "char" are not
+ * compatible, because the sign-extension is different on the "get"
+ * operations.
+ *
+ * We're not considering the actual contents of the register, so we'll
+ * never get "pseudo-types" like kRegTypeZero or kRegTypePosShort.  We
+ * could get kRegTypeUnknown in "targetType" if a field or array class
+ * lookup failed.  Category 2 types and references are checked elsewhere.
+ */
+static bool checkFieldArrayStore1nr(RegType instrType, RegType targetType)
+{
+    return (instrType == targetType);
+}
+
+/*
+ * Convert a VM PrimitiveType enum value to the equivalent RegType value.
+ */
+static RegType primitiveTypeToRegType(PrimitiveType primType)
+{
+    switch (primType) {
+        case PRIM_BOOLEAN: return kRegTypeBoolean;
+        case PRIM_BYTE:    return kRegTypeByte;
+        case PRIM_SHORT:   return kRegTypeShort;
+        case PRIM_CHAR:    return kRegTypeChar;
+        case PRIM_INT:     return kRegTypeInteger;
+        case PRIM_LONG:    return kRegTypeLongLo;
+        case PRIM_FLOAT:   return kRegTypeFloat;
+        case PRIM_DOUBLE:  return kRegTypeDoubleLo;
+        case PRIM_VOID:
+        default: {
+            assert(false);
+            return kRegTypeUnknown;
+        }
+    }
+}
+
+/*
+ * Convert a const derived RegType to the equivalent non-const RegType value.
+ * Does nothing if the argument type isn't const derived.
+ */
+static RegType constTypeToRegType(RegType constType)
+{
+    switch (constType) {
+        case kRegTypeConstPosByte: return kRegTypePosByte;
+        case kRegTypeConstByte: return kRegTypeByte;
+        case kRegTypeConstPosShort: return kRegTypePosShort;
+        case kRegTypeConstShort: return kRegTypeShort;
+        case kRegTypeConstChar: return kRegTypeChar;
+        case kRegTypeConstInteger: return kRegTypeInteger;
+        default: {
+            return constType;
+        }
+    }
+}
+
+/*
+ * Given a 32-bit constant, return the most-restricted RegType enum entry
+ * that can hold the value. The types used here indicate the value came
+ * from a const instruction, and may not correctly represent the real type
+ * of the value. Upon use, a constant derived type is updated with the
+ * type from the use, which will be unambiguous.
+ */
+static char determineCat1Const(s4 value)
+{
+    if (value < -32768)
+        return kRegTypeConstInteger;
+    else if (value < -128)
+        return kRegTypeConstShort;
+    else if (value < 0)
+        return kRegTypeConstByte;
+    else if (value == 0)
+        return kRegTypeZero;
+    else if (value == 1)
+        return kRegTypeOne;
+    else if (value < 128)
+        return kRegTypeConstPosByte;
+    else if (value < 32768)
+        return kRegTypeConstPosShort;
+    else if (value < 65536)
+        return kRegTypeConstChar;
+    else
+        return kRegTypeConstInteger;
+}
+
+/*
+ * Create a new uninitialized instance map.
+ *
+ * The map is allocated and populated with address entries.  The addresses
+ * appear in ascending order to allow binary searching.
+ *
+ * Very few methods have 10 or more new-instance instructions; the
+ * majority have 0 or 1.  Occasionally a static initializer will have 200+.
+ *
+ * TODO: merge this into the static pass or initRegisterTable; want to
+ * avoid walking through the instructions yet again just to set up this table
+ */
+UninitInstanceMap* dvmCreateUninitInstanceMap(const Method* meth,
+    const InsnFlags* insnFlags, int newInstanceCount)
+{
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns;
+    UninitInstanceMap* uninitMap;
+    bool isInit = false;
+    int idx, addr;
+
+    if (isInitMethod(meth)) {
+        newInstanceCount++;
+        isInit = true;
+    }
+
+    /*
+     * Allocate the header and map as a single unit.
+     *
+     * TODO: consider having a static instance so we can avoid allocations.
+     * I don't think the verifier is guaranteed to be single-threaded when
+     * running in the VM (rather than dexopt), so that must be taken into
+     * account.
+     */
+    int size = offsetof(UninitInstanceMap, map) +
+                newInstanceCount * sizeof(uninitMap->map[0]);
+    uninitMap = (UninitInstanceMap*)calloc(1, size);
+    if (uninitMap == NULL)
+        return NULL;
+    uninitMap->numEntries = newInstanceCount;
+
+    idx = 0;
+    if (isInit) {
+        uninitMap->map[idx++].addr = kUninitThisArgAddr;
+    }
+
+    /*
+     * Run through and find the new-instance instructions.
+     */
+    for (addr = 0; addr < insnsSize; /**/) {
+        int width = dvmInsnGetWidth(insnFlags, addr);
+
+        Opcode opcode = dexOpcodeFromCodeUnit(*insns);
+        if (opcode == OP_NEW_INSTANCE)
+            uninitMap->map[idx++].addr = addr;
+
+        addr += width;
+        insns += width;
+    }
+
+    assert(idx == newInstanceCount);
+    return uninitMap;
+}
+
+/*
+ * Free the map.
+ */
+void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap)
+{
+    free(uninitMap);
+}
+
+/*
+ * Set the class object associated with the instruction at "addr".
+ *
+ * Returns the map slot index, or -1 if the address isn't listed in the map
+ * (shouldn't happen) or if a class is already associated with the address
+ * (bad bytecode).
+ *
+ * Entries, once set, do not change -- a given address can only allocate
+ * one type of object.
+ */
+static int setUninitInstance(UninitInstanceMap* uninitMap, int addr,
+    ClassObject* clazz)
+{
+    int idx;
+
+    assert(clazz != NULL);
+
+#ifdef VERIFIER_STATS
+    gDvm.verifierStats.uninitSearches++;
+#endif
+
+    /* TODO: binary search when numEntries > 8 */
+    for (idx = uninitMap->numEntries - 1; idx >= 0; idx--) {
+        if (uninitMap->map[idx].addr == addr) {
+            if (uninitMap->map[idx].clazz != NULL &&
+                uninitMap->map[idx].clazz != clazz)
+            {
+                LOG_VFY("VFY: addr %d already set to %p, not setting to %p",
+                    addr, uninitMap->map[idx].clazz, clazz);
+                return -1;          // already set to something else??
+            }
+            uninitMap->map[idx].clazz = clazz;
+            return idx;
+        }
+    }
+
+    LOG_VFY("VFY: addr %d not found in uninit map", addr);
+    assert(false);      // shouldn't happen
+    return -1;
+}
+
+/*
+ * Get the class object at the specified index.
+ */
+static ClassObject* getUninitInstance(const UninitInstanceMap* uninitMap,
+    int idx)
+{
+    assert(idx >= 0 && idx < uninitMap->numEntries);
+    return uninitMap->map[idx].clazz;
+}
+
+/* determine if "type" is actually an object reference (init/uninit/zero) */
+static inline bool regTypeIsReference(RegType type) {
+    return (type > kRegTypeMAX || type == kRegTypeUninit ||
+            type == kRegTypeZero);
+}
+
+/* determine if "type" is an uninitialized object reference */
+static inline bool regTypeIsUninitReference(RegType type) {
+    return ((type & kRegTypeUninitMask) == kRegTypeUninit);
+}
+
+/* convert the initialized reference "type" to a ClassObject pointer */
+/* (does not expect uninit ref types or "zero") */
+static ClassObject* regTypeInitializedReferenceToClass(RegType type)
+{
+    assert(regTypeIsReference(type) && type != kRegTypeZero);
+    if ((type & 0x01) == 0) {
+        return (ClassObject*) type;
+    } else {
+        //LOG_VFY("VFY: attempted to use uninitialized reference");
+        return NULL;
+    }
+}
+
+/* extract the index into the uninitialized instance map table */
+static inline int regTypeToUninitIndex(RegType type) {
+    assert(regTypeIsUninitReference(type));
+    return (type & ~kRegTypeUninitMask) >> kRegTypeUninitShift;
+}
+
+/* convert the reference "type" to a ClassObject pointer */
+static ClassObject* regTypeReferenceToClass(RegType type,
+    const UninitInstanceMap* uninitMap)
+{
+    assert(regTypeIsReference(type) && type != kRegTypeZero);
+    if (regTypeIsUninitReference(type)) {
+        assert(uninitMap != NULL);
+        return getUninitInstance(uninitMap, regTypeToUninitIndex(type));
+    } else {
+        return (ClassObject*) type;
+    }
+}
+
+/* convert the ClassObject pointer to an (initialized) register type */
+static inline RegType regTypeFromClass(ClassObject* clazz) {
+    return (u4) clazz;
+}
+
+/* return the RegType for the uninitialized reference in slot "uidx" */
+static RegType regTypeFromUninitIndex(int uidx) {
+    return (u4) (kRegTypeUninit | (uidx << kRegTypeUninitShift));
+}
+
+
+/*
+ * ===========================================================================
+ *      Signature operations
+ * ===========================================================================
+ */
+
+/*
+ * Is this method a constructor?
+ */
+static bool isInitMethod(const Method* meth)
+{
+    return (*meth->name == '<' && strcmp(meth->name+1, "init>") == 0);
+}
+
+/*
+ * Is this method a class initializer?
+ */
+#if 0
+static bool isClassInitMethod(const Method* meth)
+{
+    return (*meth->name == '<' && strcmp(meth->name+1, "clinit>") == 0);
+}
+#endif
+
+/*
+ * Look up a class reference given as a simple string descriptor.
+ *
+ * If we can't find it, return a generic substitute when possible.
+ */
+static ClassObject* lookupClassByDescriptor(const Method* meth,
+    const char* pDescriptor, VerifyError* pFailure)
+{
+    /*
+     * The javac compiler occasionally puts references to nonexistent
+     * classes in signatures.  For example, if you have a non-static
+     * inner class with no constructor, the compiler provides
+     * a private <init> for you.  Constructing the class
+     * requires <init>(parent), but the outer class can't call
+     * that because the method is private.  So the compiler
+     * generates a package-scope <init>(parent,bogus) method that
+     * just calls the regular <init> (the "bogus" part being necessary
+     * to distinguish the signature of the synthetic method).
+     * Treating the bogus class as an instance of java.lang.Object
+     * allows the verifier to process the class successfully.
+     */
+
+    //ALOGI("Looking up '%s'", typeStr);
+    ClassObject* clazz;
+    clazz = dvmFindClassNoInit(pDescriptor, meth->clazz->classLoader);
+    if (clazz == NULL) {
+        dvmClearOptException(dvmThreadSelf());
+        if (strchr(pDescriptor, '$') != NULL) {
+            ALOGV("VFY: unable to find class referenced in signature (%s)",
+                pDescriptor);
+        } else {
+            LOG_VFY("VFY: unable to find class referenced in signature (%s)",
+                pDescriptor);
+        }
+
+        if (pDescriptor[0] == '[') {
+            /* We are looking at an array descriptor. */
+
+            /*
+             * There should never be a problem loading primitive arrays.
+             */
+            if (pDescriptor[1] != 'L' && pDescriptor[1] != '[') {
+                LOG_VFY("VFY: invalid char in signature in '%s'",
+                    pDescriptor);
+                *pFailure = VERIFY_ERROR_GENERIC;
+            }
+
+            /*
+             * Try to continue with base array type.  This will let
+             * us pass basic stuff (e.g. get array len) that wouldn't
+             * fly with an Object.  This is NOT correct if the
+             * missing type is a primitive array, but we should never
+             * have a problem loading those.  (I'm not convinced this
+             * is correct or even useful.  Just use Object here?)
+             */
+            clazz = dvmFindClassNoInit("[Ljava/lang/Object;",
+                meth->clazz->classLoader);
+        } else if (pDescriptor[0] == 'L') {
+            /*
+             * We are looking at a non-array reference descriptor;
+             * try to continue with base reference type.
+             */
+            clazz = gDvm.classJavaLangObject;
+        } else {
+            /* We are looking at a primitive type. */
+            LOG_VFY("VFY: invalid char in signature in '%s'", pDescriptor);
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+
+        if (clazz == NULL) {
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    }
+
+    if (dvmIsPrimitiveClass(clazz)) {
+        LOG_VFY("VFY: invalid use of primitive type '%s'", pDescriptor);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        clazz = NULL;
+    }
+
+    return clazz;
+}
+
+/*
+ * Look up a class reference in a signature.  Could be an arg or the
+ * return value.
+ *
+ * Advances "*pSig" to the last character in the signature (that is, to
+ * the ';').
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+static ClassObject* lookupSignatureClass(const Method* meth, const char** pSig,
+    VerifyError* pFailure)
+{
+    const char* sig = *pSig;
+    const char* endp = sig;
+
+    assert(sig != NULL && *sig == 'L');
+
+    while (*++endp != ';' && *endp != '\0')
+        ;
+    if (*endp != ';') {
+        LOG_VFY("VFY: bad signature component '%s' (missing ';')", sig);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return NULL;
+    }
+
+    endp++;    /* Advance past the ';'. */
+    int typeLen = endp - sig;
+    char typeStr[typeLen+1]; /* +1 for the '\0' */
+    memcpy(typeStr, sig, typeLen);
+    typeStr[typeLen] = '\0';
+
+    *pSig = endp - 1; /* - 1 so that *pSig points at, not past, the ';' */
+
+    return lookupClassByDescriptor(meth, typeStr, pFailure);
+}
+
+/*
+ * Look up an array class reference in a signature.  Could be an arg or the
+ * return value.
+ *
+ * Advances "*pSig" to the last character in the signature.
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+static ClassObject* lookupSignatureArrayClass(const Method* meth,
+    const char** pSig, VerifyError* pFailure)
+{
+    const char* sig = *pSig;
+    const char* endp = sig;
+
+    assert(sig != NULL && *sig == '[');
+
+    /* find the end */
+    while (*++endp == '[' && *endp != '\0')
+        ;
+
+    if (*endp == 'L') {
+        while (*++endp != ';' && *endp != '\0')
+            ;
+        if (*endp != ';') {
+            LOG_VFY("VFY: bad signature component '%s' (missing ';')", sig);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+    }
+
+    int typeLen = endp - sig +1;
+    char typeStr[typeLen+1];
+    memcpy(typeStr, sig, typeLen);
+    typeStr[typeLen] = '\0';
+
+    *pSig = endp;
+
+    return lookupClassByDescriptor(meth, typeStr, pFailure);
+}
+
+/*
+ * Set the register types for the first instruction in the method based on
+ * the method signature.
+ *
+ * This has the side-effect of validating the signature.
+ *
+ * Returns "true" on success.
+ */
+static bool setTypesFromSignature(const Method* meth, RegType* regTypes,
+    UninitInstanceMap* uninitMap)
+{
+    DexParameterIterator iterator;
+    int actualArgs, expectedArgs, argStart;
+    VerifyError failure = VERIFY_ERROR_NONE;
+    const char* descriptor;
+
+    dexParameterIteratorInit(&iterator, &meth->prototype);
+    argStart = meth->registersSize - meth->insSize;
+    expectedArgs = meth->insSize;     /* long/double count as two */
+    actualArgs = 0;
+
+    assert(argStart >= 0);      /* should have been verified earlier */
+
+    /*
+     * Include the "this" pointer.
+     */
+    if (!dvmIsStaticMethod(meth)) {
+        /*
+         * If this is a constructor for a class other than java.lang.Object,
+         * mark the first ("this") argument as uninitialized.  This restricts
+         * field access until the superclass constructor is called.
+         */
+        if (isInitMethod(meth) && meth->clazz != gDvm.classJavaLangObject) {
+            int uidx = setUninitInstance(uninitMap, kUninitThisArgAddr,
+                            meth->clazz);
+            assert(uidx == 0);
+            regTypes[argStart + actualArgs] = regTypeFromUninitIndex(uidx);
+        } else {
+            regTypes[argStart + actualArgs] = regTypeFromClass(meth->clazz);
+        }
+        actualArgs++;
+    }
+
+    for (;;) {
+        descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (descriptor == NULL) {
+            break;
+        }
+
+        if (actualArgs >= expectedArgs) {
+            LOG_VFY("VFY: expected %d args, found more (%s)",
+                expectedArgs, descriptor);
+            goto bad_sig;
+        }
+
+        switch (*descriptor) {
+        case 'L':
+        case '[':
+            /*
+             * We assume that reference arguments are initialized.  The
+             * only way it could be otherwise (assuming the caller was
+             * verified) is if the current method is <init>, but in that
+             * case it's effectively considered initialized the instant
+             * we reach here (in the sense that we can return without
+             * doing anything or call virtual methods).
+             */
+            {
+                ClassObject* clazz =
+                    lookupClassByDescriptor(meth, descriptor, &failure);
+                if (!VERIFY_OK(failure))
+                    goto bad_sig;
+                regTypes[argStart + actualArgs] = regTypeFromClass(clazz);
+            }
+            actualArgs++;
+            break;
+        case 'Z':
+            regTypes[argStart + actualArgs] = kRegTypeBoolean;
+            actualArgs++;
+            break;
+        case 'C':
+            regTypes[argStart + actualArgs] = kRegTypeChar;
+            actualArgs++;
+            break;
+        case 'B':
+            regTypes[argStart + actualArgs] = kRegTypeByte;
+            actualArgs++;
+            break;
+        case 'I':
+            regTypes[argStart + actualArgs] = kRegTypeInteger;
+            actualArgs++;
+            break;
+        case 'S':
+            regTypes[argStart + actualArgs] = kRegTypeShort;
+            actualArgs++;
+            break;
+        case 'F':
+            regTypes[argStart + actualArgs] = kRegTypeFloat;
+            actualArgs++;
+            break;
+        case 'D':
+            regTypes[argStart + actualArgs] = kRegTypeDoubleLo;
+            regTypes[argStart + actualArgs +1] = kRegTypeDoubleHi;
+            actualArgs += 2;
+            break;
+        case 'J':
+            regTypes[argStart + actualArgs] = kRegTypeLongLo;
+            regTypes[argStart + actualArgs +1] = kRegTypeLongHi;
+            actualArgs += 2;
+            break;
+        default:
+            LOG_VFY("VFY: unexpected signature type char '%c'", *descriptor);
+            goto bad_sig;
+        }
+    }
+
+    if (actualArgs != expectedArgs) {
+        LOG_VFY("VFY: expected %d args, found %d", expectedArgs, actualArgs);
+        goto bad_sig;
+    }
+
+    descriptor = dexProtoGetReturnType(&meth->prototype);
+
+    /*
+     * Validate return type.  We don't do the type lookup; just want to make
+     * sure that it has the right format.  Only major difference from the
+     * method argument format is that 'V' is supported.
+     */
+    switch (*descriptor) {
+    case 'I':
+    case 'C':
+    case 'S':
+    case 'B':
+    case 'Z':
+    case 'V':
+    case 'F':
+    case 'D':
+    case 'J':
+        if (*(descriptor+1) != '\0')
+            goto bad_sig;
+        break;
+    case '[':
+        /* single/multi, object/primitive */
+        while (*++descriptor == '[')
+            ;
+        if (*descriptor == 'L') {
+            while (*++descriptor != ';' && *descriptor != '\0')
+                ;
+            if (*descriptor != ';')
+                goto bad_sig;
+        } else {
+            if (*(descriptor+1) != '\0')
+                goto bad_sig;
+        }
+        break;
+    case 'L':
+        /* could be more thorough here, but shouldn't be required */
+        while (*++descriptor != ';' && *descriptor != '\0')
+            ;
+        if (*descriptor != ';')
+            goto bad_sig;
+        break;
+    default:
+        goto bad_sig;
+    }
+
+    return true;
+
+//fail:
+//    LOG_VFY_METH(meth, "VFY:  bad sig");
+//    return false;
+
+bad_sig:
+    {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        LOG_VFY("VFY: bad signature '%s' for %s.%s",
+            desc, meth->clazz->descriptor, meth->name);
+        free(desc);
+    }
+    return false;
+}
+
+/*
+ * Return the register type for the method.  We can't just use the
+ * already-computed DalvikJniReturnType, because if it's a reference type
+ * we need to do the class lookup.
+ *
+ * Returned references are assumed to be initialized.
+ *
+ * Returns kRegTypeUnknown for "void".
+ */
+static RegType getMethodReturnType(const Method* meth)
+{
+    RegType type;
+    const char* descriptor = dexProtoGetReturnType(&meth->prototype);
+
+    switch (*descriptor) {
+    case 'I':
+        type = kRegTypeInteger;
+        break;
+    case 'C':
+        type = kRegTypeChar;
+        break;
+    case 'S':
+        type = kRegTypeShort;
+        break;
+    case 'B':
+        type = kRegTypeByte;
+        break;
+    case 'Z':
+        type = kRegTypeBoolean;
+        break;
+    case 'V':
+        type = kRegTypeUnknown;
+        break;
+    case 'F':
+        type = kRegTypeFloat;
+        break;
+    case 'D':
+        type = kRegTypeDoubleLo;
+        break;
+    case 'J':
+        type = kRegTypeLongLo;
+        break;
+    case 'L':
+    case '[':
+        {
+            VerifyError failure = VERIFY_ERROR_NONE;
+            ClassObject* clazz =
+                lookupClassByDescriptor(meth, descriptor, &failure);
+            assert(VERIFY_OK(failure));
+            type = regTypeFromClass(clazz);
+        }
+        break;
+    default:
+        /* we verified signature return type earlier, so this is impossible */
+        assert(false);
+        type = kRegTypeConflict;
+        break;
+    }
+
+    return type;
+}
+
+/*
+ * Convert a single-character signature value (i.e. a primitive type) to
+ * the corresponding RegType.  This is intended for access to object fields
+ * holding primitive types.
+ *
+ * Returns kRegTypeUnknown for objects, arrays, and void.
+ */
+static RegType primSigCharToRegType(char sigChar)
+{
+    RegType type;
+
+    switch (sigChar) {
+    case 'I':
+        type = kRegTypeInteger;
+        break;
+    case 'C':
+        type = kRegTypeChar;
+        break;
+    case 'S':
+        type = kRegTypeShort;
+        break;
+    case 'B':
+        type = kRegTypeByte;
+        break;
+    case 'Z':
+        type = kRegTypeBoolean;
+        break;
+    case 'F':
+        type = kRegTypeFloat;
+        break;
+    case 'D':
+        type = kRegTypeDoubleLo;
+        break;
+    case 'J':
+        type = kRegTypeLongLo;
+        break;
+    case 'V':
+    case 'L':
+    case '[':
+        type = kRegTypeUnknown;
+        break;
+    default:
+        assert(false);
+        type = kRegTypeUnknown;
+        break;
+    }
+
+    return type;
+}
+
+/*
+ * See if the method matches the MethodType.
+ */
+static bool isCorrectInvokeKind(MethodType methodType, Method* resMethod)
+{
+    switch (methodType) {
+    case METHOD_DIRECT:
+        return dvmIsDirectMethod(resMethod);
+    case METHOD_STATIC:
+        return dvmIsStaticMethod(resMethod);
+    case METHOD_VIRTUAL:
+    case METHOD_INTERFACE:
+        return !dvmIsDirectMethod(resMethod);
+    default:
+        return false;
+    }
+}
+
+/*
+ * Verify the arguments to a method.  We're executing in "method", making
+ * a call to the method reference in vB.
+ *
+ * If this is a "direct" invoke, we allow calls to <init>.  For calls to
+ * <init>, the first argument may be an uninitialized reference.  Otherwise,
+ * calls to anything starting with '<' will be rejected, as will any
+ * uninitialized reference arguments.
+ *
+ * For non-static method calls, this will verify that the method call is
+ * appropriate for the "this" argument.
+ *
+ * The method reference is in vBBBB.  The "isRange" parameter determines
+ * whether we use 0-4 "args" values or a range of registers defined by
+ * vAA and vCCCC.
+ *
+ * Widening conversions on integers and references are allowed, but
+ * narrowing conversions are not.
+ *
+ * Returns the resolved method on success, NULL on failure (with *pFailure
+ * set appropriately).
+ */
+static Method* verifyInvocationArgs(const Method* meth,
+    RegisterLine* registerLine, const int insnRegCount,
+    const DecodedInstruction* pDecInsn, UninitInstanceMap* uninitMap,
+    MethodType methodType, bool isRange, bool isSuper, VerifyError* pFailure)
+{
+    Method* resMethod;
+    char* sigOriginal = NULL;
+    const char* sig;
+    int expectedArgs;
+    int actualArgs;
+
+    /*
+     * Resolve the method.  This could be an abstract or concrete method
+     * depending on what sort of call we're making.
+     */
+    if (methodType == METHOD_INTERFACE) {
+        resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB);
+    } else {
+        resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType,
+            pFailure);
+    }
+    if (resMethod == NULL) {
+        /* failed; print a meaningful failure message */
+        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+
+        const DexMethodId* pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB);
+        const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+        char* methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+        const char* classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+        if (!gDvm.optimizing) {
+            std::string dotMissingClass =
+                dvmHumanReadableDescriptor(classDescriptor);
+            std::string dotMethClass =
+                dvmHumanReadableDescriptor(meth->clazz->descriptor);
+
+            ALOGI("Could not find method %s.%s, referenced from method %s.%s",
+                    dotMissingClass.c_str(), methodName,
+                    dotMethClass.c_str(), meth->name);
+        }
+
+        LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s",
+            dvmMethodTypeStr(methodType), pDecInsn->vB,
+            classDescriptor, methodName, methodDesc);
+        free(methodDesc);
+        if (VERIFY_OK(*pFailure))       /* not set for interface resolve */
+            *pFailure = VERIFY_ERROR_NO_METHOD;
+        goto fail;
+    }
+
+    /*
+     * Only time you can explicitly call a method starting with '<' is when
+     * making a "direct" invocation on "<init>".  There are additional
+     * restrictions but we don't enforce them here.
+     */
+    if (resMethod->name[0] == '<') {
+        if (methodType != METHOD_DIRECT || !isInitMethod(resMethod)) {
+            LOG_VFY("VFY: invalid call to %s.%s",
+                    resMethod->clazz->descriptor, resMethod->name);
+            goto bad_sig;
+        }
+    }
+
+    /*
+     * See if the method type implied by the invoke instruction matches the
+     * access flags for the target method.
+     */
+    if (!isCorrectInvokeKind(methodType, resMethod)) {
+        LOG_VFY("VFY: invoke type does not match method type of %s.%s",
+            resMethod->clazz->descriptor, resMethod->name);
+        goto fail;
+    }
+
+    /*
+     * If we're using invoke-super(method), make sure that the executing
+     * method's class' superclass has a vtable entry for the target method.
+     */
+    if (isSuper) {
+        assert(methodType == METHOD_VIRTUAL);
+        ClassObject* super = meth->clazz->super;
+        if (super == NULL || resMethod->methodIndex > super->vtableCount) {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            LOG_VFY("VFY: invalid invoke-super from %s.%s to super %s.%s %s",
+                    meth->clazz->descriptor, meth->name,
+                    (super == NULL) ? "-" : super->descriptor,
+                    resMethod->name, desc);
+            free(desc);
+            *pFailure = VERIFY_ERROR_NO_METHOD;
+            goto fail;
+        }
+    }
+
+    /*
+     * We use vAA as our expected arg count, rather than resMethod->insSize,
+     * because we need to match the call to the signature.  Also, we might
+     * might be calling through an abstract method definition (which doesn't
+     * have register count values).
+     */
+    sigOriginal = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+    sig = sigOriginal;
+    expectedArgs = pDecInsn->vA;
+    actualArgs = 0;
+
+    /* caught by static verifier */
+    assert(isRange || expectedArgs <= 5);
+
+    if (expectedArgs > meth->outsSize) {
+        LOG_VFY("VFY: invalid arg count (%d) exceeds outsSize (%d)",
+            expectedArgs, meth->outsSize);
+        goto fail;
+    }
+
+    if (*sig++ != '(')
+        goto bad_sig;
+
+    /*
+     * Check the "this" argument, which must be an instance of the class
+     * that declared the method.  For an interface class, we don't do the
+     * full interface merge, so we can't do a rigorous check here (which
+     * is okay since we have to do it at runtime).
+     */
+    if (!dvmIsStaticMethod(resMethod)) {
+        ClassObject* actualThisRef;
+        RegType actualArgType;
+
+        actualArgType = getInvocationThis(registerLine, pDecInsn, pFailure);
+        if (!VERIFY_OK(*pFailure))
+            goto fail;
+
+        if (regTypeIsUninitReference(actualArgType) && resMethod->name[0] != '<')
+        {
+            LOG_VFY("VFY: 'this' arg must be initialized");
+            goto fail;
+        }
+        if (methodType != METHOD_INTERFACE && actualArgType != kRegTypeZero) {
+            actualThisRef = regTypeReferenceToClass(actualArgType, uninitMap);
+            if (!dvmInstanceof(actualThisRef, resMethod->clazz)) {
+                LOG_VFY("VFY: 'this' arg '%s' not instance of '%s'",
+                        actualThisRef->descriptor,
+                        resMethod->clazz->descriptor);
+                goto fail;
+            }
+        }
+        actualArgs++;
+    }
+
+    /*
+     * Process the target method's signature.  This signature may or may not
+     * have been verified, so we can't assume it's properly formed.
+     */
+    while (*sig != '\0' && *sig != ')') {
+        if (actualArgs >= expectedArgs) {
+            LOG_VFY("VFY: expected %d args, found more (%c)",
+                expectedArgs, *sig);
+            goto bad_sig;
+        }
+
+        u4 getReg;
+        if (isRange)
+            getReg = pDecInsn->vC + actualArgs;
+        else
+            getReg = pDecInsn->arg[actualArgs];
+
+        switch (*sig) {
+        case 'L':
+            {
+                ClassObject* clazz = lookupSignatureClass(meth, &sig, pFailure);
+                if (!VERIFY_OK(*pFailure))
+                    goto bad_sig;
+                verifyRegisterType(registerLine, getReg,
+                    regTypeFromClass(clazz), pFailure);
+                if (!VERIFY_OK(*pFailure)) {
+                    LOG_VFY("VFY: bad arg %d (into %s)",
+                            actualArgs, clazz->descriptor);
+                    goto bad_sig;
+                }
+            }
+            actualArgs++;
+            break;
+        case '[':
+            {
+                ClassObject* clazz =
+                    lookupSignatureArrayClass(meth, &sig, pFailure);
+                if (!VERIFY_OK(*pFailure))
+                    goto bad_sig;
+                verifyRegisterType(registerLine, getReg,
+                    regTypeFromClass(clazz), pFailure);
+                if (!VERIFY_OK(*pFailure)) {
+                    LOG_VFY("VFY: bad arg %d (into %s)",
+                            actualArgs, clazz->descriptor);
+                    goto bad_sig;
+                }
+            }
+            actualArgs++;
+            break;
+        case 'Z':
+            verifyRegisterType(registerLine, getReg, kRegTypeBoolean, pFailure);
+            actualArgs++;
+            break;
+        case 'C':
+            verifyRegisterType(registerLine, getReg, kRegTypeChar, pFailure);
+            actualArgs++;
+            break;
+        case 'B':
+            verifyRegisterType(registerLine, getReg, kRegTypeByte, pFailure);
+            actualArgs++;
+            break;
+        case 'I':
+            verifyRegisterType(registerLine, getReg, kRegTypeInteger, pFailure);
+            actualArgs++;
+            break;
+        case 'S':
+            verifyRegisterType(registerLine, getReg, kRegTypeShort, pFailure);
+            actualArgs++;
+            break;
+        case 'F':
+            verifyRegisterType(registerLine, getReg, kRegTypeFloat, pFailure);
+            actualArgs++;
+            break;
+        case 'D':
+            verifyRegisterType(registerLine, getReg, kRegTypeDoubleLo, pFailure);
+            actualArgs += 2;
+            break;
+        case 'J':
+            verifyRegisterType(registerLine, getReg, kRegTypeLongLo, pFailure);
+            actualArgs += 2;
+            break;
+        default:
+            LOG_VFY("VFY: invocation target: bad signature type char '%c'",
+                *sig);
+            goto bad_sig;
+        }
+
+        sig++;
+    }
+    if (*sig != ')') {
+        char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+        LOG_VFY("VFY: invocation target: bad signature '%s'", desc);
+        free(desc);
+        goto bad_sig;
+    }
+
+    if (actualArgs != expectedArgs) {
+        LOG_VFY("VFY: expected %d args, found %d", expectedArgs, actualArgs);
+        goto bad_sig;
+    }
+
+    free(sigOriginal);
+    return resMethod;
+
+bad_sig:
+    if (resMethod != NULL) {
+        char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+        LOG_VFY("VFY:  rejecting call to %s.%s %s",
+            resMethod->clazz->descriptor, resMethod->name, desc);
+        free(desc);
+    }
+
+fail:
+    free(sigOriginal);
+    if (*pFailure == VERIFY_ERROR_NONE)
+        *pFailure = VERIFY_ERROR_GENERIC;
+    return NULL;
+}
+
+/*
+ * Get the class object for the type of data stored in a field.  This isn't
+ * stored in the Field struct, so we have to recover it from the signature.
+ *
+ * This only works for reference types.  Don't call this for primitive types.
+ *
+ * If we can't find the class, we return java.lang.Object, so that
+ * verification can continue if a field is only accessed in trivial ways.
+ */
+static ClassObject* getFieldClass(const Method* meth, const Field* field)
+{
+    ClassObject* fieldClass;
+    const char* signature = field->signature;
+
+    if ((*signature == 'L') || (*signature == '[')) {
+        fieldClass = dvmFindClassNoInit(signature,
+                meth->clazz->classLoader);
+    } else {
+        return NULL;
+    }
+
+    if (fieldClass == NULL) {
+        dvmClearOptException(dvmThreadSelf());
+        ALOGV("VFY: unable to find class '%s' for field %s.%s, trying Object",
+            field->signature, meth->clazz->descriptor, field->name);
+        fieldClass = gDvm.classJavaLangObject;
+    } else {
+        assert(!dvmIsPrimitiveClass(fieldClass));
+    }
+    return fieldClass;
+}
+
+
+/*
+ * ===========================================================================
+ *      Register operations
+ * ===========================================================================
+ */
+
+/*
+ * Get the type of register N.
+ *
+ * The register index was validated during the static pass, so we don't
+ * need to check it here.
+ */
+static inline RegType getRegisterType(const RegisterLine* registerLine, u4 vsrc)
+{
+    return registerLine->regTypes[vsrc];
+}
+
+/*
+ * Get the value from a register, and cast it to a ClassObject.  Sets
+ * "*pFailure" if something fails.
+ *
+ * This fails if the register holds an uninitialized class.
+ *
+ * If the register holds kRegTypeZero, this returns a NULL pointer.
+ */
+static ClassObject* getClassFromRegister(const RegisterLine* registerLine,
+    u4 vsrc, VerifyError* pFailure)
+{
+    ClassObject* clazz = NULL;
+    RegType type;
+
+    /* get the element type of the array held in vsrc */
+    type = getRegisterType(registerLine, vsrc);
+
+    /* if "always zero", we allow it to fail at runtime */
+    if (type == kRegTypeZero)
+        goto bail;
+
+    if (!regTypeIsReference(type)) {
+        LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)",
+            vsrc, type);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+    if (regTypeIsUninitReference(type)) {
+        LOG_VFY("VFY: register %u holds uninitialized reference", vsrc);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+    clazz = regTypeInitializedReferenceToClass(type);
+
+bail:
+    return clazz;
+}
+
+/*
+ * Get the "this" pointer from a non-static method invocation.  This
+ * returns the RegType so the caller can decide whether it needs the
+ * reference to be initialized or not.  (Can also return kRegTypeZero
+ * if the reference can only be zero at this point.)
+ *
+ * The argument count is in vA, and the first argument is in vC, for both
+ * "simple" and "range" versions.  We just need to make sure vA is >= 1
+ * and then return vC.
+ */
+static RegType getInvocationThis(const RegisterLine* registerLine,
+    const DecodedInstruction* pDecInsn, VerifyError* pFailure)
+{
+    RegType thisType = kRegTypeUnknown;
+
+    if (pDecInsn->vA < 1) {
+        LOG_VFY("VFY: invoke lacks 'this'");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+    /* get the element type of the array held in vsrc */
+    thisType = getRegisterType(registerLine, pDecInsn->vC);
+    if (!regTypeIsReference(thisType)) {
+        LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)",
+            pDecInsn->vC, thisType);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+bail:
+    return thisType;
+}
+
+/*
+ * Set the type of register N, verifying that the register is valid.  If
+ * "newType" is the "Lo" part of a 64-bit value, register N+1 will be
+ * set to "newType+1".
+ *
+ * The register index was validated during the static pass, so we don't
+ * need to check it here.
+ *
+ * TODO: clear mon stack bits
+ */
+static void setRegisterType(RegisterLine* registerLine, u4 vdst,
+    RegType newType)
+{
+    RegType* insnRegs = registerLine->regTypes;
+
+    switch (newType) {
+    case kRegTypeUnknown:
+    case kRegTypeBoolean:
+    case kRegTypeOne:
+    case kRegTypeConstByte:
+    case kRegTypeConstPosByte:
+    case kRegTypeConstShort:
+    case kRegTypeConstPosShort:
+    case kRegTypeConstChar:
+    case kRegTypeConstInteger:
+    case kRegTypeByte:
+    case kRegTypePosByte:
+    case kRegTypeShort:
+    case kRegTypePosShort:
+    case kRegTypeChar:
+    case kRegTypeInteger:
+    case kRegTypeFloat:
+    case kRegTypeZero:
+    case kRegTypeUninit:
+        insnRegs[vdst] = newType;
+        break;
+    case kRegTypeConstLo:
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+        insnRegs[vdst] = newType;
+        insnRegs[vdst+1] = newType+1;
+        break;
+    case kRegTypeConstHi:
+    case kRegTypeLongHi:
+    case kRegTypeDoubleHi:
+        /* should never set these explicitly */
+        ALOGE("BUG: explicit set of high register type");
+        dvmAbort();
+        break;
+
+    default:
+        /* can't switch for ref types, so we check explicitly */
+        if (regTypeIsReference(newType)) {
+            insnRegs[vdst] = newType;
+
+            /*
+             * In most circumstances we won't see a reference to a primitive
+             * class here (e.g. "D"), since that would mean the object in the
+             * register is actually a primitive type.  It can happen as the
+             * result of an assumed-successful check-cast instruction in
+             * which the second argument refers to a primitive class.  (In
+             * practice, such an instruction will always throw an exception.)
+             *
+             * This is not an issue for instructions like const-class, where
+             * the object in the register is a java.lang.Class instance.
+             */
+            break;
+        }
+        /* bad type - fall through */
+
+    case kRegTypeConflict:      // should only be set during a merge
+        ALOGE("BUG: set register to unknown type %d", newType);
+        dvmAbort();
+        break;
+    }
+
+    /*
+     * Clear the monitor entry bits for this register.
+     */
+    if (registerLine->monitorEntries != NULL)
+        registerLine->monitorEntries[vdst] = 0;
+}
+
+/*
+ * Verify that the contents of the specified register have the specified
+ * type (or can be converted to it through an implicit widening conversion).
+ *
+ * This will modify the type of the source register if it was originally
+ * derived from a constant to prevent mixing of int/float and long/double.
+ *
+ * If "vsrc" is a reference, both it and the "vsrc" register must be
+ * initialized ("vsrc" may be Zero).  This will verify that the value in
+ * the register is an instance of checkType, or if checkType is an
+ * interface, verify that the register implements checkType.
+ */
+static void verifyRegisterType(RegisterLine* registerLine, u4 vsrc,
+    RegType checkType, VerifyError* pFailure)
+{
+    const RegType* insnRegs = registerLine->regTypes;
+    RegType srcType = insnRegs[vsrc];
+
+    //ALOGD("check-reg v%u = %d", vsrc, checkType);
+    switch (checkType) {
+    case kRegTypeFloat:
+    case kRegTypeBoolean:
+    case kRegTypePosByte:
+    case kRegTypeByte:
+    case kRegTypePosShort:
+    case kRegTypeShort:
+    case kRegTypeChar:
+    case kRegTypeInteger:
+        if (!canConvertTo1nr(srcType, checkType)) {
+            LOG_VFY("VFY: register1 v%u type %d, wanted %d",
+                vsrc, srcType, checkType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* Update type if result is float */
+        if (checkType == kRegTypeFloat) {
+            setRegisterType(registerLine, vsrc, checkType);
+        } else {
+            /* Update const type to actual type after use */
+            setRegisterType(registerLine, vsrc, constTypeToRegType(srcType));
+        }
+        break;
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+        if (insnRegs[vsrc+1] != srcType+1) {
+            LOG_VFY("VFY: register2 v%u-%u values %d,%d",
+                vsrc, vsrc+1, insnRegs[vsrc], insnRegs[vsrc+1]);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        } else if (!canConvertTo2(srcType, checkType)) {
+            LOG_VFY("VFY: register2 v%u type %d, wanted %d",
+                vsrc, srcType, checkType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* Update type if source is from const */
+        if (srcType == kRegTypeConstLo) {
+            setRegisterType(registerLine, vsrc, checkType);
+        }
+        break;
+    case kRegTypeConstLo:
+    case kRegTypeConstHi:
+    case kRegTypeLongHi:
+    case kRegTypeDoubleHi:
+    case kRegTypeZero:
+    case kRegTypeOne:
+    case kRegTypeUnknown:
+    case kRegTypeConflict:
+        /* should never be checking for these explicitly */
+        assert(false);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    case kRegTypeUninit:
+    default:
+        /* make sure checkType is initialized reference */
+        if (!regTypeIsReference(checkType)) {
+            LOG_VFY("VFY: unexpected check type %d", checkType);
+            assert(false);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (regTypeIsUninitReference(checkType)) {
+            LOG_VFY("VFY: uninitialized ref not expected as reg check");
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* make sure srcType is initialized reference or always-NULL */
+        if (!regTypeIsReference(srcType)) {
+            LOG_VFY("VFY: register1 v%u type %d, wanted ref", vsrc, srcType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (regTypeIsUninitReference(srcType)) {
+            LOG_VFY("VFY: register1 v%u holds uninitialized ref", vsrc);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* if the register isn't Zero, make sure it's an instance of check */
+        if (srcType != kRegTypeZero) {
+            ClassObject* srcClass = regTypeInitializedReferenceToClass(srcType);
+            ClassObject* checkClass = regTypeInitializedReferenceToClass(checkType);
+            assert(srcClass != NULL);
+            assert(checkClass != NULL);
+
+            if (dvmIsInterfaceClass(checkClass)) {
+                /*
+                 * All objects implement all interfaces as far as the
+                 * verifier is concerned.  The runtime has to sort it out.
+                 * See comments above findCommonSuperclass.
+                 */
+                /*
+                if (srcClass != checkClass &&
+                    !dvmImplements(srcClass, checkClass))
+                {
+                    LOG_VFY("VFY: %s does not implement %s",
+                            srcClass->descriptor, checkClass->descriptor);
+                    *pFailure = VERIFY_ERROR_GENERIC;
+                }
+                */
+            } else {
+                if (!dvmInstanceof(srcClass, checkClass)) {
+                    LOG_VFY("VFY: %s is not instance of %s",
+                            srcClass->descriptor, checkClass->descriptor);
+                    *pFailure = VERIFY_ERROR_GENERIC;
+                }
+            }
+        }
+        break;
+    }
+}
+
+/*
+ * Set the type of the "result" register.
+ */
+static void setResultRegisterType(RegisterLine* registerLine,
+    const int insnRegCount, RegType newType)
+{
+    setRegisterType(registerLine, RESULT_REGISTER(insnRegCount), newType);
+}
+
+
+/*
+ * Update all registers holding "uninitType" to instead hold the
+ * corresponding initialized reference type.  This is called when an
+ * appropriate <init> method is invoked -- all copies of the reference
+ * must be marked as initialized.
+ */
+static void markRefsAsInitialized(RegisterLine* registerLine, int insnRegCount,
+    UninitInstanceMap* uninitMap, RegType uninitType, VerifyError* pFailure)
+{
+    RegType* insnRegs = registerLine->regTypes;
+    ClassObject* clazz;
+    RegType initType;
+    int i, changed;
+
+    clazz = getUninitInstance(uninitMap, regTypeToUninitIndex(uninitType));
+    if (clazz == NULL) {
+        ALOGE("VFY: unable to find type=%#x (idx=%d)",
+            uninitType, regTypeToUninitIndex(uninitType));
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+    initType = regTypeFromClass(clazz);
+
+    changed = 0;
+    for (i = 0; i < insnRegCount; i++) {
+        if (insnRegs[i] == uninitType) {
+            insnRegs[i] = initType;
+            changed++;
+        }
+    }
+    //ALOGD("VFY: marked %d registers as initialized", changed);
+    assert(changed > 0);
+
+    return;
+}
+
+/*
+ * We're creating a new instance of class C at address A.  Any registers
+ * holding instances previously created at address A must be initialized
+ * by now.  If not, we mark them as "conflict" to prevent them from being
+ * used (otherwise, markRefsAsInitialized would mark the old ones and the
+ * new ones at the same time).
+ */
+static void markUninitRefsAsInvalid(RegisterLine* registerLine,
+    int insnRegCount, UninitInstanceMap* uninitMap, RegType uninitType)
+{
+    RegType* insnRegs = registerLine->regTypes;
+    int i, changed;
+
+    changed = 0;
+    for (i = 0; i < insnRegCount; i++) {
+        if (insnRegs[i] == uninitType) {
+            insnRegs[i] = kRegTypeConflict;
+            if (registerLine->monitorEntries != NULL)
+                registerLine->monitorEntries[i] = 0;
+            changed++;
+        }
+    }
+
+    //if (changed)
+    //    ALOGD("VFY: marked %d uninitialized registers as invalid", changed);
+}
+
+/*
+ * Find the register line for the specified instruction in the current method.
+ */
+static inline RegisterLine* getRegisterLine(const RegisterTable* regTable,
+    int insnIdx)
+{
+    return &regTable->registerLines[insnIdx];
+}
+
+/*
+ * Copy a register line.
+ */
+static inline void copyRegisterLine(RegisterLine* dst, const RegisterLine* src,
+    size_t numRegs)
+{
+    memcpy(dst->regTypes, src->regTypes, numRegs * sizeof(RegType));
+
+    assert((src->monitorEntries == NULL && dst->monitorEntries == NULL) ||
+           (src->monitorEntries != NULL && dst->monitorEntries != NULL));
+    if (dst->monitorEntries != NULL) {
+        assert(dst->monitorStack != NULL);
+        memcpy(dst->monitorEntries, src->monitorEntries,
+            numRegs * sizeof(MonitorEntries));
+        memcpy(dst->monitorStack, src->monitorStack,
+            kMaxMonitorStackDepth * sizeof(u4));
+        dst->monitorStackTop = src->monitorStackTop;
+    }
+}
+
+/*
+ * Copy a register line into the table.
+ */
+static inline void copyLineToTable(RegisterTable* regTable, int insnIdx,
+    const RegisterLine* src)
+{
+    RegisterLine* dst = getRegisterLine(regTable, insnIdx);
+    assert(dst->regTypes != NULL);
+    copyRegisterLine(dst, src, regTable->insnRegCountPlus);
+}
+
+/*
+ * Copy a register line out of the table.
+ */
+static inline void copyLineFromTable(RegisterLine* dst,
+    const RegisterTable* regTable, int insnIdx)
+{
+    RegisterLine* src = getRegisterLine(regTable, insnIdx);
+    assert(src->regTypes != NULL);
+    copyRegisterLine(dst, src, regTable->insnRegCountPlus);
+}
+
+
+#ifndef NDEBUG
+/*
+ * Compare two register lines.  Returns 0 if they match.
+ *
+ * Using this for a sort is unwise, since the value can change based on
+ * machine endianness.
+ */
+static inline int compareLineToTable(const RegisterTable* regTable,
+    int insnIdx, const RegisterLine* line2)
+{
+    const RegisterLine* line1 = getRegisterLine(regTable, insnIdx);
+    if (line1->monitorEntries != NULL) {
+        int result;
+
+        if (line2->monitorEntries == NULL)
+            return 1;
+        result = memcmp(line1->monitorEntries, line2->monitorEntries,
+            regTable->insnRegCountPlus * sizeof(MonitorEntries));
+        if (result != 0) {
+            LOG_VFY("monitorEntries mismatch");
+            return result;
+        }
+        result = line1->monitorStackTop - line2->monitorStackTop;
+        if (result != 0) {
+            LOG_VFY("monitorStackTop mismatch");
+            return result;
+        }
+        result = memcmp(line1->monitorStack, line2->monitorStack,
+            line1->monitorStackTop);
+        if (result != 0) {
+            LOG_VFY("monitorStack mismatch");
+            return result;
+        }
+    }
+    return memcmp(line1->regTypes, line2->regTypes,
+            regTable->insnRegCountPlus * sizeof(RegType));
+}
+#endif
+
+/*
+ * Register type categories, for type checking.
+ *
+ * The spec says category 1 includes boolean, byte, char, short, int, float,
+ * reference, and returnAddress.  Category 2 includes long and double.
+ *
+ * We treat object references separately, so we have "category1nr".  We
+ * don't support jsr/ret, so there is no "returnAddress" type.
+ */
+enum TypeCategory {
+    kTypeCategoryUnknown = 0,
+    kTypeCategory1nr,           // boolean, byte, char, short, int, float
+    kTypeCategory2,             // long, double
+    kTypeCategoryRef,           // object reference
+};
+
+/*
+ * See if "type" matches "cat".  All we're really looking for here is that
+ * we're not mixing and matching 32-bit and 64-bit quantities, and we're
+ * not mixing references with numerics.  (For example, the arguments to
+ * "a < b" could be integers of different sizes, but they must both be
+ * integers.  Dalvik is less specific about int vs. float, so we treat them
+ * as equivalent here.)
+ *
+ * For category 2 values, "type" must be the "low" half of the value.
+ *
+ * Sets "*pFailure" if something looks wrong.
+ */
+static void checkTypeCategory(RegType type, TypeCategory cat,
+    VerifyError* pFailure)
+{
+    switch (cat) {
+    case kTypeCategory1nr:
+        switch (type) {
+        case kRegTypeZero:
+        case kRegTypeOne:
+        case kRegTypeBoolean:
+        case kRegTypeConstPosByte:
+        case kRegTypeConstByte:
+        case kRegTypeConstPosShort:
+        case kRegTypeConstShort:
+        case kRegTypeConstChar:
+        case kRegTypeConstInteger:
+        case kRegTypePosByte:
+        case kRegTypeByte:
+        case kRegTypePosShort:
+        case kRegTypeShort:
+        case kRegTypeChar:
+        case kRegTypeInteger:
+        case kRegTypeFloat:
+            break;
+        default:
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+
+    case kTypeCategory2:
+        switch (type) {
+        case kRegTypeConstLo:
+        case kRegTypeLongLo:
+        case kRegTypeDoubleLo:
+            break;
+        default:
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+
+    case kTypeCategoryRef:
+        if (type != kRegTypeZero && !regTypeIsReference(type))
+            *pFailure = VERIFY_ERROR_GENERIC;
+        break;
+
+    default:
+        assert(false);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        break;
+    }
+}
+
+/*
+ * For a category 2 register pair, verify that "typeh" is the appropriate
+ * high part for "typel".
+ *
+ * Does not verify that "typel" is in fact the low part of a 64-bit
+ * register pair.
+ */
+static void checkWidePair(RegType typel, RegType typeh, VerifyError* pFailure)
+{
+    if ((typeh != typel+1))
+        *pFailure = VERIFY_ERROR_GENERIC;
+}
+
+/*
+ * Implement category-1 "move" instructions.  Copy a 32-bit value from
+ * "vsrc" to "vdst".
+ */
+static void copyRegister1(RegisterLine* registerLine, u4 vdst, u4 vsrc,
+    TypeCategory cat, VerifyError* pFailure)
+{
+    assert(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
+    RegType type = getRegisterType(registerLine, vsrc);
+    checkTypeCategory(type, cat, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copy1 v%u<-v%u type=%d cat=%d", vdst, vsrc, type, cat);
+    } else {
+        setRegisterType(registerLine, vdst, type);
+        if (cat == kTypeCategoryRef && registerLine->monitorEntries != NULL) {
+            registerLine->monitorEntries[vdst] =
+                registerLine->monitorEntries[vsrc];
+        }
+    }
+}
+
+/*
+ * Implement category-2 "move" instructions.  Copy a 64-bit value from
+ * "vsrc" to "vdst".  This copies both halves of the register.
+ */
+static void copyRegister2(RegisterLine* registerLine, u4 vdst, u4 vsrc,
+    VerifyError* pFailure)
+{
+    RegType typel = getRegisterType(registerLine, vsrc);
+    RegType typeh = getRegisterType(registerLine, vsrc+1);
+
+    checkTypeCategory(typel, kTypeCategory2, pFailure);
+    checkWidePair(typel, typeh, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copy2 v%u<-v%u type=%d/%d", vdst, vsrc, typel, typeh);
+    } else {
+        setRegisterType(registerLine, vdst, typel);
+        /* target monitor stack bits will be cleared */
+    }
+}
+
+/*
+ * Implement "move-result".  Copy the category-1 value from the result
+ * register to another register, and reset the result register.
+ */
+static void copyResultRegister1(RegisterLine* registerLine,
+    const int insnRegCount, u4 vdst, TypeCategory cat, VerifyError* pFailure)
+{
+    RegType type;
+    u4 vsrc;
+
+    assert(vdst < (u4) insnRegCount);
+
+    vsrc = RESULT_REGISTER(insnRegCount);
+    type = getRegisterType(registerLine, vsrc);
+    checkTypeCategory(type, cat, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copyRes1 v%u<-v%u cat=%d type=%d",
+            vdst, vsrc, cat, type);
+    } else {
+        setRegisterType(registerLine, vdst, type);
+        setRegisterType(registerLine, vsrc, kRegTypeUnknown);
+        /* target monitor stack bits will be cleared */
+    }
+}
+
+/*
+ * Implement "move-result-wide".  Copy the category-2 value from the result
+ * register to another register, and reset the result register.
+ */
+static void copyResultRegister2(RegisterLine* registerLine,
+    const int insnRegCount, u4 vdst, VerifyError* pFailure)
+{
+    RegType typel, typeh;
+    u4 vsrc;
+
+    assert(vdst < (u4) insnRegCount);
+
+    vsrc = RESULT_REGISTER(insnRegCount);
+    typel = getRegisterType(registerLine, vsrc);
+    typeh = getRegisterType(registerLine, vsrc+1);
+    checkTypeCategory(typel, kTypeCategory2, pFailure);
+    checkWidePair(typel, typeh, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copyRes2 v%u<-v%u type=%d/%d",
+            vdst, vsrc, typel, typeh);
+    } else {
+        setRegisterType(registerLine, vdst, typel);
+        setRegisterType(registerLine, vsrc, kRegTypeUnknown);
+        setRegisterType(registerLine, vsrc+1, kRegTypeUnknown);
+        /* target monitor stack bits will be cleared */
+    }
+}
+
+/*
+ * Verify types for a simple two-register instruction (e.g. "neg-int").
+ * "dstType" is stored into vA, and "srcType" is verified against vB.
+ */
+static void checkUnop(RegisterLine* registerLine, DecodedInstruction* pDecInsn,
+    RegType dstType, RegType srcType, VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType, pFailure);
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * We're performing an operation like "and-int/2addr" that can be
+ * performed on booleans as well as integers.  We get no indication of
+ * boolean-ness, but we can infer it from the types of the arguments.
+ *
+ * Assumes we've already validated reg1/reg2.
+ *
+ * TODO: consider generalizing this.  The key principle is that the
+ * result of a bitwise operation can only be as wide as the widest of
+ * the operands.  You can safely AND/OR/XOR two chars together and know
+ * you still have a char, so it's reasonable for the compiler or "dx"
+ * to skip the int-to-char instruction.  (We need to do this for boolean
+ * because there is no int-to-boolean operation.)
+ *
+ * Returns true if both args are Boolean, Zero, or One.
+ */
+static bool upcastBooleanOp(RegisterLine* registerLine, u4 reg1, u4 reg2)
+{
+    RegType type1, type2;
+
+    type1 = getRegisterType(registerLine, reg1);
+    type2 = getRegisterType(registerLine, reg2);
+
+    if ((type1 == kRegTypeBoolean || type1 == kRegTypeZero ||
+            type1 == kRegTypeOne) &&
+        (type2 == kRegTypeBoolean || type2 == kRegTypeZero ||
+            type2 == kRegTypeOne))
+    {
+        return true;
+    }
+    return false;
+}
+
+/*
+ * Verify types for A two-register instruction with a literal constant
+ * (e.g. "add-int/lit8").  "dstType" is stored into vA, and "srcType" is
+ * verified against vB.
+ *
+ * If "checkBooleanOp" is set, we use the constant value in vC.
+ */
+static void checkLitop(RegisterLine* registerLine, DecodedInstruction* pDecInsn,
+    RegType dstType, RegType srcType, bool checkBooleanOp,
+    VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType, pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        /* check vB with the call, then check the constant manually */
+        if (upcastBooleanOp(registerLine, pDecInsn->vB, pDecInsn->vB)
+            && (pDecInsn->vC == 0 || pDecInsn->vC == 1))
+        {
+            dstType = kRegTypeBoolean;
+        }
+    }
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * Verify types for a simple three-register instruction (e.g. "add-int").
+ * "dstType" is stored into vA, and "srcType1"/"srcType2" are verified
+ * against vB/vC.
+ */
+static void checkBinop(RegisterLine* registerLine, DecodedInstruction* pDecInsn,
+    RegType dstType, RegType srcType1, RegType srcType2, bool checkBooleanOp,
+    VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType1, pFailure);
+    verifyRegisterType(registerLine, pDecInsn->vC, srcType2, pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        if (upcastBooleanOp(registerLine, pDecInsn->vB, pDecInsn->vC))
+            dstType = kRegTypeBoolean;
+    }
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * Verify types for a binary "2addr" operation.  "srcType1"/"srcType2"
+ * are verified against vA/vB, then "dstType" is stored into vA.
+ */
+static void checkBinop2addr(RegisterLine* registerLine,
+    DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1,
+    RegType srcType2, bool checkBooleanOp, VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vA, srcType1, pFailure);
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType2, pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        if (upcastBooleanOp(registerLine, pDecInsn->vA, pDecInsn->vB))
+            dstType = kRegTypeBoolean;
+    }
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * Treat right-shifting as a narrowing conversion when possible.
+ *
+ * For example, right-shifting an int 24 times results in a value that can
+ * be treated as a byte.
+ *
+ * Things get interesting when contemplating sign extension.  Right-
+ * shifting an integer by 16 yields a value that can be represented in a
+ * "short" but not a "char", but an unsigned right shift by 16 yields a
+ * value that belongs in a char rather than a short.  (Consider what would
+ * happen if the result of the shift were cast to a char or short and then
+ * cast back to an int.  If sign extension, or the lack thereof, causes
+ * a change in the 32-bit representation, then the conversion was lossy.)
+ *
+ * A signed right shift by 17 on an integer results in a short.  An unsigned
+ * right shfit by 17 on an integer results in a posshort, which can be
+ * assigned to a short or a char.
+ *
+ * An unsigned right shift on a short can actually expand the result into
+ * a 32-bit integer.  For example, 0xfffff123 >>> 8 becomes 0x00fffff1,
+ * which can't be represented in anything smaller than an int.
+ *
+ * javac does not generate code that takes advantage of this, but some
+ * of the code optimizers do.  It's generally a peephole optimization
+ * that replaces a particular sequence, e.g. (bipush 24, ishr, i2b) is
+ * replaced by (bipush 24, ishr).  Knowing that shifting a short 8 times
+ * to the right yields a byte is really more than we need to handle the
+ * code that's out there, but support is not much more complex than just
+ * handling integer.
+ *
+ * Right-shifting never yields a boolean value.
+ *
+ * Returns the new register type.
+ */
+static RegType adjustForRightShift(RegisterLine* registerLine, int reg,
+    unsigned int shiftCount, bool isUnsignedShift, VerifyError* pFailure)
+{
+    RegType srcType = getRegisterType(registerLine, reg);
+    RegType newType;
+
+    /* convert const derived types to their actual types */
+    srcType = constTypeToRegType(srcType);
+
+    /* no-op */
+    if (shiftCount == 0)
+        return srcType;
+
+    /* safe defaults */
+    if (isUnsignedShift)
+        newType = kRegTypeInteger;
+    else
+        newType = srcType;
+
+    if (shiftCount >= 32) {
+        LOG_VFY("Got unexpectedly large shift count %u", shiftCount);
+        /* fail? */
+        return newType;
+    }
+
+    switch (srcType) {
+    case kRegTypeInteger:               /* 32-bit signed value */
+        if (isUnsignedShift) {
+            if (shiftCount > 24)
+                newType = kRegTypePosByte;
+            else if (shiftCount >= 16)
+                newType = kRegTypeChar;
+        } else {
+            if (shiftCount >= 24)
+                newType = kRegTypeByte;
+            else if (shiftCount >= 16)
+                newType = kRegTypeShort;
+        }
+        break;
+    case kRegTypeShort:                 /* 16-bit signed value */
+        if (isUnsignedShift) {
+            /* default (kRegTypeInteger) is correct */
+        } else {
+            if (shiftCount >= 8)
+                newType = kRegTypeByte;
+        }
+        break;
+    case kRegTypePosShort:              /* 15-bit unsigned value */
+        if (shiftCount >= 8)
+            newType = kRegTypePosByte;
+        break;
+    case kRegTypeChar:                  /* 16-bit unsigned value */
+        if (shiftCount > 8)
+            newType = kRegTypePosByte;
+        break;
+    case kRegTypeByte:                  /* 8-bit signed value */
+        /* defaults (u=kRegTypeInteger / s=srcType) are correct */
+        break;
+    case kRegTypePosByte:               /* 7-bit unsigned value */
+        /* always use newType=srcType */
+        newType = srcType;
+        break;
+    case kRegTypeZero:                  /* 1-bit unsigned value */
+    case kRegTypeOne:
+    case kRegTypeBoolean:
+        /* unnecessary? */
+        newType = kRegTypeZero;
+        break;
+    default:
+        /* long, double, references; shouldn't be here! */
+        assert(false);
+        break;
+    }
+
+    if (newType != srcType) {
+        LOGVV("narrowing: %d(%d) --> %d to %d",
+            shiftCount, isUnsignedShift, srcType, newType);
+    } else {
+        LOGVV("not narrowed: %d(%d) --> %d",
+            shiftCount, isUnsignedShift, srcType);
+    }
+    return newType;
+}
+
+
+/*
+ * ===========================================================================
+ *      Register merge
+ * ===========================================================================
+ */
+
+/*
+ * Compute the "class depth" of a class.  This is the distance from the
+ * class to the top of the tree, chasing superclass links.  java.lang.Object
+ * has a class depth of 0.
+ */
+static int getClassDepth(ClassObject* clazz)
+{
+    int depth = 0;
+
+    while (clazz->super != NULL) {
+        clazz = clazz->super;
+        depth++;
+    }
+    return depth;
+}
+
+/*
+ * Given two classes, walk up the superclass tree to find a common
+ * ancestor.  (Called from findCommonSuperclass().)
+ *
+ * TODO: consider caching the class depth in the class object so we don't
+ * have to search for it here.
+ */
+static ClassObject* digForSuperclass(ClassObject* c1, ClassObject* c2)
+{
+    int depth1, depth2;
+
+    depth1 = getClassDepth(c1);
+    depth2 = getClassDepth(c2);
+
+    if (gDebugVerbose) {
+        LOGVV("COMMON: %s(%d) + %s(%d)",
+            c1->descriptor, depth1, c2->descriptor, depth2);
+    }
+
+    /* pull the deepest one up */
+    if (depth1 > depth2) {
+        while (depth1 > depth2) {
+            c1 = c1->super;
+            depth1--;
+        }
+    } else {
+        while (depth2 > depth1) {
+            c2 = c2->super;
+            depth2--;
+        }
+    }
+
+    /* walk up in lock-step */
+    while (c1 != c2) {
+        c1 = c1->super;
+        c2 = c2->super;
+
+        assert(c1 != NULL && c2 != NULL);
+    }
+
+    if (gDebugVerbose) {
+        LOGVV("      : --> %s", c1->descriptor);
+    }
+    return c1;
+}
+
+/*
+ * Merge two array classes.  We can't use the general "walk up to the
+ * superclass" merge because the superclass of an array is always Object.
+ * We want String[] + Integer[] = Object[].  This works for higher dimensions
+ * as well, e.g. String[][] + Integer[][] = Object[][].
+ *
+ * If Foo1 and Foo2 are subclasses of Foo, Foo1[] + Foo2[] = Foo[].
+ *
+ * If Class implements Type, Class[] + Type[] = Type[].
+ *
+ * If the dimensions don't match, we want to convert to an array of Object
+ * with the least dimension, e.g. String[][] + String[][][][] = Object[][].
+ *
+ * Arrays of primitive types effectively have one less dimension when
+ * merging.  int[] + float[] = Object, int[] + String[] = Object,
+ * int[][] + float[][] = Object[], int[][] + String[] = Object[].  (The
+ * only time this function doesn't return an array class is when one of
+ * the arguments is a 1-dimensional primitive array.)
+ *
+ * This gets a little awkward because we may have to ask the VM to create
+ * a new array type with the appropriate element and dimensions.  However, we
+ * shouldn't be doing this often.
+ */
+static ClassObject* findCommonArraySuperclass(ClassObject* c1, ClassObject* c2)
+{
+    ClassObject* arrayClass = NULL;
+    ClassObject* commonElem;
+    int arrayDim1, arrayDim2;
+    int i, numDims;
+    bool hasPrimitive = false;
+
+    arrayDim1 = c1->arrayDim;
+    arrayDim2 = c2->arrayDim;
+    assert(c1->arrayDim > 0);
+    assert(c2->arrayDim > 0);
+
+    if (dvmIsPrimitiveClass(c1->elementClass)) {
+        arrayDim1--;
+        hasPrimitive = true;
+    }
+    if (dvmIsPrimitiveClass(c2->elementClass)) {
+        arrayDim2--;
+        hasPrimitive = true;
+    }
+
+    if (!hasPrimitive && arrayDim1 == arrayDim2) {
+        /*
+         * Two arrays of reference types with equal dimensions.  Try to
+         * find a good match.
+         */
+        commonElem = findCommonSuperclass(c1->elementClass, c2->elementClass);
+        numDims = arrayDim1;
+    } else {
+        /*
+         * Mismatched array depths and/or array(s) of primitives.  We want
+         * Object, or an Object array with appropriate dimensions.
+         *
+         * We initialize arrayClass to Object here, because it's possible
+         * for us to set numDims=0.
+         */
+        if (arrayDim1 < arrayDim2)
+            numDims = arrayDim1;
+        else
+            numDims = arrayDim2;
+        arrayClass = commonElem = c1->super;     // == java.lang.Object
+    }
+
+    /*
+     * Find an appropriately-dimensioned array class.  This is easiest
+     * to do iteratively, using the array class found by the current round
+     * as the element type for the next round.
+     */
+    for (i = 0; i < numDims; i++) {
+        arrayClass = dvmFindArrayClassForElement(commonElem);
+        commonElem = arrayClass;
+    }
+    assert(arrayClass != NULL);
+
+    LOGVV("ArrayMerge '%s' + '%s' --> '%s'",
+        c1->descriptor, c2->descriptor, arrayClass->descriptor);
+    return arrayClass;
+}
+
+/*
+ * Find the first common superclass of the two classes.  We're not
+ * interested in common interfaces.
+ *
+ * The easiest way to do this for concrete classes is to compute the "class
+ * depth" of each, move up toward the root of the deepest one until they're
+ * at the same depth, then walk both up to the root until they match.
+ *
+ * If both classes are arrays, we need to merge based on array depth and
+ * element type.
+ *
+ * If one class is an interface, we check to see if the other class/interface
+ * (or one of its predecessors) implements the interface.  If so, we return
+ * the interface; otherwise, we return Object.
+ *
+ * NOTE: we continue the tradition of "lazy interface handling".  To wit,
+ * suppose we have three classes:
+ *   One implements Fancy, Free
+ *   Two implements Fancy, Free
+ *   Three implements Free
+ * where Fancy and Free are unrelated interfaces.  The code requires us
+ * to merge One into Two.  Ideally we'd use a common interface, which
+ * gives us a choice between Fancy and Free, and no guidance on which to
+ * use.  If we use Free, we'll be okay when Three gets merged in, but if
+ * we choose Fancy, we're hosed.  The "ideal" solution is to create a
+ * set of common interfaces and carry that around, merging further references
+ * into it.  This is a pain.  The easy solution is to simply boil them
+ * down to Objects and let the runtime invokeinterface call fail, which
+ * is what we do.
+ */
+static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2)
+{
+    assert(!dvmIsPrimitiveClass(c1) && !dvmIsPrimitiveClass(c2));
+
+    if (c1 == c2)
+        return c1;
+
+    if (dvmIsInterfaceClass(c1) && dvmImplements(c2, c1)) {
+        if (gDebugVerbose)
+            LOGVV("COMMON/I1: %s + %s --> %s",
+                c1->descriptor, c2->descriptor, c1->descriptor);
+        return c1;
+    }
+    if (dvmIsInterfaceClass(c2) && dvmImplements(c1, c2)) {
+        if (gDebugVerbose)
+            LOGVV("COMMON/I2: %s + %s --> %s",
+                c1->descriptor, c2->descriptor, c2->descriptor);
+        return c2;
+    }
+
+    if (dvmIsArrayClass(c1) && dvmIsArrayClass(c2)) {
+        return findCommonArraySuperclass(c1, c2);
+    }
+
+    return digForSuperclass(c1, c2);
+}
+
+/*
+ * Merge two RegType values.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "type1".
+ */
+static RegType mergeTypes(RegType type1, RegType type2, bool* pChanged)
+{
+    RegType result;
+
+    /*
+     * Check for trivial case so we don't have to hit memory.
+     */
+    if (type1 == type2)
+        return type1;
+
+    /*
+     * Use the table if we can, and reject any attempts to merge something
+     * from the table with a reference type.
+     *
+     * Uninitialized references are composed of the enum ORed with an
+     * index value.  The uninitialized table entry at index zero *will*
+     * show up as a simple kRegTypeUninit value.  Since this cannot be
+     * merged with anything but itself, the rules do the right thing.
+     */
+    if (type1 < kRegTypeMAX) {
+        if (type2 < kRegTypeMAX) {
+            result = gDvmMergeTab[type1][type2];
+        } else {
+            /* simple + reference == conflict, usually */
+            if (type1 == kRegTypeZero)
+                result = type2;
+            else
+                result = kRegTypeConflict;
+        }
+    } else {
+        if (type2 < kRegTypeMAX) {
+            /* reference + simple == conflict, usually */
+            if (type2 == kRegTypeZero)
+                result = type1;
+            else
+                result = kRegTypeConflict;
+        } else {
+            /* merging two references */
+            if (regTypeIsUninitReference(type1) ||
+                regTypeIsUninitReference(type2))
+            {
+                /* can't merge uninit with anything but self */
+                result = kRegTypeConflict;
+            } else {
+                ClassObject* clazz1 = regTypeInitializedReferenceToClass(type1);
+                ClassObject* clazz2 = regTypeInitializedReferenceToClass(type2);
+                ClassObject* mergedClass;
+
+                mergedClass = findCommonSuperclass(clazz1, clazz2);
+                assert(mergedClass != NULL);
+                result = regTypeFromClass(mergedClass);
+            }
+        }
+    }
+
+    if (result != type1)
+        *pChanged = true;
+    return result;
+}
+
+/*
+ * Merge the bits that indicate which monitor entry addresses on the stack
+ * are associated with this register.
+ *
+ * The merge is a simple bitwise AND.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "ents1".
+ */
+static MonitorEntries mergeMonitorEntries(MonitorEntries ents1,
+    MonitorEntries ents2, bool* pChanged)
+{
+    MonitorEntries result = ents1 & ents2;
+    if (result != ents1)
+        *pChanged = true;
+    return result;
+}
+
+/*
+ * Control can transfer to "nextInsn".
+ *
+ * Merge the registers from "workLine" into "regTable" at "nextInsn", and
+ * set the "changed" flag on the target address if any of the registers
+ * has changed.
+ *
+ * Returns "false" if we detect mis-matched monitor stacks.
+ */
+static bool updateRegisters(const Method* meth, InsnFlags* insnFlags,
+    RegisterTable* regTable, int nextInsn, const RegisterLine* workLine)
+{
+    const size_t insnRegCountPlus = regTable->insnRegCountPlus;
+    assert(workLine != NULL);
+    const RegType* workRegs = workLine->regTypes;
+
+    if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) {
+        /*
+         * We haven't processed this instruction before, and we haven't
+         * touched the registers here, so there's nothing to "merge".  Copy
+         * the registers over and mark it as changed.  (This is the only
+         * way a register can transition out of "unknown", so this is not
+         * just an optimization.)
+         */
+        LOGVV("COPY into 0x%04x", nextInsn);
+        copyLineToTable(regTable, nextInsn, workLine);
+        dvmInsnSetChanged(insnFlags, nextInsn, true);
+#ifdef VERIFIER_STATS
+        gDvm.verifierStats.copyRegCount++;
+#endif
+    } else {
+        if (gDebugVerbose) {
+            LOGVV("MERGE into 0x%04x", nextInsn);
+            //dumpRegTypes(vdata, targetRegs, 0, "targ", NULL, 0);
+            //dumpRegTypes(vdata, workRegs, 0, "work", NULL, 0);
+        }
+        /* merge registers, set Changed only if different */
+        RegisterLine* targetLine = getRegisterLine(regTable, nextInsn);
+        RegType* targetRegs = targetLine->regTypes;
+        MonitorEntries* workMonEnts = workLine->monitorEntries;
+        MonitorEntries* targetMonEnts = targetLine->monitorEntries;
+        bool changed = false;
+        unsigned int idx;
+
+        assert(targetRegs != NULL);
+
+        if (targetMonEnts != NULL) {
+            /*
+             * Monitor stacks must be identical.
+             */
+            if (targetLine->monitorStackTop != workLine->monitorStackTop) {
+                LOG_VFY_METH(meth,
+                    "VFY: mismatched stack depth %d vs. %d at 0x%04x",
+                    targetLine->monitorStackTop, workLine->monitorStackTop,
+                    nextInsn);
+                return false;
+            }
+            if (memcmp(targetLine->monitorStack, workLine->monitorStack,
+                    targetLine->monitorStackTop * sizeof(u4)) != 0)
+            {
+                LOG_VFY_METH(meth, "VFY: mismatched monitor stacks at 0x%04x",
+                    nextInsn);
+                return false;
+            }
+        }
+
+        for (idx = 0; idx < insnRegCountPlus; idx++) {
+            targetRegs[idx] =
+                    mergeTypes(targetRegs[idx], workRegs[idx], &changed);
+
+            if (targetMonEnts != NULL) {
+                targetMonEnts[idx] = mergeMonitorEntries(targetMonEnts[idx],
+                    workMonEnts[idx], &changed);
+            }
+        }
+
+        if (gDebugVerbose) {
+            //ALOGI(" RESULT (changed=%d)", changed);
+            //dumpRegTypes(vdata, targetRegs, 0, "rslt", NULL, 0);
+        }
+#ifdef VERIFIER_STATS
+        gDvm.verifierStats.mergeRegCount++;
+        if (changed)
+            gDvm.verifierStats.mergeRegChanged++;
+#endif
+
+        if (changed)
+            dvmInsnSetChanged(insnFlags, nextInsn, true);
+    }
+
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Look up an instance field, specified by "fieldIdx", that is going to be
+ * accessed in object "objType".  This resolves the field and then verifies
+ * that the class containing the field is an instance of the reference in
+ * "objType".
+ *
+ * It is possible for "objType" to be kRegTypeZero, meaning that we might
+ * have a null reference.  This is a runtime problem, so we allow it,
+ * skipping some of the type checks.
+ *
+ * In general, "objType" must be an initialized reference.  However, we
+ * allow it to be uninitialized if this is an "<init>" method and the field
+ * is declared within the "objType" class.
+ *
+ * Returns an InstField on success, returns NULL and sets "*pFailure"
+ * on failure.
+ */
+static InstField* getInstField(const Method* meth,
+    const UninitInstanceMap* uninitMap, RegType objType, int fieldIdx,
+    VerifyError* pFailure)
+{
+    InstField* instField = NULL;
+    ClassObject* objClass;
+    bool mustBeLocal = false;
+
+    if (!regTypeIsReference(objType)) {
+        LOG_VFY("VFY: attempt to access field in non-reference type %d",
+            objType);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+    instField = dvmOptResolveInstField(meth->clazz, fieldIdx, pFailure);
+    if (instField == NULL) {
+        LOG_VFY("VFY: unable to resolve instance field %u", fieldIdx);
+        assert(!VERIFY_OK(*pFailure));
+        goto bail;
+    }
+
+    if (objType == kRegTypeZero)
+        goto bail;
+
+    /*
+     * Access to fields in uninitialized objects is allowed if this is
+     * the <init> method for the object and the field in question is
+     * declared by this class.
+     */
+    objClass = regTypeReferenceToClass(objType, uninitMap);
+    assert(objClass != NULL);
+    if (regTypeIsUninitReference(objType)) {
+        if (!isInitMethod(meth) || meth->clazz != objClass) {
+            LOG_VFY("VFY: attempt to access field via uninitialized ref");
+            *pFailure = VERIFY_ERROR_GENERIC;
+            goto bail;
+        }
+        mustBeLocal = true;
+    }
+
+    if (!dvmInstanceof(objClass, instField->clazz)) {
+        LOG_VFY("VFY: invalid field access (field %s.%s, through %s ref)",
+                instField->clazz->descriptor, instField->name,
+                objClass->descriptor);
+        *pFailure = VERIFY_ERROR_NO_FIELD;
+        goto bail;
+    }
+
+    if (mustBeLocal) {
+        /* for uninit ref, make sure it's defined by this class, not super */
+        if (instField < objClass->ifields ||
+            instField >= objClass->ifields + objClass->ifieldCount)
+        {
+            LOG_VFY("VFY: invalid constructor field access (field %s in %s)",
+                    instField->name, objClass->descriptor);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            goto bail;
+        }
+    }
+
+bail:
+    return instField;
+}
+
+/*
+ * Look up a static field.
+ *
+ * Returns a StaticField on success, returns NULL and sets "*pFailure"
+ * on failure.
+ */
+static StaticField* getStaticField(const Method* meth, int fieldIdx,
+    VerifyError* pFailure)
+{
+    StaticField* staticField;
+
+    staticField = dvmOptResolveStaticField(meth->clazz, fieldIdx, pFailure);
+    if (staticField == NULL) {
+        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+        const DexFieldId* pFieldId;
+
+        pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+
+        LOG_VFY("VFY: unable to resolve static field %u (%s) in %s", fieldIdx,
+            dexStringById(pDexFile, pFieldId->nameIdx),
+            dexStringByTypeIdx(pDexFile, pFieldId->classIdx));
+        assert(!VERIFY_OK(*pFailure));
+        goto bail;
+    }
+
+bail:
+    return staticField;
+}
+
+/*
+ * If "field" is marked "final", make sure this is the either <clinit>
+ * or <init> as appropriate.
+ *
+ * Sets "*pFailure" on failure.
+ */
+static void checkFinalFieldAccess(const Method* meth, const Field* field,
+    VerifyError* pFailure)
+{
+    if (!dvmIsFinalField(field))
+        return;
+
+    /* make sure we're in the same class */
+    if (meth->clazz != field->clazz) {
+        LOG_VFY_METH(meth, "VFY: can't modify final field %s.%s",
+            field->clazz->descriptor, field->name);
+        *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return;
+    }
+
+    /*
+     * The VM spec descriptions of putfield and putstatic say that
+     * IllegalAccessError is only thrown when the instructions appear
+     * outside the declaring class.  Our earlier attempts to restrict
+     * final field modification to constructors are, therefore, wrong.
+     */
+#if 0
+    /* make sure we're in the right kind of constructor */
+    if (dvmIsStaticField(field)) {
+        if (!isClassInitMethod(meth)) {
+            LOG_VFY_METH(meth,
+                "VFY: can't modify final static field outside <clinit>");
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    } else {
+        if (!isInitMethod(meth)) {
+            LOG_VFY_METH(meth,
+                "VFY: can't modify final field outside <init>");
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    }
+#endif
+}
+
+/*
+ * Make sure that the register type is suitable for use as an array index.
+ *
+ * Sets "*pFailure" if not.
+ */
+static void checkArrayIndexType(const Method* meth, RegType regType,
+    VerifyError* pFailure)
+{
+    if (VERIFY_OK(*pFailure)) {
+        /*
+         * The 1nr types are interchangeable at this level. However,
+         * check that a float is not used as the index.
+         */
+        checkTypeCategory(regType, kTypeCategory1nr, pFailure);
+        if (regType == kRegTypeFloat) {
+          *pFailure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(*pFailure)) {
+            LOG_VFY_METH(meth, "Invalid reg type for array index (%d)",
+                regType);
+        }
+    }
+}
+
+/*
+ * Check constraints on constructor return.  Specifically, make sure that
+ * the "this" argument got initialized.
+ *
+ * The "this" argument to <init> uses code offset kUninitThisArgAddr, which
+ * puts it at the start of the list in slot 0.  If we see a register with
+ * an uninitialized slot 0 reference, we know it somehow didn't get
+ * initialized.
+ *
+ * Returns "true" if all is well.
+ */
+static bool checkConstructorReturn(const Method* meth,
+    const RegisterLine* registerLine, const int insnRegCount)
+{
+    const RegType* insnRegs = registerLine->regTypes;
+    int i;
+
+    if (!isInitMethod(meth))
+        return true;
+
+    RegType uninitThis = regTypeFromUninitIndex(kUninitThisArgSlot);
+
+    for (i = 0; i < insnRegCount; i++) {
+        if (insnRegs[i] == uninitThis) {
+            LOG_VFY("VFY: <init> returning without calling superclass init");
+            return false;
+        }
+    }
+    return true;
+}
+
+/*
+ * Verify that the target instruction is not "move-exception".  It's important
+ * that the only way to execute a move-exception is as the first instruction
+ * of an exception handler.
+ *
+ * Returns "true" if all is well, "false" if the target instruction is
+ * move-exception.
+ */
+static bool checkMoveException(const Method* meth, int insnIdx,
+    const char* logNote)
+{
+    assert(insnIdx >= 0 && insnIdx < (int)dvmGetMethodInsnsSize(meth));
+
+    if ((meth->insns[insnIdx] & 0xff) == OP_MOVE_EXCEPTION) {
+        LOG_VFY("VFY: invalid use of move-exception");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * For the "move-exception" instruction at "insnIdx", which must be at an
+ * exception handler address, determine the first common superclass of
+ * all exceptions that can land here.  (For javac output, we're probably
+ * looking at multiple spans of bytecode covered by one "try" that lands
+ * at an exception-specific "catch", but in general the handler could be
+ * shared for multiple exceptions.)
+ *
+ * Returns NULL if no matching exception handler can be found, or if the
+ * exception is not a subclass of Throwable.
+ */
+static ClassObject* getCaughtExceptionType(const Method* meth, int insnIdx,
+    VerifyError* pFailure)
+{
+    VerifyError localFailure;
+    const DexCode* pCode;
+    DexFile* pDexFile;
+    ClassObject* commonSuper = NULL;
+    u4 handlersSize;
+    u4 offset;
+    u4 i;
+
+    pDexFile = meth->clazz->pDvmDex->pDexFile;
+    pCode = dvmGetMethodCode(meth);
+
+    if (pCode->triesSize != 0) {
+        handlersSize = dexGetHandlersSize(pCode);
+        offset = dexGetFirstHandlerOffset(pCode);
+    } else {
+        handlersSize = 0;
+        offset = 0;
+    }
+
+    for (i = 0; i < handlersSize; i++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+
+        for (;;) {
+            const DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            if (handler->address == (u4) insnIdx) {
+                ClassObject* clazz;
+
+                if (handler->typeIdx == kDexNoIndex)
+                    clazz = gDvm.exThrowable;
+                else
+                    clazz = dvmOptResolveClass(meth->clazz, handler->typeIdx,
+                                &localFailure);
+
+                if (clazz == NULL) {
+                    LOG_VFY("VFY: unable to resolve exception class %u (%s)",
+                        handler->typeIdx,
+                        dexStringByTypeIdx(pDexFile, handler->typeIdx));
+                    /* TODO: do we want to keep going?  If we don't fail
+                     * this we run the risk of having a non-Throwable
+                     * introduced at runtime.  However, that won't pass
+                     * an instanceof test, so is essentially harmless. */
+                } else {
+                    if (commonSuper == NULL)
+                        commonSuper = clazz;
+                    else
+                        commonSuper = findCommonSuperclass(clazz, commonSuper);
+                }
+            }
+        }
+
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+
+    if (commonSuper == NULL) {
+        /* no catch blocks, or no catches with classes we can find */
+        LOG_VFY_METH(meth,
+            "VFY: unable to find exception handler at addr %#x", insnIdx);
+        *pFailure = VERIFY_ERROR_GENERIC;
+    } else {
+        // TODO: verify the class is an instance of Throwable?
+    }
+
+    return commonSuper;
+}
+
+/*
+ * Helper for initRegisterTable.
+ *
+ * Returns an updated copy of "storage".
+ */
+static u1* assignLineStorage(u1* storage, RegisterLine* line,
+    bool trackMonitors, size_t regTypeSize, size_t monEntSize, size_t stackSize)
+{
+    line->regTypes = (RegType*) storage;
+    storage += regTypeSize;
+
+    if (trackMonitors) {
+        line->monitorEntries = (MonitorEntries*) storage;
+        storage += monEntSize;
+        line->monitorStack = (u4*) storage;
+        storage += stackSize;
+
+        assert(line->monitorStackTop == 0);
+    }
+
+    return storage;
+}
+
+/*
+ * Initialize the RegisterTable.
+ *
+ * Every instruction address can have a different set of information about
+ * what's in which register, but for verification purposes we only need to
+ * store it at branch target addresses (because we merge into that).
+ *
+ * By zeroing out the regType storage we are effectively initializing the
+ * register information to kRegTypeUnknown.
+ *
+ * We jump through some hoops here to minimize the total number of
+ * allocations we have to perform per method verified.
+ */
+static bool initRegisterTable(const VerifierData* vdata,
+    RegisterTable* regTable, RegisterTrackingMode trackRegsFor)
+{
+    const Method* meth = vdata->method;
+    const int insnsSize = vdata->insnsSize;
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const int kExtraLines = 2;  /* workLine, savedLine */
+    int i;
+
+    /*
+     * Every address gets a RegisterLine struct.  This is wasteful, but
+     * not so much that it's worth chasing through an extra level of
+     * indirection.
+     */
+    regTable->insnRegCountPlus = meth->registersSize + kExtraRegs;
+    regTable->registerLines =
+        (RegisterLine*) calloc(insnsSize, sizeof(RegisterLine));
+    if (regTable->registerLines == NULL)
+        return false;
+
+    assert(insnsSize > 0);
+
+    /*
+     * Count up the number of "interesting" instructions.
+     *
+     * "All" means "every address that holds the start of an instruction".
+     * "Branches" and "GcPoints" mean just those addresses.
+     *
+     * "GcPoints" fills about half the addresses, "Branches" about 15%.
+     */
+    int interestingCount = kExtraLines;
+
+    for (i = 0; i < insnsSize; i++) {
+        bool interesting;
+
+        switch (trackRegsFor) {
+        case kTrackRegsAll:
+            interesting = dvmInsnIsOpcode(insnFlags, i);
+            break;
+        case kTrackRegsGcPoints:
+            interesting = dvmInsnIsGcPoint(insnFlags, i) ||
+                          dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        case kTrackRegsBranches:
+            interesting = dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        default:
+            dvmAbort();
+            return false;
+        }
+
+        if (interesting)
+            interestingCount++;
+
+        /* count instructions, for display only */
+        //if (dvmInsnIsOpcode(insnFlags, i))
+        //    insnCount++;
+    }
+
+    /*
+     * Allocate storage for the register type arrays.
+     * TODO: set trackMonitors based on global config option
+     */
+    size_t regTypeSize = regTable->insnRegCountPlus * sizeof(RegType);
+    size_t monEntSize = regTable->insnRegCountPlus * sizeof(MonitorEntries);
+    size_t stackSize = kMaxMonitorStackDepth * sizeof(u4);
+    bool trackMonitors;
+
+    if (gDvm.monitorVerification) {
+        trackMonitors = (vdata->monitorEnterCount != 0);
+    } else {
+        trackMonitors = false;
+    }
+
+    size_t spacePerEntry = regTypeSize +
+        (trackMonitors ? monEntSize + stackSize : 0);
+    regTable->lineAlloc = calloc(interestingCount, spacePerEntry);
+    if (regTable->lineAlloc == NULL)
+        return false;
+
+#ifdef VERIFIER_STATS
+    size_t totalSpace = interestingCount * spacePerEntry +
+        insnsSize * sizeof(RegisterLine);
+    if (gDvm.verifierStats.biggestAlloc < totalSpace)
+        gDvm.verifierStats.biggestAlloc = totalSpace;
+#endif
+
+    /*
+     * Populate the sparse register line table.
+     *
+     * There is a RegisterLine associated with every address, but not
+     * every RegisterLine has non-NULL pointers to storage for its fields.
+     */
+    u1* storage = (u1*)regTable->lineAlloc;
+    for (i = 0; i < insnsSize; i++) {
+        bool interesting;
+
+        switch (trackRegsFor) {
+        case kTrackRegsAll:
+            interesting = dvmInsnIsOpcode(insnFlags, i);
+            break;
+        case kTrackRegsGcPoints:
+            interesting = dvmInsnIsGcPoint(insnFlags, i) ||
+                          dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        case kTrackRegsBranches:
+            interesting = dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        default:
+            dvmAbort();
+            return false;
+        }
+
+        if (interesting) {
+            storage = assignLineStorage(storage, &regTable->registerLines[i],
+                trackMonitors, regTypeSize, monEntSize, stackSize);
+        }
+    }
+
+    /*
+     * Grab storage for our "temporary" register lines.
+     */
+    storage = assignLineStorage(storage, &regTable->workLine,
+        trackMonitors, regTypeSize, monEntSize, stackSize);
+    storage = assignLineStorage(storage, &regTable->savedLine,
+        trackMonitors, regTypeSize, monEntSize, stackSize);
+
+    //ALOGD("Tracking registers for [%d], total %d in %d units",
+    //    trackRegsFor, interestingCount-kExtraLines, insnsSize);
+
+    assert(storage - (u1*)regTable->lineAlloc ==
+        (int) (interestingCount * spacePerEntry));
+    assert(regTable->registerLines[0].regTypes != NULL);
+    return true;
+}
+
+/*
+ * Free up any "hairy" structures associated with register lines.
+ */
+static void freeRegisterLineInnards(VerifierData* vdata)
+{
+    unsigned int idx;
+
+    if (vdata->registerLines == NULL)
+        return;
+
+    for (idx = 0; idx < vdata->insnsSize; idx++) {
+        BitVector* liveRegs = vdata->registerLines[idx].liveRegs;
+        if (liveRegs != NULL)
+            dvmFreeBitVector(liveRegs);
+    }
+}
+
+
+/*
+ * Verify that the arguments in a filled-new-array instruction are valid.
+ *
+ * "resClass" is the class refered to by pDecInsn->vB.
+ */
+static void verifyFilledNewArrayRegs(const Method* meth,
+    RegisterLine* registerLine, const DecodedInstruction* pDecInsn,
+    ClassObject* resClass, bool isRange, VerifyError* pFailure)
+{
+    u4 argCount = pDecInsn->vA;
+    RegType expectedType;
+    PrimitiveType elemType;
+    unsigned int ui;
+
+    assert(dvmIsArrayClass(resClass));
+    elemType = resClass->elementClass->primitiveType;
+    if (elemType == PRIM_NOT) {
+        expectedType = regTypeFromClass(resClass->elementClass);
+    } else {
+        expectedType = primitiveTypeToRegType(elemType);
+    }
+    //ALOGI("filled-new-array: %s -> %d", resClass->descriptor, expectedType);
+
+    /*
+     * Verify each register.  If "argCount" is bad, verifyRegisterType()
+     * will run off the end of the list and fail.  It's legal, if silly,
+     * for argCount to be zero.
+     */
+    for (ui = 0; ui < argCount; ui++) {
+        u4 getReg;
+
+        if (isRange)
+            getReg = pDecInsn->vC + ui;
+        else
+            getReg = pDecInsn->arg[ui];
+
+        verifyRegisterType(registerLine, getReg, expectedType, pFailure);
+        if (!VERIFY_OK(*pFailure)) {
+            LOG_VFY("VFY: filled-new-array arg %u(%u) not valid", ui, getReg);
+            return;
+        }
+    }
+}
+
+
+/*
+ * Replace an instruction with "throw-verification-error".  This allows us to
+ * defer error reporting until the code path is first used.
+ *
+ * This is expected to be called during "just in time" verification, not
+ * from within dexopt.  (Verification failures in dexopt will result in
+ * postponement of verification to first use of the class.)
+ *
+ * The throw-verification-error instruction requires two code units.  Some
+ * of the replaced instructions require three; the third code unit will
+ * receive a "nop".  The instruction's length will be left unchanged
+ * in "insnFlags".
+ *
+ * The VM postpones setting of debugger breakpoints in unverified classes,
+ * so there should be no clashes with the debugger.
+ *
+ * Returns "true" on success.
+ */
+static bool replaceFailingInstruction(const Method* meth, InsnFlags* insnFlags,
+    int insnIdx, VerifyError failure)
+{
+    VerifyErrorRefType refType;
+    u2* oldInsns = (u2*) meth->insns + insnIdx;
+    int width;
+
+    if (gDvm.optimizing)
+        ALOGD("Weird: RFI during dexopt?");
+
+    /*
+     * Generate the new instruction out of the old.
+     *
+     * First, make sure this is an instruction we're expecting to stomp on.
+     */
+    Opcode opcode = dexOpcodeFromCodeUnit(*oldInsns);
+    switch (opcode) {
+    case OP_CONST_CLASS:                // insn[1] == class ref, 2 bytes
+    case OP_CHECK_CAST:
+    case OP_INSTANCE_OF:
+    case OP_NEW_INSTANCE:
+    case OP_NEW_ARRAY:
+    case OP_FILLED_NEW_ARRAY:           // insn[1] == class ref, 3 bytes
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        refType = VERIFY_ERROR_REF_CLASS;
+        break;
+
+    case OP_IGET:                       // insn[1] == field ref, 2 bytes
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_WIDE:
+    case OP_IGET_OBJECT:
+    case OP_IPUT:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_OBJECT:
+    case OP_SGET:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+    case OP_SGET_WIDE:
+    case OP_SGET_OBJECT:
+    case OP_SPUT:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_OBJECT:
+        refType = VERIFY_ERROR_REF_FIELD;
+        break;
+
+    case OP_INVOKE_VIRTUAL:             // insn[1] == method ref, 3 bytes
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        refType = VERIFY_ERROR_REF_METHOD;
+        break;
+
+    default:
+        /* could handle this in a generic way, but this is probably safer */
+        LOG_VFY("GLITCH: verifier asked to replace opcode 0x%02x", opcode);
+        return false;
+    }
+
+    assert((dexGetFlagsFromOpcode(opcode) & kInstrCanThrow) != 0);
+
+    /* write a NOP over the third code unit, if necessary */
+    width = dvmInsnGetWidth(insnFlags, insnIdx);
+    switch (width) {
+    case 2:
+    case 4:
+        /* nothing to do */
+        break;
+    case 3:
+        dvmUpdateCodeUnit(meth, oldInsns+2, OP_NOP);
+        break;
+    default:
+        /* whoops */
+        ALOGE("ERROR: stomped a %d-unit instruction with a verifier error",
+            width);
+        dvmAbort();
+    }
+
+    /* encode the opcode, with the failure code in the high byte */
+    assert(width == 2 || width == 3);
+    u2 newVal = OP_THROW_VERIFICATION_ERROR |
+        (failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift));
+    dvmUpdateCodeUnit(meth, oldInsns, newVal);
+
+    return true;
+}
+
+/*
+ * Handle a monitor-enter instruction.
+ */
+void handleMonitorEnter(RegisterLine* workLine, u4 regIdx, u4 insnIdx,
+    VerifyError* pFailure)
+{
+    if (!regTypeIsReference(getRegisterType(workLine, regIdx))) {
+        LOG_VFY("VFY: monitor-enter on non-object");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    if (workLine->monitorEntries == NULL) {
+        /* should only be true if monitor verification is disabled */
+        assert(!gDvm.monitorVerification);
+        return;
+    }
+
+    if (workLine->monitorStackTop == kMaxMonitorStackDepth) {
+        LOG_VFY("VFY: monitor-enter stack overflow (%d)",
+            kMaxMonitorStackDepth);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    /*
+     * Push an entry on the stack, and set a bit in the register flags to
+     * indicate that it's associated with this register.
+     */
+    workLine->monitorEntries[regIdx] |= 1 << workLine->monitorStackTop;
+    workLine->monitorStack[workLine->monitorStackTop++] = insnIdx;
+}
+
+/*
+ * Handle a monitor-exit instruction.
+ */
+void handleMonitorExit(RegisterLine* workLine, u4 regIdx, u4 insnIdx,
+    VerifyError* pFailure)
+{
+    if (!regTypeIsReference(getRegisterType(workLine, regIdx))) {
+        LOG_VFY("VFY: monitor-exit on non-object");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    if (workLine->monitorEntries == NULL) {
+        /* should only be true if monitor verification is disabled */
+        assert(!gDvm.monitorVerification);
+        return;
+    }
+
+    if (workLine->monitorStackTop == 0) {
+        LOG_VFY("VFY: monitor-exit stack underflow");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    /*
+     * Confirm that the entry at the top of the stack is associated with
+     * the register.  Pop the top entry off.
+     */
+    workLine->monitorStackTop--;
+#ifdef BUG_3215458_FIXED
+    /*
+     * TODO: This code can safely be enabled if know we are working on
+     * a dex file of format version 036 or later. (That is, we'll need to
+     * add a check for the version number.)
+     */
+    if ((workLine->monitorEntries[regIdx] & (1 << workLine->monitorStackTop))
+            == 0)
+    {
+        LOG_VFY("VFY: monitor-exit bit %d not set: addr=0x%04x (bits[%d]=%#x)",
+            workLine->monitorStackTop, insnIdx, regIdx,
+            workLine->monitorEntries[regIdx]);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+#endif
+    workLine->monitorStack[workLine->monitorStackTop] = 0;
+
+    /*
+     * Clear the bit from the register flags.
+     */
+    workLine->monitorEntries[regIdx] &= ~(1 << workLine->monitorStackTop);
+}
+
+
+/*
+ * ===========================================================================
+ *      Entry point and driver loop
+ * ===========================================================================
+ */
+
+/*
+ * One-time preparation.
+ */
+static void verifyPrep()
+{
+#ifndef NDEBUG
+    /* only need to do this if the table was updated */
+    checkMergeTab();
+#endif
+}
+
+/*
+ * Entry point for the detailed code-flow analysis of a single method.
+ */
+bool dvmVerifyCodeFlow(VerifierData* vdata)
+{
+    bool result = false;
+    const Method* meth = vdata->method;
+    const int insnsSize = vdata->insnsSize;
+    const bool generateRegisterMap = gDvm.generateRegisterMaps;
+    RegisterTable regTable;
+
+    memset(&regTable, 0, sizeof(regTable));
+
+#ifdef VERIFIER_STATS
+    gDvm.verifierStats.methodsExamined++;
+    if (vdata->monitorEnterCount)
+        gDvm.verifierStats.monEnterMethods++;
+#endif
+
+    /* TODO: move this elsewhere -- we don't need to do this for every method */
+    verifyPrep();
+
+    if (meth->registersSize * insnsSize > 4*1024*1024) {
+        LOG_VFY_METH(meth,
+            "VFY: warning: method is huge (regs=%d insnsSize=%d)",
+            meth->registersSize, insnsSize);
+        /* might be bogus data, might be some huge generated method */
+    }
+
+    /*
+     * Create register lists, and initialize them to "Unknown".  If we're
+     * also going to create the register map, we need to retain the
+     * register lists for a larger set of addresses.
+     */
+    if (!initRegisterTable(vdata, &regTable,
+            generateRegisterMap ? kTrackRegsGcPoints : kTrackRegsBranches))
+        goto bail;
+
+    vdata->registerLines = regTable.registerLines;
+
+    /*
+     * Perform liveness analysis.
+     *
+     * We can do this before or after the main verifier pass.  The choice
+     * affects whether or not we see the effects of verifier instruction
+     * changes, i.e. substitution of throw-verification-error.
+     *
+     * In practice the ordering doesn't really matter, because T-V-E
+     * just prunes "can continue", creating regions of dead code (with
+     * corresponding register map data that will never be used).
+     */
+    if (generateRegisterMap &&
+        gDvm.registerMapMode == kRegisterMapModeLivePrecise)
+    {
+        /*
+         * Compute basic blocks and predecessor lists.
+         */
+        if (!dvmComputeVfyBasicBlocks(vdata))
+            goto bail;
+
+        /*
+         * Compute liveness.
+         */
+        if (!dvmComputeLiveness(vdata))
+            goto bail;
+    }
+
+    /*
+     * Initialize the types of the registers that correspond to the
+     * method arguments.  We can determine this from the method signature.
+     */
+    if (!setTypesFromSignature(meth, regTable.registerLines[0].regTypes,
+            vdata->uninitMap))
+        goto bail;
+
+    /*
+     * Run the verifier.
+     */
+    if (!doCodeVerification(vdata, &regTable))
+        goto bail;
+
+    /*
+     * Generate a register map.
+     */
+    if (generateRegisterMap) {
+        RegisterMap* pMap = dvmGenerateRegisterMapV(vdata);
+        if (pMap != NULL) {
+            /*
+             * Tuck it into the Method struct.  It will either get used
+             * directly or, if we're in dexopt, will be packed up and
+             * appended to the DEX file.
+             */
+            dvmSetRegisterMap((Method*)meth, pMap);
+        }
+    }
+
+    /*
+     * Success.
+     */
+    result = true;
+
+bail:
+    freeRegisterLineInnards(vdata);
+    free(regTable.registerLines);
+    free(regTable.lineAlloc);
+    return result;
+}
+
+/*
+ * Grind through the instructions.
+ *
+ * The basic strategy is as outlined in v3 4.11.1.2: set the "changed" bit
+ * on the first instruction, process it (setting additional "changed" bits),
+ * and repeat until there are no more.
+ *
+ * v3 4.11.1.1
+ * - (N/A) operand stack is always the same size
+ * - operand stack [registers] contain the correct types of values
+ * - local variables [registers] contain the correct types of values
+ * - methods are invoked with the appropriate arguments
+ * - fields are assigned using values of appropriate types
+ * - opcodes have the correct type values in operand registers
+ * - there is never an uninitialized class instance in a local variable in
+ *   code protected by an exception handler (operand stack is okay, because
+ *   the operand stack is discarded when an exception is thrown) [can't
+ *   know what's a local var w/o the debug info -- should fall out of
+ *   register typing]
+ *
+ * v3 4.11.1.2
+ * - execution cannot fall off the end of the code
+ *
+ * (We also do many of the items described in the "static checks" sections,
+ * because it's easier to do them here.)
+ *
+ * We need an array of RegType values, one per register, for every
+ * instruction.  If the method uses monitor-enter, we need extra data
+ * for every register, and a stack for every "interesting" instruction.
+ * In theory this could become quite large -- up to several megabytes for
+ * a monster function.
+ *
+ * NOTE:
+ * The spec forbids backward branches when there's an uninitialized reference
+ * in a register.  The idea is to prevent something like this:
+ *   loop:
+ *     move r1, r0
+ *     new-instance r0, MyClass
+ *     ...
+ *     if-eq rN, loop  // once
+ *   initialize r0
+ *
+ * This leaves us with two different instances, both allocated by the
+ * same instruction, but only one is initialized.  The scheme outlined in
+ * v3 4.11.1.4 wouldn't catch this, so they work around it by preventing
+ * backward branches.  We achieve identical results without restricting
+ * code reordering by specifying that you can't execute the new-instance
+ * instruction if a register contains an uninitialized instance created
+ * by that same instrutcion.
+ */
+static bool doCodeVerification(VerifierData* vdata, RegisterTable* regTable)
+{
+    const Method* meth = vdata->method;
+    InsnFlags* insnFlags = vdata->insnFlags;
+    UninitInstanceMap* uninitMap = vdata->uninitMap;
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    bool result = false;
+    bool debugVerbose = false;
+    int insnIdx, startGuess;
+
+    /*
+     * Begin by marking the first instruction as "changed".
+     */
+    dvmInsnSetChanged(insnFlags, 0, true);
+
+    if (dvmWantVerboseVerification(meth)) {
+        IF_ALOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            ALOGI("Now verifying: %s.%s %s (ins=%d regs=%d)",
+                meth->clazz->descriptor, meth->name, desc,
+                meth->insSize, meth->registersSize);
+            ALOGI(" ------ [0    4    8    12   16   20   24   28   32   36");
+            free(desc);
+        }
+        debugVerbose = true;
+        gDebugVerbose = true;
+    } else {
+        gDebugVerbose = false;
+    }
+
+    startGuess = 0;
+
+    /*
+     * Continue until no instructions are marked "changed".
+     */
+    while (true) {
+        /*
+         * Find the first marked one.  Use "startGuess" as a way to find
+         * one quickly.
+         */
+        for (insnIdx = startGuess; insnIdx < insnsSize; insnIdx++) {
+            if (dvmInsnIsChanged(insnFlags, insnIdx))
+                break;
+        }
+
+        if (insnIdx == insnsSize) {
+            if (startGuess != 0) {
+                /* try again, starting from the top */
+                startGuess = 0;
+                continue;
+            } else {
+                /* all flags are clear */
+                break;
+            }
+        }
+
+        /*
+         * We carry the working set of registers from instruction to
+         * instruction.  If this address can be the target of a branch
+         * (or throw) instruction, or if we're skipping around chasing
+         * "changed" flags, we need to load the set of registers from
+         * the table.
+         *
+         * Because we always prefer to continue on to the next instruction,
+         * we should never have a situation where we have a stray
+         * "changed" flag set on an instruction that isn't a branch target.
+         */
+        if (dvmInsnIsBranchTarget(insnFlags, insnIdx)) {
+            RegisterLine* workLine = &regTable->workLine;
+
+            copyLineFromTable(workLine, regTable, insnIdx);
+        } else {
+#ifndef NDEBUG
+            /*
+             * Sanity check: retrieve the stored register line (assuming
+             * a full table) and make sure it actually matches.
+             */
+            RegisterLine* registerLine = getRegisterLine(regTable, insnIdx);
+            if (registerLine->regTypes != NULL &&
+                compareLineToTable(regTable, insnIdx, &regTable->workLine) != 0)
+            {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                LOG_VFY("HUH? workLine diverged in %s.%s %s",
+                        meth->clazz->descriptor, meth->name, desc);
+                free(desc);
+                dumpRegTypes(vdata, registerLine, 0, "work",
+                    uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+                dumpRegTypes(vdata, registerLine, 0, "insn",
+                    uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+            }
+#endif
+        }
+        if (debugVerbose) {
+            dumpRegTypes(vdata, &regTable->workLine, insnIdx,
+                NULL, uninitMap, SHOW_REG_DETAILS);
+        }
+
+        //ALOGI("process %s.%s %s %d",
+        //    meth->clazz->descriptor, meth->name, meth->descriptor, insnIdx);
+        if (!verifyInstruction(meth, insnFlags, regTable, insnIdx,
+                uninitMap, &startGuess))
+        {
+            //ALOGD("+++ %s bailing at %d", meth->name, insnIdx);
+            goto bail;
+        }
+
+        /*
+         * Clear "changed" and mark as visited.
+         */
+        dvmInsnSetVisited(insnFlags, insnIdx, true);
+        dvmInsnSetChanged(insnFlags, insnIdx, false);
+    }
+
+    if (DEAD_CODE_SCAN && !IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+        /*
+         * Scan for dead code.  There's nothing "evil" about dead code
+         * (besides the wasted space), but it indicates a flaw somewhere
+         * down the line, possibly in the verifier.
+         *
+         * If we've substituted "always throw" instructions into the stream,
+         * we are almost certainly going to have some dead code.
+         */
+        int deadStart = -1;
+        for (insnIdx = 0; insnIdx < insnsSize;
+            insnIdx += dvmInsnGetWidth(insnFlags, insnIdx))
+        {
+            /*
+             * Switch-statement data doesn't get "visited" by scanner.  It
+             * may or may not be preceded by a padding NOP (for alignment).
+             */
+            int instr = meth->insns[insnIdx];
+            if (instr == kPackedSwitchSignature ||
+                instr == kSparseSwitchSignature ||
+                instr == kArrayDataSignature ||
+                (instr == OP_NOP && (insnIdx + 1 < insnsSize) &&
+                 (meth->insns[insnIdx+1] == kPackedSwitchSignature ||
+                  meth->insns[insnIdx+1] == kSparseSwitchSignature ||
+                  meth->insns[insnIdx+1] == kArrayDataSignature)))
+            {
+                dvmInsnSetVisited(insnFlags, insnIdx, true);
+            }
+
+            if (!dvmInsnIsVisited(insnFlags, insnIdx)) {
+                if (deadStart < 0)
+                    deadStart = insnIdx;
+            } else if (deadStart >= 0) {
+                IF_ALOGD() {
+                    char* desc =
+                        dexProtoCopyMethodDescriptor(&meth->prototype);
+                    ALOGD("VFY: dead code 0x%04x-%04x in %s.%s %s",
+                        deadStart, insnIdx-1,
+                        meth->clazz->descriptor, meth->name, desc);
+                    free(desc);
+                }
+
+                deadStart = -1;
+            }
+        }
+        if (deadStart >= 0) {
+            IF_ALOGD() {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                ALOGD("VFY: dead code 0x%04x-%04x in %s.%s %s",
+                    deadStart, insnIdx-1,
+                    meth->clazz->descriptor, meth->name, desc);
+                free(desc);
+            }
+        }
+    }
+
+    result = true;
+
+bail:
+    return result;
+}
+
+
+/*
+ * Perform verification for a single instruction.
+ *
+ * This requires fully decoding the instruction to determine the effect
+ * it has on registers.
+ *
+ * Finds zero or more following instructions and sets the "changed" flag
+ * if execution at that point needs to be (re-)evaluated.  Register changes
+ * are merged into "regTypes" at the target addresses.  Does not set or
+ * clear any other flags in "insnFlags".
+ *
+ * This may alter meth->insns if we need to replace an instruction with
+ * throw-verification-error.
+ */
+static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,
+    RegisterTable* regTable, int insnIdx, UninitInstanceMap* uninitMap,
+    int* pStartGuess)
+{
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns + insnIdx;
+    bool result = false;
+
+#ifdef VERIFIER_STATS
+    if (dvmInsnIsVisited(insnFlags, insnIdx)) {
+        gDvm.verifierStats.instrsReexamined++;
+    } else {
+        gDvm.verifierStats.instrsExamined++;
+    }
+#endif
+
+    /*
+     * Once we finish decoding the instruction, we need to figure out where
+     * we can go from here.  There are three possible ways to transfer
+     * control to another statement:
+     *
+     * (1) Continue to the next instruction.  Applies to all but
+     *     unconditional branches, method returns, and exception throws.
+     * (2) Branch to one or more possible locations.  Applies to branches
+     *     and switch statements.
+     * (3) Exception handlers.  Applies to any instruction that can
+     *     throw an exception that is handled by an encompassing "try"
+     *     block.
+     *
+     * We can also return, in which case there is no successor instruction
+     * from this point.
+     *
+     * The behavior can be determined from the OpcodeFlags.
+     */
+
+    RegisterLine* workLine = &regTable->workLine;
+    const DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+    ClassObject* resClass;
+    s4 branchTarget = 0;
+    const int insnRegCount = meth->registersSize;
+    RegType tmpType;
+    DecodedInstruction decInsn;
+    bool justSetResult = false;
+    VerifyError failure = VERIFY_ERROR_NONE;
+
+#ifndef NDEBUG
+    memset(&decInsn, 0x81, sizeof(decInsn));
+#endif
+    dexDecodeInstruction(insns, &decInsn);
+
+    int nextFlags = dexGetFlagsFromOpcode(decInsn.opcode);
+
+    /*
+     * Make a copy of the previous register state.  If the instruction
+     * can throw an exception, we will copy/merge this into the "catch"
+     * address rather than workLine, because we don't want the result
+     * from the "successful" code path (e.g. a check-cast that "improves"
+     * a type) to be visible to the exception handler.
+     */
+    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+    {
+        copyRegisterLine(&regTable->savedLine, workLine,
+            regTable->insnRegCountPlus);
+    } else {
+#ifndef NDEBUG
+        memset(regTable->savedLine.regTypes, 0xdd,
+            regTable->insnRegCountPlus * sizeof(RegType));
+#endif
+    }
+
+    switch (decInsn.opcode) {
+    case OP_NOP:
+        /*
+         * A "pure" NOP has no effect on anything.  Data tables start with
+         * a signature that looks like a NOP; if we see one of these in
+         * the course of executing code then we have a problem.
+         */
+        if (decInsn.vA != 0) {
+            LOG_VFY("VFY: encountered data table in instruction stream");
+            failure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+
+    case OP_MOVE:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_16:
+        copyRegister1(workLine, decInsn.vA, decInsn.vB, kTypeCategory1nr,
+            &failure);
+        break;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        copyRegister2(workLine, decInsn.vA, decInsn.vB, &failure);
+        break;
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_OBJECT_16:
+        copyRegister1(workLine, decInsn.vA, decInsn.vB, kTypeCategoryRef,
+            &failure);
+        break;
+
+    /*
+     * The move-result instructions copy data out of a "pseudo-register"
+     * with the results from the last method invocation.  In practice we
+     * might want to hold the result in an actual CPU register, so the
+     * Dalvik spec requires that these only appear immediately after an
+     * invoke or filled-new-array.
+     *
+     * These calls invalidate the "result" register.  (This is now
+     * redundant with the reset done below, but it can make the debug info
+     * easier to read in some cases.)
+     */
+    case OP_MOVE_RESULT:
+        copyResultRegister1(workLine, insnRegCount, decInsn.vA,
+            kTypeCategory1nr, &failure);
+        break;
+    case OP_MOVE_RESULT_WIDE:
+        copyResultRegister2(workLine, insnRegCount, decInsn.vA, &failure);
+        break;
+    case OP_MOVE_RESULT_OBJECT:
+        copyResultRegister1(workLine, insnRegCount, decInsn.vA,
+            kTypeCategoryRef, &failure);
+        break;
+
+    case OP_MOVE_EXCEPTION:
+        /*
+         * This statement can only appear as the first instruction in an
+         * exception handler (though not all exception handlers need to
+         * have one of these).  We verify that as part of extracting the
+         * exception type from the catch block list.
+         *
+         * "resClass" will hold the closest common superclass of all
+         * exceptions that can be handled here.
+         */
+        resClass = getCaughtExceptionType(meth, insnIdx, &failure);
+        if (resClass == NULL) {
+            assert(!VERIFY_OK(failure));
+        } else {
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(resClass));
+        }
+        break;
+
+    case OP_RETURN_VOID:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else if (getMethodReturnType(meth) != kRegTypeUnknown) {
+            LOG_VFY("VFY: return-void not expected");
+            failure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+    case OP_RETURN:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            /* check the method signature */
+            RegType returnType = getMethodReturnType(meth);
+            checkTypeCategory(returnType, kTypeCategory1nr, &failure);
+            if (!VERIFY_OK(failure))
+                LOG_VFY("VFY: return-1nr not expected");
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields. Also, it may use integer values for
+             * boolean, byte, short, and character return types.
+             */
+            RegType srcType = getRegisterType(workLine, decInsn.vA);
+            if ((returnType == kRegTypeBoolean && srcType == kRegTypeByte) ||
+                ((returnType == kRegTypeBoolean || returnType == kRegTypeByte ||
+                  returnType == kRegTypeShort || returnType == kRegTypeChar) &&
+                 srcType == kRegTypeInteger))
+                returnType = srcType;
+
+            /* check the register contents */
+            verifyRegisterType(workLine, decInsn.vA, returnType, &failure);
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: return-1nr on invalid register v%d",
+                    decInsn.vA);
+            }
+        }
+        break;
+    case OP_RETURN_WIDE:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            RegType returnType;
+
+            /* check the method signature */
+            returnType = getMethodReturnType(meth);
+            checkTypeCategory(returnType, kTypeCategory2, &failure);
+            if (!VERIFY_OK(failure))
+                LOG_VFY("VFY: return-wide not expected");
+
+            /* check the register contents */
+            verifyRegisterType(workLine, decInsn.vA, returnType, &failure);
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: return-wide on invalid register pair v%d",
+                    decInsn.vA);
+            }
+        }
+        break;
+    case OP_RETURN_OBJECT:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            RegType returnType = getMethodReturnType(meth);
+            checkTypeCategory(returnType, kTypeCategoryRef, &failure);
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: return-object not expected");
+                break;
+            }
+
+            /* returnType is the *expected* return type, not register value */
+            assert(returnType != kRegTypeZero);
+            assert(!regTypeIsUninitReference(returnType));
+
+            /*
+             * Verify that the reference in vAA is an instance of the type
+             * in "returnType".  The Zero type is allowed here.  If the
+             * method is declared to return an interface, then any
+             * initialized reference is acceptable.
+             *
+             * Note getClassFromRegister fails if the register holds an
+             * uninitialized reference, so we do not allow them to be
+             * returned.
+             */
+            ClassObject* declClass;
+
+            declClass = regTypeInitializedReferenceToClass(returnType);
+            resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                if (!dvmIsInterfaceClass(declClass) &&
+                    !dvmInstanceof(resClass, declClass))
+                {
+                    LOG_VFY("VFY: returning %s (cl=%p), declared %s (cl=%p)",
+                            resClass->descriptor, resClass->classLoader,
+                            declClass->descriptor, declClass->classLoader);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_CONST_4:
+    case OP_CONST_16:
+    case OP_CONST:
+        /* could be boolean, int, float, or a null reference */
+        setRegisterType(workLine, decInsn.vA,
+            determineCat1Const((s4)decInsn.vB));
+        break;
+    case OP_CONST_HIGH16:
+        /* could be boolean, int, float, or a null reference */
+        setRegisterType(workLine, decInsn.vA,
+            determineCat1Const((s4) decInsn.vB << 16));
+        break;
+    case OP_CONST_WIDE_16:
+    case OP_CONST_WIDE_32:
+    case OP_CONST_WIDE:
+    case OP_CONST_WIDE_HIGH16:
+        /* could be long or double; resolved upon use */
+        setRegisterType(workLine, decInsn.vA, kRegTypeConstLo);
+        break;
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+        assert(gDvm.classJavaLangString != NULL);
+        setRegisterType(workLine, decInsn.vA,
+            regTypeFromClass(gDvm.classJavaLangString));
+        break;
+    case OP_CONST_CLASS:
+        assert(gDvm.classJavaLangClass != NULL);
+        /* make sure we can resolve the class; access check is important */
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve const-class %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            setRegisterType(workLine, decInsn.vA,
+                regTypeFromClass(gDvm.classJavaLangClass));
+        }
+        break;
+
+    case OP_MONITOR_ENTER:
+        handleMonitorEnter(workLine, decInsn.vA, insnIdx, &failure);
+        break;
+    case OP_MONITOR_EXIT:
+        /*
+         * monitor-exit instructions are odd.  They can throw exceptions,
+         * but when they do they act as if they succeeded and the PC is
+         * pointing to the following instruction.  (This behavior goes back
+         * to the need to handle asynchronous exceptions, a now-deprecated
+         * feature that Dalvik doesn't support.)
+         *
+         * In practice we don't need to worry about this.  The only
+         * exceptions that can be thrown from monitor-exit are for a
+         * null reference and -exit without a matching -enter.  If the
+         * structured locking checks are working, the former would have
+         * failed on the -enter instruction, and the latter is impossible.
+         *
+         * This is fortunate, because issue 3221411 prevents us from
+         * chasing the "can throw" path when monitor verification is
+         * enabled.  If we can fully verify the locking we can ignore
+         * some catch blocks (which will show up as "dead" code when
+         * we skip them here); if we can't, then the code path could be
+         * "live" so we still need to check it.
+         */
+        if (workLine->monitorEntries != NULL)
+            nextFlags &= ~kInstrCanThrow;
+        handleMonitorExit(workLine, decInsn.vA, insnIdx, &failure);
+        break;
+
+    case OP_CHECK_CAST:
+        /*
+         * If this instruction succeeds, we will promote register vA to
+         * the type in vB.  (This could be a demotion -- not expected, so
+         * we don't try to address it.)
+         *
+         * If it fails, an exception is thrown, which we deal with later
+         * by ignoring the update to decInsn.vA when branching to a handler.
+         */
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve check-cast %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            RegType origType;
+
+            origType = getRegisterType(workLine, decInsn.vA);
+            if (!regTypeIsReference(origType)) {
+                LOG_VFY("VFY: check-cast on non-reference in v%u",decInsn.vA);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(resClass));
+        }
+        break;
+    case OP_INSTANCE_OF:
+        /* make sure we're checking a reference type */
+        tmpType = getRegisterType(workLine, decInsn.vB);
+        if (!regTypeIsReference(tmpType)) {
+            LOG_VFY("VFY: vB not a reference (%d)", tmpType);
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+
+        /* make sure we can resolve the class; access check is important */
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve instanceof %d (%s) in %s",
+                decInsn.vC, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            /* result is boolean */
+            setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        }
+        break;
+
+    case OP_ARRAY_LENGTH:
+        resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (resClass != NULL && !dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: array-length on non-array");
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        setRegisterType(workLine, decInsn.vA, kRegTypeInteger);
+        break;
+
+    case OP_NEW_INSTANCE:
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve new-instance %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            RegType uninitType;
+
+            /* can't create an instance of an interface or abstract class */
+            if (dvmIsAbstractClass(resClass) || dvmIsInterfaceClass(resClass)) {
+                LOG_VFY("VFY: new-instance on interface or abstract class %s",
+                    resClass->descriptor);
+                failure = VERIFY_ERROR_INSTANTIATION;
+                break;
+            }
+
+            /* add resolved class to uninit map if not already there */
+            int uidx = setUninitInstance(uninitMap, insnIdx, resClass);
+            assert(uidx >= 0);
+            uninitType = regTypeFromUninitIndex(uidx);
+
+            /*
+             * Any registers holding previous allocations from this address
+             * that have not yet been initialized must be marked invalid.
+             */
+            markUninitRefsAsInvalid(workLine, insnRegCount, uninitMap,
+                uninitType);
+
+            /* add the new uninitialized reference to the register ste */
+            setRegisterType(workLine, decInsn.vA, uninitType);
+        }
+        break;
+    case OP_NEW_ARRAY:
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve new-array %d (%s) in %s",
+                decInsn.vC, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else if (!dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: new-array on non-array class");
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            /* make sure "size" register is valid type */
+            verifyRegisterType(workLine, decInsn.vB, kRegTypeInteger, &failure);
+            /* set register type to array class */
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(resClass));
+        }
+        break;
+    case OP_FILLED_NEW_ARRAY:
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve filled-array %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else if (!dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: filled-new-array on non-array class");
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            bool isRange = (decInsn.opcode == OP_FILLED_NEW_ARRAY_RANGE);
+
+            /* check the arguments to the instruction */
+            verifyFilledNewArrayRegs(meth, workLine, &decInsn,
+                resClass, isRange, &failure);
+            /* filled-array result goes into "result" register */
+            setResultRegisterType(workLine, insnRegCount,
+                regTypeFromClass(resClass));
+            justSetResult = true;
+        }
+        break;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+        verifyRegisterType(workLine, decInsn.vB, kRegTypeFloat, &failure);
+        verifyRegisterType(workLine, decInsn.vC, kRegTypeFloat, &failure);
+        setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        break;
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+        verifyRegisterType(workLine, decInsn.vB, kRegTypeDoubleLo, &failure);
+        verifyRegisterType(workLine, decInsn.vC, kRegTypeDoubleLo, &failure);
+        setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        break;
+    case OP_CMP_LONG:
+        verifyRegisterType(workLine, decInsn.vB, kRegTypeLongLo, &failure);
+        verifyRegisterType(workLine, decInsn.vC, kRegTypeLongLo, &failure);
+        setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        break;
+
+    case OP_THROW:
+        resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+        if (VERIFY_OK(failure) && resClass != NULL) {
+            if (!dvmInstanceof(resClass, gDvm.exThrowable)) {
+                LOG_VFY("VFY: thrown class %s not instanceof Throwable",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+            }
+        }
+        break;
+
+    case OP_GOTO:
+    case OP_GOTO_16:
+    case OP_GOTO_32:
+        /* no effect on or use of registers */
+        break;
+
+    case OP_PACKED_SWITCH:
+    case OP_SPARSE_SWITCH:
+        /* verify that vAA is an integer, or can be converted to one */
+        verifyRegisterType(workLine, decInsn.vA, kRegTypeInteger, &failure);
+        break;
+
+    case OP_FILL_ARRAY_DATA:
+        {
+            RegType valueType;
+            const u2 *arrayData;
+            u2 elemWidth;
+
+            /* Similar to the verification done for APUT */
+            resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* resClass can be null if the reg type is Zero */
+            if (resClass == NULL)
+                break;
+
+            if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                resClass->elementClass->primitiveType == PRIM_NOT ||
+                resClass->elementClass->primitiveType == PRIM_VOID)
+            {
+                LOG_VFY("VFY: invalid fill-array-data on %s",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = primitiveTypeToRegType(
+                                    resClass->elementClass->primitiveType);
+            assert(valueType != kRegTypeUnknown);
+
+            /*
+             * Now verify if the element width in the table matches the element
+             * width declared in the array
+             */
+            arrayData = insns + (insns[1] | (((s4)insns[2]) << 16));
+            if (arrayData[0] != kArrayDataSignature) {
+                LOG_VFY("VFY: invalid magic for array-data");
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            switch (resClass->elementClass->primitiveType) {
+                case PRIM_BOOLEAN:
+                case PRIM_BYTE:
+                     elemWidth = 1;
+                     break;
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                     elemWidth = 2;
+                     break;
+                case PRIM_FLOAT:
+                case PRIM_INT:
+                     elemWidth = 4;
+                     break;
+                case PRIM_DOUBLE:
+                case PRIM_LONG:
+                     elemWidth = 8;
+                     break;
+                default:
+                     elemWidth = 0;
+                     break;
+            }
+
+            /*
+             * Since we don't compress the data in Dex, expect to see equal
+             * width of data stored in the table and expected from the array
+             * class.
+             */
+            if (arrayData[1] != elemWidth) {
+                LOG_VFY("VFY: array-data size mismatch (%d vs %d)",
+                        arrayData[1], elemWidth);
+                failure = VERIFY_ERROR_GENERIC;
+            }
+        }
+        break;
+
+    case OP_IF_EQ:
+    case OP_IF_NE:
+        {
+            RegType type1, type2;
+
+            type1 = getRegisterType(workLine, decInsn.vA);
+            type2 = getRegisterType(workLine, decInsn.vB);
+
+            /* both references? */
+            if (regTypeIsReference(type1) && regTypeIsReference(type2))
+                break;
+
+            /* both category-1nr? */
+            checkTypeCategory(type1, kTypeCategory1nr, &failure);
+            checkTypeCategory(type2, kTypeCategory1nr, &failure);
+            if (type1 == kRegTypeFloat || type2 == kRegTypeFloat) {
+              failure = VERIFY_ERROR_GENERIC;
+            }
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: args to if-eq/if-ne must both be refs or cat1");
+                break;
+            }
+        }
+        break;
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure)) {
+            LOG_VFY("VFY: args to 'if' must be cat-1nr and not float");
+            break;
+        }
+        tmpType = getRegisterType(workLine, decInsn.vB);
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure)) {
+            LOG_VFY("VFY: args to 'if' must be cat-1nr and not float");
+            break;
+        }
+        break;
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        if (regTypeIsReference(tmpType))
+            break;
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure))
+            LOG_VFY("VFY: expected non-float cat-1 arg to if");
+        break;
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure))
+            LOG_VFY("VFY: expected non-float cat-1 arg to if");
+        break;
+
+    case OP_AGET:
+        tmpType = kRegTypeInteger;
+        goto aget_1nr_common;
+    case OP_AGET_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto aget_1nr_common;
+    case OP_AGET_BYTE:
+        tmpType = kRegTypeByte;
+        goto aget_1nr_common;
+    case OP_AGET_CHAR:
+        tmpType = kRegTypeChar;
+        goto aget_1nr_common;
+    case OP_AGET_SHORT:
+        tmpType = kRegTypeShort;
+        goto aget_1nr_common;
+aget_1nr_common:
+        {
+            RegType srcType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                /* verify the class */
+                if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                    resClass->elementClass->primitiveType == PRIM_NOT)
+                {
+                    LOG_VFY("VFY: invalid aget-1nr target %s",
+                        resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* make sure array type matches instruction */
+                srcType = primitiveTypeToRegType(
+                                        resClass->elementClass->primitiveType);
+
+                /* correct if float */
+                if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                    tmpType = kRegTypeFloat;
+
+                if (!checkFieldArrayStore1nr(tmpType, srcType)) {
+                    LOG_VFY("VFY: invalid aget-1nr, array type=%d with"
+                            " inst type=%d (on %s)",
+                        srcType, tmpType, resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            } else {
+                /*
+                 * Null array ref; this code path will fail at runtime. Label
+                 * result as zero to allow it to remain mergeable.
+                 */
+                tmpType = kRegTypeZero;
+            }
+            setRegisterType(workLine, decInsn.vA, tmpType);
+        }
+        break;
+
+    case OP_AGET_WIDE:
+        {
+            RegType dstType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                /* verify the class */
+                if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                    resClass->elementClass->primitiveType == PRIM_NOT)
+                {
+                    LOG_VFY("VFY: invalid aget-wide target %s",
+                        resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* try to refine "dstType" */
+                switch (resClass->elementClass->primitiveType) {
+                case PRIM_LONG:
+                    dstType = kRegTypeLongLo;
+                    break;
+                case PRIM_DOUBLE:
+                    dstType = kRegTypeDoubleLo;
+                    break;
+                default:
+                    LOG_VFY("VFY: invalid aget-wide on %s",
+                        resClass->descriptor);
+                    dstType = kRegTypeUnknown;
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            } else {
+                /*
+                 * Null array ref; this code path will fail at runtime.  We
+                 * know this is either long or double, so label it const.
+                 */
+                dstType = kRegTypeConstLo;
+            }
+            setRegisterType(workLine, decInsn.vA, dstType);
+        }
+        break;
+
+    case OP_AGET_OBJECT:
+        {
+            RegType dstType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* get the class of the array we're pulling an object from */
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                ClassObject* elementClass;
+
+                assert(resClass != NULL);
+                if (!dvmIsArrayClass(resClass)) {
+                    LOG_VFY("VFY: aget-object on non-array class");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+                assert(resClass->elementClass != NULL);
+
+                /*
+                 * Find the element class.  resClass->elementClass indicates
+                 * the basic type, which won't be what we want for a
+                 * multi-dimensional array.
+                 */
+                if (resClass->descriptor[1] == '[') {
+                    assert(resClass->arrayDim > 1);
+                    elementClass = dvmFindArrayClass(&resClass->descriptor[1],
+                                        resClass->classLoader);
+                } else if (resClass->descriptor[1] == 'L') {
+                    assert(resClass->arrayDim == 1);
+                    elementClass = resClass->elementClass;
+                } else {
+                    LOG_VFY("VFY: aget-object on non-ref array class (%s)",
+                        resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                dstType = regTypeFromClass(elementClass);
+            } else {
+                /*
+                 * The array reference is NULL, so the current code path will
+                 * throw an exception.  For proper merging with later code
+                 * paths, and correct handling of "if-eqz" tests on the
+                 * result of the array get, we want to treat this as a null
+                 * reference.
+                 */
+                dstType = kRegTypeZero;
+            }
+            setRegisterType(workLine, decInsn.vA, dstType);
+        }
+        break;
+    case OP_APUT:
+        tmpType = kRegTypeInteger;
+        goto aput_1nr_common;
+    case OP_APUT_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto aput_1nr_common;
+    case OP_APUT_BYTE:
+        tmpType = kRegTypeByte;
+        goto aput_1nr_common;
+    case OP_APUT_CHAR:
+        tmpType = kRegTypeChar;
+        goto aput_1nr_common;
+    case OP_APUT_SHORT:
+        tmpType = kRegTypeShort;
+        goto aput_1nr_common;
+aput_1nr_common:
+        {
+            RegType srcType, dstType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            srcType = getRegisterType(workLine, decInsn.vA);
+
+            /* correct if float */
+            if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            /* make sure the source register has the correct type */
+            if (!canConvertTo1nr(srcType, tmpType)) {
+                LOG_VFY("VFY: invalid reg type %d on aput instr (need %d)",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* resClass can be null if the reg type is Zero */
+            if (resClass == NULL)
+                break;
+
+            if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                resClass->elementClass->primitiveType == PRIM_NOT)
+            {
+                LOG_VFY("VFY: invalid aput-1nr on %s", resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            /* verify that instruction matches array */
+            dstType = primitiveTypeToRegType(
+                                    resClass->elementClass->primitiveType);
+
+            /* correct if float */
+            if (dstType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            verifyRegisterType(workLine, decInsn.vA, dstType, &failure);
+
+            if (dstType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, dstType)) {
+                LOG_VFY("VFY: invalid aput-1nr on %s (inst=%d dst=%d)",
+                        resClass->descriptor, tmpType, dstType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_APUT_WIDE:
+        tmpType = getRegisterType(workLine, decInsn.vC);
+        checkArrayIndexType(meth, tmpType, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (resClass != NULL) {
+            /* verify the class and try to refine "dstType" */
+            if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                resClass->elementClass->primitiveType == PRIM_NOT)
+            {
+                LOG_VFY("VFY: invalid aput-wide on %s",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            switch (resClass->elementClass->primitiveType) {
+            case PRIM_LONG:
+                verifyRegisterType(workLine, decInsn.vA, kRegTypeLongLo, &failure);
+                break;
+            case PRIM_DOUBLE:
+                verifyRegisterType(workLine, decInsn.vA, kRegTypeDoubleLo, &failure);
+                break;
+            default:
+                LOG_VFY("VFY: invalid aput-wide on %s",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_APUT_OBJECT:
+        tmpType = getRegisterType(workLine, decInsn.vC);
+        checkArrayIndexType(meth, tmpType, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        /* get the ref we're storing; Zero is okay, Uninit is not */
+        resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (resClass != NULL) {
+            ClassObject* arrayClass;
+            ClassObject* elementClass;
+
+            /*
+             * Get the array class.  If the array ref is null, we won't
+             * have type information (and we'll crash at runtime with a
+             * null pointer exception).
+             */
+            arrayClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+
+            if (arrayClass != NULL) {
+                /* see if the array holds a compatible type */
+                if (!dvmIsArrayClass(arrayClass)) {
+                    LOG_VFY("VFY: invalid aput-object on %s",
+                            arrayClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /*
+                 * Find the element class.  resClass->elementClass indicates
+                 * the basic type, which won't be what we want for a
+                 * multi-dimensional array.
+                 *
+                 * All we want to check here is that the element type is a
+                 * reference class.  We *don't* check instanceof here, because
+                 * you can still put a String into a String[] after the latter
+                 * has been cast to an Object[].
+                 */
+                if (arrayClass->descriptor[1] == '[') {
+                    assert(arrayClass->arrayDim > 1);
+                    elementClass = dvmFindArrayClass(&arrayClass->descriptor[1],
+                                        arrayClass->classLoader);
+                } else {
+                    assert(arrayClass->arrayDim == 1);
+                    elementClass = arrayClass->elementClass;
+                }
+                if (elementClass->primitiveType != PRIM_NOT) {
+                    LOG_VFY("VFY: invalid aput-object of %s into %s",
+                            resClass->descriptor, arrayClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_IGET:
+        tmpType = kRegTypeInteger;
+        goto iget_1nr_common;
+    case OP_IGET_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto iget_1nr_common;
+    case OP_IGET_BYTE:
+        tmpType = kRegTypeByte;
+        goto iget_1nr_common;
+    case OP_IGET_CHAR:
+        tmpType = kRegTypeChar;
+        goto iget_1nr_common;
+    case OP_IGET_SHORT:
+        tmpType = kRegTypeShort;
+        goto iget_1nr_common;
+iget_1nr_common:
+        {
+            InstField* instField;
+            RegType objType, fieldType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* make sure the field's type is compatible with expectation */
+            fieldType = primSigCharToRegType(instField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType))
+            {
+                LOG_VFY("VFY: invalid iget-1nr of %s.%s (inst=%d field=%d)",
+                        instField->clazz->descriptor,
+                        instField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            setRegisterType(workLine, decInsn.vA, tmpType);
+        }
+        break;
+    case OP_IGET_WIDE:
+        {
+            RegType dstType;
+            InstField* instField;
+            RegType objType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            /* check the type, which should be prim */
+            switch (instField->signature[0]) {
+            case 'D':
+                dstType = kRegTypeDoubleLo;
+                break;
+            case 'J':
+                dstType = kRegTypeLongLo;
+                break;
+            default:
+                LOG_VFY("VFY: invalid iget-wide of %s.%s",
+                        instField->clazz->descriptor,
+                        instField->name);
+                dstType = kRegTypeUnknown;
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                setRegisterType(workLine, decInsn.vA, dstType);
+            }
+        }
+        break;
+    case OP_IGET_OBJECT:
+        {
+            ClassObject* fieldClass;
+            InstField* instField;
+            RegType objType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            fieldClass = getFieldClass(meth, instField);
+            if (fieldClass == NULL) {
+                /* class not found or primitive type */
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    instField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                assert(!dvmIsPrimitiveClass(fieldClass));
+                setRegisterType(workLine, decInsn.vA,
+                    regTypeFromClass(fieldClass));
+            }
+        }
+        break;
+    case OP_IPUT:
+        tmpType = kRegTypeInteger;
+        goto iput_1nr_common;
+    case OP_IPUT_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto iput_1nr_common;
+    case OP_IPUT_BYTE:
+        tmpType = kRegTypeByte;
+        goto iput_1nr_common;
+    case OP_IPUT_CHAR:
+        tmpType = kRegTypeChar;
+        goto iput_1nr_common;
+    case OP_IPUT_SHORT:
+        tmpType = kRegTypeShort;
+        goto iput_1nr_common;
+iput_1nr_common:
+        {
+            RegType srcType, fieldType, objType;
+            InstField* instField;
+
+            srcType = getRegisterType(workLine, decInsn.vA);
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields.
+             */
+            if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+                tmpType = kRegTypeByte;
+
+            /* correct if float */
+            if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+              tmpType = kRegTypeFloat;
+
+            /* make sure the source register has the correct type */
+            if (!canConvertTo1nr(srcType, tmpType)) {
+                LOG_VFY("VFY: invalid reg type %d on iput instr (need %d)",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, instField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* get type of field we're storing into */
+            fieldType = primSigCharToRegType(instField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (fieldType == kRegTypeBoolean && srcType == kRegTypeByte)
+                fieldType = kRegTypeByte;
+
+            verifyRegisterType(workLine, decInsn.vA, fieldType, &failure);
+
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType))
+            {
+                LOG_VFY("VFY: invalid iput-1nr of %s.%s (inst=%d field=%d)",
+                        instField->clazz->descriptor,
+                        instField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_IPUT_WIDE:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        {
+            RegType typeHi = getRegisterType(workLine, decInsn.vA + 1);
+            checkTypeCategory(tmpType, kTypeCategory2, &failure);
+            checkWidePair(tmpType, typeHi, &failure);
+        }
+        if (!VERIFY_OK(failure))
+            break;
+
+        InstField* instField;
+        RegType objType;
+
+        objType = getRegisterType(workLine, decInsn.vB);
+        instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                        &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        checkFinalFieldAccess(meth, instField, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        /* check the type, which should be prim */
+        switch (instField->signature[0]) {
+        case 'D':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeDoubleLo, &failure);
+            break;
+        case 'J':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeLongLo, &failure);
+            break;
+        default:
+            LOG_VFY("VFY: invalid iput-wide of %s.%s",
+                    instField->clazz->descriptor,
+                    instField->name);
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+    case OP_IPUT_OBJECT:
+        {
+            ClassObject* fieldClass;
+            ClassObject* valueClass;
+            InstField* instField;
+            RegType objType, valueType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, instField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            fieldClass = getFieldClass(meth, instField);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    instField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = getRegisterType(workLine, decInsn.vA);
+            if (!regTypeIsReference(valueType)) {
+                LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)",
+                        decInsn.vA, instField->name,
+                        fieldClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (valueType != kRegTypeZero) {
+                valueClass = regTypeInitializedReferenceToClass(valueType);
+                if (valueClass == NULL) {
+                    LOG_VFY("VFY: storing uninit ref v%d into ref field",
+                        decInsn.vA);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+                /* allow if field is any interface or field is base class */
+                if (!dvmIsInterfaceClass(fieldClass) &&
+                    !dvmInstanceof(valueClass, fieldClass))
+                {
+                    LOG_VFY("VFY: storing type '%s' into field type '%s' (%s.%s)",
+                            valueClass->descriptor, fieldClass->descriptor,
+                            instField->clazz->descriptor,
+                            instField->name);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_SGET:
+        tmpType = kRegTypeInteger;
+        goto sget_1nr_common;
+    case OP_SGET_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto sget_1nr_common;
+    case OP_SGET_BYTE:
+        tmpType = kRegTypeByte;
+        goto sget_1nr_common;
+    case OP_SGET_CHAR:
+        tmpType = kRegTypeChar;
+        goto sget_1nr_common;
+    case OP_SGET_SHORT:
+        tmpType = kRegTypeShort;
+        goto sget_1nr_common;
+sget_1nr_common:
+        {
+            StaticField* staticField;
+            RegType fieldType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /*
+             * Make sure the field's type is compatible with expectation.
+             * We can get ourselves into trouble if we mix & match loads
+             * and stores with different widths, so rather than just checking
+             * "canConvertTo1nr" we require that the field types have equal
+             * widths.
+             */
+            fieldType = primSigCharToRegType(staticField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (!checkFieldArrayStore1nr(tmpType, fieldType)) {
+                LOG_VFY("VFY: invalid sget-1nr of %s.%s (inst=%d actual=%d)",
+                    staticField->clazz->descriptor,
+                    staticField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            setRegisterType(workLine, decInsn.vA, tmpType);
+        }
+        break;
+    case OP_SGET_WIDE:
+        {
+            StaticField* staticField;
+            RegType dstType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            /* check the type, which should be prim */
+            switch (staticField->signature[0]) {
+            case 'D':
+                dstType = kRegTypeDoubleLo;
+                break;
+            case 'J':
+                dstType = kRegTypeLongLo;
+                break;
+            default:
+                LOG_VFY("VFY: invalid sget-wide of %s.%s",
+                        staticField->clazz->descriptor,
+                        staticField->name);
+                dstType = kRegTypeUnknown;
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                setRegisterType(workLine, decInsn.vA, dstType);
+            }
+        }
+        break;
+    case OP_SGET_OBJECT:
+        {
+            StaticField* staticField;
+            ClassObject* fieldClass;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            fieldClass = getFieldClass(meth, staticField);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    staticField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (dvmIsPrimitiveClass(fieldClass)) {
+                LOG_VFY("VFY: attempt to get prim field with sget-object");
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(fieldClass));
+        }
+        break;
+    case OP_SPUT:
+        tmpType = kRegTypeInteger;
+        goto sput_1nr_common;
+    case OP_SPUT_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto sput_1nr_common;
+    case OP_SPUT_BYTE:
+        tmpType = kRegTypeByte;
+        goto sput_1nr_common;
+    case OP_SPUT_CHAR:
+        tmpType = kRegTypeChar;
+        goto sput_1nr_common;
+    case OP_SPUT_SHORT:
+        tmpType = kRegTypeShort;
+        goto sput_1nr_common;
+sput_1nr_common:
+        {
+            RegType srcType, fieldType;
+            StaticField* staticField;
+
+            srcType = getRegisterType(workLine, decInsn.vA);
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields.
+             */
+            if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+                tmpType = kRegTypeByte;
+
+            /* correct if float */
+            if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+              tmpType = kRegTypeFloat;
+
+            /* make sure the source register has the correct type */
+            if (!canConvertTo1nr(srcType, tmpType)) {
+                LOG_VFY("VFY: invalid reg type %d on sput instr (need %d)",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, staticField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /*
+             * Get type of field we're storing into.  We know that the
+             * contents of the register match the instruction, but we also
+             * need to ensure that the instruction matches the field type.
+             * Using e.g. sput-short to write into a 32-bit integer field
+             * can lead to trouble if we do 16-bit writes.
+             */
+            fieldType = primSigCharToRegType(staticField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (fieldType == kRegTypeBoolean && srcType == kRegTypeByte)
+                fieldType = kRegTypeByte;
+
+            verifyRegisterType(workLine, decInsn.vA, fieldType, &failure);
+
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType)) {
+                LOG_VFY("VFY: invalid sput-1nr of %s.%s (inst=%d actual=%d)",
+                    staticField->clazz->descriptor,
+                    staticField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_SPUT_WIDE:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        {
+            RegType typeHi = getRegisterType(workLine, decInsn.vA + 1);
+            checkTypeCategory(tmpType, kTypeCategory2, &failure);
+            checkWidePair(tmpType, typeHi, &failure);
+        }
+        if (!VERIFY_OK(failure))
+            break;
+
+        StaticField* staticField;
+
+        staticField = getStaticField(meth, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        checkFinalFieldAccess(meth, staticField, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        /* check the type, which should be prim */
+        switch (staticField->signature[0]) {
+        case 'D':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeDoubleLo, &failure);
+            break;
+        case 'J':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeLongLo, &failure);
+            break;
+        default:
+            LOG_VFY("VFY: invalid sput-wide of %s.%s",
+                    staticField->clazz->descriptor,
+                    staticField->name);
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+    case OP_SPUT_OBJECT:
+        {
+            ClassObject* fieldClass;
+            ClassObject* valueClass;
+            StaticField* staticField;
+            RegType valueType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, staticField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            fieldClass = getFieldClass(meth, staticField);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    staticField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = getRegisterType(workLine, decInsn.vA);
+            if (!regTypeIsReference(valueType)) {
+                LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)",
+                        decInsn.vA, staticField->name,
+                        fieldClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (valueType != kRegTypeZero) {
+                valueClass = regTypeInitializedReferenceToClass(valueType);
+                if (valueClass == NULL) {
+                    LOG_VFY("VFY: storing uninit ref v%d into ref field",
+                        decInsn.vA);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+                /* allow if field is any interface or field is base class */
+                if (!dvmIsInterfaceClass(fieldClass) &&
+                    !dvmInstanceof(valueClass, fieldClass))
+                {
+                    LOG_VFY("VFY: storing type '%s' into field type '%s' (%s.%s)",
+                            valueClass->descriptor, fieldClass->descriptor,
+                            staticField->clazz->descriptor,
+                            staticField->name);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_SUPER_RANGE:
+        {
+            Method* calledMethod;
+            RegType returnType;
+            bool isRange;
+            bool isSuper;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_VIRTUAL_RANGE ||
+                        decInsn.opcode == OP_INVOKE_SUPER_RANGE);
+            isSuper =  (decInsn.opcode == OP_INVOKE_SUPER ||
+                        decInsn.opcode == OP_INVOKE_SUPER_RANGE);
+
+            calledMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_VIRTUAL, isRange,
+                            isSuper, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_DIRECT_RANGE:
+        {
+            RegType returnType;
+            Method* calledMethod;
+            bool isRange;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_DIRECT_RANGE);
+            calledMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_DIRECT, isRange,
+                            false, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /*
+             * Some additional checks when calling <init>.  We know from
+             * the invocation arg check that the "this" argument is an
+             * instance of calledMethod->clazz.  Now we further restrict
+             * that to require that calledMethod->clazz is the same as
+             * this->clazz or this->super, allowing the latter only if
+             * the "this" argument is the same as the "this" argument to
+             * this method (which implies that we're in <init> ourselves).
+             */
+            if (isInitMethod(calledMethod)) {
+                RegType thisType;
+                thisType = getInvocationThis(workLine, &decInsn, &failure);
+                if (!VERIFY_OK(failure))
+                    break;
+
+                /* no null refs allowed (?) */
+                if (thisType == kRegTypeZero) {
+                    LOG_VFY("VFY: unable to initialize null ref");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                ClassObject* thisClass;
+
+                thisClass = regTypeReferenceToClass(thisType, uninitMap);
+                assert(thisClass != NULL);
+
+                /* must be in same class or in superclass */
+                if (calledMethod->clazz == thisClass->super) {
+                    if (thisClass != meth->clazz) {
+                        LOG_VFY("VFY: invoke-direct <init> on super only "
+                            "allowed for 'this' in <init>");
+                        failure = VERIFY_ERROR_GENERIC;
+                        break;
+                    }
+                }  else if (calledMethod->clazz != thisClass) {
+                    LOG_VFY("VFY: invoke-direct <init> must be on current "
+                            "class or super");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* arg must be an uninitialized reference */
+                if (!regTypeIsUninitReference(thisType)) {
+                    LOG_VFY("VFY: can only initialize the uninitialized");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /*
+                 * Replace the uninitialized reference with an initialized
+                 * one, and clear the entry in the uninit map.  We need to
+                 * do this for all registers that have the same object
+                 * instance in them, not just the "this" register.
+                 */
+                markRefsAsInitialized(workLine, insnRegCount, uninitMap,
+                    thisType, &failure);
+                if (!VERIFY_OK(failure))
+                    break;
+            }
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_STATIC_RANGE:
+        {
+            RegType returnType;
+            Method* calledMethod;
+            bool isRange;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_STATIC_RANGE);
+            calledMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_STATIC, isRange,
+                            false, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        {
+            RegType /*thisType,*/ returnType;
+            Method* absMethod;
+            bool isRange;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_INTERFACE_RANGE);
+            absMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_INTERFACE, isRange,
+                            false, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+#if 0       /* can't do this here, fails on dalvik test 052-verifier-fun */
+            /*
+             * Get the type of the "this" arg, which should always be an
+             * interface class.  Because we don't do a full merge on
+             * interface classes, this might have reduced to Object.
+             */
+            thisType = getInvocationThis(workLine, &decInsn, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            if (thisType == kRegTypeZero) {
+                /* null pointer always passes (and always fails at runtime) */
+            } else {
+                ClassObject* thisClass;
+
+                thisClass = regTypeInitializedReferenceToClass(thisType);
+                if (thisClass == NULL) {
+                    LOG_VFY("VFY: interface call on uninitialized");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /*
+                 * Either "thisClass" needs to be the interface class that
+                 * defined absMethod, or absMethod's class needs to be one
+                 * of the interfaces implemented by "thisClass".  (Or, if
+                 * we couldn't complete the merge, this will be Object.)
+                 */
+                if (thisClass != absMethod->clazz &&
+                    thisClass != gDvm.classJavaLangObject &&
+                    !dvmImplements(thisClass, absMethod->clazz))
+                {
+                    LOG_VFY("VFY: unable to match absMethod '%s' with %s interfaces",
+                            absMethod->name, thisClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+#endif
+
+            /*
+             * We don't have an object instance, so we can't find the
+             * concrete method.  However, all of the type information is
+             * in the abstract method, so we're good.
+             */
+            returnType = getMethodReturnType(absMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, &failure);
+        break;
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, &failure);
+        break;
+    case OP_NEG_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeFloat, &failure);
+        break;
+    case OP_NEG_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_INT_TO_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeInteger, &failure);
+        break;
+    case OP_LONG_TO_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeLongLo, &failure);
+        break;
+    case OP_LONG_TO_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeLongLo, &failure);
+        break;
+    case OP_LONG_TO_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeLongLo, &failure);
+        break;
+    case OP_FLOAT_TO_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeFloat, &failure);
+        break;
+    case OP_FLOAT_TO_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeFloat, &failure);
+        break;
+    case OP_FLOAT_TO_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeFloat, &failure);
+        break;
+    case OP_DOUBLE_TO_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_DOUBLE_TO_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_DOUBLE_TO_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_INT_TO_BYTE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeByte, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_CHAR:
+        checkUnop(workLine, &decInsn,
+            kRegTypeChar, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_SHORT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeShort, kRegTypeInteger, &failure);
+        break;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_REM_INT:
+    case OP_DIV_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+        checkBinop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+        checkBinop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_MUL_LONG:
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+        checkBinop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure);
+        break;
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+        /* shift distance is Int, making these different from other binops */
+        checkBinop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure);
+        break;
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+        checkBinop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure);
+        break;
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        checkBinop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false,
+            &failure);
+        break;
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+    case OP_DIV_INT_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_MUL_LONG_2ADDR:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure);
+        break;
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure);
+        break;
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_REM_FLOAT_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure);
+        break;
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_DOUBLE_2ADDR:
+    case OP_REM_DOUBLE_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false,
+            &failure);
+        break;
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+    case OP_ADD_INT_LIT8:
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_SHR_INT_LIT8:
+        tmpType = adjustForRightShift(workLine,
+            decInsn.vB, decInsn.vC, false, &failure);
+        checkLitop(workLine, &decInsn,
+            tmpType, kRegTypeInteger, false, &failure);
+        break;
+    case OP_USHR_INT_LIT8:
+        tmpType = adjustForRightShift(workLine,
+            decInsn.vB, decInsn.vC, true, &failure);
+        checkLitop(workLine, &decInsn,
+            tmpType, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+
+    /*
+     * This falls into the general category of "optimized" instructions,
+     * which don't generally appear during verification.  Because it's
+     * inserted in the course of verification, we can expect to see it here.
+     */
+    case OP_THROW_VERIFICATION_ERROR:
+        break;
+
+    /*
+     * Verifying "quickened" instructions is tricky, because we have
+     * discarded the original field/method information.  The byte offsets
+     * and vtable indices only have meaning in the context of an object
+     * instance.
+     *
+     * If a piece of code declares a local reference variable, assigns
+     * null to it, and then issues a virtual method call on it, we
+     * cannot evaluate the method call during verification.  This situation
+     * isn't hard to handle, since we know the call will always result in an
+     * NPE, and the arguments and return value don't matter.  Any code that
+     * depends on the result of the method call is inaccessible, so the
+     * fact that we can't fully verify anything that comes after the bad
+     * call is not a problem.
+     *
+     * We must also consider the case of multiple code paths, only some of
+     * which involve a null reference.  We can completely verify the method
+     * if we sidestep the results of executing with a null reference.
+     * For example, if on the first pass through the code we try to do a
+     * virtual method invocation through a null ref, we have to skip the
+     * method checks and have the method return a "wildcard" type (which
+     * merges with anything to become that other thing).  The move-result
+     * will tell us if it's a reference, single-word numeric, or double-word
+     * value.  We continue to perform the verification, and at the end of
+     * the function any invocations that were never fully exercised are
+     * marked as null-only.
+     *
+     * We would do something similar for the field accesses.  The field's
+     * type, once known, can be used to recover the width of short integers.
+     * If the object reference was null, the field-get returns the "wildcard"
+     * type, which is acceptable for any operation.
+     */
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        /* fall through to failure */
+
+    /*
+     * These instructions are equivalent (from the verifier's point of view)
+     * to the original form.  The change was made for correctness rather
+     * than improved performance (except for invoke-object-init, which
+     * provides both).  The substitution takes place after verification
+     * completes, though, so we don't expect to see them here.
+     */
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+    case OP_RETURN_VOID_BARRIER:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+        /* fall through to failure */
+
+    /* these should never appear during verification */
+    case OP_UNUSED_3E:
+    case OP_UNUSED_3F:
+    case OP_UNUSED_40:
+    case OP_UNUSED_41:
+    case OP_UNUSED_42:
+    case OP_UNUSED_43:
+    case OP_UNUSED_73:
+    case OP_UNUSED_79:
+    case OP_UNUSED_7A:
+    case OP_BREAKPOINT:
+    case OP_UNUSED_FF:
+        failure = VERIFY_ERROR_GENERIC;
+        break;
+
+    /*
+     * DO NOT add a "default" clause here.  Without it the compiler will
+     * complain if an instruction is missing (which is desirable).
+     */
+    }
+
+    if (!VERIFY_OK(failure)) {
+        if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing) {
+            /* immediate failure, reject class */
+            LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x",
+                decInsn.opcode, insnIdx);
+            goto bail;
+        } else {
+            /* replace opcode and continue on */
+            ALOGD("VFY: replacing opcode 0x%02x at 0x%04x",
+                decInsn.opcode, insnIdx);
+            if (!replaceFailingInstruction(meth, insnFlags, insnIdx, failure)) {
+                LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x",
+                    decInsn.opcode, insnIdx);
+                goto bail;
+            }
+            /* IMPORTANT: meth->insns may have been changed */
+            insns = meth->insns + insnIdx;
+
+            /* continue on as if we just handled a throw-verification-error */
+            failure = VERIFY_ERROR_NONE;
+            nextFlags = kInstrCanThrow;
+        }
+    }
+
+    /*
+     * If we didn't just set the result register, clear it out.  This
+     * ensures that you can only use "move-result" immediately after the
+     * result is set.  (We could check this statically, but it's not
+     * expensive and it makes our debugging output cleaner.)
+     */
+    if (!justSetResult) {
+        int reg = RESULT_REGISTER(insnRegCount);
+        setRegisterType(workLine, reg, kRegTypeUnknown);
+        setRegisterType(workLine, reg+1, kRegTypeUnknown);
+    }
+
+    /*
+     * Handle "continue".  Tag the next consecutive instruction.
+     */
+    if ((nextFlags & kInstrCanContinue) != 0) {
+        int insnWidth = dvmInsnGetWidth(insnFlags, insnIdx);
+        if (insnIdx+insnWidth >= insnsSize) {
+            LOG_VFY_METH(meth,
+                "VFY: execution can walk off end of code area (from %#x)",
+                insnIdx);
+            goto bail;
+        }
+
+        /*
+         * The only way to get to a move-exception instruction is to get
+         * thrown there.  Make sure the next instruction isn't one.
+         */
+        if (!checkMoveException(meth, insnIdx+insnWidth, "next"))
+            goto bail;
+
+        if (getRegisterLine(regTable, insnIdx+insnWidth)->regTypes != NULL) {
+            /*
+             * Merge registers into what we have for the next instruction,
+             * and set the "changed" flag if needed.
+             */
+            if (!updateRegisters(meth, insnFlags, regTable, insnIdx+insnWidth,
+                    workLine))
+                goto bail;
+        } else {
+            /*
+             * We're not recording register data for the next instruction,
+             * so we don't know what the prior state was.  We have to
+             * assume that something has changed and re-evaluate it.
+             */
+            dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
+        }
+    }
+
+    /*
+     * Handle "branch".  Tag the branch target.
+     *
+     * NOTE: instructions like OP_EQZ provide information about the state
+     * of the register when the branch is taken or not taken.  For example,
+     * somebody could get a reference field, check it for zero, and if the
+     * branch is taken immediately store that register in a boolean field
+     * since the value is known to be zero.  We do not currently account for
+     * that, and will reject the code.
+     *
+     * TODO: avoid re-fetching the branch target
+     */
+    if ((nextFlags & kInstrCanBranch) != 0) {
+        bool isConditional;
+
+        if (!dvmGetBranchOffset(meth, insnFlags, insnIdx, &branchTarget,
+                &isConditional))
+        {
+            /* should never happen after static verification */
+            LOG_VFY_METH(meth, "VFY: bad branch at %d", insnIdx);
+            goto bail;
+        }
+        assert(isConditional || (nextFlags & kInstrCanContinue) == 0);
+        assert(!isConditional || (nextFlags & kInstrCanContinue) != 0);
+
+        if (!checkMoveException(meth, insnIdx+branchTarget, "branch"))
+            goto bail;
+
+        /* update branch target, set "changed" if appropriate */
+        if (!updateRegisters(meth, insnFlags, regTable, insnIdx+branchTarget,
+                workLine))
+            goto bail;
+    }
+
+    /*
+     * Handle "switch".  Tag all possible branch targets.
+     *
+     * We've already verified that the table is structurally sound, so we
+     * just need to walk through and tag the targets.
+     */
+    if ((nextFlags & kInstrCanSwitch) != 0) {
+        int offsetToSwitch = insns[1] | (((s4)insns[2]) << 16);
+        const u2* switchInsns = insns + offsetToSwitch;
+        int switchCount = switchInsns[1];
+        int offsetToTargets, targ;
+
+        if ((*insns & 0xff) == OP_PACKED_SWITCH) {
+            /* 0=sig, 1=count, 2/3=firstKey */
+            offsetToTargets = 4;
+        } else {
+            /* 0=sig, 1=count, 2..count*2 = keys */
+            assert((*insns & 0xff) == OP_SPARSE_SWITCH);
+            offsetToTargets = 2 + 2*switchCount;
+        }
+
+        /* verify each switch target */
+        for (targ = 0; targ < switchCount; targ++) {
+            int offset, absOffset;
+
+            /* offsets are 32-bit, and only partly endian-swapped */
+            offset = switchInsns[offsetToTargets + targ*2] |
+                     (((s4) switchInsns[offsetToTargets + targ*2 +1]) << 16);
+            absOffset = insnIdx + offset;
+
+            assert(absOffset >= 0 && absOffset < insnsSize);
+
+            if (!checkMoveException(meth, absOffset, "switch"))
+                goto bail;
+
+            if (!updateRegisters(meth, insnFlags, regTable, absOffset,
+                    workLine))
+                goto bail;
+        }
+    }
+
+    /*
+     * Handle instructions that can throw and that are sitting in a
+     * "try" block.  (If they're not in a "try" block when they throw,
+     * control transfers out of the method.)
+     */
+    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+    {
+        const DexCode* pCode = dvmGetMethodCode(meth);
+        DexCatchIterator iterator;
+        bool hasCatchAll = false;
+
+        if (dexFindCatchHandler(&iterator, pCode, insnIdx)) {
+            for (;;) {
+                DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+                if (handler == NULL) {
+                    break;
+                }
+
+                if (handler->typeIdx == kDexNoIndex)
+                    hasCatchAll = true;
+
+                /*
+                 * Merge registers into the "catch" block.  We want to
+                 * use the "savedRegs" rather than "workRegs", because
+                 * at runtime the exception will be thrown before the
+                 * instruction modifies any registers.
+                 */
+                if (!updateRegisters(meth, insnFlags, regTable,
+                        handler->address, &regTable->savedLine))
+                    goto bail;
+            }
+        }
+
+        /*
+         * If the monitor stack depth is nonzero, there must be a "catch all"
+         * handler for this instruction.  This does apply to monitor-exit
+         * because of async exception handling.
+         */
+        if (workLine->monitorStackTop != 0 && !hasCatchAll) {
+            /*
+             * The state in workLine reflects the post-execution state.
+             * If the current instruction is a monitor-enter and the monitor
+             * stack was empty, we don't need a catch-all (if it throws,
+             * it will do so before grabbing the lock).
+             */
+            if (!(decInsn.opcode == OP_MONITOR_ENTER &&
+                  workLine->monitorStackTop == 1))
+            {
+                LOG_VFY_METH(meth,
+                    "VFY: no catch-all for instruction at 0x%04x", insnIdx);
+                goto bail;
+            }
+        }
+    }
+
+    /*
+     * If we're returning from the method, make sure our monitor stack
+     * is empty.
+     */
+    if ((nextFlags & kInstrCanReturn) != 0 && workLine->monitorStackTop != 0) {
+        LOG_VFY_METH(meth, "VFY: return with stack depth=%d at 0x%04x",
+            workLine->monitorStackTop, insnIdx);
+        goto bail;
+    }
+
+    /*
+     * Update startGuess.  Advance to the next instruction of that's
+     * possible, otherwise use the branch target if one was found.  If
+     * neither of those exists we're in a return or throw; leave startGuess
+     * alone and let the caller sort it out.
+     */
+    if ((nextFlags & kInstrCanContinue) != 0) {
+        *pStartGuess = insnIdx + dvmInsnGetWidth(insnFlags, insnIdx);
+    } else if ((nextFlags & kInstrCanBranch) != 0) {
+        /* we're still okay if branchTarget is zero */
+        *pStartGuess = insnIdx + branchTarget;
+    }
+
+    assert(*pStartGuess >= 0 && *pStartGuess < insnsSize &&
+        dvmInsnGetWidth(insnFlags, *pStartGuess) != 0);
+
+    result = true;
+
+bail:
+    return result;
+}
+
+
+/*
+ * callback function used in dumpRegTypes to print local vars
+ * valid at a given address.
+ */
+static void logLocalsCb(void *cnxt, u2 reg, u4 startAddress, u4 endAddress,
+        const char *name, const char *descriptor,
+        const char *signature)
+{
+    int addr = *((int *)cnxt);
+
+    if (addr >= (int) startAddress && addr < (int) endAddress)
+    {
+        ALOGI("        %2d: '%s' %s", reg, name, descriptor);
+    }
+}
+
+/*
+ * Dump the register types for the specifed address to the log file.
+ */
+static void dumpRegTypes(const VerifierData* vdata,
+    const RegisterLine* registerLine, int addr, const char* addrName,
+    const UninitInstanceMap* uninitMap, int displayFlags)
+{
+    const Method* meth = vdata->method;
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const RegType* addrRegs = registerLine->regTypes;
+    int regCount = meth->registersSize;
+    int fullRegCount = regCount + kExtraRegs;
+    bool branchTarget = dvmInsnIsBranchTarget(insnFlags, addr);
+    int i;
+
+    assert(addr >= 0 && addr < (int) dvmGetMethodInsnsSize(meth));
+
+    int regCharSize = fullRegCount + (fullRegCount-1)/4 + 2 +1;
+    char regChars[regCharSize +1];
+    memset(regChars, ' ', regCharSize);
+    regChars[0] = '[';
+    if (regCount == 0)
+        regChars[1] = ']';
+    else
+        regChars[1 + (regCount-1) + (regCount-1)/4 +1] = ']';
+    regChars[regCharSize] = '\0';
+
+    for (i = 0; i < regCount + kExtraRegs; i++) {
+        char tch;
+
+        switch (addrRegs[i]) {
+        case kRegTypeUnknown:       tch = '.';  break;
+        case kRegTypeConflict:      tch = 'X';  break;
+        case kRegTypeZero:          tch = '0';  break;
+        case kRegTypeOne:           tch = '1';  break;
+        case kRegTypeBoolean:       tch = 'Z';  break;
+        case kRegTypeConstPosByte:  tch = 'y';  break;
+        case kRegTypeConstByte:     tch = 'Y';  break;
+        case kRegTypeConstPosShort: tch = 'h';  break;
+        case kRegTypeConstShort:    tch = 'H';  break;
+        case kRegTypeConstChar:     tch = 'c';  break;
+        case kRegTypeConstInteger:  tch = 'i';  break;
+        case kRegTypePosByte:       tch = 'b';  break;
+        case kRegTypeByte:          tch = 'B';  break;
+        case kRegTypePosShort:      tch = 's';  break;
+        case kRegTypeShort:         tch = 'S';  break;
+        case kRegTypeChar:          tch = 'C';  break;
+        case kRegTypeInteger:       tch = 'I';  break;
+        case kRegTypeFloat:         tch = 'F';  break;
+        case kRegTypeConstLo:       tch = 'N';  break;
+        case kRegTypeConstHi:       tch = 'n';  break;
+        case kRegTypeLongLo:        tch = 'J';  break;
+        case kRegTypeLongHi:        tch = 'j';  break;
+        case kRegTypeDoubleLo:      tch = 'D';  break;
+        case kRegTypeDoubleHi:      tch = 'd';  break;
+        default:
+            if (regTypeIsReference(addrRegs[i])) {
+                if (regTypeIsUninitReference(addrRegs[i]))
+                    tch = 'U';
+                else
+                    tch = 'L';
+            } else {
+                tch = '*';
+                assert(false);
+            }
+            break;
+        }
+
+        if (i < regCount)
+            regChars[1 + i + (i/4)] = tch;
+        else
+            regChars[1 + i + (i/4) + 2] = tch;
+    }
+
+    if (addr == 0 && addrName != NULL) {
+        ALOGI("%c%s %s mst=%d", branchTarget ? '>' : ' ',
+            addrName, regChars, registerLine->monitorStackTop);
+    } else {
+        ALOGI("%c0x%04x %s mst=%d", branchTarget ? '>' : ' ',
+            addr, regChars, registerLine->monitorStackTop);
+    }
+    if (displayFlags & DRT_SHOW_LIVENESS) {
+        /*
+         * We can't use registerLine->liveRegs because it might be the
+         * "work line" rather than the copy from RegisterTable.
+         */
+        BitVector* liveRegs = vdata->registerLines[addr].liveRegs;
+        if (liveRegs != NULL)  {
+            char liveChars[regCharSize + 1];
+            memset(liveChars, ' ', regCharSize);
+            liveChars[regCharSize] = '\0';
+
+            for (i = 0; i < regCount; i++) {
+                bool isLive = dvmIsBitSet(liveRegs, i);
+                liveChars[i + 1 + (i / 4)] = isLive ? '+' : '-';
+            }
+            ALOGI("        %s", liveChars);
+        } else {
+            ALOGI("        %c", '#');
+        }
+    }
+
+    if (displayFlags & DRT_SHOW_REF_TYPES) {
+        for (i = 0; i < regCount + kExtraRegs; i++) {
+            if (regTypeIsReference(addrRegs[i]) && addrRegs[i] != kRegTypeZero)
+            {
+                ClassObject* clazz = regTypeReferenceToClass(addrRegs[i], uninitMap);
+                assert(dvmIsHeapAddress((Object*)clazz));
+                if (i < regCount) {
+                    ALOGI("        %2d: 0x%08x %s%s",
+                        i, addrRegs[i],
+                        regTypeIsUninitReference(addrRegs[i]) ? "[U]" : "",
+                        clazz->descriptor);
+                } else {
+                    ALOGI("        RS: 0x%08x %s%s",
+                        addrRegs[i],
+                        regTypeIsUninitReference(addrRegs[i]) ? "[U]" : "",
+                        clazz->descriptor);
+                }
+            }
+        }
+    }
+    if (displayFlags & DRT_SHOW_LOCALS) {
+        dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile,
+                dvmGetMethodCode(meth),
+                meth->clazz->descriptor,
+                meth->prototype.protoIdx,
+                meth->accessFlags,
+                NULL, logLocalsCb, &addr);
+    }
+}
diff --git a/vm/analysis/CodeVerify.h b/vm/analysis/CodeVerify.h
new file mode 100644
index 0000000..5857895
--- /dev/null
+++ b/vm/analysis/CodeVerify.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik bytecode verifier.
+ */
+#ifndef DALVIK_CODEVERIFY_H_
+#define DALVIK_CODEVERIFY_H_
+
+#include "analysis/VerifySubs.h"
+#include "analysis/VfyBasicBlock.h"
+
+/*
+ * Enumeration for register type values.  The "hi" piece of a 64-bit value
+ * MUST immediately follow the "lo" piece in the enumeration, so we can check
+ * that hi==lo+1.
+ *
+ * Assignment of constants:
+ *   [-MAXINT,-32768)   : integer
+ *   [-32768,-128)      : short
+ *   [-128,0)           : byte
+ *   0                  : zero
+ *   1                  : one
+ *   [2,128)            : posbyte
+ *   [128,32768)        : posshort
+ *   [32768,65536)      : char
+ *   [65536,MAXINT]     : integer
+ *
+ * Allowed "implicit" widening conversions:
+ *   zero -> boolean, posbyte, byte, posshort, short, char, integer, ref (null)
+ *   one -> boolean, posbyte, byte, posshort, short, char, integer
+ *   boolean -> posbyte, byte, posshort, short, char, integer
+ *   posbyte -> posshort, short, integer, char
+ *   byte -> short, integer
+ *   posshort -> integer, char
+ *   short -> integer
+ *   char -> integer
+ *
+ * In addition, all of the above can convert to "float".
+ *
+ * We're more careful with integer values than the spec requires.  The
+ * motivation is to restrict byte/char/short to the correct range of values.
+ * For example, if a method takes a byte argument, we don't want to allow
+ * the code to load the constant "1024" and pass it in.
+ */
+enum {
+    kRegTypeUnknown = 0,    /* initial state; use value=0 so calloc works */
+    kRegTypeUninit = 1,     /* MUST be odd to distinguish from pointer */
+    kRegTypeConflict,       /* merge clash makes this reg's type unknowable */
+
+    /*
+     * Category-1nr types.  The order of these is chiseled into a couple
+     * of tables, so don't add, remove, or reorder if you can avoid it.
+     */
+#define kRegType1nrSTART    kRegTypeZero
+    kRegTypeZero,           /* 32-bit 0, could be Boolean, Int, Float, or Ref */
+    kRegTypeOne,            /* 32-bit 1, could be Boolean, Int, Float */
+    kRegTypeBoolean,        /* must be 0 or 1 */
+    kRegTypeConstPosByte,   /* const derived byte, known positive */
+    kRegTypeConstByte,      /* const derived byte */
+    kRegTypeConstPosShort,  /* const derived short, known positive */
+    kRegTypeConstShort,     /* const derived short */
+    kRegTypeConstChar,      /* const derived char */
+    kRegTypeConstInteger,   /* const derived integer */
+    kRegTypePosByte,        /* byte, known positive (can become char) */
+    kRegTypeByte,
+    kRegTypePosShort,       /* short, known positive (can become char) */
+    kRegTypeShort,
+    kRegTypeChar,
+    kRegTypeInteger,
+    kRegTypeFloat,
+#define kRegType1nrEND      kRegTypeFloat
+
+    kRegTypeConstLo,        /* const derived wide, lower half */
+    kRegTypeConstHi,        /* const derived wide, upper half */
+    kRegTypeLongLo,         /* lower-numbered register; endian-independent */
+    kRegTypeLongHi,
+    kRegTypeDoubleLo,
+    kRegTypeDoubleHi,
+
+    /*
+     * Enumeration max; this is used with "full" (32-bit) RegType values.
+     *
+     * Anything larger than this is a ClassObject or uninit ref.  Mask off
+     * all but the low 8 bits; if you're left with kRegTypeUninit, pull
+     * the uninit index out of the high 24.  Because kRegTypeUninit has an
+     * odd value, there is no risk of a particular ClassObject pointer bit
+     * pattern being confused for it (assuming our class object allocator
+     * uses word alignment).
+     */
+    kRegTypeMAX
+};
+#define kRegTypeUninitMask  0xff
+#define kRegTypeUninitShift 8
+
+/*
+ * RegType holds information about the type of data held in a register.
+ * For most types it's a simple enum.  For reference types it holds a
+ * pointer to the ClassObject, and for uninitialized references it holds
+ * an index into the UninitInstanceMap.
+ */
+typedef u4 RegType;
+
+/*
+ * A bit vector indicating which entries in the monitor stack are
+ * associated with this register.  The low bit corresponds to the stack's
+ * bottom-most entry.
+ */
+typedef u4 MonitorEntries;
+#define kMaxMonitorStackDepth   (sizeof(MonitorEntries) * 8)
+
+/*
+ * During verification, we associate one of these with every "interesting"
+ * instruction.  We track the status of all registers, and (if the method
+ * has any monitor-enter instructions) maintain a stack of entered monitors
+ * (identified by code unit offset).
+ *
+ * If live-precise register maps are enabled, the "liveRegs" vector will
+ * be populated.  Unlike the other lists of registers here, we do not
+ * track the liveness of the method result register (which is not visible
+ * to the GC).
+ */
+struct RegisterLine {
+    RegType*        regTypes;
+    MonitorEntries* monitorEntries;
+    u4*             monitorStack;
+    unsigned int    monitorStackTop;
+    BitVector*      liveRegs;
+};
+
+/*
+ * Table that maps uninitialized instances to classes, based on the
+ * address of the new-instance instruction.  One per method.
+ */
+struct UninitInstanceMap {
+    int numEntries;
+    struct {
+        int             addr;   /* code offset, or -1 for method arg ("this") */
+        ClassObject*    clazz;  /* class created at this address */
+    } map[1];
+};
+#define kUninitThisArgAddr  (-1)
+#define kUninitThisArgSlot  0
+
+/*
+ * Various bits of data used by the verifier and register map generator.
+ */
+struct VerifierData {
+    /*
+     * The method we're working on.
+     */
+    const Method*   method;
+
+    /*
+     * Number of code units of instructions in the method.  A cache of the
+     * value calculated by dvmGetMethodInsnsSize().
+     */
+    u4              insnsSize;
+
+    /*
+     * Number of registers we track for each instruction.  This is equal
+     * to the method's declared "registersSize".  (Does not include the
+     * pending return value.)
+     */
+    u4              insnRegCount;
+
+    /*
+     * Instruction widths and flags, one entry per code unit.
+     */
+    InsnFlags*      insnFlags;
+
+    /*
+     * Uninitialized instance map, used for tracking the movement of
+     * objects that have been allocated but not initialized.
+     */
+    UninitInstanceMap* uninitMap;
+
+    /*
+     * Array of RegisterLine structs, one entry per code unit.  We only need
+     * entries for code units that hold the start of an "interesting"
+     * instruction.  For register map generation, we're only interested
+     * in GC points.
+     */
+    RegisterLine*   registerLines;
+
+    /*
+     * The number of occurrences of specific opcodes.
+     */
+    size_t          newInstanceCount;
+    size_t          monitorEnterCount;
+
+    /*
+     * Array of pointers to basic blocks, one entry per code unit.  Used
+     * for liveness analysis.
+     */
+    VfyBasicBlock** basicBlocks;
+};
+
+
+/* table with static merge logic for primitive types */
+extern const char gDvmMergeTab[kRegTypeMAX][kRegTypeMAX];
+
+
+/*
+ * Returns "true" if the flags indicate that this address holds the start
+ * of an instruction.
+ */
+INLINE bool dvmInsnIsOpcode(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagWidthMask) != 0;
+}
+
+/*
+ * Extract the unsigned 16-bit instruction width from "flags".
+ */
+INLINE int dvmInsnGetWidth(const InsnFlags* insnFlags, int addr) {
+    return insnFlags[addr] & kInsnFlagWidthMask;
+}
+
+/*
+ * Changed?
+ */
+INLINE bool dvmInsnIsChanged(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagChanged) != 0;
+}
+INLINE void dvmInsnSetChanged(InsnFlags* insnFlags, int addr, bool changed)
+{
+    if (changed)
+        insnFlags[addr] |= kInsnFlagChanged;
+    else
+        insnFlags[addr] &= ~kInsnFlagChanged;
+}
+
+/*
+ * Visited?
+ */
+INLINE bool dvmInsnIsVisited(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagVisited) != 0;
+}
+INLINE void dvmInsnSetVisited(InsnFlags* insnFlags, int addr, bool changed)
+{
+    if (changed)
+        insnFlags[addr] |= kInsnFlagVisited;
+    else
+        insnFlags[addr] &= ~kInsnFlagVisited;
+}
+
+/*
+ * Visited or changed?
+ */
+INLINE bool dvmInsnIsVisitedOrChanged(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & (kInsnFlagVisited|kInsnFlagChanged)) != 0;
+}
+
+/*
+ * In a "try" block?
+ */
+INLINE bool dvmInsnIsInTry(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagInTry) != 0;
+}
+INLINE void dvmInsnSetInTry(InsnFlags* insnFlags, int addr, bool inTry)
+{
+    assert(inTry);
+    //if (inTry)
+        insnFlags[addr] |= kInsnFlagInTry;
+    //else
+    //    insnFlags[addr] &= ~kInsnFlagInTry;
+}
+
+/*
+ * Instruction is a branch target or exception handler?
+ */
+INLINE bool dvmInsnIsBranchTarget(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagBranchTarget) != 0;
+}
+INLINE void dvmInsnSetBranchTarget(InsnFlags* insnFlags, int addr,
+    bool isBranch)
+{
+    assert(isBranch);
+    //if (isBranch)
+        insnFlags[addr] |= kInsnFlagBranchTarget;
+    //else
+    //    insnFlags[addr] &= ~kInsnFlagBranchTarget;
+}
+
+/*
+ * Instruction is a GC point?
+ */
+INLINE bool dvmInsnIsGcPoint(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagGcPoint) != 0;
+}
+INLINE void dvmInsnSetGcPoint(InsnFlags* insnFlags, int addr,
+    bool isGcPoint)
+{
+    assert(isGcPoint);
+    //if (isGcPoint)
+        insnFlags[addr] |= kInsnFlagGcPoint;
+    //else
+    //    insnFlags[addr] &= ~kInsnFlagGcPoint;
+}
+
+
+/*
+ * Create a new UninitInstanceMap.
+ */
+UninitInstanceMap* dvmCreateUninitInstanceMap(const Method* meth,
+    const InsnFlags* insnFlags, int newInstanceCount);
+
+/*
+ * Release the storage associated with an UninitInstanceMap.
+ */
+void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap);
+
+/*
+ * Verify bytecode in "meth".  "insnFlags" should be populated with
+ * instruction widths and "in try" flags.
+ */
+bool dvmVerifyCodeFlow(VerifierData* vdata);
+
+#endif  // DALVIK_CODEVERIFY_H_
diff --git a/vm/analysis/DexPrepare.cpp b/vm/analysis/DexPrepare.cpp
new file mode 100644
index 0000000..e8112d5
--- /dev/null
+++ b/vm/analysis/DexPrepare.cpp
@@ -0,0 +1,1563 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Prepare a DEX file for use by the VM.  Depending upon the VM options
+ * we will attempt to verify and/or optimize the code, possibly appending
+ * register maps.
+ *
+ * TODO: the format of the optimized header is currently "whatever we
+ * happen to write", since the VM that writes it is by definition the same
+ * as the VM that reads it.  Still, it should be better documented and
+ * more rigorously structured.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+#include "analysis/RegisterMap.h"
+#include "analysis/Optimize.h"
+
+#include <string>
+
+#include <libgen.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <zlib.h>
+
+/* fwd */
+static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+    DexClassLookup** ppClassLookup, DvmDex** ppDvmDex);
+static bool loadAllClasses(DvmDex* pDvmDex);
+static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
+    bool doOpt);
+static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
+    const DexClassDef* pClassDef, bool doVerify, bool doOpt);
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
+static int writeDependencies(int fd, u4 modWhen, u4 crc);
+static bool writeOptData(int fd, const DexClassLookup* pClassLookup,\
+    const RegisterMapBuilder* pRegMapBuilder);
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
+
+/*
+ * Get just the directory portion of the given path. Equivalent to dirname(3).
+ */
+static std::string saneDirName(const std::string& path) {
+    size_t n = path.rfind('/');
+    if (n == std::string::npos) {
+        return ".";
+    }
+    return path.substr(0, n);
+}
+
+/*
+ * Helper for dvmOpenCacheDexFile() in a known-error case: Check to
+ * see if the directory part of the given path (all but the last
+ * component) exists and is writable. Complain to the log if not.
+ */
+static bool directoryIsValid(const std::string& fileName)
+{
+    std::string dirName(saneDirName(fileName));
+
+    struct stat sb;
+    if (stat(dirName.c_str(), &sb) < 0) {
+        ALOGE("Could not stat dex cache directory '%s': %s", dirName.c_str(), strerror(errno));
+        return false;
+    }
+
+    if (!S_ISDIR(sb.st_mode)) {
+        ALOGE("Dex cache directory isn't a directory: %s", dirName.c_str());
+        return false;
+    }
+
+    if (access(dirName.c_str(), W_OK) < 0) {
+        ALOGE("Dex cache directory isn't writable: %s", dirName.c_str());
+        return false;
+    }
+
+    if (access(dirName.c_str(), R_OK) < 0) {
+        ALOGE("Dex cache directory isn't readable: %s", dirName.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Return the fd of an open file in the DEX file cache area.  If the cache
+ * file doesn't exist or is out of date, this will remove the old entry,
+ * create a new one (writing only the file header), and return with the
+ * "new file" flag set.
+ *
+ * It's possible to execute from an unoptimized DEX file directly,
+ * assuming the byte ordering and structure alignment is correct, but
+ * disadvantageous because some significant optimizations are not possible.
+ * It's not generally possible to do the same from an uncompressed Jar
+ * file entry, because we have to guarantee 32-bit alignment in the
+ * memory-mapped file.
+ *
+ * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
+ * and "crc32" come from the Zip directory entry.  For a stand-alone DEX
+ * file, it's the modification date of the file and the Adler32 from the
+ * DEX header (which immediately follows the magic).  If these don't
+ * match what's stored in the opt header, we reject the file immediately.
+ *
+ * On success, the file descriptor will be positioned just past the "opt"
+ * file header, and will be locked with flock.  "*pCachedName" will point
+ * to newly-allocated storage.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
+    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
+{
+    int fd, cc;
+    struct stat fdStat, fileStat;
+    bool readOnly = false;
+
+    *pNewFile = false;
+
+retry:
+    /*
+     * Try to open the cache file.  If we've been asked to,
+     * create it if it doesn't exist.
+     */
+    fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
+    if (fd < 0) {
+        fd = open(cacheFileName, O_RDONLY, 0);
+        if (fd < 0) {
+            if (createIfMissing) {
+                // TODO: write an equivalent of strerror_r that returns a std::string.
+                const std::string errnoString(strerror(errno));
+                if (directoryIsValid(cacheFileName)) {
+                    ALOGE("Can't open dex cache file '%s': %s", cacheFileName, errnoString.c_str());
+                }
+            }
+            return fd;
+        }
+        readOnly = true;
+    } else {
+        fchmod(fd, 0644);
+    }
+
+    /*
+     * Grab an exclusive lock on the cache file.  If somebody else is
+     * working on it, we'll block here until they complete.  Because
+     * we're waiting on an external resource, we go into VMWAIT mode.
+     */
+    ALOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)",
+        cacheFileName, fd, isBootstrap);
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+    cc = flock(fd, LOCK_EX | LOCK_NB);
+    if (cc != 0) {
+        ALOGD("DexOpt: sleeping on flock(%s)", cacheFileName);
+        cc = flock(fd, LOCK_EX);
+    }
+    dvmChangeStatus(NULL, oldStatus);
+    if (cc != 0) {
+        ALOGE("Can't lock dex cache '%s': %d", cacheFileName, cc);
+        close(fd);
+        return -1;
+    }
+    ALOGV("DexOpt:  locked cache file");
+
+    /*
+     * Check to see if the fd we opened and locked matches the file in
+     * the filesystem.  If they don't, then somebody else unlinked ours
+     * and created a new file, and we need to use that one instead.  (If
+     * we caught them between the unlink and the create, we'll get an
+     * ENOENT from the file stat.)
+     */
+    cc = fstat(fd, &fdStat);
+    if (cc != 0) {
+        ALOGE("Can't stat open file '%s'", cacheFileName);
+        LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
+        goto close_fail;
+    }
+    cc = stat(cacheFileName, &fileStat);
+    if (cc != 0 ||
+        fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
+    {
+        ALOGD("DexOpt: our open cache file is stale; sleeping and retrying");
+        LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
+        flock(fd, LOCK_UN);
+        close(fd);
+        usleep(250 * 1000);     /* if something is hosed, don't peg machine */
+        goto retry;
+    }
+
+    /*
+     * We have the correct file open and locked.  If the file size is zero,
+     * then it was just created by us, and we want to fill in some fields
+     * in the "opt" header and set "*pNewFile".  Otherwise, we want to
+     * verify that the fields in the header match our expectations, and
+     * reset the file if they don't.
+     */
+    if (fdStat.st_size == 0) {
+        if (readOnly) {
+            ALOGW("DexOpt: file has zero length and isn't writable");
+            goto close_fail;
+        }
+        cc = dexOptCreateEmptyHeader(fd);
+        if (cc != 0)
+            goto close_fail;
+        *pNewFile = true;
+        ALOGV("DexOpt: successfully initialized new cache file");
+    } else {
+        bool expectVerify, expectOpt;
+
+        if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {
+            expectVerify = false;
+        } else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {
+            expectVerify = !isBootstrap;
+        } else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {
+            expectVerify = true;
+        }
+
+        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {
+            expectOpt = false;
+        } else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
+                   gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {
+            expectOpt = expectVerify;
+        } else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {
+            expectOpt = true;
+        }
+
+        ALOGV("checking deps, expecting vfy=%d opt=%d",
+            expectVerify, expectOpt);
+
+        if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
+                expectVerify, expectOpt))
+        {
+            if (readOnly) {
+                /*
+                 * We could unlink and rewrite the file if we own it or
+                 * the "sticky" bit isn't set on the directory.  However,
+                 * we're not able to truncate it, which spoils things.  So,
+                 * give up now.
+                 */
+                if (createIfMissing) {
+                    ALOGW("Cached DEX '%s' (%s) is stale and not writable",
+                        fileName, cacheFileName);
+                }
+                goto close_fail;
+            }
+
+            /*
+             * If we truncate the existing file before unlinking it, any
+             * process that has it mapped will fail when it tries to touch
+             * the pages.
+             *
+             * This is very important.  The zygote process will have the
+             * boot DEX files (core, framework, etc.) mapped early.  If
+             * (say) core.dex gets updated, and somebody launches an app
+             * that uses App.dex, then App.dex gets reoptimized because it's
+             * dependent upon the boot classes.  However, dexopt will be
+             * using the *new* core.dex to do the optimizations, while the
+             * app will actually be running against the *old* core.dex
+             * because it starts from zygote.
+             *
+             * Even without zygote, it's still possible for a class loader
+             * to pull in an APK that was optimized against an older set
+             * of DEX files.  We must ensure that everything fails when a
+             * boot DEX gets updated, and for general "why aren't my
+             * changes doing anything" purposes its best if we just make
+             * everything crash when a DEX they're using gets updated.
+             */
+            ALOGD("ODEX file is stale or bad; removing and retrying (%s)",
+                cacheFileName);
+            if (ftruncate(fd, 0) != 0) {
+                ALOGW("Warning: unable to truncate cache file '%s': %s",
+                    cacheFileName, strerror(errno));
+                /* keep going */
+            }
+            if (unlink(cacheFileName) != 0) {
+                ALOGW("Warning: unable to remove cache file '%s': %d %s",
+                    cacheFileName, errno, strerror(errno));
+                /* keep going; permission failure should probably be fatal */
+            }
+            LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
+            flock(fd, LOCK_UN);
+            close(fd);
+            goto retry;
+        } else {
+            ALOGV("DexOpt: good deps in cache file");
+        }
+    }
+
+    assert(fd >= 0);
+    return fd;
+
+close_fail:
+    flock(fd, LOCK_UN);
+    close(fd);
+    return -1;
+}
+
+/*
+ * Unlock the file descriptor.
+ *
+ * Returns "true" on success.
+ */
+bool dvmUnlockCachedDexFile(int fd)
+{
+    LOGVV("DexOpt: unlocking cache file fd=%d", fd);
+    return (flock(fd, LOCK_UN) == 0);
+}
+
+
+/*
+ * Given a descriptor for a file with DEX data in it, produce an
+ * optimized version.
+ *
+ * The file pointed to by "fd" is expected to be a locked shared resource
+ * (or private); we make no efforts to enforce multi-process correctness
+ * here.
+ *
+ * "fileName" is only used for debug output.  "modWhen" and "crc" are stored
+ * in the dependency set.
+ *
+ * The "isBootstrap" flag determines how the optimizer and verifier handle
+ * package-scope access checks.  When optimizing, we only load the bootstrap
+ * class DEX files and the target DEX, so the flag determines whether the
+ * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
+ * This only really matters if the target DEX contains classes that claim to
+ * be in the same package as bootstrap classes.
+ *
+ * The optimizer will need to load every class in the target DEX file.
+ * This is generally undesirable, so we start a subprocess to do the
+ * work and wait for it to complete.
+ *
+ * Returns "true" on success.  All data will have been written to "fd".
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+    const char* lastPart = strrchr(fileName, '/');
+    if (lastPart != NULL)
+        lastPart++;
+    else
+        lastPart = fileName;
+
+    ALOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---", lastPart, isBootstrap);
+
+    pid_t pid;
+
+    /*
+     * This could happen if something in our bootclasspath, which we thought
+     * was all optimized, got rejected.
+     */
+    if (gDvm.optimizing) {
+        ALOGW("Rejecting recursive optimization attempt on '%s'", fileName);
+        return false;
+    }
+
+    pid = fork();
+    if (pid == 0) {
+        static const int kUseValgrind = 0;
+        static const char* kDexOptBin = "/bin/dexopt";
+        static const char* kValgrinder = "/usr/bin/valgrind";
+        static const int kFixedArgCount = 10;
+        static const int kValgrindArgCount = 5;
+        static const int kMaxIntLen = 12;   // '-'+10dig+'\0' -OR- 0x+8dig
+        int bcpSize = dvmGetBootPathSize();
+        int argc = kFixedArgCount + bcpSize
+            + (kValgrindArgCount * kUseValgrind);
+        const char* argv[argc+1];             // last entry is NULL
+        char values[argc][kMaxIntLen];
+        char* execFile;
+        const char* androidRoot;
+        int flags;
+
+        /* change process groups, so we don't clash with ProcessManager */
+        setpgid(0, 0);
+
+        /* full path to optimizer */
+        androidRoot = getenv("ANDROID_ROOT");
+        if (androidRoot == NULL) {
+            ALOGW("ANDROID_ROOT not set, defaulting to /system");
+            androidRoot = "/system";
+        }
+        execFile = (char*)alloca(strlen(androidRoot) + strlen(kDexOptBin) + 1);
+        strcpy(execFile, androidRoot);
+        strcat(execFile, kDexOptBin);
+
+        /*
+         * Create arg vector.
+         */
+        int curArg = 0;
+
+        if (kUseValgrind) {
+            /* probably shouldn't ship the hard-coded path */
+            argv[curArg++] = (char*)kValgrinder;
+            argv[curArg++] = "--tool=memcheck";
+            argv[curArg++] = "--leak-check=yes";        // check for leaks too
+            argv[curArg++] = "--leak-resolution=med";   // increase from 2 to 4
+            argv[curArg++] = "--num-callers=16";        // default is 12
+            assert(curArg == kValgrindArgCount);
+        }
+        argv[curArg++] = execFile;
+
+        argv[curArg++] = "--dex";
+
+        sprintf(values[2], "%d", DALVIK_VM_BUILD);
+        argv[curArg++] = values[2];
+
+        sprintf(values[3], "%d", fd);
+        argv[curArg++] = values[3];
+
+        sprintf(values[4], "%d", (int) dexOffset);
+        argv[curArg++] = values[4];
+
+        sprintf(values[5], "%d", (int) dexLength);
+        argv[curArg++] = values[5];
+
+        argv[curArg++] = (char*)fileName;
+
+        sprintf(values[7], "%d", (int) modWhen);
+        argv[curArg++] = values[7];
+
+        sprintf(values[8], "%d", (int) crc);
+        argv[curArg++] = values[8];
+
+        flags = 0;
+        if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
+            flags |= DEXOPT_OPT_ENABLED;
+            if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
+                flags |= DEXOPT_OPT_ALL;
+        }
+        if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
+            flags |= DEXOPT_VERIFY_ENABLED;
+            if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
+                flags |= DEXOPT_VERIFY_ALL;
+        }
+        if (isBootstrap)
+            flags |= DEXOPT_IS_BOOTSTRAP;
+        if (gDvm.generateRegisterMaps)
+            flags |= DEXOPT_GEN_REGISTER_MAPS;
+        sprintf(values[9], "%d", flags);
+        argv[curArg++] = values[9];
+
+        assert(((!kUseValgrind && curArg == kFixedArgCount) ||
+               ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
+
+        ClassPathEntry* cpe;
+        for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+            argv[curArg++] = cpe->fileName;
+        }
+        assert(curArg == argc);
+
+        argv[curArg] = NULL;
+
+        if (kUseValgrind)
+            execv(kValgrinder, const_cast<char**>(argv));
+        else
+            execv(execFile, const_cast<char**>(argv));
+
+        ALOGE("execv '%s'%s failed: %s", execFile,
+            kUseValgrind ? " [valgrind]" : "", strerror(errno));
+        exit(1);
+    } else {
+        ALOGV("DexOpt: waiting for verify+opt, pid=%d", (int) pid);
+        int status;
+        pid_t gotPid;
+
+        /*
+         * Wait for the optimization process to finish.  We go into VMWAIT
+         * mode here so GC suspension won't have to wait for us.
+         */
+        ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+        while (true) {
+            gotPid = waitpid(pid, &status, 0);
+            if (gotPid == -1 && errno == EINTR) {
+                ALOGD("waitpid interrupted, retrying");
+            } else {
+                break;
+            }
+        }
+        dvmChangeStatus(NULL, oldStatus);
+        if (gotPid != pid) {
+            ALOGE("waitpid failed: wanted %d, got %d: %s",
+                (int) pid, (int) gotPid, strerror(errno));
+            return false;
+        }
+
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+            ALOGD("DexOpt: --- END '%s' (success) ---", lastPart);
+            return true;
+        } else {
+            ALOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed",
+                lastPart, status);
+            return false;
+        }
+    }
+}
+
+/*
+ * Do the actual optimization.  This is executed in the dexopt process.
+ *
+ * For best use of disk/memory, we want to extract once and perform
+ * optimizations in place.  If the file has to expand or contract
+ * to match local structure padding/alignment expectations, we want
+ * to do the rewrite as part of the extract, rather than extracting
+ * into a temp file and slurping it back out.  (The structure alignment
+ * is currently correct for all platforms, and this isn't expected to
+ * change, so we should be okay with having it already extracted.)
+ *
+ * Returns "true" on success.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+    DexClassLookup* pClassLookup = NULL;
+    RegisterMapBuilder* pRegMapBuilder = NULL;
+
+    assert(gDvm.optimizing);
+
+    ALOGV("Continuing optimization (%s, isb=%d)", fileName, isBootstrap);
+
+    assert(dexOffset >= 0);
+
+    /* quick test so we don't blow up on empty file */
+    if (dexLength < (int) sizeof(DexHeader)) {
+        ALOGE("too small to be DEX");
+        return false;
+    }
+    if (dexOffset < (int) sizeof(DexOptHeader)) {
+        ALOGE("not enough room for opt header");
+        return false;
+    }
+
+    bool result = false;
+
+    /*
+     * Drop this into a global so we don't have to pass it around.  We could
+     * also add a field to DexFile, but since it only pertains to DEX
+     * creation that probably doesn't make sense.
+     */
+    gDvm.optimizingBootstrapClass = isBootstrap;
+
+    {
+        /*
+         * Map the entire file (so we don't have to worry about page
+         * alignment).  The expectation is that the output file contains
+         * our DEX data plus room for a small header.
+         */
+        bool success;
+        void* mapAddr;
+        mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
+                    MAP_SHARED, fd, 0);
+        if (mapAddr == MAP_FAILED) {
+            ALOGE("unable to mmap DEX cache: %s", strerror(errno));
+            goto bail;
+        }
+
+        bool doVerify, doOpt;
+        if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {
+            doVerify = false;
+        } else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {
+            doVerify = !gDvm.optimizingBootstrapClass;
+        } else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {
+            doVerify = true;
+        }
+
+        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {
+            doOpt = false;
+        } else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
+                   gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {
+            doOpt = doVerify;
+        } else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {
+            doOpt = true;
+        }
+
+        /*
+         * Rewrite the file.  Byte reordering, structure realigning,
+         * class verification, and bytecode optimization are all performed
+         * here.
+         *
+         * In theory the file could change size and bits could shift around.
+         * In practice this would be annoying to deal with, so the file
+         * layout is designed so that it can always be rewritten in place.
+         *
+         * This creates the class lookup table as part of doing the processing.
+         */
+        success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
+                    doVerify, doOpt, &pClassLookup, NULL);
+
+        if (success) {
+            DvmDex* pDvmDex = NULL;
+            u1* dexAddr = ((u1*) mapAddr) + dexOffset;
+
+            if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
+                ALOGE("Unable to create DexFile");
+                success = false;
+            } else {
+                /*
+                 * If configured to do so, generate register map output
+                 * for all verified classes.  The register maps were
+                 * generated during verification, and will now be serialized.
+                 */
+                if (gDvm.generateRegisterMaps) {
+                    pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
+                    if (pRegMapBuilder == NULL) {
+                        ALOGE("Failed generating register maps");
+                        success = false;
+                    }
+                }
+
+                DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
+                updateChecksum(dexAddr, dexLength, pHeader);
+
+                dvmDexFileFree(pDvmDex);
+            }
+        }
+
+        /* unmap the read-write version, forcing writes to disk */
+        if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
+            ALOGW("msync failed: %s", strerror(errno));
+            // weird, but keep going
+        }
+#if 1
+        /*
+         * This causes clean shutdown to fail, because we have loaded classes
+         * that point into it.  For the optimizer this isn't a problem,
+         * because it's more efficient for the process to simply exit.
+         * Exclude this code when doing clean shutdown for valgrind.
+         */
+        if (munmap(mapAddr, dexOffset + dexLength) != 0) {
+            ALOGE("munmap failed: %s", strerror(errno));
+            goto bail;
+        }
+#endif
+
+        if (!success)
+            goto bail;
+    }
+
+    /* get start offset, and adjust deps start for 64-bit alignment */
+    off_t depsOffset, optOffset, endOffset, adjOffset;
+    int depsLength, optLength;
+    u4 optChecksum;
+
+    depsOffset = lseek(fd, 0, SEEK_END);
+    if (depsOffset < 0) {
+        ALOGE("lseek to EOF failed: %s", strerror(errno));
+        goto bail;
+    }
+    adjOffset = (depsOffset + 7) & ~(0x07);
+    if (adjOffset != depsOffset) {
+        ALOGV("Adjusting deps start from %d to %d",
+            (int) depsOffset, (int) adjOffset);
+        depsOffset = adjOffset;
+        lseek(fd, depsOffset, SEEK_SET);
+    }
+
+    /*
+     * Append the dependency list.
+     */
+    if (writeDependencies(fd, modWhen, crc) != 0) {
+        ALOGW("Failed writing dependencies");
+        goto bail;
+    }
+
+    /* compute deps length, then adjust opt start for 64-bit alignment */
+    optOffset = lseek(fd, 0, SEEK_END);
+    depsLength = optOffset - depsOffset;
+
+    adjOffset = (optOffset + 7) & ~(0x07);
+    if (adjOffset != optOffset) {
+        ALOGV("Adjusting opt start from %d to %d",
+            (int) optOffset, (int) adjOffset);
+        optOffset = adjOffset;
+        lseek(fd, optOffset, SEEK_SET);
+    }
+
+    /*
+     * Append any optimized pre-computed data structures.
+     */
+    if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {
+        ALOGW("Failed writing opt data");
+        goto bail;
+    }
+
+    endOffset = lseek(fd, 0, SEEK_END);
+    optLength = endOffset - optOffset;
+
+    /* compute checksum from start of deps to end of opt area */
+    if (!computeFileChecksum(fd, depsOffset,
+            (optOffset+optLength) - depsOffset, &optChecksum))
+    {
+        goto bail;
+    }
+
+    /*
+     * Output the "opt" header with all values filled in and a correct
+     * magic number.
+     */
+    DexOptHeader optHdr;
+    memset(&optHdr, 0xff, sizeof(optHdr));
+    memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
+    memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
+    optHdr.dexOffset = (u4) dexOffset;
+    optHdr.dexLength = (u4) dexLength;
+    optHdr.depsOffset = (u4) depsOffset;
+    optHdr.depsLength = (u4) depsLength;
+    optHdr.optOffset = (u4) optOffset;
+    optHdr.optLength = (u4) optLength;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+    optHdr.flags = DEX_OPT_FLAG_BIG;
+#else
+    optHdr.flags = 0;
+#endif
+    optHdr.checksum = optChecksum;
+
+    fsync(fd);      /* ensure previous writes go before header is written */
+
+    lseek(fd, 0, SEEK_SET);
+    if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0)
+        goto bail;
+
+    ALOGV("Successfully wrote DEX header");
+    result = true;
+
+    //dvmRegisterMapDumpStats();
+
+bail:
+    dvmFreeRegisterMapBuilder(pRegMapBuilder);
+    free(pClassLookup);
+    return result;
+}
+
+/*
+ * Prepare an in-memory DEX file.
+ *
+ * The data was presented to the VM as a byte array rather than a file.
+ * We want to do the same basic set of operations, but we can just leave
+ * them in memory instead of writing them out to a cached optimized DEX file.
+ */
+bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex)
+{
+    DexClassLookup* pClassLookup = NULL;
+
+    /*
+     * Byte-swap, realign, verify basic DEX file structure.
+     *
+     * We could load + verify + optimize here as well, but that's probably
+     * not desirable.
+     *
+     * (The bulk-verification code is currently only setting the DEX
+     * file's "verified" flag, not updating the ClassObject.  This would
+     * also need to be changed, or we will try to verify the class twice,
+     * and possibly reject it when optimized opcodes are encountered.)
+     */
+    if (!rewriteDex(addr, len, false, false, &pClassLookup, ppDvmDex)) {
+        return false;
+    }
+
+    (*ppDvmDex)->pDexFile->pClassLookup = pClassLookup;
+
+    return true;
+}
+
+/*
+ * Perform in-place rewrites on a memory-mapped DEX file.
+ *
+ * If this is called from a short-lived child process (dexopt), we can
+ * go nutty with loading classes and allocating memory.  When it's
+ * called to prepare classes provided in a byte array, we may want to
+ * be more conservative.
+ *
+ * If "ppClassLookup" is non-NULL, a pointer to a newly-allocated
+ * DexClassLookup will be returned on success.
+ *
+ * If "ppDvmDex" is non-NULL, a newly-allocated DvmDex struct will be
+ * returned on success.
+ */
+static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+    DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)
+{
+    DexClassLookup* pClassLookup = NULL;
+    u8 prepWhen, loadWhen, verifyOptWhen;
+    DvmDex* pDvmDex = NULL;
+    bool result = false;
+    const char* msgStr = "???";
+
+    /* if the DEX is in the wrong byte order, swap it now */
+    if (dexSwapAndVerify(addr, len) != 0)
+        goto bail;
+
+    /*
+     * Now that the DEX file can be read directly, create a DexFile struct
+     * for it.
+     */
+    if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
+        ALOGE("Unable to create DexFile");
+        goto bail;
+    }
+
+    /*
+     * Create the class lookup table.  This will eventually be appended
+     * to the end of the .odex.
+     *
+     * We create a temporary link from the DexFile for the benefit of
+     * class loading, below.
+     */
+    pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
+    if (pClassLookup == NULL)
+        goto bail;
+    pDvmDex->pDexFile->pClassLookup = pClassLookup;
+
+    /*
+     * If we're not going to attempt to verify or optimize the classes,
+     * there's no value in loading them, so bail out early.
+     */
+    if (!doVerify && !doOpt) {
+        result = true;
+        goto bail;
+    }
+
+    prepWhen = dvmGetRelativeTimeUsec();
+
+    /*
+     * Load all classes found in this DEX file.  If they fail to load for
+     * some reason, they won't get verified (which is as it should be).
+     */
+    if (!loadAllClasses(pDvmDex))
+        goto bail;
+    loadWhen = dvmGetRelativeTimeUsec();
+
+    /*
+     * Create a data structure for use by the bytecode optimizer.
+     * We need to look up methods in a few classes, so this may cause
+     * a bit of class loading.  We usually do this during VM init, but
+     * for dexopt on core.jar the order of operations gets a bit tricky,
+     * so we defer it to here.
+     */
+    if (!dvmCreateInlineSubsTable())
+        goto bail;
+
+    /*
+     * Verify and optimize all classes in the DEX file (command-line
+     * options permitting).
+     *
+     * This is best-effort, so there's really no way for dexopt to
+     * fail at this point.
+     */
+    verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
+    verifyOptWhen = dvmGetRelativeTimeUsec();
+
+    if (doVerify && doOpt)
+        msgStr = "verify+opt";
+    else if (doVerify)
+        msgStr = "verify";
+    else if (doOpt)
+        msgStr = "opt";
+    ALOGD("DexOpt: load %dms, %s %dms, %d bytes",
+        (int) (loadWhen - prepWhen) / 1000,
+        msgStr,
+        (int) (verifyOptWhen - loadWhen) / 1000,
+        gDvm.pBootLoaderAlloc->curOffset);
+
+    result = true;
+
+bail:
+    /*
+     * On success, return the pieces that the caller asked for.
+     */
+
+    if (pDvmDex != NULL) {
+        /* break link between the two */
+        pDvmDex->pDexFile->pClassLookup = NULL;
+    }
+
+    if (ppDvmDex == NULL || !result) {
+        dvmDexFileFree(pDvmDex);
+    } else {
+        *ppDvmDex = pDvmDex;
+    }
+
+    if (ppClassLookup == NULL || !result) {
+        free(pClassLookup);
+    } else {
+        *ppClassLookup = pClassLookup;
+    }
+
+    return result;
+}
+
+/*
+ * Try to load all classes in the specified DEX.  If they have some sort
+ * of broken dependency, e.g. their superclass lives in a different DEX
+ * that wasn't previously loaded into the bootstrap class path, loading
+ * will fail.  This is the desired behavior.
+ *
+ * We have no notion of class loader at this point, so we load all of
+ * the classes with the bootstrap class loader.  It turns out this has
+ * exactly the behavior we want, and has no ill side effects because we're
+ * running in a separate process and anything we load here will be forgotten.
+ *
+ * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
+ * This works because we only call here as part of optimization / pre-verify,
+ * not during verification as part of loading a class into a running VM.
+ *
+ * This returns "false" if the world is too screwed up to do anything
+ * useful at all.
+ */
+static bool loadAllClasses(DvmDex* pDvmDex)
+{
+    u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
+    u4 idx;
+    int loaded = 0;
+
+    ALOGV("DexOpt: +++ trying to load %d classes", count);
+
+    dvmSetBootPathExtraDex(pDvmDex);
+
+    /*
+     * At this point, it is safe -- and necessary! -- to look up the
+     * VM's required classes and members, even when what we are in the
+     * process of processing is the core library that defines these
+     * classes itself. (The reason it is necessary is that in the act
+     * of initializing the class Class, below, the system will end up
+     * referring to many of the class references that got set up by
+     * this call.)
+     */
+    if (!dvmFindRequiredClassesAndMembers()) {
+        return false;
+    }
+
+    /*
+     * We have some circularity issues with Class and Object that are
+     * most easily avoided by ensuring that Object is never the first
+     * thing we try to find-and-initialize. The call to
+     * dvmFindSystemClass() here takes care of that situation. (We
+     * only need to do this when loading classes from the DEX file
+     * that contains Object, and only when Object comes first in the
+     * list, but it costs very little to do it in all cases.)
+     */
+    if (!dvmInitClass(gDvm.classJavaLangClass)) {
+        ALOGE("ERROR: failed to initialize the class Class!");
+        return false;
+    }
+
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* newClass;
+
+        pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
+        classDescriptor =
+            dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
+
+        ALOGV("+++  loading '%s'", classDescriptor);
+        //newClass = dvmDefineClass(pDexFile, classDescriptor,
+        //        NULL);
+        newClass = dvmFindSystemClassNoInit(classDescriptor);
+        if (newClass == NULL) {
+            ALOGV("DexOpt: failed loading '%s'", classDescriptor);
+            dvmClearOptException(dvmThreadSelf());
+        } else if (newClass->pDvmDex != pDvmDex) {
+            /*
+             * We don't load the new one, and we tag the first one found
+             * with the "multiple def" flag so the resolver doesn't try
+             * to make it available.
+             */
+            ALOGD("DexOpt: '%s' has an earlier definition; blocking out",
+                classDescriptor);
+            SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
+        } else {
+            loaded++;
+        }
+    }
+    ALOGV("DexOpt: +++ successfully loaded %d classes", loaded);
+
+    dvmSetBootPathExtraDex(NULL);
+    return true;
+}
+
+/*
+ * Verify and/or optimize all classes that were successfully loaded from
+ * this DEX file.
+ */
+static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
+    bool doOpt)
+{
+    u4 count = pDexFile->pHeader->classDefsSize;
+    u4 idx;
+
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* clazz;
+
+        pClassDef = dexGetClassDef(pDexFile, idx);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        /* all classes are loaded into the bootstrap class loader */
+        clazz = dvmLookupClass(classDescriptor, NULL, false);
+        if (clazz != NULL) {
+            verifyAndOptimizeClass(pDexFile, clazz, pClassDef, doVerify, doOpt);
+
+        } else {
+            // TODO: log when in verbose mode
+            ALOGV("DexOpt: not optimizing unavailable class '%s'",
+                classDescriptor);
+        }
+    }
+
+#ifdef VERIFIER_STATS
+    ALOGI("Verifier stats:");
+    ALOGI(" methods examined        : %u", gDvm.verifierStats.methodsExamined);
+    ALOGI(" monitor-enter methods   : %u", gDvm.verifierStats.monEnterMethods);
+    ALOGI(" instructions examined   : %u", gDvm.verifierStats.instrsExamined);
+    ALOGI(" instructions re-examined: %u", gDvm.verifierStats.instrsReexamined);
+    ALOGI(" copying of register sets: %u", gDvm.verifierStats.copyRegCount);
+    ALOGI(" merging of register sets: %u", gDvm.verifierStats.mergeRegCount);
+    ALOGI(" ...that caused changes  : %u", gDvm.verifierStats.mergeRegChanged);
+    ALOGI(" uninit searches         : %u", gDvm.verifierStats.uninitSearches);
+    ALOGI(" max memory required     : %u", gDvm.verifierStats.biggestAlloc);
+#endif
+}
+
+/*
+ * Verify and/or optimize a specific class.
+ */
+static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
+    const DexClassDef* pClassDef, bool doVerify, bool doOpt)
+{
+    const char* classDescriptor;
+    bool verified = false;
+
+    if (clazz->pDvmDex->pDexFile != pDexFile) {
+        /*
+         * The current DEX file defined a class that is also present in the
+         * bootstrap class path.  The class loader favored the bootstrap
+         * version, which means that we have a pointer to a class that is
+         * (a) not the one we want to examine, and (b) mapped read-only,
+         * so we will seg fault if we try to rewrite instructions inside it.
+         */
+        ALOGD("DexOpt: not verifying/optimizing '%s': multiple definitions",
+            clazz->descriptor);
+        return;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+    /*
+     * First, try to verify it.
+     */
+    if (doVerify) {
+        if (dvmVerifyClass(clazz)) {
+            /*
+             * Set the "is preverified" flag in the DexClassDef.  We
+             * do it here, rather than in the ClassObject structure,
+             * because the DexClassDef is part of the odex file.
+             */
+            assert((clazz->accessFlags & JAVA_FLAGS_MASK) ==
+                pClassDef->accessFlags);
+            ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISPREVERIFIED;
+            verified = true;
+        } else {
+            // TODO: log when in verbose mode
+            ALOGV("DexOpt: '%s' failed verification", classDescriptor);
+        }
+    }
+
+    if (doOpt) {
+        bool needVerify = (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
+                           gDvm.dexOptMode == OPTIMIZE_MODE_FULL);
+        if (!verified && needVerify) {
+            ALOGV("DexOpt: not optimizing '%s': not verified",
+                classDescriptor);
+        } else {
+            dvmOptimizeClass(clazz, false);
+
+            /* set the flag whether or not we actually changed anything */
+            ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISOPTIMIZED;
+        }
+    }
+}
+
+
+/*
+ * Get the cache file name from a ClassPathEntry.
+ */
+static const char* getCacheFileName(const ClassPathEntry* cpe)
+{
+    switch (cpe->kind) {
+    case kCpeJar:
+        return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
+    case kCpeDex:
+        return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
+    default:
+        ALOGE("DexOpt: unexpected cpe kind %d", cpe->kind);
+        dvmAbort();
+        return NULL;
+    }
+}
+
+/*
+ * Get the SHA-1 signature.
+ */
+static const u1* getSignature(const ClassPathEntry* cpe)
+{
+    DvmDex* pDvmDex;
+
+    switch (cpe->kind) {
+    case kCpeJar:
+        pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
+        break;
+    case kCpeDex:
+        pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
+        break;
+    default:
+        ALOGE("unexpected cpe kind %d", cpe->kind);
+        dvmAbort();
+        pDvmDex = NULL;         // make gcc happy
+    }
+
+    assert(pDvmDex != NULL);
+    return pDvmDex->pDexFile->pHeader->signature;
+}
+
+
+/*
+ * Dependency layout:
+ *  4b  Source file modification time, in seconds since 1970 UTC
+ *  4b  CRC-32 from Zip entry, or Adler32 from source DEX header
+ *  4b  Dalvik VM build number
+ *  4b  Number of dependency entries that follow
+ *  Dependency entries:
+ *    4b  Name length (including terminating null)
+ *    var Full path of cache entry (null terminated)
+ *    20b SHA-1 signature from source DEX file
+ *
+ * If this changes, update DEX_OPT_MAGIC_VERS.
+ */
+static const size_t kMinDepSize = 4 * 4;
+static const size_t kMaxDepSize = 4 * 4 + 2048;     // sanity check
+
+/*
+ * Read the "opt" header, verify it, then read the dependencies section
+ * and verify that data as well.
+ *
+ * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
+ * match up with what is stored in the header.  If they don't, we reject
+ * the file so that it can be recreated from the updated original.  If
+ * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
+ *
+ * On successful return, the file will be seeked immediately past the
+ * "opt" header.
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+    u4 crc, bool expectVerify, bool expectOpt)
+{
+    DexOptHeader optHdr;
+    u1* depData = NULL;
+    const u1* magic;
+    off_t posn;
+    int result = false;
+    ssize_t actual;
+
+    /*
+     * Start at the start.  The "opt" header, when present, will always be
+     * the first thing in the file.
+     */
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+        ALOGE("DexOpt: failed to seek to start of file: %s", strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Read and do trivial verification on the opt header.  The header is
+     * always in host byte order.
+     */
+    actual = read(fd, &optHdr, sizeof(optHdr));
+    if (actual < 0) {
+        ALOGE("DexOpt: failed reading opt header: %s", strerror(errno));
+        goto bail;
+    } else if (actual != sizeof(optHdr)) {
+        ALOGE("DexOpt: failed reading opt header (got %d of %zd)",
+            (int) actual, sizeof(optHdr));
+        goto bail;
+    }
+
+    magic = optHdr.magic;
+    if (memcmp(magic, DEX_MAGIC, 4) == 0) {
+        /* somebody probably pointed us at the wrong file */
+        ALOGD("DexOpt: expected optimized DEX, found unoptimized");
+        goto bail;
+    } else if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
+        /* not a DEX file, or previous attempt was interrupted */
+        ALOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)",
+            magic[0], magic[1], magic[2], magic[3]);
+        goto bail;
+    }
+    if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+        ALOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)",
+            magic[4], magic[5], magic[6], magic[7]);
+        goto bail;
+    }
+    if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
+        ALOGW("DexOpt: weird deps length %d, bailing", optHdr.depsLength);
+        goto bail;
+    }
+
+    /*
+     * Do the header flags match up with what we want?
+     *
+     * The only thing we really can't handle is incorrect byte ordering.
+     */
+    {
+        const u4 matchMask = DEX_OPT_FLAG_BIG;
+        u4 expectedFlags = 0;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+        expectedFlags |= DEX_OPT_FLAG_BIG;
+#endif
+        if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
+            ALOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)",
+                expectedFlags, optHdr.flags, matchMask);
+            goto bail;
+        }
+    }
+
+    posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
+    if (posn < 0) {
+        ALOGW("DexOpt: seek to deps failed: %s", strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Read all of the dependency stuff into memory.
+     */
+    depData = (u1*) malloc(optHdr.depsLength);
+    if (depData == NULL) {
+        ALOGW("DexOpt: unable to allocate %d bytes for deps",
+            optHdr.depsLength);
+        goto bail;
+    }
+    actual = read(fd, depData, optHdr.depsLength);
+    if (actual < 0) {
+        ALOGW("DexOpt: failed reading deps: %s", strerror(errno));
+        goto bail;
+    } else if (actual != (ssize_t) optHdr.depsLength) {
+        ALOGW("DexOpt: failed reading deps: got %d of %d",
+            (int) actual, optHdr.depsLength);
+        goto bail;
+    }
+
+    /*
+     * Verify simple items.
+     */
+    const u1* ptr;
+    u4 val;
+
+    ptr = depData;
+    val = read4LE(&ptr);
+    if (sourceAvail && val != modWhen) {
+        ALOGI("DexOpt: source file mod time mismatch (%08x vs %08x)",
+            val, modWhen);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (sourceAvail && val != crc) {
+        ALOGI("DexOpt: source file CRC mismatch (%08x vs %08x)", val, crc);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (val != DALVIK_VM_BUILD) {
+        ALOGD("DexOpt: VM build version mismatch (%d vs %d)",
+            val, DALVIK_VM_BUILD);
+        goto bail;
+    }
+
+    /*
+     * Verify dependencies on other cached DEX files.  It must match
+     * exactly with what is currently defined in the bootclasspath.
+     */
+    ClassPathEntry* cpe;
+    u4 numDeps;
+
+    numDeps = read4LE(&ptr);
+    ALOGV("+++ DexOpt: numDeps = %d", numDeps);
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName =
+            dvmPathToAbsolutePortion(getCacheFileName(cpe));
+        assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+        const u1* signature = getSignature(cpe);
+        size_t len = strlen(cacheFileName) +1;
+        u4 storedStrLen;
+
+        if (numDeps == 0) {
+            /* more entries in bootclasspath than in deps list */
+            ALOGI("DexOpt: not all deps represented");
+            goto bail;
+        }
+
+        storedStrLen = read4LE(&ptr);
+        if (len != storedStrLen ||
+            strcmp(cacheFileName, (const char*) ptr) != 0)
+        {
+            ALOGI("DexOpt: mismatch dep name: '%s' vs. '%s'",
+                cacheFileName, ptr);
+            goto bail;
+        }
+
+        ptr += storedStrLen;
+
+        if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
+            ALOGI("DexOpt: mismatch dep signature for '%s'", cacheFileName);
+            goto bail;
+        }
+        ptr += kSHA1DigestLen;
+
+        ALOGV("DexOpt: dep match on '%s'", cacheFileName);
+
+        numDeps--;
+    }
+
+    if (numDeps != 0) {
+        /* more entries in deps list than in classpath */
+        ALOGI("DexOpt: Some deps went away");
+        goto bail;
+    }
+
+    // consumed all data and no more?
+    if (ptr != depData + optHdr.depsLength) {
+        ALOGW("DexOpt: Spurious dep data? %d vs %d",
+            (int) (ptr - depData), optHdr.depsLength);
+        assert(false);
+    }
+
+    result = true;
+
+bail:
+    free(depData);
+    return result;
+}
+
+/*
+ * Write the dependency info to "fd" at the current file position.
+ */
+static int writeDependencies(int fd, u4 modWhen, u4 crc)
+{
+    u1* buf = NULL;
+    int result = -1;
+    ssize_t bufLen;
+    ClassPathEntry* cpe;
+    int numDeps;
+
+    /*
+     * Count up the number of completed entries in the bootclasspath.
+     */
+    numDeps = 0;
+    bufLen = 0;
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName =
+            dvmPathToAbsolutePortion(getCacheFileName(cpe));
+        assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+        ALOGV("+++ DexOpt: found dep '%s'", cacheFileName);
+
+        numDeps++;
+        bufLen += strlen(cacheFileName) +1;
+    }
+
+    bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
+
+    buf = (u1*)malloc(bufLen);
+
+    set4LE(buf+0, modWhen);
+    set4LE(buf+4, crc);
+    set4LE(buf+8, DALVIK_VM_BUILD);
+    set4LE(buf+12, numDeps);
+
+    // TODO: do we want to add dvmGetInlineOpsTableLength() here?  Won't
+    // help us if somebody replaces an existing entry, but it'd catch
+    // additions/removals.
+
+    u1* ptr = buf + 4*4;
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName =
+            dvmPathToAbsolutePortion(getCacheFileName(cpe));
+        assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+        const u1* signature = getSignature(cpe);
+        int len = strlen(cacheFileName) +1;
+
+        if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
+            ALOGE("DexOpt: overran buffer");
+            dvmAbort();
+        }
+
+        set4LE(ptr, len);
+        ptr += 4;
+        memcpy(ptr, cacheFileName, len);
+        ptr += len;
+        memcpy(ptr, signature, kSHA1DigestLen);
+        ptr += kSHA1DigestLen;
+    }
+
+    assert(ptr == buf + bufLen);
+
+    result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info");
+
+    free(buf);
+    return result;
+}
+
+
+/*
+ * Write a block of data in "chunk" format.
+ *
+ * The chunk header fields are always in "native" byte order.  If "size"
+ * is not a multiple of 8 bytes, the data area is padded out.
+ */
+static bool writeChunk(int fd, u4 type, const void* data, size_t size)
+{
+    union {             /* save a syscall by grouping these together */
+        char raw[8];
+        struct {
+            u4 type;
+            u4 size;
+        } ts;
+    } header;
+
+    assert(sizeof(header) == 8);
+
+    ALOGV("Writing chunk, type=%.4s size=%d", (char*) &type, size);
+
+    header.ts.type = type;
+    header.ts.size = (u4) size;
+    if (sysWriteFully(fd, &header, sizeof(header),
+            "DexOpt opt chunk header write") != 0)
+    {
+        return false;
+    }
+
+    if (size > 0) {
+        if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0)
+            return false;
+    }
+
+    /* if necessary, pad to 64-bit alignment */
+    if ((size & 7) != 0) {
+        int padSize = 8 - (size & 7);
+        ALOGV("size was %d, inserting %d pad bytes", size, padSize);
+        lseek(fd, padSize, SEEK_CUR);
+    }
+
+    assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
+
+    return true;
+}
+
+/*
+ * Write opt data.
+ *
+ * We have different pieces, some of which may be optional.  To make the
+ * most effective use of space, we use a "chunk" format, with a 4-byte
+ * type and a 4-byte length.  We guarantee 64-bit alignment for the data,
+ * so it can be used directly when the file is mapped for reading.
+ */
+static bool writeOptData(int fd, const DexClassLookup* pClassLookup,
+    const RegisterMapBuilder* pRegMapBuilder)
+{
+    /* pre-computed class lookup hash table */
+    if (!writeChunk(fd, (u4) kDexChunkClassLookup,
+            pClassLookup, pClassLookup->size))
+    {
+        return false;
+    }
+
+    /* register maps (optional) */
+    if (pRegMapBuilder != NULL) {
+        if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
+                pRegMapBuilder->data, pRegMapBuilder->size))
+        {
+            return false;
+        }
+    }
+
+    /* write the end marker */
+    if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Compute a checksum on a piece of an open file.
+ *
+ * File will be positioned at end of checksummed area.
+ *
+ * Returns "true" on success.
+ */
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
+{
+    unsigned char readBuf[8192];
+    ssize_t actual;
+    uLong adler;
+
+    if (lseek(fd, start, SEEK_SET) != start) {
+        ALOGE("Unable to seek to start of checksum area (%ld): %s",
+            (long) start, strerror(errno));
+        return false;
+    }
+
+    adler = adler32(0L, Z_NULL, 0);
+
+    while (length != 0) {
+        size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
+        actual = read(fd, readBuf, wanted);
+        if (actual <= 0) {
+            ALOGE("Read failed (%d) while computing checksum (len=%zu): %s",
+                (int) actual, length, strerror(errno));
+            return false;
+        }
+
+        adler = adler32(adler, readBuf, actual);
+
+        length -= actual;
+    }
+
+    *pSum = adler;
+    return true;
+}
+
+/*
+ * Update the Adler-32 checksum stored in the DEX file.  This covers the
+ * swapped and optimized DEX data, but does not include the opt header
+ * or optimized data.
+ */
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
+{
+    /*
+     * Rewrite the checksum.  We leave the SHA-1 signature alone.
+     */
+    uLong adler = adler32(0L, Z_NULL, 0);
+    const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+    adler = adler32(adler, addr + nonSum, len - nonSum);
+    pHeader->checksum = adler;
+}
diff --git a/vm/analysis/DexPrepare.h b/vm/analysis/DexPrepare.h
new file mode 100644
index 0000000..297bbc6
--- /dev/null
+++ b/vm/analysis/DexPrepare.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * DEX preparation declarations.
+ */
+#ifndef DALVIK_DEXPREPARE_H_
+#define DALVIK_DEXPREPARE_H_
+
+/*
+ * Global DEX optimizer control.  Determines the circumstances in which we
+ * try to rewrite instructions in the DEX file.
+ *
+ * Optimizing is performed ahead-of-time by dexopt and, in some cases, at
+ * load time by the VM.
+ */
+enum DexOptimizerMode {
+    OPTIMIZE_MODE_UNKNOWN = 0,
+    OPTIMIZE_MODE_NONE,         /* never optimize (except "essential") */
+    OPTIMIZE_MODE_VERIFIED,     /* only optimize verified classes (default) */
+    OPTIMIZE_MODE_ALL,          /* optimize verified & unverified (risky) */
+    OPTIMIZE_MODE_FULL          /* fully opt verified classes at load time */
+};
+
+/* some additional bit flags for dexopt */
+enum DexoptFlags {
+    DEXOPT_OPT_ENABLED       = 1,       /* optimizations enabled? */
+    DEXOPT_OPT_ALL           = 1 << 1,  /* optimize when verify fails? */
+    DEXOPT_VERIFY_ENABLED    = 1 << 2,  /* verification enabled? */
+    DEXOPT_VERIFY_ALL        = 1 << 3,  /* verify bootstrap classes? */
+    DEXOPT_IS_BOOTSTRAP      = 1 << 4,  /* is dex in bootstrap class path? */
+    DEXOPT_GEN_REGISTER_MAPS = 1 << 5,  /* generate register maps during vfy */
+    DEXOPT_UNIPROCESSOR      = 1 << 6,  /* specify uniprocessor target */
+    DEXOPT_SMP               = 1 << 7   /* specify SMP target */
+};
+
+/*
+ * An enumeration of problems that can turn up during verification.
+ */
+enum VerifyError {
+    VERIFY_ERROR_NONE = 0,      /* no error; must be zero */
+    VERIFY_ERROR_GENERIC,       /* VerifyError */
+
+    VERIFY_ERROR_NO_CLASS,      /* NoClassDefFoundError */
+    VERIFY_ERROR_NO_FIELD,      /* NoSuchFieldError */
+    VERIFY_ERROR_NO_METHOD,     /* NoSuchMethodError */
+    VERIFY_ERROR_ACCESS_CLASS,  /* IllegalAccessError */
+    VERIFY_ERROR_ACCESS_FIELD,  /* IllegalAccessError */
+    VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError */
+    VERIFY_ERROR_CLASS_CHANGE,  /* IncompatibleClassChangeError */
+    VERIFY_ERROR_INSTANTIATION, /* InstantiationError */
+};
+
+/*
+ * Identifies the type of reference in the instruction that generated the
+ * verify error (e.g. VERIFY_ERROR_ACCESS_CLASS could come from a method,
+ * field, or class reference).
+ *
+ * This must fit in two bits.
+ */
+enum VerifyErrorRefType {
+    VERIFY_ERROR_REF_CLASS  = 0,
+    VERIFY_ERROR_REF_FIELD  = 1,
+    VERIFY_ERROR_REF_METHOD = 2,
+};
+
+#define kVerifyErrorRefTypeShift 6
+
+#define VERIFY_OK(_failure) ((_failure) == VERIFY_ERROR_NONE)
+
+/*
+ * Given the full path to a DEX or Jar file, and (if appropriate) the name
+ * within the Jar, open the optimized version from the cache.
+ *
+ * If "*pNewFile" is set, a new file has been created with only a stub
+ * "opt" header, and the caller is expected to fill in the blanks.
+ *
+ * Returns the file descriptor, locked and seeked past the "opt" header.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cachedFile,
+    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing);
+
+/*
+ * Unlock the specified file descriptor.  Use in conjunction with
+ * dvmOpenCachedDexFile().
+ *
+ * Returns true on success.
+ */
+bool dvmUnlockCachedDexFile(int fd);
+
+/*
+ * Verify the contents of the "opt" header, and check the DEX file's
+ * dependencies on its source zip (if available).
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+    u4 crc, bool expectVerify, bool expectOpt);
+
+/*
+ * Optimize a DEX file.  The file must start with the "opt" header, followed
+ * by the plain DEX data.  It must be mmap()able.
+ *
+ * "fileName" is only used for debug output.
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLen,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
+
+/*
+ * Continue the optimization process on the other side of a fork/exec.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
+
+/*
+ * Prepare DEX data that is only available to the VM as in-memory data.
+ */
+bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex);
+
+/*
+ * Prep data structures.
+ */
+bool dvmCreateInlineSubsTable(void);
+void dvmFreeInlineSubsTable(void);
+
+#endif  // DALVIK_DEXPREPARE_H_
diff --git a/vm/analysis/DexVerify.cpp b/vm/analysis/DexVerify.cpp
new file mode 100644
index 0000000..c47c301
--- /dev/null
+++ b/vm/analysis/DexVerify.cpp
@@ -0,0 +1,1226 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik classfile verification.  This file contains the verifier entry
+ * points and the static constraint checks.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "libdex/DexCatch.h"
+
+
+/* fwd */
+static bool verifyMethod(Method* meth);
+static bool verifyInstructions(VerifierData* vdata);
+
+
+/*
+ * Verify a class.
+ *
+ * By the time we get here, the value of gDvm.classVerifyMode should already
+ * have been factored in.  If you want to call into the verifier even
+ * though verification is disabled, that's your business.
+ *
+ * Returns "true" on success.
+ */
+bool dvmVerifyClass(ClassObject* clazz)
+{
+    int i;
+
+    if (dvmIsClassVerified(clazz)) {
+        ALOGD("Ignoring duplicate verify attempt on %s", clazz->descriptor);
+        return true;
+    }
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        if (!verifyMethod(&clazz->directMethods[i])) {
+            LOG_VFY("Verifier rejected class %s", clazz->descriptor);
+            return false;
+        }
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        if (!verifyMethod(&clazz->virtualMethods[i])) {
+            LOG_VFY("Verifier rejected class %s", clazz->descriptor);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Compute the width of the instruction at each address in the instruction
+ * stream, and store it in vdata->insnFlags.  Addresses that are in the
+ * middle of an instruction, or that are part of switch table data, are not
+ * touched (so the caller should probably initialize "insnFlags" to zero).
+ *
+ * The "newInstanceCount" and "monitorEnterCount" fields in vdata are
+ * also set.
+ *
+ * Performs some static checks, notably:
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ *
+ * Logs an error and returns "false" on failure.
+ */
+static bool computeWidthsAndCountOps(VerifierData* vdata)
+{
+    const Method* meth = vdata->method;
+    InsnFlags* insnFlags = vdata->insnFlags;
+    size_t insnCount = vdata->insnsSize;
+    const u2* insns = meth->insns;
+    bool result = false;
+    int newInstanceCount = 0;
+    int monitorEnterCount = 0;
+    int i;
+
+    for (i = 0; i < (int) insnCount; /**/) {
+        size_t width = dexGetWidthFromInstruction(insns);
+        if (width == 0) {
+            LOG_VFY_METH(meth, "VFY: invalid instruction (0x%04x)", *insns);
+            goto bail;
+        } else if (width > 65535) {
+            LOG_VFY_METH(meth,
+                "VFY: warning: unusually large instr width (%d)", width);
+        }
+
+        Opcode opcode = dexOpcodeFromCodeUnit(*insns);
+        if (opcode == OP_NEW_INSTANCE)
+            newInstanceCount++;
+        if (opcode == OP_MONITOR_ENTER)
+            monitorEnterCount++;
+
+        insnFlags[i] |= width;
+        i += width;
+        insns += width;
+    }
+    if (i != (int) vdata->insnsSize) {
+        LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)",
+            i, dvmGetMethodInsnsSize(meth));
+        goto bail;
+    }
+
+    result = true;
+    vdata->newInstanceCount = newInstanceCount;
+    vdata->monitorEnterCount = monitorEnterCount;
+
+bail:
+    return result;
+}
+
+/*
+ * Set the "in try" flags for all instructions protected by "try" statements.
+ * Also sets the "branch target" flags for exception handlers.
+ *
+ * Call this after widths have been set in "insnFlags".
+ *
+ * Returns "false" if something in the exception table looks fishy, but
+ * we're expecting the exception table to be somewhat sane.
+ */
+static bool scanTryCatchBlocks(const Method* meth, InsnFlags* insnFlags)
+{
+    u4 insnsSize = dvmGetMethodInsnsSize(meth);
+    const DexCode* pCode = dvmGetMethodCode(meth);
+    u4 triesSize = pCode->triesSize;
+    const DexTry* pTries;
+    u4 idx;
+
+    if (triesSize == 0) {
+        return true;
+    }
+
+    pTries = dexGetTries(pCode);
+
+    for (idx = 0; idx < triesSize; idx++) {
+        const DexTry* pTry = &pTries[idx];
+        u4 start = pTry->startAddr;
+        u4 end = start + pTry->insnCount;
+        u4 addr;
+
+        if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) {
+            LOG_VFY_METH(meth,
+                "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)",
+                start, end, insnsSize);
+            return false;
+        }
+
+        if (dvmInsnGetWidth(insnFlags, start) == 0) {
+            LOG_VFY_METH(meth,
+                "VFY: 'try' block starts inside an instruction (%d)",
+                start);
+            return false;
+        }
+
+        for (addr = start; addr < end;
+            addr += dvmInsnGetWidth(insnFlags, addr))
+        {
+            assert(dvmInsnGetWidth(insnFlags, addr) != 0);
+            dvmInsnSetInTry(insnFlags, addr, true);
+        }
+    }
+
+    /* Iterate over each of the handlers to verify target addresses. */
+    u4 handlersSize = dexGetHandlersSize(pCode);
+    u4 offset = dexGetFirstHandlerOffset(pCode);
+    for (idx = 0; idx < handlersSize; idx++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+            u4 addr;
+
+            if (handler == NULL) {
+                break;
+            }
+
+            addr = handler->address;
+            if (dvmInsnGetWidth(insnFlags, addr) == 0) {
+                LOG_VFY_METH(meth,
+                    "VFY: exception handler starts at bad address (%d)",
+                    addr);
+                return false;
+            }
+
+            dvmInsnSetBranchTarget(insnFlags, addr, true);
+        }
+
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+
+    return true;
+}
+
+/*
+ * Perform verification on a single method.
+ *
+ * We do this in three passes:
+ *  (1) Walk through all code units, determining instruction locations,
+ *      widths, and other characteristics.
+ *  (2) Walk through all code units, performing static checks on
+ *      operands.
+ *  (3) Iterate through the method, checking type safety and looking
+ *      for code flow problems.
+ *
+ * Some checks may be bypassed depending on the verification mode.  We can't
+ * turn this stuff off completely if we want to do "exact" GC.
+ *
+ * TODO: cite source?
+ * Confirmed here:
+ * - code array must not be empty
+ * - (N/A) code_length must be less than 65536
+ * Confirmed by computeWidthsAndCountOps():
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ */
+static bool verifyMethod(Method* meth)
+{
+    bool result = false;
+
+    /*
+     * Verifier state blob.  Various values will be cached here so we
+     * can avoid expensive lookups and pass fewer arguments around.
+     */
+    VerifierData vdata;
+#if 1   // ndef NDEBUG
+    memset(&vdata, 0x99, sizeof(vdata));
+#endif
+
+    vdata.method = meth;
+    vdata.insnsSize = dvmGetMethodInsnsSize(meth);
+    vdata.insnRegCount = meth->registersSize;
+    vdata.insnFlags = NULL;
+    vdata.uninitMap = NULL;
+    vdata.basicBlocks = NULL;
+
+    /*
+     * If there aren't any instructions, make sure that's expected, then
+     * exit successfully.  Note: for native methods, meth->insns gets set
+     * to a native function pointer on first call, so don't use that as
+     * an indicator.
+     */
+    if (vdata.insnsSize == 0) {
+        if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) {
+            LOG_VFY_METH(meth,
+                "VFY: zero-length code in concrete non-native method");
+            goto bail;
+        }
+
+        goto success;
+    }
+
+    /*
+     * Sanity-check the register counts.  ins + locals = registers, so make
+     * sure that ins <= registers.
+     */
+    if (meth->insSize > meth->registersSize) {
+        LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)",
+            meth->insSize, meth->registersSize);
+        goto bail;
+    }
+
+    /*
+     * Allocate and populate an array to hold instruction data.
+     *
+     * TODO: Consider keeping a reusable pre-allocated array sitting
+     * around for smaller methods.
+     */
+    vdata.insnFlags = (InsnFlags*) calloc(vdata.insnsSize, sizeof(InsnFlags));
+    if (vdata.insnFlags == NULL)
+        goto bail;
+
+    /*
+     * Compute the width of each instruction and store the result in insnFlags.
+     * Count up the #of occurrences of certain opcodes while we're at it.
+     */
+    if (!computeWidthsAndCountOps(&vdata))
+        goto bail;
+
+    /*
+     * Allocate a map to hold the classes of uninitialized instances.
+     */
+    vdata.uninitMap = dvmCreateUninitInstanceMap(meth, vdata.insnFlags,
+        vdata.newInstanceCount);
+    if (vdata.uninitMap == NULL)
+        goto bail;
+
+    /*
+     * Set the "in try" flags for all instructions guarded by a "try" block.
+     * Also sets the "branch target" flag on exception handlers.
+     */
+    if (!scanTryCatchBlocks(meth, vdata.insnFlags))
+        goto bail;
+
+    /*
+     * Perform static instruction verification.  Also sets the "branch
+     * target" flags.
+     */
+    if (!verifyInstructions(&vdata))
+        goto bail;
+
+    /*
+     * Do code-flow analysis.
+     *
+     * We could probably skip this for a method with no registers, but
+     * that's so rare that there's little point in checking.
+     */
+    if (!dvmVerifyCodeFlow(&vdata)) {
+        //ALOGD("+++ %s failed code flow", meth->name);
+        goto bail;
+    }
+
+success:
+    result = true;
+
+bail:
+    dvmFreeVfyBasicBlocks(&vdata);
+    dvmFreeUninitInstanceMap(vdata.uninitMap);
+    free(vdata.insnFlags);
+    return result;
+}
+
+
+/*
+ * Verify an array data table.  "curOffset" is the offset of the
+ * fill-array-data instruction.
+ */
+static bool checkArrayData(const Method* meth, u4 curOffset)
+{
+    const u4 insnCount = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns + curOffset;
+    const u2* arrayData;
+    u4 valueCount, valueWidth, tableSize;
+    s4 offsetToArrayData;
+
+    assert(curOffset < insnCount);
+
+    /* make sure the start of the array data table is in range */
+    offsetToArrayData = insns[1] | (((s4)insns[2]) << 16);
+    if ((s4)curOffset + offsetToArrayData < 0 ||
+        curOffset + offsetToArrayData + 2 >= insnCount)
+    {
+        LOG_VFY("VFY: invalid array data start: at %d, data offset %d, "
+                "count %d",
+            curOffset, offsetToArrayData, insnCount);
+        return false;
+    }
+
+    /* offset to array data table is a relative branch-style offset */
+    arrayData = insns + offsetToArrayData;
+
+    /* make sure the table is 32-bit aligned */
+    if ((((u4) arrayData) & 0x03) != 0) {
+        LOG_VFY("VFY: unaligned array data table: at %d, data offset %d",
+            curOffset, offsetToArrayData);
+        return false;
+    }
+
+    valueWidth = arrayData[1];
+    valueCount = *(u4*)(&arrayData[2]);
+
+    tableSize = 4 + (valueWidth * valueCount + 1) / 2;
+
+    /* make sure the end of the switch is in range */
+    if (curOffset + offsetToArrayData + tableSize > insnCount) {
+        LOG_VFY("VFY: invalid array data end: at %d, data offset %d, end %d, "
+                "count %d",
+            curOffset, offsetToArrayData,
+            curOffset + offsetToArrayData + tableSize, insnCount);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Perform static checks on a "new-instance" instruction.  Specifically,
+ * make sure the class reference isn't for an array class.
+ *
+ * We don't need the actual class, just a pointer to the class name.
+ */
+static bool checkNewInstance(const DvmDex* pDvmDex, u4 idx)
+{
+    const char* classDescriptor;
+
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY("VFY: bad type index %d (max %d)",
+            idx, pDvmDex->pHeader->typeIdsSize);
+        return false;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
+    if (classDescriptor[0] != 'L') {
+        LOG_VFY("VFY: can't call new-instance on type '%s'",
+            classDescriptor);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Perform static checks on a "new-array" instruction.  Specifically, make
+ * sure they aren't creating an array of arrays that causes the number of
+ * dimensions to exceed 255.
+ */
+static bool checkNewArray(const DvmDex* pDvmDex, u4 idx)
+{
+    const char* classDescriptor;
+
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY("VFY: bad type index %d (max %d)",
+            idx, pDvmDex->pHeader->typeIdsSize);
+        return false;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
+
+    int bracketCount = 0;
+    const char* cp = classDescriptor;
+    while (*cp++ == '[')
+        bracketCount++;
+
+    if (bracketCount == 0) {
+        /* The given class must be an array type. */
+        LOG_VFY("VFY: can't new-array class '%s' (not an array)",
+            classDescriptor);
+        return false;
+    } else if (bracketCount > 255) {
+        /* It is illegal to create an array of more than 255 dimensions. */
+        LOG_VFY("VFY: can't new-array class '%s' (exceeds limit)",
+            classDescriptor);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Perform static checks on an instruction that takes a class constant.
+ * Ensure that the class index is in the valid range.
+ */
+static bool checkTypeIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY("VFY: bad type index %d (max %d)",
+            idx, pDvmDex->pHeader->typeIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Perform static checks on a field get or set instruction.  All we do
+ * here is ensure that the field index is in the valid range.
+ */
+static bool checkFieldIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->fieldIdsSize) {
+        LOG_VFY("VFY: bad field index %d (max %d)",
+            idx, pDvmDex->pHeader->fieldIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Perform static checks on a method invocation instruction.  All we do
+ * here is ensure that the method index is in the valid range.
+ */
+static bool checkMethodIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->methodIdsSize) {
+        LOG_VFY("VFY: bad method index %d (max %d)",
+            idx, pDvmDex->pHeader->methodIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Ensure that the string index is in the valid range.
+ */
+static bool checkStringIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->stringIdsSize) {
+        LOG_VFY("VFY: bad string index %d (max %d)",
+            idx, pDvmDex->pHeader->stringIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Ensure that the register index is valid for this method.
+ */
+static bool checkRegisterIndex(const Method* meth, u4 idx)
+{
+    if (idx >= meth->registersSize) {
+        LOG_VFY("VFY: register index out of range (%d >= %d)",
+            idx, meth->registersSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Ensure that the wide register index is valid for this method.
+ */
+static bool checkWideRegisterIndex(const Method* meth, u4 idx)
+{
+    if (idx+1 >= meth->registersSize) {
+        LOG_VFY("VFY: wide register index out of range (%d+1 >= %d)",
+            idx, meth->registersSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Check the register indices used in a "vararg" instruction, such as
+ * invoke-virtual or filled-new-array.
+ *
+ * vA holds word count (0-5), args[] have values.
+ *
+ * There are some tests we don't do here, e.g. we don't try to verify
+ * that invoking a method that takes a double is done with consecutive
+ * registers.  This requires parsing the target method signature, which
+ * we will be doing later on during the code flow analysis.
+ */
+static bool checkVarargRegs(const Method* meth,
+    const DecodedInstruction* pDecInsn)
+{
+    u2 registersSize = meth->registersSize;
+    unsigned int idx;
+
+    if (pDecInsn->vA > 5) {
+        LOG_VFY("VFY: invalid arg count (%d) in non-range invoke)",
+            pDecInsn->vA);
+        return false;
+    }
+
+    for (idx = 0; idx < pDecInsn->vA; idx++) {
+        if (pDecInsn->arg[idx] > registersSize) {
+            LOG_VFY("VFY: invalid reg index (%d) in non-range invoke (> %d)",
+                pDecInsn->arg[idx], registersSize);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Check the register indices used in a "vararg/range" instruction, such as
+ * invoke-virtual/range or filled-new-array/range.
+ *
+ * vA holds word count, vC holds index of first reg.
+ */
+static bool checkVarargRangeRegs(const Method* meth,
+    const DecodedInstruction* pDecInsn)
+{
+    u2 registersSize = meth->registersSize;
+
+    /*
+     * vA/vC are unsigned 8-bit/16-bit quantities for /range instructions,
+     * so there's no risk of integer overflow when adding them here.
+     */
+    if (pDecInsn->vA + pDecInsn->vC > registersSize) {
+        LOG_VFY("VFY: invalid reg index %d+%d in range invoke (> %d)",
+            pDecInsn->vA, pDecInsn->vC, registersSize);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Verify a switch table.  "curOffset" is the offset of the switch
+ * instruction.
+ *
+ * Updates "insnFlags", setting the "branch target" flag.
+ */
+static bool checkSwitchTargets(const Method* meth, InsnFlags* insnFlags,
+    u4 curOffset)
+{
+    const u4 insnCount = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns + curOffset;
+    const u2* switchInsns;
+    u2 expectedSignature;
+    u4 switchCount, tableSize;
+    s4 offsetToSwitch, offsetToKeys, offsetToTargets;
+    s4 offset, absOffset;
+    u4 targ;
+
+    assert(curOffset < insnCount);
+
+    /* make sure the start of the switch is in range */
+    offsetToSwitch = insns[1] | ((s4) insns[2]) << 16;
+    if ((s4) curOffset + offsetToSwitch < 0 ||
+        curOffset + offsetToSwitch + 2 >= insnCount)
+    {
+        LOG_VFY("VFY: invalid switch start: at %d, switch offset %d, "
+                "count %d",
+            curOffset, offsetToSwitch, insnCount);
+        return false;
+    }
+
+    /* offset to switch table is a relative branch-style offset */
+    switchInsns = insns + offsetToSwitch;
+
+    /* make sure the table is 32-bit aligned */
+    if ((((u4) switchInsns) & 0x03) != 0) {
+        LOG_VFY("VFY: unaligned switch table: at %d, switch offset %d",
+            curOffset, offsetToSwitch);
+        return false;
+    }
+
+    switchCount = switchInsns[1];
+
+    if ((*insns & 0xff) == OP_PACKED_SWITCH) {
+        /* 0=sig, 1=count, 2/3=firstKey */
+        offsetToTargets = 4;
+        offsetToKeys = -1;
+        expectedSignature = kPackedSwitchSignature;
+    } else {
+        /* 0=sig, 1=count, 2..count*2 = keys */
+        offsetToKeys = 2;
+        offsetToTargets = 2 + 2*switchCount;
+        expectedSignature = kSparseSwitchSignature;
+    }
+    tableSize = offsetToTargets + switchCount*2;
+
+    if (switchInsns[0] != expectedSignature) {
+        LOG_VFY("VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)",
+            switchInsns[0], expectedSignature);
+        return false;
+    }
+
+    /* make sure the end of the switch is in range */
+    if (curOffset + offsetToSwitch + tableSize > (u4) insnCount) {
+        LOG_VFY("VFY: invalid switch end: at %d, switch offset %d, end %d, "
+                "count %d",
+            curOffset, offsetToSwitch, curOffset + offsetToSwitch + tableSize,
+            insnCount);
+        return false;
+    }
+
+    /* for a sparse switch, verify the keys are in ascending order */
+    if (offsetToKeys > 0 && switchCount > 1) {
+        s4 lastKey;
+
+        lastKey = switchInsns[offsetToKeys] |
+                  (switchInsns[offsetToKeys+1] << 16);
+        for (targ = 1; targ < switchCount; targ++) {
+            s4 key = (s4) switchInsns[offsetToKeys + targ*2] |
+                    (s4) (switchInsns[offsetToKeys + targ*2 +1] << 16);
+            if (key <= lastKey) {
+                LOG_VFY("VFY: invalid packed switch: last key=%d, this=%d",
+                    lastKey, key);
+                return false;
+            }
+
+            lastKey = key;
+        }
+    }
+
+    /* verify each switch target */
+    for (targ = 0; targ < switchCount; targ++) {
+        offset = (s4) switchInsns[offsetToTargets + targ*2] |
+                (s4) (switchInsns[offsetToTargets + targ*2 +1] << 16);
+        absOffset = curOffset + offset;
+
+        if (absOffset < 0 || absOffset >= (s4)insnCount ||
+            !dvmInsnIsOpcode(insnFlags, absOffset))
+        {
+            LOG_VFY("VFY: invalid switch target %d (-> %#x) at %#x[%d]",
+                offset, absOffset, curOffset, targ);
+            return false;
+        }
+        dvmInsnSetBranchTarget(insnFlags, absOffset, true);
+    }
+
+    return true;
+}
+
+/*
+ * Verify that the target of a branch instruction is valid.
+ *
+ * We don't expect code to jump directly into an exception handler, but
+ * it's valid to do so as long as the target isn't a "move-exception"
+ * instruction.  We verify that in a later stage.
+ *
+ * The VM spec doesn't forbid an instruction from branching to itself,
+ * but the Dalvik spec declares that only certain instructions can do so.
+ *
+ * Updates "insnFlags", setting the "branch target" flag.
+ */
+static bool checkBranchTarget(const Method* meth, InsnFlags* insnFlags,
+    int curOffset, bool selfOkay)
+{
+    const int insnCount = dvmGetMethodInsnsSize(meth);
+    s4 offset, absOffset;
+    bool isConditional;
+
+    if (!dvmGetBranchOffset(meth, insnFlags, curOffset, &offset,
+            &isConditional))
+        return false;
+
+    if (!selfOkay && offset == 0) {
+        LOG_VFY_METH(meth, "VFY: branch offset of zero not allowed at %#x",
+            curOffset);
+        return false;
+    }
+
+    /*
+     * Check for 32-bit overflow.  This isn't strictly necessary if we can
+     * depend on the VM to have identical "wrap-around" behavior, but
+     * it's unwise to depend on that.
+     */
+    if (((s8) curOffset + (s8) offset) != (s8)(curOffset + offset)) {
+        LOG_VFY_METH(meth, "VFY: branch target overflow %#x +%d",
+            curOffset, offset);
+        return false;
+    }
+    absOffset = curOffset + offset;
+    if (absOffset < 0 || absOffset >= insnCount ||
+        !dvmInsnIsOpcode(insnFlags, absOffset))
+    {
+        LOG_VFY_METH(meth,
+            "VFY: invalid branch target %d (-> %#x) at %#x",
+            offset, absOffset, curOffset);
+        return false;
+    }
+    dvmInsnSetBranchTarget(insnFlags, absOffset, true);
+
+    return true;
+}
+
+
+/*
+ * Perform static verification on instructions.
+ *
+ * As a side effect, this sets the "branch target" flags in InsnFlags.
+ *
+ * "(CF)" items are handled during code-flow analysis.
+ *
+ * v3 4.10.1
+ * - target of each jump and branch instruction must be valid
+ * - targets of switch statements must be valid
+ * - operands referencing constant pool entries must be valid
+ * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
+ * - (new) verify operands of "quick" field ops
+ * - (CF) operands of method invocation instructions must be valid
+ * - (new) verify operands of "quick" method invoke ops
+ * - (CF) only invoke-direct can call a method starting with '<'
+ * - (CF) <clinit> must never be called explicitly
+ * - operands of instanceof, checkcast, new (and variants) must be valid
+ * - new-array[-type] limited to 255 dimensions
+ * - can't use "new" on an array class
+ * - (?) limit dimensions in multi-array creation
+ * - local variable load/store register values must be in valid range
+ *
+ * v3 4.11.1.2
+ * - branches must be within the bounds of the code array
+ * - targets of all control-flow instructions are the start of an instruction
+ * - register accesses fall within range of allocated registers
+ * - (N/A) access to constant pool must be of appropriate type
+ * - code does not end in the middle of an instruction
+ * - execution cannot fall off the end of the code
+ * - (earlier) for each exception handler, the "try" area must begin and
+ *   end at the start of an instruction (end can be at the end of the code)
+ * - (earlier) for each exception handler, the handler must start at a valid
+ *   instruction
+ */
+static bool verifyInstructions(VerifierData* vdata)
+{
+    const Method* meth = vdata->method;
+    const DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    InsnFlags* insnFlags = vdata->insnFlags;
+    const u2* insns = meth->insns;
+    unsigned int codeOffset;
+
+    /* the start of the method is a "branch target" */
+    dvmInsnSetBranchTarget(insnFlags, 0, true);
+
+    for (codeOffset = 0; codeOffset < vdata->insnsSize; /**/) {
+        /*
+         * Pull the instruction apart.
+         */
+        int width = dvmInsnGetWidth(insnFlags, codeOffset);
+        DecodedInstruction decInsn;
+        bool okay = true;
+
+        dexDecodeInstruction(meth->insns + codeOffset, &decInsn);
+
+        /*
+         * Check register, type, class, field, method, and string indices
+         * for out-of-range values.  Do additional checks on branch targets
+         * and some special cases like new-instance and new-array.
+         */
+        switch (decInsn.opcode) {
+        case OP_NOP:
+        case OP_RETURN_VOID:
+            /* nothing to check */
+            break;
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_OBJECT:
+        case OP_MOVE_EXCEPTION:
+        case OP_RETURN:
+        case OP_RETURN_OBJECT:
+        case OP_CONST_4:
+        case OP_CONST_16:
+        case OP_CONST:
+        case OP_CONST_HIGH16:
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_THROW:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            break;
+        case OP_MOVE_RESULT_WIDE:
+        case OP_RETURN_WIDE:
+        case OP_CONST_WIDE_16:
+        case OP_CONST_WIDE_32:
+        case OP_CONST_WIDE:
+        case OP_CONST_WIDE_HIGH16:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            break;
+        case OP_GOTO:
+        case OP_GOTO_16:
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
+            break;
+        case OP_GOTO_32:
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, true);
+            break;
+        case OP_MOVE:
+        case OP_MOVE_FROM16:
+        case OP_MOVE_16:
+        case OP_MOVE_OBJECT:
+        case OP_MOVE_OBJECT_FROM16:
+        case OP_MOVE_OBJECT_16:
+        case OP_ARRAY_LENGTH:
+        case OP_NEG_INT:
+        case OP_NOT_INT:
+        case OP_NEG_FLOAT:
+        case OP_INT_TO_FLOAT:
+        case OP_FLOAT_TO_INT:
+        case OP_INT_TO_BYTE:
+        case OP_INT_TO_CHAR:
+        case OP_INT_TO_SHORT:
+        case OP_ADD_INT_2ADDR:
+        case OP_SUB_INT_2ADDR:
+        case OP_MUL_INT_2ADDR:
+        case OP_DIV_INT_2ADDR:
+        case OP_REM_INT_2ADDR:
+        case OP_AND_INT_2ADDR:
+        case OP_OR_INT_2ADDR:
+        case OP_XOR_INT_2ADDR:
+        case OP_SHL_INT_2ADDR:
+        case OP_SHR_INT_2ADDR:
+        case OP_USHR_INT_2ADDR:
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_REM_FLOAT_2ADDR:
+        case OP_ADD_INT_LIT16:
+        case OP_RSUB_INT:
+        case OP_MUL_INT_LIT16:
+        case OP_DIV_INT_LIT16:
+        case OP_REM_INT_LIT16:
+        case OP_AND_INT_LIT16:
+        case OP_OR_INT_LIT16:
+        case OP_XOR_INT_LIT16:
+        case OP_ADD_INT_LIT8:
+        case OP_RSUB_INT_LIT8:
+        case OP_MUL_INT_LIT8:
+        case OP_DIV_INT_LIT8:
+        case OP_REM_INT_LIT8:
+        case OP_AND_INT_LIT8:
+        case OP_OR_INT_LIT8:
+        case OP_XOR_INT_LIT8:
+        case OP_SHL_INT_LIT8:
+        case OP_SHR_INT_LIT8:
+        case OP_USHR_INT_LIT8:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_INT_TO_LONG:
+        case OP_INT_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_FLOAT_TO_DOUBLE:
+        case OP_SHL_LONG_2ADDR:
+        case OP_SHR_LONG_2ADDR:
+        case OP_USHR_LONG_2ADDR:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_LONG_TO_INT:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_INT:
+        case OP_DOUBLE_TO_FLOAT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_MOVE_WIDE:
+        case OP_MOVE_WIDE_FROM16:
+        case OP_MOVE_WIDE_16:
+        case OP_DOUBLE_TO_LONG:
+        case OP_LONG_TO_DOUBLE:
+        case OP_NEG_DOUBLE:
+        case OP_NEG_LONG:
+        case OP_NOT_LONG:
+        case OP_ADD_LONG_2ADDR:
+        case OP_SUB_LONG_2ADDR:
+        case OP_MUL_LONG_2ADDR:
+        case OP_DIV_LONG_2ADDR:
+        case OP_REM_LONG_2ADDR:
+        case OP_AND_LONG_2ADDR:
+        case OP_OR_LONG_2ADDR:
+        case OP_XOR_LONG_2ADDR:
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE_2ADDR:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_CONST_STRING:
+        case OP_CONST_STRING_JUMBO:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkStringIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_CONST_CLASS:
+        case OP_CHECK_CAST:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_INSTANCE_OF:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkTypeIndex(pDvmDex, decInsn.vC);
+            break;
+        case OP_NEW_INSTANCE:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkNewInstance(pDvmDex, decInsn.vB);
+            break;
+        case OP_NEW_ARRAY:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkNewArray(pDvmDex, decInsn.vC);
+            break;
+        case OP_FILL_ARRAY_DATA:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkArrayData(meth, codeOffset);
+            break;
+        case OP_PACKED_SWITCH:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkSwitchTargets(meth, insnFlags, codeOffset);
+            break;
+        case OP_SPARSE_SWITCH:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkSwitchTargets(meth, insnFlags, codeOffset);
+            break;
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_AGET:
+        case OP_AGET_OBJECT:
+        case OP_AGET_BOOLEAN:
+        case OP_AGET_BYTE:
+        case OP_AGET_CHAR:
+        case OP_AGET_SHORT:
+        case OP_APUT:
+        case OP_APUT_OBJECT:
+        case OP_APUT_BOOLEAN:
+        case OP_APUT_BYTE:
+        case OP_APUT_CHAR:
+        case OP_APUT_SHORT:
+        case OP_ADD_INT:
+        case OP_SUB_INT:
+        case OP_MUL_INT:
+        case OP_DIV_INT:
+        case OP_REM_INT:
+        case OP_AND_INT:
+        case OP_OR_INT:
+        case OP_XOR_INT:
+        case OP_SHL_INT:
+        case OP_SHR_INT:
+        case OP_USHR_INT:
+        case OP_ADD_FLOAT:
+        case OP_SUB_FLOAT:
+        case OP_MUL_FLOAT:
+        case OP_DIV_FLOAT:
+        case OP_REM_FLOAT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_AGET_WIDE:
+        case OP_APUT_WIDE:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+        case OP_CMP_LONG:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            okay &= checkWideRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_ADD_DOUBLE:
+        case OP_SUB_DOUBLE:
+        case OP_MUL_DOUBLE:
+        case OP_DIV_DOUBLE:
+        case OP_REM_DOUBLE:
+        case OP_ADD_LONG:
+        case OP_SUB_LONG:
+        case OP_MUL_LONG:
+        case OP_DIV_LONG:
+        case OP_REM_LONG:
+        case OP_AND_LONG:
+        case OP_OR_LONG:
+        case OP_XOR_LONG:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            okay &= checkWideRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_SHL_LONG:
+        case OP_SHR_LONG:
+        case OP_USHR_LONG:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            okay &= checkRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_IF_EQ:
+        case OP_IF_NE:
+        case OP_IF_LT:
+        case OP_IF_GE:
+        case OP_IF_GT:
+        case OP_IF_LE:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
+            break;
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
+            break;
+        case OP_IGET:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+        case OP_IPUT:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vC);
+            break;
+        case OP_IGET_WIDE:
+        case OP_IPUT_WIDE:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vC);
+            break;
+        case OP_SGET:
+        case OP_SGET_OBJECT:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+        case OP_SPUT:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_SGET_WIDE:
+        case OP_SPUT_WIDE:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_FILLED_NEW_ARRAY:
+            /* decoder uses B, not C, for type ref */
+            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRegs(meth, &decInsn);
+            break;
+        case OP_FILLED_NEW_ARRAY_RANGE:
+            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRangeRegs(meth, &decInsn);
+            break;
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_INTERFACE:
+            /* decoder uses B, not C, for type ref */
+            okay &= checkMethodIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRegs(meth, &decInsn);
+            break;
+        case OP_INVOKE_VIRTUAL_RANGE:
+        case OP_INVOKE_SUPER_RANGE:
+        case OP_INVOKE_DIRECT_RANGE:
+        case OP_INVOKE_STATIC_RANGE:
+        case OP_INVOKE_INTERFACE_RANGE:
+            okay &= checkMethodIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRangeRegs(meth, &decInsn);
+            break;
+
+        /* verifier/optimizer output; we should never see these */
+        case OP_IGET_VOLATILE:
+        case OP_IPUT_VOLATILE:
+        case OP_SGET_VOLATILE:
+        case OP_SPUT_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_IGET_WIDE_VOLATILE:
+        case OP_IPUT_WIDE_VOLATILE:
+        case OP_SGET_WIDE_VOLATILE:
+        case OP_SPUT_WIDE_VOLATILE:
+        case OP_BREAKPOINT:
+        case OP_THROW_VERIFICATION_ERROR:
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+        case OP_INVOKE_OBJECT_INIT_RANGE:
+        case OP_RETURN_VOID_BARRIER:
+        case OP_IGET_QUICK:
+        case OP_IGET_WIDE_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+        case OP_IPUT_QUICK:
+        case OP_IPUT_WIDE_QUICK:
+        case OP_IPUT_OBJECT_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE:
+        case OP_UNUSED_3E:
+        case OP_UNUSED_3F:
+        case OP_UNUSED_40:
+        case OP_UNUSED_41:
+        case OP_UNUSED_42:
+        case OP_UNUSED_43:
+        case OP_UNUSED_73:
+        case OP_UNUSED_79:
+        case OP_UNUSED_7A:
+        case OP_UNUSED_FF:
+            ALOGE("VFY: unexpected opcode %04x", decInsn.opcode);
+            okay = false;
+            break;
+
+        /*
+         * DO NOT add a "default" clause here.  Without it the compiler will
+         * complain if an instruction is missing (which is desirable).
+         */
+        }
+
+        if (!okay) {
+            LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x",
+                decInsn.opcode, codeOffset);
+            return false;
+        }
+
+        OpcodeFlags opFlags = dexGetFlagsFromOpcode(decInsn.opcode);
+        if ((opFlags & VERIFY_GC_INST_MASK) != 0) {
+            /*
+             * This instruction is a GC point.  If space is a concern,
+             * the set of GC points could be reduced by eliminating
+             * foward branches.
+             *
+             * TODO: we could also scan the targets of a "switch" statement,
+             * and if none of them branch backward we could ignore that
+             * instruction as well.
+             */
+            dvmInsnSetGcPoint(insnFlags, codeOffset, true);
+        }
+
+        assert(width > 0);
+        codeOffset += width;
+        insns += width;
+    }
+
+    /* make sure the last instruction ends at the end of the insn area */
+    if (codeOffset != vdata->insnsSize) {
+        LOG_VFY_METH(meth,
+            "VFY: code did not end when expected (end at %d, count %d)",
+            codeOffset, vdata->insnsSize);
+        return false;
+    }
+
+    return true;
+}
diff --git a/vm/analysis/DexVerify.h b/vm/analysis/DexVerify.h
new file mode 100644
index 0000000..4487720
--- /dev/null
+++ b/vm/analysis/DexVerify.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik classfile verification.
+ */
+#ifndef DALVIK_DEXVERIFY_H_
+#define DALVIK_DEXVERIFY_H_
+
+/*
+ * Global verification mode.  These must be in order from least verification
+ * to most.  If we're using "exact GC", we may need to perform some of
+ * the verification steps anyway.
+ */
+enum DexClassVerifyMode {
+    VERIFY_MODE_UNKNOWN = 0,
+    VERIFY_MODE_NONE,
+    VERIFY_MODE_REMOTE,
+    VERIFY_MODE_ALL
+};
+
+/* some verifier counters, for debugging */
+struct VerifierStats {
+    size_t methodsExamined;    /* number of methods examined */
+    size_t monEnterMethods;    /* number of methods with monitor-enter */
+    size_t instrsExamined;     /* incr on first visit of instruction */
+    size_t instrsReexamined;   /* incr on each repeat visit of instruction */
+    size_t copyRegCount;       /* calls from updateRegisters->copyRegisters */
+    size_t mergeRegCount;      /* calls from updateRegisters->merge */
+    size_t mergeRegChanged;    /* calls from updateRegisters->merge, changed */
+    size_t uninitSearches;     /* times we've had to search the uninit table */
+    size_t biggestAlloc;       /* largest RegisterLine table alloc */
+};
+
+/*
+ * Certain types of instructions can be GC points.  To support precise
+ * GC, all such instructions must export the PC in the interpreter,
+ * or the GC won't be able to identify the current PC for the thread.
+ */
+#define VERIFY_GC_INST_MASK (kInstrCanBranch | kInstrCanSwitch |\
+                             kInstrCanThrow | kInstrCanReturn)
+
+/*
+ * Verify a single class.
+ */
+bool dvmVerifyClass(ClassObject* clazz);
+
+/*
+ * Release the storage associated with a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap);
+
+#endif  // DALVIK_DEXVERIFY_H_
diff --git a/vm/analysis/Liveness.cpp b/vm/analysis/Liveness.cpp
new file mode 100644
index 0000000..18d5a15
--- /dev/null
+++ b/vm/analysis/Liveness.cpp
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Liveness analysis for Dalvik bytecode.
+ */
+#include "Dalvik.h"
+#include "analysis/Liveness.h"
+#include "analysis/CodeVerify.h"
+
+static bool processInstruction(VerifierData* vdata, u4 curIdx,
+    BitVector* workBits);
+static bool markDebugLocals(VerifierData* vdata);
+static void dumpLiveState(const VerifierData* vdata, u4 curIdx,
+    const BitVector* workBits);
+
+
+/*
+ * Create a table of instruction widths that indicate the width of the
+ * *previous* instruction.  The values are copied from the width table
+ * in "vdata", not derived from the instruction stream.
+ *
+ * Caller must free the return value.
+ */
+static InstructionWidth* createBackwardWidthTable(VerifierData* vdata)
+{
+    InstructionWidth* widths;
+
+    widths = (InstructionWidth*)
+            calloc(vdata->insnsSize, sizeof(InstructionWidth));
+    if (widths == NULL)
+        return NULL;
+
+    u4 insnWidth = 0;
+    for (u4 idx = 0; idx < vdata->insnsSize; ) {
+        widths[idx] = insnWidth;
+        insnWidth = dvmInsnGetWidth(vdata->insnFlags, idx);
+        idx += insnWidth;
+    }
+
+    return widths;
+}
+
+/*
+ * Compute the "liveness" of every register at all GC points.
+ */
+bool dvmComputeLiveness(VerifierData* vdata)
+{
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    InstructionWidth* backwardWidth;
+    VfyBasicBlock* startGuess = NULL;
+    BitVector* workBits;
+    bool result = false;
+
+    bool verbose = false; //= dvmWantVerboseVerification(vdata->method);
+    if (verbose) {
+        const Method* meth = vdata->method;
+        ALOGI("Computing liveness for %s.%s:%s",
+            meth->clazz->descriptor, meth->name, meth->shorty);
+    }
+
+    assert(vdata->registerLines != NULL);
+
+    backwardWidth = createBackwardWidthTable(vdata);
+    if (backwardWidth == NULL)
+        goto bail;
+
+    /*
+     * Allocate space for intra-block work set.  Does not include space
+     * for method result "registers", which aren't visible to the GC.
+     * (They would be made live by move-result and then die on the
+     * instruction immediately before it.)
+     */
+    workBits = dvmAllocBitVector(vdata->insnRegCount, false);
+    if (workBits == NULL)
+        goto bail;
+
+    /*
+     * We continue until all blocks have been visited, and no block
+     * requires further attention ("visited" is set and "changed" is
+     * clear).
+     *
+     * TODO: consider creating a "dense" array of basic blocks to make
+     * the walking faster.
+     */
+    for (int iter = 0;;) {
+        VfyBasicBlock* workBlock = NULL;
+
+        if (iter++ > 100000) {
+            LOG_VFY_METH(vdata->method, "oh dear");
+            dvmAbort();
+        }
+
+        /*
+         * If a block is marked "changed", we stop and handle it.  If it
+         * just hasn't been visited yet, we remember it but keep searching
+         * for one that has been changed.
+         *
+         * The thought here is that this is more likely to let us work
+         * from end to start, which reduces the amount of re-evaluation
+         * required (both by using "changed" as a work list, and by picking
+         * un-visited blocks from the tail end of the method).
+         */
+        if (startGuess != NULL) {
+            assert(startGuess->changed);
+            workBlock = startGuess;
+        } else {
+            for (u4 idx = 0; idx < vdata->insnsSize; idx++) {
+                VfyBasicBlock* block = vdata->basicBlocks[idx];
+                if (block == NULL)
+                    continue;
+
+                if (block->changed) {
+                    workBlock = block;
+                    break;
+                } else if (!block->visited) {
+                    workBlock = block;
+                }
+            }
+        }
+
+        if (workBlock == NULL) {
+            /* all done */
+            break;
+        }
+
+        assert(workBlock->changed || !workBlock->visited);
+        startGuess = NULL;
+
+        /*
+         * Load work bits.  These represent the liveness of registers
+         * after the last instruction in the block has finished executing.
+         */
+        assert(workBlock->liveRegs != NULL);
+        dvmCopyBitVector(workBits, workBlock->liveRegs);
+        if (verbose) {
+            ALOGI("Loaded work bits from last=0x%04x", workBlock->lastAddr);
+            dumpLiveState(vdata, 0xfffd, workBlock->liveRegs);
+            dumpLiveState(vdata, 0xffff, workBits);
+        }
+
+        /*
+         * Process a single basic block.
+         *
+         * If this instruction is a GC point, we want to save the result
+         * in the RegisterLine.
+         *
+         * We don't break basic blocks on every GC point -- in particular,
+         * instructions that might throw but have no "try" block don't
+         * end a basic block -- so there could be more than one GC point
+         * in a given basic block.
+         *
+         * We could change this, but it turns out to be not all that useful.
+         * At first glance it appears that we could share the liveness bit
+         * vector between the basic block struct and the register line,
+         * but the basic block needs to reflect the state *after* the
+         * instruction has finished, while the GC points need to describe
+         * the state before the instruction starts.
+         */
+        u4 curIdx = workBlock->lastAddr;
+        while (true) {
+            if (!processInstruction(vdata, curIdx, workBits))
+                goto bail;
+
+            if (verbose) {
+                dumpLiveState(vdata, curIdx + 0x8000, workBits);
+            }
+
+            if (dvmInsnIsGcPoint(insnFlags, curIdx)) {
+                BitVector* lineBits = vdata->registerLines[curIdx].liveRegs;
+                if (lineBits == NULL) {
+                    lineBits = vdata->registerLines[curIdx].liveRegs =
+                        dvmAllocBitVector(vdata->insnRegCount, false);
+                }
+                dvmCopyBitVector(lineBits, workBits);
+            }
+
+            if (curIdx == workBlock->firstAddr)
+                break;
+            assert(curIdx >= backwardWidth[curIdx]);
+            curIdx -= backwardWidth[curIdx];
+        }
+
+        workBlock->visited = true;
+        workBlock->changed = false;
+
+        if (verbose) {
+            dumpLiveState(vdata, curIdx, workBits);
+        }
+
+        /*
+         * Merge changes to all predecessors.  If the new bits don't match
+         * the old bits, set the "changed" flag.
+         */
+        PointerSet* preds = workBlock->predecessors;
+        size_t numPreds = dvmPointerSetGetCount(preds);
+        unsigned int predIdx;
+
+        for (predIdx = 0; predIdx < numPreds; predIdx++) {
+            VfyBasicBlock* pred =
+                    (VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx);
+
+            pred->changed = dvmCheckMergeBitVectors(pred->liveRegs, workBits);
+            if (verbose) {
+                ALOGI("merging cur=%04x into pred last=%04x (ch=%d)",
+                    curIdx, pred->lastAddr, pred->changed);
+                dumpLiveState(vdata, 0xfffa, pred->liveRegs);
+                dumpLiveState(vdata, 0xfffb, workBits);
+            }
+
+            /*
+             * We want to set the "changed" flag on unvisited predecessors
+             * as a way of guiding the verifier through basic blocks in
+             * a reasonable order.  We can't count on variable liveness
+             * changing, so we force "changed" to true even if it hasn't.
+             */
+            if (!pred->visited)
+                pred->changed = true;
+
+            /*
+             * Keep track of one of the changed blocks so we can start
+             * there instead of having to scan through the list.
+             */
+            if (pred->changed)
+                startGuess = pred;
+        }
+    }
+
+#ifndef NDEBUG
+    /*
+     * Sanity check: verify that all GC point register lines have a
+     * liveness bit vector allocated.  Also, we're not expecting non-GC
+     * points to have them.
+     */
+    u4 checkIdx;
+    for (checkIdx = 0; checkIdx < vdata->insnsSize; ) {
+        if (dvmInsnIsGcPoint(insnFlags, checkIdx)) {
+            if (vdata->registerLines[checkIdx].liveRegs == NULL) {
+                LOG_VFY_METH(vdata->method,
+                    "GLITCH: no liveRegs for GC point 0x%04x", checkIdx);
+                dvmAbort();
+            }
+        } else if (vdata->registerLines[checkIdx].liveRegs != NULL) {
+            LOG_VFY_METH(vdata->method,
+                "GLITCH: liveRegs for non-GC point 0x%04x", checkIdx);
+            dvmAbort();
+        }
+        u4 insnWidth = dvmInsnGetWidth(insnFlags, checkIdx);
+        checkIdx += insnWidth;
+    }
+#endif
+
+    /*
+     * Factor in the debug info, if any.
+     */
+    if (!markDebugLocals(vdata))
+        goto bail;
+
+    result = true;
+
+bail:
+    free(backwardWidth);
+    return result;
+}
+
+
+/*
+ * Add a register to the LIVE set.
+ */
+static inline void GEN(BitVector* workBits, u4 regIndex)
+{
+    dvmSetBit(workBits, regIndex);
+}
+
+/*
+ * Add a register pair to the LIVE set.
+ */
+static inline void GENW(BitVector* workBits, u4 regIndex)
+{
+    dvmSetBit(workBits, regIndex);
+    dvmSetBit(workBits, regIndex+1);
+}
+
+/*
+ * Remove a register from the LIVE set.
+ */
+static inline void KILL(BitVector* workBits, u4 regIndex)
+{
+    dvmClearBit(workBits, regIndex);
+}
+
+/*
+ * Remove a register pair from the LIVE set.
+ */
+static inline void KILLW(BitVector* workBits, u4 regIndex)
+{
+    dvmClearBit(workBits, regIndex);
+    dvmClearBit(workBits, regIndex+1);
+}
+
+/*
+ * Process a single instruction.
+ *
+ * Returns "false" if something goes fatally wrong.
+ */
+static bool processInstruction(VerifierData* vdata, u4 insnIdx,
+    BitVector* workBits)
+{
+    const Method* meth = vdata->method;
+    const u2* insns = meth->insns + insnIdx;
+    DecodedInstruction decInsn;
+
+    dexDecodeInstruction(insns, &decInsn);
+
+    /*
+     * Add registers to the "GEN" or "KILL" sets.  We want to do KILL
+     * before GEN to handle cases where the source and destination
+     * register is the same.
+     */
+    switch (decInsn.opcode) {
+    case OP_NOP:
+    case OP_RETURN_VOID:
+    case OP_GOTO:
+    case OP_GOTO_16:
+    case OP_GOTO_32:
+        /* no registers are used */
+        break;
+
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+    case OP_MONITOR_ENTER:
+    case OP_MONITOR_EXIT:
+    case OP_CHECK_CAST:
+    case OP_THROW:
+    case OP_PACKED_SWITCH:
+    case OP_SPARSE_SWITCH:
+    case OP_FILL_ARRAY_DATA:
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+    case OP_SPUT:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+    case OP_SPUT_OBJECT:
+        /* action <- vA */
+        GEN(workBits, decInsn.vA);
+        break;
+
+    case OP_RETURN_WIDE:
+    case OP_SPUT_WIDE:
+        /* action <- vA(wide) */
+        GENW(workBits, decInsn.vA);
+        break;
+
+    case OP_IF_EQ:
+    case OP_IF_NE:
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+    case OP_IPUT:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+    case OP_IPUT_OBJECT:
+        /* action <- vA, vB */
+        GEN(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_IPUT_WIDE:
+        /* action <- vA(wide), vB */
+        GENW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_APUT:
+    case OP_APUT_BOOLEAN:
+    case OP_APUT_BYTE:
+    case OP_APUT_CHAR:
+    case OP_APUT_SHORT:
+    case OP_APUT_OBJECT:
+        /* action <- vA, vB, vC */
+        GEN(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_APUT_WIDE:
+        /* action <- vA(wide), vB, vC */
+        GENW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_FILLED_NEW_ARRAY:
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_INTERFACE:
+        /* action <- vararg */
+        {
+            unsigned int idx;
+            for (idx = 0; idx < decInsn.vA; idx++) {
+                GEN(workBits, decInsn.arg[idx]);
+            }
+        }
+        break;
+
+    case OP_FILLED_NEW_ARRAY_RANGE:
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        /* action <- vararg/range */
+        {
+            unsigned int idx;
+            for (idx = 0; idx < decInsn.vA; idx++) {
+                GEN(workBits, decInsn.vC + idx);
+            }
+        }
+        break;
+
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_WIDE:
+    case OP_MOVE_RESULT_OBJECT:
+    case OP_MOVE_EXCEPTION:
+    case OP_CONST_4:
+    case OP_CONST_16:
+    case OP_CONST:
+    case OP_CONST_HIGH16:
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+    case OP_CONST_CLASS:
+    case OP_NEW_INSTANCE:
+    case OP_SGET:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+    case OP_SGET_OBJECT:
+        /* vA <- value */
+        KILL(workBits, decInsn.vA);
+        break;
+
+    case OP_CONST_WIDE_16:
+    case OP_CONST_WIDE_32:
+    case OP_CONST_WIDE:
+    case OP_CONST_WIDE_HIGH16:
+    case OP_SGET_WIDE:
+        /* vA(wide) <- value */
+        KILLW(workBits, decInsn.vA);
+        break;
+
+    case OP_MOVE:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_OBJECT_16:
+    case OP_INSTANCE_OF:
+    case OP_ARRAY_LENGTH:
+    case OP_NEW_ARRAY:
+    case OP_IGET:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_OBJECT:
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_NEG_FLOAT:
+    case OP_INT_TO_FLOAT:
+    case OP_FLOAT_TO_INT:
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+    case OP_ADD_INT_LIT8:
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+        /* vA <- vB */
+        KILL(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_IGET_WIDE:
+    case OP_INT_TO_LONG:
+    case OP_INT_TO_DOUBLE:
+    case OP_FLOAT_TO_LONG:
+    case OP_FLOAT_TO_DOUBLE:
+        /* vA(wide) <- vB */
+        KILLW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_LONG_TO_INT:
+    case OP_LONG_TO_FLOAT:
+    case OP_DOUBLE_TO_INT:
+    case OP_DOUBLE_TO_FLOAT:
+        /* vA <- vB(wide) */
+        KILL(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        break;
+
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_NEG_DOUBLE:
+    case OP_LONG_TO_DOUBLE:
+    case OP_DOUBLE_TO_LONG:
+        /* vA(wide) <- vB(wide) */
+        KILLW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        break;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+    case OP_AGET:
+    case OP_AGET_BOOLEAN:
+    case OP_AGET_BYTE:
+    case OP_AGET_CHAR:
+    case OP_AGET_SHORT:
+    case OP_AGET_OBJECT:
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_REM_INT:
+    case OP_DIV_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+        /* vA <- vB, vC */
+        KILL(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_AGET_WIDE:
+        /* vA(wide) <- vB, vC */
+        KILLW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+    case OP_CMP_LONG:
+        /* vA <- vB(wide), vC(wide) */
+        KILL(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        GENW(workBits, decInsn.vC);
+        break;
+
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+        /* vA(wide) <- vB(wide), vC */
+        KILLW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_MUL_LONG:
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        /* vA(wide) <- vB(wide), vC(wide) */
+        KILLW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        GENW(workBits, decInsn.vC);
+        break;
+
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+    case OP_DIV_INT_2ADDR:
+        /* vA <- vA, vB */
+        /* KILL(workBits, decInsn.vA); */
+        GEN(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        /* vA(wide) <- vA(wide), vB */
+        /* KILLW(workBits, decInsn.vA); */
+        GENW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_MUL_LONG_2ADDR:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_REM_FLOAT_2ADDR:
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_DOUBLE_2ADDR:
+    case OP_REM_DOUBLE_2ADDR:
+        /* vA(wide) <- vA(wide), vB(wide) */
+        /* KILLW(workBits, decInsn.vA); */
+        GENW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        break;
+
+    /* we will only see this if liveness analysis is done after general vfy */
+    case OP_THROW_VERIFICATION_ERROR:
+        /* no registers used */
+        break;
+
+    /* quickened instructions, not expected to appear */
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        /* fall through to failure */
+
+    /* correctness fixes, not expected to appear */
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+    case OP_RETURN_VOID_BARRIER:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+        /* fall through to failure */
+
+    /* these should never appear during verification */
+    case OP_UNUSED_3E:
+    case OP_UNUSED_3F:
+    case OP_UNUSED_40:
+    case OP_UNUSED_41:
+    case OP_UNUSED_42:
+    case OP_UNUSED_43:
+    case OP_UNUSED_73:
+    case OP_UNUSED_79:
+    case OP_UNUSED_7A:
+    case OP_BREAKPOINT:
+    case OP_UNUSED_FF:
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * This is a dexDecodeDebugInfo callback, used by markDebugLocals().
+ */
+static void markLocalsCb(void* ctxt, u2 reg, u4 startAddress, u4 endAddress,
+    const char* name, const char* descriptor, const char* signature)
+{
+    VerifierData* vdata = (VerifierData*) ctxt;
+    bool verbose = dvmWantVerboseVerification(vdata->method);
+
+    if (verbose) {
+        ALOGI("%04x-%04x %2d (%s %s)",
+            startAddress, endAddress, reg, name, descriptor);
+    }
+
+    bool wide = (descriptor[0] == 'D' || descriptor[0] == 'J');
+    assert(reg <= vdata->insnRegCount + (wide ? 1 : 0));
+
+    /*
+     * Set the bit in all GC point instructions in the range
+     * [startAddress, endAddress).
+     */
+    unsigned int idx;
+    for (idx = startAddress; idx < endAddress; idx++) {
+        BitVector* liveRegs = vdata->registerLines[idx].liveRegs;
+        if (liveRegs != NULL) {
+            if (wide) {
+                GENW(liveRegs, reg);
+            } else {
+                GEN(liveRegs, reg);
+            }
+        }
+    }
+}
+
+/*
+ * Mark all debugger-visible locals as live.
+ *
+ * The "locals" table describes the positions of the various locals in the
+ * stack frame based on the current execution address.  If the debugger
+ * wants to display one, it issues a request by "slot number".  We need
+ * to ensure that references in stack slots that might be queried by the
+ * debugger aren't GCed.
+ *
+ * (If the GC had some way to mark the slot as invalid we wouldn't have
+ * to do this.  We could also have the debugger interface check the
+ * register map and simply refuse to return a "dead" value, but that's
+ * potentially confusing since the referred-to object might actually be
+ * alive, and being able to see it without having to hunt around for a
+ * "live" stack frame is useful.)
+ */
+static bool markDebugLocals(VerifierData* vdata)
+{
+    const Method* meth = vdata->method;
+
+    dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile, dvmGetMethodCode(meth),
+        meth->clazz->descriptor, meth->prototype.protoIdx, meth->accessFlags,
+        NULL, markLocalsCb, vdata);
+
+    return true;
+}
+
+
+/*
+ * Dump the liveness bits to the log.
+ *
+ * "curIdx" is for display only.
+ */
+static void dumpLiveState(const VerifierData* vdata, u4 curIdx,
+    const BitVector* workBits)
+{
+    u4 insnRegCount = vdata->insnRegCount;
+    size_t regCharSize = insnRegCount + (insnRegCount-1)/4 + 2 +1;
+    char regChars[regCharSize +1];
+    unsigned int idx;
+
+    memset(regChars, ' ', regCharSize);
+    regChars[0] = '[';
+    if (insnRegCount == 0)
+        regChars[1] = ']';
+    else
+        regChars[1 + (insnRegCount-1) + (insnRegCount-1)/4 +1] = ']';
+    regChars[regCharSize] = '\0';
+
+    for (idx = 0; idx < insnRegCount; idx++) {
+        char ch = dvmIsBitSet(workBits, idx) ? '+' : '-';
+        regChars[1 + idx + (idx/4)] = ch;
+    }
+
+    ALOGI("0x%04x %s", curIdx, regChars);
+}
diff --git a/vm/analysis/Liveness.h b/vm/analysis/Liveness.h
new file mode 100644
index 0000000..5f4acfe
--- /dev/null
+++ b/vm/analysis/Liveness.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Liveness analysis.
+ */
+#ifndef DALVIK_LIVENESS_H_
+#define DALVIK_LIVENESS_H_
+
+struct VerifierData;
+
+bool dvmComputeLiveness(struct VerifierData* vdata);
+
+#endif  // DALVIK_LIVENESS_H_
diff --git a/vm/analysis/Optimize.cpp b/vm/analysis/Optimize.cpp
new file mode 100644
index 0000000..953924e
--- /dev/null
+++ b/vm/analysis/Optimize.cpp
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Perform some simple bytecode optimizations, chiefly "quickening" of
+ * opcodes.
+ */
+#include "Dalvik.h"
+#include "libdex/InstrUtils.h"
+#include "Optimize.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+
+/*
+ * Virtual/direct calls to "method" are replaced with an execute-inline
+ * instruction with index "idx".
+ */
+struct InlineSub {
+    Method* method;
+    int     inlineIdx;
+};
+
+
+/* fwd */
+static void optimizeMethod(Method* method, bool essentialOnly);
+static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc,
+    Opcode volatileOpc);
+static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc);
+static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc);
+static bool rewriteInvokeObjectInit(Method* method, u2* insns);
+static bool rewriteExecuteInline(Method* method, u2* insns,
+    MethodType methodType);
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+    MethodType methodType);
+static void rewriteReturnVoid(Method* method, u2* insns);
+static bool needsReturnBarrier(Method* method);
+
+
+/*
+ * Create a table of inline substitutions.  Sets gDvm.inlineSubs.
+ *
+ * TODO: this is currently just a linear array.  We will want to put this
+ * into a hash table as the list size increases.
+ */
+bool dvmCreateInlineSubsTable()
+{
+    const InlineOperation* ops = dvmGetInlineOpsTable();
+    const int count = dvmGetInlineOpsTableLength();
+    InlineSub* table;
+    int i, tableIndex;
+
+    assert(gDvm.inlineSubs == NULL);
+
+    /*
+     * One slot per entry, plus an end-of-list marker.
+     */
+    table = (InlineSub*) calloc(count + 1, sizeof(InlineSub));
+
+    tableIndex = 0;
+    for (i = 0; i < count; i++) {
+        Method* method = dvmFindInlinableMethod(ops[i].classDescriptor,
+            ops[i].methodName, ops[i].methodSignature);
+        if (method == NULL) {
+            /*
+             * Not expected.  We only use this for key methods in core
+             * classes, so we should always be able to find them.
+             */
+            ALOGE("Unable to find method for inlining: %s.%s:%s",
+                ops[i].classDescriptor, ops[i].methodName,
+                ops[i].methodSignature);
+            return false;
+        }
+
+        table[tableIndex].method = method;
+        table[tableIndex].inlineIdx = i;
+        tableIndex++;
+    }
+
+    /* mark end of table */
+    table[tableIndex].method = NULL;
+
+    gDvm.inlineSubs = table;
+    return true;
+}
+
+/*
+ * Release inline sub data structure.
+ */
+void dvmFreeInlineSubsTable()
+{
+    free(gDvm.inlineSubs);
+    gDvm.inlineSubs = NULL;
+}
+
+
+/*
+ * Optimize the specified class.
+ *
+ * If "essentialOnly" is true, we only do essential optimizations.  For
+ * example, accesses to volatile 64-bit fields must be replaced with
+ * "-wide-volatile" instructions or the program could behave incorrectly.
+ * (Skipping non-essential optimizations makes us a little bit faster, and
+ * more importantly avoids dirtying DEX pages.)
+ */
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly)
+{
+    int i;
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        optimizeMethod(&clazz->directMethods[i], essentialOnly);
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        optimizeMethod(&clazz->virtualMethods[i], essentialOnly);
+    }
+}
+
+/*
+ * Optimize instructions in a method.
+ *
+ * This does a single pass through the code, examining each instruction.
+ *
+ * This is not expected to fail if the class was successfully verified.
+ * The only significant failure modes on unverified code occur when an
+ * "essential" update fails, but we can't generally identify those: if we
+ * can't look up a field, we can't know if the field access was supposed
+ * to be handled as volatile.
+ *
+ * Instead, we give it our best effort, and hope for the best.  For 100%
+ * reliability, only optimize a class after verification succeeds.
+ */
+static void optimizeMethod(Method* method, bool essentialOnly)
+{
+    bool needRetBar, forSmp;
+    u4 insnsSize;
+    u2* insns;
+
+    if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
+        return;
+
+    forSmp = gDvm.dexOptForSmp;
+    needRetBar = needsReturnBarrier(method);
+
+    insns = (u2*) method->insns;
+    assert(insns != NULL);
+    insnsSize = dvmGetMethodInsnsSize(method);
+
+    while (insnsSize > 0) {
+        Opcode opc, quickOpc, volatileOpc;
+        size_t width;
+        bool matched = true;
+
+        opc = dexOpcodeFromCodeUnit(*insns);
+        width = dexGetWidthFromInstruction(insns);
+        volatileOpc = OP_NOP;
+
+        /*
+         * Each instruction may have:
+         * - "volatile" replacement
+         *   - may be essential or essential-on-SMP
+         * - correctness replacement
+         *   - may be essential or essential-on-SMP
+         * - performance replacement
+         *   - always non-essential
+         *
+         * Replacements are considered in the order shown, and the first
+         * match is applied.  For example, iget-wide will convert to
+         * iget-wide-volatile rather than iget-wide-quick if the target
+         * field is volatile.
+         */
+
+        /*
+         * essential substitutions:
+         *  {iget,iput,sget,sput}-wide --> {op}-wide-volatile
+         *  invoke-direct[/range] --> invoke-object-init/range
+         *
+         * essential-on-SMP substitutions:
+         *  {iget,iput,sget,sput}-* --> {op}-volatile
+         *  return-void --> return-void-barrier
+         *
+         * non-essential substitutions:
+         *  {iget,iput}-* --> {op}-quick
+         *
+         * TODO: might be time to merge this with the other two switches
+         */
+        switch (opc) {
+        case OP_IGET:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            quickOpc = OP_IGET_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IGET_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IGET_WIDE:
+            quickOpc = OP_IGET_WIDE_QUICK;
+            volatileOpc = OP_IGET_WIDE_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IGET_OBJECT:
+            quickOpc = OP_IGET_OBJECT_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IGET_OBJECT_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IPUT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            quickOpc = OP_IPUT_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IPUT_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IPUT_WIDE:
+            quickOpc = OP_IPUT_WIDE_QUICK;
+            volatileOpc = OP_IPUT_WIDE_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IPUT_OBJECT:
+            quickOpc = OP_IPUT_OBJECT_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IPUT_OBJECT_VOLATILE;
+            /* fall through */
+rewrite_inst_field:
+            if (essentialOnly)
+                quickOpc = OP_NOP;      /* if essential-only, no "-quick" sub */
+            if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
+                rewriteInstField(method, insns, quickOpc, volatileOpc);
+            break;
+
+        case OP_SGET:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+            if (forSmp)
+                volatileOpc = OP_SGET_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SGET_WIDE:
+            volatileOpc = OP_SGET_WIDE_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SGET_OBJECT:
+            if (forSmp)
+                volatileOpc = OP_SGET_OBJECT_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT:
+            if (forSmp)
+                volatileOpc = OP_SPUT_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT_WIDE:
+            volatileOpc = OP_SPUT_WIDE_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT_OBJECT:
+            if (forSmp)
+                volatileOpc = OP_SPUT_OBJECT_VOLATILE;
+            /* fall through */
+rewrite_static_field:
+            if (volatileOpc != OP_NOP)
+                rewriteStaticField(method, insns, volatileOpc);
+            break;
+
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE:
+            if (!rewriteInvokeObjectInit(method, insns)) {
+                /* may want to try execute-inline, below */
+                matched = false;
+            }
+            break;
+        case OP_RETURN_VOID:
+            if (needRetBar)
+                rewriteReturnVoid(method, insns);
+            break;
+        default:
+            matched = false;
+            break;
+        }
+
+
+        /*
+         * non-essential substitutions:
+         *  invoke-{virtual,direct,static}[/range] --> execute-inline
+         *  invoke-{virtual,super}[/range] --> invoke-*-quick
+         */
+        if (!matched && !essentialOnly) {
+            switch (opc) {
+            case OP_INVOKE_VIRTUAL:
+                if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
+                    rewriteVirtualInvoke(method, insns,
+                        OP_INVOKE_VIRTUAL_QUICK);
+                }
+                break;
+            case OP_INVOKE_VIRTUAL_RANGE:
+                if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
+                    rewriteVirtualInvoke(method, insns,
+                        OP_INVOKE_VIRTUAL_QUICK_RANGE);
+                }
+                break;
+            case OP_INVOKE_SUPER:
+                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
+                break;
+            case OP_INVOKE_SUPER_RANGE:
+                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE);
+                break;
+            case OP_INVOKE_DIRECT:
+                rewriteExecuteInline(method, insns, METHOD_DIRECT);
+                break;
+            case OP_INVOKE_DIRECT_RANGE:
+                rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
+                break;
+            case OP_INVOKE_STATIC:
+                rewriteExecuteInline(method, insns, METHOD_STATIC);
+                break;
+            case OP_INVOKE_STATIC_RANGE:
+                rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
+                break;
+            default:
+                /* nothing to do for this instruction */
+                ;
+            }
+        }
+
+        assert(width > 0);
+        assert(width <= insnsSize);
+        assert(width == dexGetWidthFromInstruction(insns));
+
+        insns += width;
+        insnsSize -= width;
+    }
+
+    assert(insnsSize == 0);
+}
+
+/*
+ * Update a 16-bit code unit in "meth".  The way in which the DEX data was
+ * loaded determines how we go about the write.
+ *
+ * This will be operating on post-byte-swap DEX data, so values will
+ * be in host order.
+ */
+void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+
+    if (!pDvmDex->isMappedReadOnly) {
+        /* in-memory DEX (dexopt or byte[]), alter the output directly */
+        *ptr = newVal;
+    } else {
+        /* memory-mapped file, toggle the page read/write status */
+        dvmDexChangeDex2(pDvmDex, ptr, newVal);
+    }
+}
+
+/*
+ * Update an instruction's opcode.
+ *
+ * If "opcode" is an 8-bit op, we just replace that portion.  If it's a
+ * 16-bit op, we convert the opcode from "packed" form (e.g. 0x0108) to
+ * bytecode form (e.g. 0x08ff).
+ */
+static inline void updateOpcode(const Method* meth, u2* ptr, Opcode opcode)
+{
+    if (opcode >= 256) {
+        /* opcode low byte becomes high byte, low byte becomes 0xff */
+        assert((ptr[0] & 0xff) == 0xff);
+        dvmUpdateCodeUnit(meth, ptr, (u2) (opcode << 8) | 0x00ff);
+    } else {
+        /* 8-bit op, just replace the low byte */
+        assert((ptr[0] & 0xff) != 0xff);
+        dvmUpdateCodeUnit(meth, ptr, (ptr[0] & 0xff00) | (u2) opcode);
+    }
+}
+
+/*
+ * If "referrer" and "resClass" don't come from the same DEX file, and
+ * the DEX we're working on is not destined for the bootstrap class path,
+ * tweak the class loader so package-access checks work correctly.
+ *
+ * Only do this if we're doing pre-verification or optimization.
+ */
+static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+    if (!gDvm.optimizing)
+        return;
+    assert(referrer->classLoader == NULL);
+    assert(resClass->classLoader == NULL);
+
+    if (!gDvm.optimizingBootstrapClass) {
+        /* class loader for an array class comes from element type */
+        if (dvmIsArrayClass(resClass))
+            resClass = resClass->elementClass;
+        if (referrer->pDvmDex != resClass->pDvmDex)
+            resClass->classLoader = (Object*) 0xdead3333;
+    }
+}
+
+/*
+ * Undo the effects of tweakLoader.
+ */
+static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+    if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
+        return;
+
+    if (dvmIsArrayClass(resClass))
+        resClass = resClass->elementClass;
+    resClass->classLoader = NULL;
+}
+
+
+/*
+ * Alternate version of dvmResolveClass for use with verification and
+ * optimization.  Performs access checks on every resolve, and refuses
+ * to acknowledge the existence of classes defined in more than one DEX
+ * file.
+ *
+ * Exceptions caused by failures are cleared before returning.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+
+    /*
+     * Check the table first.  If not there, do the lookup by name.
+     */
+    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+    if (resClass == NULL) {
+        const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+        if (className[0] != '\0' && className[1] == '\0') {
+            /* primitive type */
+            resClass = dvmFindPrimitiveClass(className[0]);
+        } else {
+            resClass = dvmFindClassNoInit(className, referrer->classLoader);
+        }
+        if (resClass == NULL) {
+            /* not found, exception should be raised */
+            ALOGV("DexOpt: class %d (%s) not found",
+                classIdx,
+                dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+            if (pFailure != NULL) {
+                /* dig through the wrappers to find the original failure */
+                Object* excep = dvmGetException(dvmThreadSelf());
+                while (true) {
+                    Object* cause = dvmGetExceptionCause(excep);
+                    if (cause == NULL)
+                        break;
+                    excep = cause;
+                }
+                if (strcmp(excep->clazz->descriptor,
+                    "Ljava/lang/IncompatibleClassChangeError;") == 0)
+                {
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                } else {
+                    *pFailure = VERIFY_ERROR_NO_CLASS;
+                }
+            }
+            dvmClearOptException(dvmThreadSelf());
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+    }
+
+    /* multiple definitions? */
+    if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
+        ALOGI("DexOpt: not resolving ambiguous class '%s'",
+            resClass->descriptor);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_NO_CLASS;
+        return NULL;
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resClass);
+    bool allowed = dvmCheckClassAccess(referrer, resClass);
+    untweakLoader(referrer, resClass);
+    if (!allowed) {
+        ALOGW("DexOpt: resolve class illegal access: %s -> %s",
+            referrer->descriptor, resClass->descriptor);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_CLASS;
+        return NULL;
+    }
+
+    return resClass;
+}
+
+/*
+ * Alternate version of dvmResolveInstField().
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    InstField* resField;
+
+    resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
+    if (resField == NULL) {
+        const DexFieldId* pFieldId;
+        ClassObject* resClass;
+
+        pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+        /*
+         * Find the field's class.
+         */
+        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+        if (resClass == NULL) {
+            //dvmClearOptException(dvmThreadSelf());
+            assert(!dvmCheckException(dvmThreadSelf()));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+
+        resField = (InstField*)dvmFindFieldHier(resClass,
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+            dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+        if (resField == NULL) {
+            ALOGD("DexOpt: couldn't find field %s.%s",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (dvmIsStaticField(resField)) {
+            ALOGD("DexOpt: wanted instance, got static for field %s.%s",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resField->clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->clazz);
+    if (!allowed) {
+        ALOGI("DexOpt: access denied from %s to field %s.%s",
+            referrer->descriptor, resField->clazz->descriptor,
+            resField->name);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return NULL;
+    }
+
+    return resField;
+}
+
+/*
+ * Alternate version of dvmResolveStaticField().
+ *
+ * Does not force initialization of the resolved field's class.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    StaticField* resField;
+
+    resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
+    if (resField == NULL) {
+        const DexFieldId* pFieldId;
+        ClassObject* resClass;
+
+        pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+        /*
+         * Find the field's class.
+         */
+        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+        if (resClass == NULL) {
+            //dvmClearOptException(dvmThreadSelf());
+            assert(!dvmCheckException(dvmThreadSelf()));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+
+        const char* fieldName =
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);
+
+        resField = (StaticField*)dvmFindFieldHier(resClass, fieldName,
+                    dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+        if (resField == NULL) {
+            ALOGD("DexOpt: couldn't find static field %s.%s",
+                resClass->descriptor, fieldName);
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (!dvmIsStaticField(resField)) {
+            ALOGD("DexOpt: wanted static, got instance for field %s.%s",
+                resClass->descriptor, fieldName);
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         *
+         * We can only do this if we're in "dexopt", because the presence
+         * of a valid value in the resolution table implies that the class
+         * containing the static field has been initialized.
+         */
+        if (gDvm.optimizing)
+            dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resField->clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->clazz);
+    if (!allowed) {
+        ALOGI("DexOpt: access denied from %s to field %s.%s",
+            referrer->descriptor, resField->clazz->descriptor,
+            resField->name);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return NULL;
+    }
+
+    return resField;
+}
+
+
+/*
+ * Rewrite an iget/iput instruction if appropriate.  These all have the form:
+ *   op vA, vB, field@CCCC
+ *
+ * Where vA holds the value, vB holds the object reference, and CCCC is
+ * the field reference constant pool offset.  For a non-volatile field,
+ * we want to replace the opcode with "quickOpc" and replace CCCC with
+ * the byte offset from the start of the object.  For a volatile field,
+ * we just want to replace the opcode with "volatileOpc".
+ *
+ * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile
+ * field.  If "quickOpc" is OP_NOP, and this is a non-volatile field,
+ * we don't do anything.
+ *
+ * "method" is the referring method.
+ */
+static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc,
+    Opcode volatileOpc)
+{
+    ClassObject* clazz = method->clazz;
+    u2 fieldIdx = insns[1];
+    InstField* instField;
+
+    instField = dvmOptResolveInstField(clazz, fieldIdx, NULL);
+    if (instField == NULL) {
+        ALOGI("DexOpt: unable to optimize instance field ref "
+             "0x%04x at 0x%02x in %s.%s",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return;
+    }
+
+    if (volatileOpc != OP_NOP && dvmIsVolatileField(instField)) {
+        updateOpcode(method, insns, volatileOpc);
+        ALOGV("DexOpt: rewrote ifield access %s.%s --> volatile",
+            instField->clazz->descriptor, instField->name);
+    } else if (quickOpc != OP_NOP && instField->byteOffset < 65536) {
+        updateOpcode(method, insns, quickOpc);
+        dvmUpdateCodeUnit(method, insns+1, (u2) instField->byteOffset);
+        ALOGV("DexOpt: rewrote ifield access %s.%s --> %d",
+            instField->clazz->descriptor, instField->name,
+            instField->byteOffset);
+    } else {
+        ALOGV("DexOpt: no rewrite of ifield access %s.%s",
+            instField->clazz->descriptor, instField->name);
+    }
+
+    return;
+}
+
+/*
+ * Rewrite a static field access instruction if appropriate.  If
+ * the target field is volatile, we replace the opcode with "volatileOpc".
+ *
+ * "method" is the referring method.
+ */
+static void rewriteStaticField0(Method* method, u2* insns, Opcode volatileOpc,
+    u4 fieldIdx)
+{
+    ClassObject* clazz = method->clazz;
+    StaticField* staticField;
+
+    assert(volatileOpc != OP_NOP);
+
+    staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL);
+    if (staticField == NULL) {
+        ALOGI("DexOpt: unable to optimize static field ref "
+             "0x%04x at 0x%02x in %s.%s",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return;
+    }
+
+    if (dvmIsVolatileField(staticField)) {
+        updateOpcode(method, insns, volatileOpc);
+        ALOGV("DexOpt: rewrote sfield access %s.%s --> volatile",
+            staticField->clazz->descriptor, staticField->name);
+    }
+}
+
+static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc)
+{
+    u2 fieldIdx = insns[1];
+    rewriteStaticField0(method, insns, volatileOpc, fieldIdx);
+}
+
+/*
+ * Alternate version of dvmResolveMethod().
+ *
+ * Doesn't throw exceptions, and checks access on every lookup.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+    MethodType methodType, VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    Method* resMethod;
+
+    assert(methodType == METHOD_DIRECT ||
+           methodType == METHOD_VIRTUAL ||
+           methodType == METHOD_STATIC);
+
+    LOGVV("--- resolving method %u (referrer=%s)", methodIdx,
+        referrer->descriptor);
+
+    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (resMethod == NULL) {
+        const DexMethodId* pMethodId;
+        ClassObject* resClass;
+
+        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
+        if (resClass == NULL) {
+            /*
+             * Can't find the class that the method is a part of, or don't
+             * have permission to access the class.
+             */
+            ALOGV("DexOpt: can't find called method's class (?.%s)",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+        if (dvmIsInterfaceClass(resClass)) {
+            /* method is part of an interface; this is wrong method for that */
+            ALOGW("DexOpt: method is in an interface");
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+
+        /*
+         * We need to chase up the class hierarchy to find methods defined
+         * in super-classes.  (We only want to check the current class
+         * if we're looking for a constructor.)
+         */
+        DexProto proto;
+        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+        if (methodType == METHOD_DIRECT) {
+            resMethod = dvmFindDirectMethod(resClass,
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+        } else {
+            /* METHOD_STATIC or METHOD_VIRTUAL */
+            resMethod = dvmFindMethodHier(resClass,
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+        }
+
+        if (resMethod == NULL) {
+            ALOGV("DexOpt: couldn't find method '%s'",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_METHOD;
+            return NULL;
+        }
+        if (methodType == METHOD_STATIC) {
+            if (!dvmIsStaticMethod(resMethod)) {
+                ALOGD("DexOpt: wanted static, got instance for method %s.%s",
+                    resClass->descriptor, resMethod->name);
+                if (pFailure != NULL)
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                return NULL;
+            }
+        } else if (methodType == METHOD_VIRTUAL) {
+            if (dvmIsStaticMethod(resMethod)) {
+                ALOGD("DexOpt: wanted instance, got static for method %s.%s",
+                    resClass->descriptor, resMethod->name);
+                if (pFailure != NULL)
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                return NULL;
+            }
+        }
+
+        /* see if this is a pure-abstract method */
+        if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+            ALOGW("DexOpt: pure-abstract method '%s' in %s",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
+                resClass->descriptor);
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         *
+         * We can only do this for static methods if we're not in "dexopt",
+         * because the presence of a valid value in the resolution table
+         * implies that the class containing the static field has been
+         * initialized.
+         */
+        if (methodType != METHOD_STATIC || gDvm.optimizing)
+            dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    LOGVV("--- found method %d (%s.%s)",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* access allowed? */
+    tweakLoader(referrer, resMethod->clazz);
+    bool allowed = dvmCheckMethodAccess(referrer, resMethod);
+    untweakLoader(referrer, resMethod->clazz);
+    if (!allowed) {
+        IF_ALOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            ALOGI("DexOpt: illegal method access (call %s.%s %s from %s)",
+                resMethod->clazz->descriptor, resMethod->name, desc,
+                referrer->descriptor);
+            free(desc);
+        }
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_METHOD;
+        return NULL;
+    }
+
+    return resMethod;
+}
+
+/*
+ * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
+ * invoke-super/range if appropriate.  These all have the form:
+ *   op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * We want to replace the method constant pool index BBBB with the
+ * vtable index.
+ */
+static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc)
+{
+    ClassObject* clazz = method->clazz;
+    Method* baseMethod;
+    u2 methodIdx = insns[1];
+
+    baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
+    if (baseMethod == NULL) {
+        ALOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s",
+            methodIdx,
+            (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return;
+    }
+
+    assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
+           (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
+           (insns[0] & 0xff) == OP_INVOKE_SUPER ||
+           (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
+
+    /*
+     * Note: Method->methodIndex is a u2 and is range checked during the
+     * initial load.
+     */
+    updateOpcode(method, insns, newOpc);
+    dvmUpdateCodeUnit(method, insns+1, baseMethod->methodIndex);
+
+    //ALOGI("DexOpt: rewrote call to %s.%s --> %s.%s",
+    //    method->clazz->descriptor, method->name,
+    //    baseMethod->clazz->descriptor, baseMethod->name);
+
+    return;
+}
+
+/*
+ * Rewrite invoke-direct[/range] if the target is Object.<init>.
+ *
+ * This is useful as an optimization, because otherwise every object
+ * instantiation will cause us to call a method that does nothing.
+ * It also allows us to inexpensively mark objects as finalizable at the
+ * correct time.
+ *
+ * TODO: verifier should ensure Object.<init> contains only return-void,
+ * and issue a warning if not.
+ */
+static bool rewriteInvokeObjectInit(Method* method, u2* insns)
+{
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
+    if (calledMethod == NULL) {
+        ALOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s",
+            methodIdx, (int) (insns - method->insns),
+            clazz->descriptor, method->name);
+        return false;
+    }
+
+    if (calledMethod->clazz == gDvm.classJavaLangObject &&
+        dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
+    {
+        /*
+         * Replace the instruction.  If the debugger is attached, the
+         * interpreter will forward execution to the invoke-direct/range
+         * handler.  If this was an invoke-direct/range instruction we can
+         * just replace the opcode, but if it was an invoke-direct we
+         * have to set the argument count (high 8 bits of first code unit)
+         * to 1.
+         */
+        u1 origOp = insns[0] & 0xff;
+        if (origOp == OP_INVOKE_DIRECT) {
+            dvmUpdateCodeUnit(method, insns,
+                OP_INVOKE_OBJECT_INIT_RANGE | 0x100);
+        } else {
+            assert(origOp == OP_INVOKE_DIRECT_RANGE);
+            assert((insns[0] >> 8) == 1);
+            updateOpcode(method, insns, OP_INVOKE_OBJECT_INIT_RANGE);
+        }
+
+        LOGVV("DexOpt: replaced Object.<init> in %s.%s",
+            method->clazz->descriptor, method->name);
+    }
+
+    return true;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * No method access check here -- interface methods are always public.
+ *
+ * Returns NULL if the method was not found.  Does not throw an exception.
+ */
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    Method* resMethod;
+
+    LOGVV("--- resolving interface method %d (referrer=%s)",
+        methodIdx, referrer->descriptor);
+
+    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (resMethod == NULL) {
+        const DexMethodId* pMethodId;
+        ClassObject* resClass;
+
+        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
+        if (resClass == NULL) {
+            /* can't find the class that the method is a part of */
+            dvmClearOptException(dvmThreadSelf());
+            return NULL;
+        }
+        if (!dvmIsInterfaceClass(resClass)) {
+            /* whoops */
+            ALOGI("Interface method not part of interface class");
+            return NULL;
+        }
+
+        const char* methodName =
+            dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+        DexProto proto;
+        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+        LOGVV("+++ looking for '%s' '%s' in resClass='%s'",
+            methodName, methodSig, resClass->descriptor);
+        resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto);
+        if (resMethod == NULL) {
+            return NULL;
+        }
+
+        /* we're expecting this to be abstract */
+        if (!dvmIsAbstractMethod(resMethod)) {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            ALOGW("Found non-abstract interface method %s.%s %s",
+                resMethod->clazz->descriptor, resMethod->name, desc);
+            free(desc);
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    LOGVV("--- found interface method %d (%s.%s)",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* interface methods are always public; no need to check access */
+
+    return resMethod;
+}
+
+/*
+ * Replace invoke-virtual, invoke-direct, or invoke-static with an
+ * execute-inline operation if appropriate.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInline(Method* method, u2* insns,
+    MethodType methodType)
+{
+    const InlineSub* inlineSubs = gDvm.inlineSubs;
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    //return false;
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+    if (calledMethod == NULL) {
+        ALOGV("+++ DexOpt inline: can't find %d", methodIdx);
+        return false;
+    }
+
+    while (inlineSubs->method != NULL) {
+        /*
+        if (extra) {
+            ALOGI("comparing %p vs %p %s.%s %s",
+                inlineSubs->method, calledMethod,
+                inlineSubs->method->clazz->descriptor,
+                inlineSubs->method->name,
+                inlineSubs->method->signature);
+        }
+        */
+        if (inlineSubs->method == calledMethod) {
+            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
+                   (insns[0] & 0xff) == OP_INVOKE_STATIC ||
+                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
+            updateOpcode(method, insns, OP_EXECUTE_INLINE);
+            dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+            //ALOGI("DexOpt: execute-inline %s.%s --> %s.%s",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
+
+/*
+ * Replace invoke-virtual/range, invoke-direct/range, or invoke-static/range
+ * with an execute-inline operation if appropriate.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+    MethodType methodType)
+{
+    const InlineSub* inlineSubs = gDvm.inlineSubs;
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+    if (calledMethod == NULL) {
+        ALOGV("+++ DexOpt inline/range: can't find %d", methodIdx);
+        return false;
+    }
+
+    while (inlineSubs->method != NULL) {
+        if (inlineSubs->method == calledMethod) {
+            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
+                   (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
+                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
+            updateOpcode(method, insns, OP_EXECUTE_INLINE_RANGE);
+            dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+            //ALOGI("DexOpt: execute-inline/range %s.%s --> %s.%s",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
+
+/*
+ * Returns "true" if the return-void instructions in this method should
+ * be converted to return-void-barrier.
+ *
+ * This is needed to satisfy a Java Memory Model requirement regarding
+ * the construction of objects with final fields.  (This does not apply
+ * to <clinit> or static fields, since appropriate barriers are guaranteed
+ * by the class initialization process.)
+ */
+static bool needsReturnBarrier(Method* method)
+{
+    if (!gDvm.dexOptForSmp)
+        return false;
+    if (strcmp(method->name, "<init>") != 0)
+        return false;
+
+    /*
+     * Check to see if the class is finalizable.  The loader sets a flag
+     * if the class or one of its superclasses overrides finalize().
+     */
+    const ClassObject* clazz = method->clazz;
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE))
+        return true;
+
+    /*
+     * Check to see if the class has any final fields.  If not, we don't
+     * need to generate a barrier instruction.
+     *
+     * In theory, we only need to do this if the method actually modifies
+     * a final field.  In practice, non-constructor methods are allowed
+     * to modify final fields, and there are 3rd-party tools that rely on
+     * this behavior.  (The compiler does not allow it, but the VM does.)
+     *
+     * If we alter the verifier to restrict final-field updates to
+     * constructors, we can tighten this up as well.
+     */
+    int idx = clazz->ifieldCount;
+    while (--idx >= 0) {
+        if (dvmIsFinalField(&clazz->ifields[idx]))
+            return true;
+    }
+
+    return false;
+}
+
+/*
+ * Convert a return-void to a return-void-barrier.
+ */
+static void rewriteReturnVoid(Method* method, u2* insns)
+{
+    assert((insns[0] & 0xff) == OP_RETURN_VOID);
+    updateOpcode(method, insns, OP_RETURN_VOID_BARRIER);
+}
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
new file mode 100644
index 0000000..e1825d0
--- /dev/null
+++ b/vm/analysis/Optimize.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Bytecode optimization declarations.
+ */
+#ifndef DALVIK_OPTIMIZE_H_
+#define DALVIK_OPTIMIZE_H_
+
+/*
+ * Entry point from DEX preparation.
+ */
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly);
+
+/*
+ * Update a 16-bit code unit.
+ */
+void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal);
+
+/*
+ * Abbreviated resolution functions, for use by optimization and verification
+ * code.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+    VerifyError* pFailure);
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+    MethodType methodType, VerifyError* pFailure);
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx);
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+    VerifyError* pFailure);
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+    VerifyError* pFailure);
+
+#endif  // DALVIK_OPTIMIZE_H_
diff --git a/vm/analysis/RegisterMap.cpp b/vm/analysis/RegisterMap.cpp
new file mode 100644
index 0000000..c62ec47
--- /dev/null
+++ b/vm/analysis/RegisterMap.cpp
@@ -0,0 +1,1833 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This code generate "register maps" for Dalvik bytecode.  In a stack-based
+ * VM we might call these "stack maps".  They are used to increase the
+ * precision in the garbage collector when scanning references in the
+ * interpreter thread stacks.
+ */
+#include "Dalvik.h"
+#include "UniquePtr.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/RegisterMap.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/Leb128.h"
+
+#include <stddef.h>
+
+/* double-check the compression */
+#define REGISTER_MAP_VERIFY     false
+
+/* verbose logging */
+#define REGISTER_MAP_VERBOSE    false
+
+//#define REGISTER_MAP_STATS
+
+// fwd
+static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data);
+static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap);
+static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2);
+
+#ifdef REGISTER_MAP_STATS
+static void computeMapStats(RegisterMap* pMap, const Method* method);
+#endif
+static RegisterMap* compressMapDifferential(const RegisterMap* pMap,\
+    const Method* meth);
+static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap);
+
+#ifdef REGISTER_MAP_STATS
+/*
+ * Generate some statistics on the register maps we create and use.
+ */
+#define kMaxGcPointGap      50
+#define kUpdatePosnMinRegs  24
+#define kNumUpdatePosns     8
+#define kMaxDiffBits        20
+struct MapStats {
+    /*
+     * Buckets measuring the distance between GC points.  This tells us how
+     * many bits we need to encode the advancing program counter.  We ignore
+     * some of the "long tail" entries.
+     */
+    int gcPointGap[kMaxGcPointGap];
+
+    /*
+     * Number of gaps.  Equal to (number of gcPoints - number of methods),
+     * since the computation isn't including the initial gap.
+     */
+    int gcGapCount;
+
+    /*
+     * Number of gaps.
+     */
+    int totalGcPointCount;
+
+    /*
+     * For larger methods (>= 24 registers), measure in which octant register
+     * updates occur.  This should help us understand whether register
+     * changes tend to cluster in the low regs even for large methods.
+     */
+    int updatePosn[kNumUpdatePosns];
+
+    /*
+     * For all methods, count up the number of changes to registers < 16
+     * and >= 16.
+     */
+    int updateLT16;
+    int updateGE16;
+
+    /*
+     * Histogram of the number of bits that differ between adjacent entries.
+     */
+    int numDiffBits[kMaxDiffBits];
+
+
+    /*
+     * Track the number of expanded maps, and the heap space required to
+     * hold them.
+     */
+    int numExpandedMaps;
+    int totalExpandedMapSize;
+};
+#endif
+
+/*
+ * Prepare some things.
+ */
+bool dvmRegisterMapStartup()
+{
+#ifdef REGISTER_MAP_STATS
+    MapStats* pStats = calloc(1, sizeof(MapStats));
+    gDvm.registerMapStats = pStats;
+#endif
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmRegisterMapShutdown()
+{
+#ifdef REGISTER_MAP_STATS
+    free(gDvm.registerMapStats);
+#endif
+}
+
+/*
+ * Write stats to log file.
+ */
+void dvmRegisterMapDumpStats()
+{
+#ifdef REGISTER_MAP_STATS
+    MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+    int i, end;
+
+    for (end = kMaxGcPointGap-1; end >= 0; end--) {
+        if (pStats->gcPointGap[end] != 0)
+            break;
+    }
+
+    ALOGI("Register Map gcPointGap stats (diff count=%d, total=%d):",
+        pStats->gcGapCount, pStats->totalGcPointCount);
+    assert(pStats->gcPointGap[0] == 0);
+    for (i = 1; i <= end; i++) {
+        ALOGI(" %2d %d", i, pStats->gcPointGap[i]);
+    }
+
+
+    for (end = kMaxDiffBits-1; end >= 0; end--) {
+        if (pStats->numDiffBits[end] != 0)
+            break;
+    }
+
+    ALOGI("Register Map bit difference stats:");
+    for (i = 0; i <= end; i++) {
+        ALOGI(" %2d %d", i, pStats->numDiffBits[i]);
+    }
+
+
+    ALOGI("Register Map update position stats (lt16=%d ge16=%d):",
+        pStats->updateLT16, pStats->updateGE16);
+    for (i = 0; i < kNumUpdatePosns; i++) {
+        ALOGI(" %2d %d", i, pStats->updatePosn[i]);
+    }
+#endif
+}
+
+
+/*
+ * ===========================================================================
+ *      Map generation
+ * ===========================================================================
+ */
+
+/*
+ * Generate the register map for a method that has just been verified
+ * (i.e. we're doing this as part of verification).
+ *
+ * For type-precise determination we have all the data we need, so we
+ * just need to encode it in some clever fashion.
+ *
+ * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+ */
+RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata)
+{
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    RegisterMap* pMap = NULL;
+    RegisterMap* pResult = NULL;
+    RegisterMapFormat format;
+    u1 regWidth;
+    u1* mapData;
+    int i, bytesForAddr, gcPointCount;
+    int bufSize;
+
+    if (vdata->method->registersSize >= 2048) {
+        ALOGE("ERROR: register map can't handle %d registers",
+            vdata->method->registersSize);
+        goto bail;
+    }
+    regWidth = (vdata->method->registersSize + 7) / 8;
+
+    /*
+     * Decide if we need 8 or 16 bits to hold the address.  Strictly speaking
+     * we only need 16 bits if we actually encode an address >= 256 -- if
+     * the method has a section at the end without GC points (e.g. array
+     * data) we don't need to count it.  The situation is unusual, and
+     * detecting it requires scanning the entire method, so we don't bother.
+     */
+    if (vdata->insnsSize < 256) {
+        format = kRegMapFormatCompact8;
+        bytesForAddr = 1;
+    } else {
+        format = kRegMapFormatCompact16;
+        bytesForAddr = 2;
+    }
+
+    /*
+     * Count up the number of GC point instructions.
+     *
+     * NOTE: this does not automatically include the first instruction,
+     * since we don't count method entry as a GC point.
+     */
+    gcPointCount = 0;
+    for (i = 0; i < (int) vdata->insnsSize; i++) {
+        if (dvmInsnIsGcPoint(vdata->insnFlags, i))
+            gcPointCount++;
+    }
+    if (gcPointCount >= 65536) {
+        /* we could handle this, but in practice we don't get near this */
+        ALOGE("ERROR: register map can't handle %d gc points in one method",
+            gcPointCount);
+        goto bail;
+    }
+
+    /*
+     * Allocate a buffer to hold the map data.
+     */
+    bufSize = kHeaderSize + gcPointCount * (bytesForAddr + regWidth);
+
+    ALOGV("+++ grm: %s.%s (adr=%d gpc=%d rwd=%d bsz=%d)",
+        vdata->method->clazz->descriptor, vdata->method->name,
+        bytesForAddr, gcPointCount, regWidth, bufSize);
+
+    pMap = (RegisterMap*) malloc(bufSize);
+    dvmRegisterMapSetFormat(pMap, format);
+    dvmRegisterMapSetOnHeap(pMap, true);
+    dvmRegisterMapSetRegWidth(pMap, regWidth);
+    dvmRegisterMapSetNumEntries(pMap, gcPointCount);
+
+    /*
+     * Populate it.
+     */
+    mapData = pMap->data;
+    for (i = 0; i < (int) vdata->insnsSize; i++) {
+        if (dvmInsnIsGcPoint(vdata->insnFlags, i)) {
+            assert(vdata->registerLines[i].regTypes != NULL);
+            if (format == kRegMapFormatCompact8) {
+                *mapData++ = i;
+            } else /*kRegMapFormatCompact16*/ {
+                *mapData++ = i & 0xff;
+                *mapData++ = i >> 8;
+            }
+            outputTypeVector(vdata->registerLines[i].regTypes,
+                vdata->insnRegCount, mapData);
+            mapData += regWidth;
+        }
+    }
+
+    ALOGV("mapData=%p pMap=%p bufSize=%d", mapData, pMap, bufSize);
+    assert(mapData - (const u1*) pMap == bufSize);
+
+    if (REGISTER_MAP_VERIFY && !verifyMap(vdata, pMap))
+        goto bail;
+#ifdef REGISTER_MAP_STATS
+    computeMapStats(pMap, vdata->method);
+#endif
+
+    /*
+     * Try to compress the map.
+     */
+    RegisterMap* pCompMap;
+
+    pCompMap = compressMapDifferential(pMap, vdata->method);
+    if (pCompMap != NULL) {
+        if (REGISTER_MAP_VERIFY) {
+            /*
+             * Expand the compressed map we just created, and compare it
+             * to the original.  Abort the VM if it doesn't match up.
+             */
+            RegisterMap* pUncompMap;
+            pUncompMap = uncompressMapDifferential(pCompMap);
+            if (pUncompMap == NULL) {
+                ALOGE("Map failed to uncompress - %s.%s",
+                    vdata->method->clazz->descriptor,
+                    vdata->method->name);
+                free(pCompMap);
+                /* bad - compression is broken or we're out of memory */
+                dvmAbort();
+            } else {
+                if (compareMaps(pMap, pUncompMap) != 0) {
+                    ALOGE("Map comparison failed - %s.%s",
+                        vdata->method->clazz->descriptor,
+                        vdata->method->name);
+                    free(pCompMap);
+                    /* bad - compression is broken */
+                    dvmAbort();
+                }
+
+                /* verify succeeded */
+                delete pUncompMap;
+            }
+        }
+
+        if (REGISTER_MAP_VERBOSE) {
+            ALOGD("Good compress on %s.%s",
+                vdata->method->clazz->descriptor,
+                vdata->method->name);
+        }
+        free(pMap);
+        pMap = pCompMap;
+    } else {
+        if (REGISTER_MAP_VERBOSE) {
+            ALOGD("Unable to compress %s.%s (ent=%d rw=%d)",
+                vdata->method->clazz->descriptor,
+                vdata->method->name,
+                dvmRegisterMapGetNumEntries(pMap),
+                dvmRegisterMapGetRegWidth(pMap));
+        }
+    }
+
+    pResult = pMap;
+
+bail:
+    return pResult;
+}
+
+/*
+ * Release the storage held by a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap)
+{
+    if (pMap == NULL)
+        return;
+
+    assert(dvmRegisterMapGetOnHeap(pMap));
+    free(pMap);
+}
+
+/*
+ * Determine if the RegType value is a reference type.
+ *
+ * Ordinarily we include kRegTypeZero in the "is it a reference"
+ * check.  There's no value in doing so here, because we know
+ * the register can't hold anything but zero.
+ */
+static inline bool isReferenceType(RegType type)
+{
+    return (type > kRegTypeMAX || type == kRegTypeUninit);
+}
+
+/*
+ * Given a line of registers, output a bit vector that indicates whether
+ * or not the register holds a reference type (which could be null).
+ *
+ * We use '1' to indicate it's a reference, '0' for anything else (numeric
+ * value, uninitialized data, merge conflict).  Register 0 will be found
+ * in the low bit of the first byte.
+ */
+static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data)
+{
+    u1 val = 0;
+    int i;
+
+    for (i = 0; i < insnRegCount; i++) {
+        RegType type = *regs++;
+        val >>= 1;
+        if (isReferenceType(type))
+            val |= 0x80;        /* set hi bit */
+
+        if ((i & 0x07) == 7)
+            *data++ = val;
+    }
+    if ((i & 0x07) != 0) {
+        /* flush bits from last byte */
+        val >>= 8 - (i & 0x07);
+        *data++ = val;
+    }
+}
+
+/*
+ * Print the map as a series of binary strings.
+ *
+ * Pass in method->registersSize if known, or -1 if not.
+ */
+static void dumpRegisterMap(const RegisterMap* pMap, int registersSize)
+{
+    const u1* rawMap = pMap->data;
+    const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap);
+    const int numEntries = dvmRegisterMapGetNumEntries(pMap);
+    const int regWidth = dvmRegisterMapGetRegWidth(pMap);
+    int addrWidth;
+
+    switch (format) {
+    case kRegMapFormatCompact8:
+        addrWidth = 1;
+        break;
+    case kRegMapFormatCompact16:
+        addrWidth = 2;
+        break;
+    default:
+        /* can't happen */
+        ALOGE("Can only dump Compact8 / Compact16 maps (not %d)", format);
+        return;
+    }
+
+    if (registersSize < 0)
+        registersSize = 8 * regWidth;
+    assert(registersSize <= regWidth * 8);
+
+    int ent;
+    for (ent = 0; ent < numEntries; ent++) {
+        int i, addr;
+
+        addr = *rawMap++;
+        if (addrWidth > 1)
+            addr |= (*rawMap++) << 8;
+
+        const u1* dataStart = rawMap;
+        u1 val = 0;
+
+        /* create binary string */
+        char outBuf[registersSize +1];
+        for (i = 0; i < registersSize; i++) {
+            val >>= 1;
+            if ((i & 0x07) == 0)
+                val = *rawMap++;
+
+            outBuf[i] = '0' + (val & 0x01);
+        }
+        outBuf[i] = '\0';
+
+        /* back up and create hex dump */
+        char hexBuf[regWidth * 3 +1];
+        char* cp = hexBuf;
+        rawMap = dataStart;
+        for (i = 0; i < regWidth; i++) {
+            sprintf(cp, " %02x", *rawMap++);
+            cp += 3;
+        }
+        hexBuf[i * 3] = '\0';
+
+        ALOGD("  %04x %s %s", addr, outBuf, hexBuf);
+    }
+}
+
+/*
+ * Double-check the map.
+ *
+ * We run through all of the data in the map, and compare it to the original.
+ * Only works on uncompressed data.
+ */
+static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap)
+{
+    const u1* rawMap = pMap->data;
+    const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap);
+    const int numEntries = dvmRegisterMapGetNumEntries(pMap);
+    int ent;
+    bool dumpMap = false;
+
+    if (false) {
+        const char* cd = "Landroid/net/http/Request;";
+        const char* mn = "readResponse";
+        if (strcmp(vdata->method->clazz->descriptor, cd) == 0 &&
+            strcmp(vdata->method->name, mn) == 0)
+        {
+            char* desc;
+            desc = dexProtoCopyMethodDescriptor(&vdata->method->prototype);
+            ALOGI("Map for %s.%s %s", vdata->method->clazz->descriptor,
+                vdata->method->name, desc);
+            free(desc);
+
+            dumpMap = true;
+        }
+    }
+
+    if ((vdata->method->registersSize + 7) / 8 != pMap->regWidth) {
+        ALOGE("GLITCH: registersSize=%d, regWidth=%d",
+            vdata->method->registersSize, pMap->regWidth);
+        return false;
+    }
+
+    for (ent = 0; ent < numEntries; ent++) {
+        int addr;
+
+        switch (format) {
+        case kRegMapFormatCompact8:
+            addr = *rawMap++;
+            break;
+        case kRegMapFormatCompact16:
+            addr = *rawMap++;
+            addr |= (*rawMap++) << 8;
+            break;
+        default:
+            /* shouldn't happen */
+            ALOGE("GLITCH: bad format (%d)", format);
+            dvmAbort();
+        }
+
+        const RegType* regs = vdata->registerLines[addr].regTypes;
+        if (regs == NULL) {
+            ALOGE("GLITCH: addr %d has no data", addr);
+            return false;
+        }
+
+        u1 val = 0;
+        int i;
+
+        for (i = 0; i < vdata->method->registersSize; i++) {
+            bool bitIsRef, regIsRef;
+
+            val >>= 1;
+            if ((i & 0x07) == 0) {
+                /* load next byte of data */
+                val = *rawMap++;
+            }
+
+            bitIsRef = val & 0x01;
+
+            RegType type = regs[i];
+            regIsRef = isReferenceType(type);
+
+            if (bitIsRef != regIsRef) {
+                ALOGE("GLITCH: addr %d reg %d: bit=%d reg=%d(%d)",
+                    addr, i, bitIsRef, regIsRef, type);
+                return false;
+            }
+        }
+
+        /* rawMap now points to the address field of the next entry */
+    }
+
+    if (dumpMap)
+        dumpRegisterMap(pMap, vdata->method->registersSize);
+
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      DEX generation & parsing
+ * ===========================================================================
+ */
+
+/*
+ * Advance "ptr" to ensure 32-bit alignment.
+ */
+static inline u1* align32(u1* ptr)
+{
+    return (u1*) (((int) ptr + 3) & ~0x03);
+}
+
+/*
+ * Compute the size, in bytes, of a register map.
+ */
+static size_t computeRegisterMapSize(const RegisterMap* pMap)
+{
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    assert(pMap != NULL);
+
+    switch (format) {
+    case kRegMapFormatNone:
+        return 1;
+    case kRegMapFormatCompact8:
+        return kHeaderSize + (1 + pMap->regWidth) * numEntries;
+    case kRegMapFormatCompact16:
+        return kHeaderSize + (2 + pMap->regWidth) * numEntries;
+    case kRegMapFormatDifferential:
+        {
+            /* kHeaderSize + decoded ULEB128 length */
+            const u1* ptr = pMap->data;
+            int len = readUnsignedLeb128(&ptr);
+            return len + (ptr - (u1*) pMap);
+        }
+    default:
+        ALOGE("Bad register map format %d", format);
+        dvmAbort();
+        return 0;
+    }
+}
+
+/*
+ * Output the map for a single method, if it has one.
+ *
+ * Abstract and native methods have no map.  All others are expected to
+ * have one, since we know the class verified successfully.
+ *
+ * This strips the "allocated on heap" flag from the format byte, so that
+ * direct-mapped maps are correctly identified as such.
+ */
+static bool writeMapForMethod(const Method* meth, u1** pPtr)
+{
+    if (meth->registerMap == NULL) {
+        if (!dvmIsAbstractMethod(meth) && !dvmIsNativeMethod(meth)) {
+            ALOGW("Warning: no map available for %s.%s",
+                meth->clazz->descriptor, meth->name);
+            /* weird, but keep going */
+        }
+        *(*pPtr)++ = kRegMapFormatNone;
+        return true;
+    }
+
+    /* serialize map into the buffer */
+    size_t mapSize = computeRegisterMapSize(meth->registerMap);
+    memcpy(*pPtr, meth->registerMap, mapSize);
+
+    /* strip the "on heap" flag out of the format byte, which is always first */
+    assert(**pPtr == meth->registerMap->format);
+    **pPtr &= ~(kRegMapFormatOnHeap);
+
+    *pPtr += mapSize;
+
+    return true;
+}
+
+/*
+ * Write maps for all methods in the specified class to the buffer, which
+ * can hold at most "length" bytes.  "*pPtr" will be advanced past the end
+ * of the data we write.
+ */
+static bool writeMapsAllMethods(DvmDex* pDvmDex, const ClassObject* clazz,
+    u1** pPtr, size_t length)
+{
+    RegisterMapMethodPool* pMethodPool;
+    u1* ptr = *pPtr;
+    int i, methodCount;
+
+    /* artificial limit */
+    if (clazz->virtualMethodCount + clazz->directMethodCount >= 65536) {
+        ALOGE("Too many methods in %s", clazz->descriptor);
+        return false;
+    }
+
+    pMethodPool = (RegisterMapMethodPool*) ptr;
+    ptr += offsetof(RegisterMapMethodPool, methodData);
+    methodCount = 0;
+
+    /*
+     * Run through all methods, direct then virtual.  The class loader will
+     * traverse them in the same order.  (We could split them into two
+     * distinct pieces, but there doesn't appear to be any value in doing
+     * so other than that it makes class loading slightly less fragile.)
+     *
+     * The class loader won't know about miranda methods at the point
+     * where it parses this, so we omit those.
+     *
+     * TODO: consider omitting all native/abstract definitions.  Should be
+     * safe, though we lose the ability to sanity-check against the
+     * method counts in the DEX file.
+     */
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        const Method* meth = &clazz->directMethods[i];
+        if (dvmIsMirandaMethod(meth))
+            continue;
+        if (!writeMapForMethod(&clazz->directMethods[i], &ptr)) {
+            return false;
+        }
+        methodCount++;
+        //ptr = align32(ptr);
+    }
+
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        const Method* meth = &clazz->virtualMethods[i];
+        if (dvmIsMirandaMethod(meth))
+            continue;
+        if (!writeMapForMethod(&clazz->virtualMethods[i], &ptr)) {
+            return false;
+        }
+        methodCount++;
+        //ptr = align32(ptr);
+    }
+
+    pMethodPool->methodCount = methodCount;
+
+    *pPtr = ptr;
+    return true;
+}
+
+/*
+ * Write maps for all classes to the specified buffer, which can hold at
+ * most "length" bytes.
+ *
+ * Returns the actual length used, or 0 on failure.
+ */
+static size_t writeMapsAllClasses(DvmDex* pDvmDex, u1* basePtr, size_t length)
+{
+    DexFile* pDexFile = pDvmDex->pDexFile;
+    u4 count = pDexFile->pHeader->classDefsSize;
+    RegisterMapClassPool* pClassPool;
+    u4* offsetTable;
+    u1* ptr = basePtr;
+    u4 idx;
+
+    assert(gDvm.optimizing);
+
+    pClassPool = (RegisterMapClassPool*) ptr;
+    ptr += offsetof(RegisterMapClassPool, classDataOffset);
+    offsetTable = (u4*) ptr;
+    ptr += count * sizeof(u4);
+
+    pClassPool->numClasses = count;
+
+    /*
+     * We want an entry for every class, loaded or not.
+     */
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* clazz;
+
+        pClassDef = dexGetClassDef(pDexFile, idx);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        /*
+         * All classes have been loaded into the bootstrap class loader.
+         * If we can find it, and it was successfully pre-verified, we
+         * run through its methods and add the register maps.
+         *
+         * If it wasn't pre-verified then we know it can't have any
+         * register maps.  Classes that can't be loaded or failed
+         * verification get an empty slot in the index.
+         */
+        clazz = NULL;
+        if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) != 0)
+            clazz = dvmLookupClass(classDescriptor, NULL, false);
+
+        if (clazz != NULL) {
+            offsetTable[idx] = ptr - basePtr;
+            LOGVV("%d -> offset %d (%p-%p)",
+                idx, offsetTable[idx], ptr, basePtr);
+
+            if (!writeMapsAllMethods(pDvmDex, clazz, &ptr,
+                    length - (ptr - basePtr)))
+            {
+                return 0;
+            }
+
+            ptr = align32(ptr);
+            LOGVV("Size %s (%d+%d methods): %d", clazz->descriptor,
+                clazz->directMethodCount, clazz->virtualMethodCount,
+                (ptr - basePtr) - offsetTable[idx]);
+        } else {
+            ALOGV("%4d NOT mapadding '%s'", idx, classDescriptor);
+            assert(offsetTable[idx] == 0);
+        }
+    }
+
+    if (ptr - basePtr >= (int)length) {
+        /* a bit late */
+        ALOGE("Buffer overrun");
+        dvmAbort();
+    }
+
+    return ptr - basePtr;
+}
+
+/*
+ * Generate a register map set for all verified classes in "pDvmDex".
+ */
+RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex)
+{
+    RegisterMapBuilder* pBuilder;
+
+    pBuilder = (RegisterMapBuilder*) calloc(1, sizeof(RegisterMapBuilder));
+    if (pBuilder == NULL)
+        return NULL;
+
+    /*
+     * We have a couple of options here:
+     *  (1) Compute the size of the output, and malloc a buffer.
+     *  (2) Create a "large-enough" anonymous mmap region.
+     *
+     * The nice thing about option #2 is that we don't have to traverse
+     * all of the classes and methods twice.  The risk is that we might
+     * not make the region large enough.  Since the pages aren't mapped
+     * until used we can allocate a semi-absurd amount of memory without
+     * worrying about the effect on the rest of the system.
+     *
+     * The basic encoding on the largest jar file requires about 1MB of
+     * storage.  We map out 4MB here.  (TODO: guarantee that the last
+     * page of the mapping is marked invalid, so we reliably fail if
+     * we overrun.)
+     */
+    if (sysCreatePrivateMap(4 * 1024 * 1024, &pBuilder->memMap) != 0) {
+        free(pBuilder);
+        return NULL;
+    }
+
+    /*
+     * Create the maps.
+     */
+    size_t actual = writeMapsAllClasses(pDvmDex, (u1*)pBuilder->memMap.addr,
+                                        pBuilder->memMap.length);
+    if (actual == 0) {
+        dvmFreeRegisterMapBuilder(pBuilder);
+        return NULL;
+    }
+
+    ALOGV("TOTAL size of register maps: %d", actual);
+
+    pBuilder->data = pBuilder->memMap.addr;
+    pBuilder->size = actual;
+    return pBuilder;
+}
+
+/*
+ * Free the builder.
+ */
+void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder)
+{
+    if (pBuilder == NULL)
+        return;
+
+    sysReleaseShmem(&pBuilder->memMap);
+    free(pBuilder);
+}
+
+
+/*
+ * Find the data for the specified class.
+ *
+ * If there's no register map data, or none for this class, we return NULL.
+ */
+const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx,
+    u4* pNumMaps)
+{
+    const RegisterMapClassPool* pClassPool;
+    const RegisterMapMethodPool* pMethodPool;
+
+    pClassPool = (const RegisterMapClassPool*) pDexFile->pRegisterMapPool;
+    if (pClassPool == NULL)
+        return NULL;
+
+    if (classIdx >= pClassPool->numClasses) {
+        ALOGE("bad class index (%d vs %d)", classIdx, pClassPool->numClasses);
+        dvmAbort();
+    }
+
+    u4 classOffset = pClassPool->classDataOffset[classIdx];
+    if (classOffset == 0) {
+        ALOGV("+++ no map for classIdx=%d", classIdx);
+        return NULL;
+    }
+
+    pMethodPool =
+        (const RegisterMapMethodPool*) (((u1*) pClassPool) + classOffset);
+    if (pNumMaps != NULL)
+        *pNumMaps = pMethodPool->methodCount;
+    return pMethodPool->methodData;
+}
+
+/*
+ * This advances "*pPtr" and returns its original value.
+ */
+const RegisterMap* dvmRegisterMapGetNext(const void** pPtr)
+{
+    const RegisterMap* pMap = (const RegisterMap*) *pPtr;
+
+    *pPtr = /*align32*/(((u1*) pMap) + computeRegisterMapSize(pMap));
+    LOGVV("getNext: %p -> %p (f=%#x w=%d e=%d)",
+        pMap, *pPtr, pMap->format, pMap->regWidth,
+        dvmRegisterMapGetNumEntries(pMap));
+    return pMap;
+}
+
+
+/*
+ * ===========================================================================
+ *      Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Return the data for the specified address, or NULL if not found.
+ *
+ * The result must be released with dvmReleaseRegisterMapLine().
+ */
+const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr)
+{
+    int addrWidth, lineWidth;
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    assert(numEntries > 0);
+
+    switch (format) {
+    case kRegMapFormatNone:
+        return NULL;
+    case kRegMapFormatCompact8:
+        addrWidth = 1;
+        break;
+    case kRegMapFormatCompact16:
+        addrWidth = 2;
+        break;
+    default:
+        ALOGE("Unknown format %d", format);
+        dvmAbort();
+        return NULL;
+    }
+
+    lineWidth = addrWidth + pMap->regWidth;
+
+    /*
+     * Find the appropriate entry.  Many maps are very small, some are very
+     * large.
+     */
+    static const int kSearchThreshold = 8;
+    const u1* data = NULL;
+    int lineAddr;
+
+    if (numEntries < kSearchThreshold) {
+        int i;
+        data = pMap->data;
+        for (i = numEntries; i > 0; i--) {
+            lineAddr = data[0];
+            if (addrWidth > 1)
+                lineAddr |= data[1] << 8;
+            if (lineAddr == addr)
+                return data + addrWidth;
+
+            data += lineWidth;
+        }
+        assert(data == pMap->data + lineWidth * numEntries);
+    } else {
+        int hi, lo, mid;
+
+        lo = 0;
+        hi = numEntries -1;
+
+        while (hi >= lo) {
+            mid = (hi + lo) / 2;
+            data = pMap->data + lineWidth * mid;
+
+            lineAddr = data[0];
+            if (addrWidth > 1)
+                lineAddr |= data[1] << 8;
+
+            if (addr > lineAddr) {
+                lo = mid + 1;
+            } else if (addr < lineAddr) {
+                hi = mid - 1;
+            } else {
+                return data + addrWidth;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Compare two register maps.
+ *
+ * Returns 0 if they're equal, nonzero if not.
+ */
+static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2)
+{
+    size_t size1, size2;
+
+    size1 = computeRegisterMapSize(pMap1);
+    size2 = computeRegisterMapSize(pMap2);
+    if (size1 != size2) {
+        ALOGI("compareMaps: size mismatch (%zd vs %zd)", size1, size2);
+        return -1;
+    }
+
+    if (memcmp(pMap1, pMap2, size1) != 0) {
+        ALOGI("compareMaps: content mismatch");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Get the expanded form of the register map associated with the method.
+ *
+ * If the map is already in one of the uncompressed formats, we return
+ * immediately.  Otherwise, we expand the map and replace method's register
+ * map pointer, freeing it if it was allocated on the heap.
+ *
+ * NOTE: this function is not synchronized; external locking is mandatory
+ * (unless we're in the zygote, where single-threaded access is guaranteed).
+ */
+const RegisterMap* dvmGetExpandedRegisterMap0(Method* method)
+{
+    const RegisterMap* curMap = method->registerMap;
+    RegisterMap* newMap;
+
+    if (curMap == NULL)
+        return NULL;
+
+    /* sanity check to ensure this isn't called w/o external locking */
+    /* (if we use this at a time other than during GC, fix/remove this test) */
+    if (true) {
+        if (!gDvm.zygote && dvmTryLockMutex(&gDvm.gcHeapLock) == 0) {
+            ALOGE("GLITCH: dvmGetExpandedRegisterMap not called at GC time");
+            dvmAbort();
+        }
+    }
+
+    RegisterMapFormat format = dvmRegisterMapGetFormat(curMap);
+    switch (format) {
+    case kRegMapFormatCompact8:
+    case kRegMapFormatCompact16:
+        if (REGISTER_MAP_VERBOSE) {
+            if (dvmRegisterMapGetOnHeap(curMap)) {
+                ALOGD("RegMap: already expanded: %s.%s",
+                    method->clazz->descriptor, method->name);
+            } else {
+                ALOGD("RegMap: stored w/o compression: %s.%s",
+                    method->clazz->descriptor, method->name);
+            }
+        }
+        return curMap;
+    case kRegMapFormatDifferential:
+        newMap = uncompressMapDifferential(curMap);
+        break;
+    default:
+        ALOGE("Unknown format %d in dvmGetExpandedRegisterMap", format);
+        dvmAbort();
+        newMap = NULL;      // make gcc happy
+    }
+
+    if (newMap == NULL) {
+        ALOGE("Map failed to uncompress (fmt=%d) %s.%s",
+            format, method->clazz->descriptor, method->name);
+        return NULL;
+    }
+
+#ifdef REGISTER_MAP_STATS
+    /*
+     * Gather and display some stats.
+     */
+    {
+        MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+        pStats->numExpandedMaps++;
+        pStats->totalExpandedMapSize += computeRegisterMapSize(newMap);
+        ALOGD("RMAP: count=%d size=%d",
+            pStats->numExpandedMaps, pStats->totalExpandedMapSize);
+    }
+#endif
+
+    IF_ALOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGV("Expanding map -> %s.%s:%s",
+            method->clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    /*
+     * Update method, and free compressed map if it was sitting on the heap.
+     */
+    dvmSetRegisterMap(method, newMap);
+
+    if (dvmRegisterMapGetOnHeap(curMap))
+        dvmFreeRegisterMap((RegisterMap*) curMap);
+
+    return newMap;
+}
+
+
+/*
+ * ===========================================================================
+ *      Map compression
+ * ===========================================================================
+ */
+
+/*
+Notes on map compression
+
+The idea is to create a compressed form that will be uncompressed before
+use, with the output possibly saved in a cache.  This means we can use an
+approach that is unsuited for random access if we choose.
+
+In the event that a map simply does not work with our compression scheme,
+it's reasonable to store the map without compression.  In the future we
+may want to have more than one compression scheme, and try each in turn,
+retaining the best.  (We certainly want to keep the uncompressed form if it
+turns out to be smaller or even slightly larger than the compressed form.)
+
+Each entry consists of an address and a bit vector.  Adjacent entries are
+strongly correlated, suggesting differential encoding.
+
+
+Ideally we would avoid outputting adjacent entries with identical
+bit vectors.  However, the register values at a given address do not
+imply anything about the set of valid registers at subsequent addresses.
+We therefore cannot omit an entry.
+
+  If the thread stack has a PC at an address without a corresponding
+  entry in the register map, we must conservatively scan the registers in
+  that thread.  This can happen when single-stepping in the debugger,
+  because the debugger is allowed to invoke arbitrary methods when
+  a thread is stopped at a breakpoint.  If we can guarantee that a GC
+  thread scan will never happen while the debugger has that thread stopped,
+  then we can lift this restriction and simply omit entries that don't
+  change the bit vector from its previous state.
+
+Each entry advances the address value by at least 1 (measured in 16-bit
+"code units").  Looking at the bootclasspath entries, advancing by 2 units
+is most common.  Advances by 1 unit are far less common than advances by
+2 units, but more common than 5, and things fall off rapidly.  Gaps of
+up to 220 code units appear in some computationally intensive bits of code,
+but are exceedingly rare.
+
+If we sum up the number of transitions in a couple of ranges in framework.jar:
+  [1,4]: 188998 of 218922 gaps (86.3%)
+  [1,7]: 211647 of 218922 gaps (96.7%)
+Using a 3-bit delta, with one value reserved as an escape code, should
+yield good results for the address.
+
+These results would change dramatically if we reduced the set of GC
+points by e.g. removing instructions like integer divide that are only
+present because they can throw and cause an allocation.
+
+We also need to include an "initial gap", because the first few instructions
+in a method may not be GC points.
+
+
+By observation, many entries simply repeat the previous bit vector, or
+change only one or two bits.  (This is with type-precise information;
+the rate of change of bits will be different if live-precise information
+is factored in).
+
+Looking again at adjacent entries in framework.jar:
+  0 bits changed: 63.0%
+  1 bit changed: 32.2%
+After that it falls off rapidly, e.g. the number of entries with 2 bits
+changed is usually less than 1/10th of the number of entries with 1 bit
+changed.  A solution that allows us to encode 0- or 1- bit changes
+efficiently will do well.
+
+We still need to handle cases where a large number of bits change.  We
+probably want a way to drop in a full copy of the bit vector when it's
+smaller than the representation of multiple bit changes.
+
+
+The bit-change information can be encoded as an index that tells the
+decoder to toggle the state.  We want to encode the index in as few bits
+as possible, but we need to allow for fairly wide vectors (e.g. we have a
+method with 175 registers).  We can deal with this in a couple of ways:
+(1) use an encoding that assumes few registers and has an escape code
+for larger numbers of registers; or (2) use different encodings based
+on how many total registers the method has.  The choice depends to some
+extent on whether methods with large numbers of registers tend to modify
+the first 16 regs more often than the others.
+
+The last N registers hold method arguments.  If the bytecode is expected
+to be examined in a debugger, "dx" ensures that the contents of these
+registers won't change.  Depending upon the encoding format, we may be
+able to take advantage of this.  We still have to encode the initial
+state, but we know we'll never have to output a bit change for the last
+N registers.
+
+Considering only methods with 16 or more registers, the "target octant"
+for register changes looks like this:
+  [ 43.1%, 16.4%, 6.5%, 6.2%, 7.4%, 8.8%, 9.7%, 1.8% ]
+As expected, there are fewer changes at the end of the list where the
+arguments are kept, and more changes at the start of the list because
+register values smaller than 16 can be used in compact Dalvik instructions
+and hence are favored for frequently-used values.  In general, the first
+octant is considerably more active than later entries, the last octant
+is much less active, and the rest are all about the same.
+
+Looking at all bit changes in all methods, 94% are to registers 0-15.  The
+encoding will benefit greatly by favoring the low-numbered registers.
+
+
+Some of the smaller methods have identical maps, and space could be
+saved by simply including a pointer to an earlier definition.  This would
+be best accomplished by specifying a "pointer" format value, followed by
+a 3-byte (or ULEB128) offset.  Implementing this would probably involve
+generating a hash value for each register map and maintaining a hash table.
+
+In some cases there are repeating patterns in the bit vector that aren't
+adjacent.  These could benefit from a dictionary encoding.  This doesn't
+really become useful until the methods reach a certain size though,
+and managing the dictionary may incur more overhead than we want.
+
+Large maps can be compressed significantly.  The trouble is that, when
+we need to use them, we have to uncompress them onto the heap.  We may
+get a better trade-off between storage size and heap usage by refusing to
+compress large maps, so that they can be memory mapped and used directly.
+(OTOH, only about 2% of the maps will ever actually be used.)
+
+
+----- differential format -----
+
+// common header
++00 1B format
++01 1B regWidth
++02 2B numEntries (little-endian)
++04 nB length in bytes of the data that follows, in ULEB128 format
+       (not strictly necessary; allows determination of size w/o full parse)
++05+ 1B initial address (0-127), high bit set if max addr >= 256
++06+ nB initial value for bit vector
+
+// for each entry
++00: CCCCBAAA
+
+  AAA: address difference.  Values from 0 to 6 indicate an increment of 1
+  to 7.  A value of 7 indicates that the address difference is large,
+  and the next byte is a ULEB128-encoded difference value.
+
+  B: determines the meaning of CCCC.
+
+  CCCC: if B is 0, this is the number of the bit to toggle (0-15).
+  If B is 1, this is a count of the number of changed bits (1-14).  A value
+  of 0 means that no bits were changed, and a value of 15 indicates
+  that enough bits were changed that it required less space to output
+  the entire bit vector.
+
++01: (optional) ULEB128-encoded address difference
+
++01+: (optional) one or more ULEB128-encoded bit numbers, OR the entire
+  bit vector.
+
+The most common situation is an entry whose address has changed by 2-4
+code units, has no changes or just a single bit change, and the changed
+register is less than 16.  We should therefore be able to encode a large
+number of entries with a single byte, which is half the size of the
+Compact8 encoding method.
+*/
+
+/*
+ * Compute some stats on an uncompressed register map.
+ */
+#ifdef REGISTER_MAP_STATS
+static void computeMapStats(RegisterMap* pMap, const Method* method)
+{
+    MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+    const u1 format = dvmRegisterMapGetFormat(pMap);
+    const u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+    const u1* rawMap = pMap->data;
+    const u1* prevData = NULL;
+    int ent, addr, prevAddr = -1;
+
+    for (ent = 0; ent < numEntries; ent++) {
+        switch (format) {
+        case kRegMapFormatCompact8:
+            addr = *rawMap++;
+            break;
+        case kRegMapFormatCompact16:
+            addr = *rawMap++;
+            addr |= (*rawMap++) << 8;
+            break;
+        default:
+            /* shouldn't happen */
+            ALOGE("GLITCH: bad format (%d)", format);
+            dvmAbort();
+        }
+
+        const u1* dataStart = rawMap;
+
+        pStats->totalGcPointCount++;
+
+        /*
+         * Gather "gap size" stats, i.e. the difference in addresses between
+         * successive GC points.
+         */
+        if (prevData != NULL) {
+            assert(prevAddr >= 0);
+            int addrDiff = addr - prevAddr;
+
+            if (addrDiff < 0) {
+                ALOGE("GLITCH: address went backward (0x%04x->0x%04x, %s.%s)",
+                    prevAddr, addr, method->clazz->descriptor, method->name);
+            } else if (addrDiff > kMaxGcPointGap) {
+                if (REGISTER_MAP_VERBOSE) {
+                    ALOGI("HEY: addrDiff is %d, max %d (0x%04x->0x%04x %s.%s)",
+                        addrDiff, kMaxGcPointGap, prevAddr, addr,
+                        method->clazz->descriptor, method->name);
+                }
+                /* skip this one */
+            } else {
+                pStats->gcPointGap[addrDiff]++;
+            }
+            pStats->gcGapCount++;
+
+
+            /*
+             * Compare bit vectors in adjacent entries.  We want to count
+             * up the number of bits that differ (to see if we frequently
+             * change 0 or 1 bits) and get a sense for which part of the
+             * vector changes the most often (near the start, middle, end).
+             *
+             * We only do the vector position quantization if we have at
+             * least 16 registers in the method.
+             */
+            int numDiff = 0;
+            float div = (float) kNumUpdatePosns / method->registersSize;
+            int regByte;
+            for (regByte = 0; regByte < pMap->regWidth; regByte++) {
+                int prev, cur, bit;
+
+                prev = prevData[regByte];
+                cur = dataStart[regByte];
+
+                for (bit = 0; bit < 8; bit++) {
+                    if (((prev >> bit) & 1) != ((cur >> bit) & 1)) {
+                        numDiff++;
+
+                        int bitNum = regByte * 8 + bit;
+
+                        if (bitNum < 16)
+                            pStats->updateLT16++;
+                        else
+                            pStats->updateGE16++;
+
+                        if (method->registersSize < 16)
+                            continue;
+
+                        if (bitNum >= method->registersSize) {
+                            /* stuff off the end should be zero in both */
+                            ALOGE("WEIRD: bit=%d (%d/%d), prev=%02x cur=%02x",
+                                bit, regByte, method->registersSize,
+                                prev, cur);
+                            assert(false);
+                        }
+                        int idx = (int) (bitNum * div);
+                        if (!(idx >= 0 && idx < kNumUpdatePosns)) {
+                            ALOGE("FAIL: bitNum=%d (of %d) div=%.3f idx=%d",
+                                bitNum, method->registersSize, div, idx);
+                            assert(false);
+                        }
+                        pStats->updatePosn[idx]++;
+                    }
+                }
+            }
+
+            if (numDiff > kMaxDiffBits) {
+                if (REGISTER_MAP_VERBOSE) {
+                    ALOGI("WOW: numDiff is %d, max %d", numDiff, kMaxDiffBits);
+                }
+            } else {
+                pStats->numDiffBits[numDiff]++;
+            }
+        }
+
+        /* advance to start of next line */
+        rawMap += pMap->regWidth;
+
+        prevAddr = addr;
+        prevData = dataStart;
+    }
+}
+#endif
+
+/*
+ * Compute the difference between two bit vectors.
+ *
+ * If "lebOutBuf" is non-NULL, we output the bit indices in ULEB128 format
+ * as we go.  Otherwise, we just generate the various counts.
+ *
+ * The bit vectors are compared byte-by-byte, so any unused bits at the
+ * end must be zero.
+ *
+ * Returns the number of bytes required to hold the ULEB128 output.
+ *
+ * If "pFirstBitChanged" or "pNumBitsChanged" are non-NULL, they will
+ * receive the index of the first changed bit and the number of changed
+ * bits, respectively.
+ */
+static int computeBitDiff(const u1* bits1, const u1* bits2, int byteWidth,
+    int* pFirstBitChanged, int* pNumBitsChanged, u1* lebOutBuf)
+{
+    int numBitsChanged = 0;
+    int firstBitChanged = -1;
+    int lebSize = 0;
+    int byteNum;
+
+    /*
+     * Run through the vectors, first comparing them at the byte level.  This
+     * will yield a fairly quick result if nothing has changed between them.
+     */
+    for (byteNum = 0; byteNum < byteWidth; byteNum++) {
+        u1 byte1 = *bits1++;
+        u1 byte2 = *bits2++;
+        if (byte1 != byte2) {
+            /*
+             * Walk through the byte, identifying the changed bits.
+             */
+            int bitNum;
+            for (bitNum = 0; bitNum < 8; bitNum++) {
+                if (((byte1 >> bitNum) & 0x01) != ((byte2 >> bitNum) & 0x01)) {
+                    int bitOffset = (byteNum << 3) + bitNum;
+
+                    if (firstBitChanged < 0)
+                        firstBitChanged = bitOffset;
+                    numBitsChanged++;
+
+                    if (lebOutBuf == NULL) {
+                        lebSize += unsignedLeb128Size(bitOffset);
+                    } else {
+                        u1* curBuf = lebOutBuf;
+                        lebOutBuf = writeUnsignedLeb128(lebOutBuf, bitOffset);
+                        lebSize += lebOutBuf - curBuf;
+                    }
+                }
+            }
+        }
+    }
+
+    if (numBitsChanged > 0)
+        assert(firstBitChanged >= 0);
+
+    if (pFirstBitChanged != NULL)
+        *pFirstBitChanged = firstBitChanged;
+    if (pNumBitsChanged != NULL)
+        *pNumBitsChanged = numBitsChanged;
+
+    return lebSize;
+}
+
+/*
+ * Compress the register map with differential encoding.
+ *
+ * "meth" is only needed for debug output.
+ *
+ * On success, returns a newly-allocated RegisterMap.  If the map is not
+ * compatible for some reason, or fails to get smaller, this will return NULL.
+ */
+static RegisterMap* compressMapDifferential(const RegisterMap* pMap,
+    const Method* meth)
+{
+    RegisterMap* pNewMap = NULL;
+    int origSize = computeRegisterMapSize(pMap);
+    u1* tmpPtr;
+    int addrWidth, regWidth, numEntries;
+    bool debug = false;
+
+    if (false &&
+        strcmp(meth->clazz->descriptor, "Landroid/text/StaticLayout;") == 0 &&
+        strcmp(meth->name, "generate") == 0)
+    {
+        debug = true;
+    }
+
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    switch (format) {
+    case kRegMapFormatCompact8:
+        addrWidth = 1;
+        break;
+    case kRegMapFormatCompact16:
+        addrWidth = 2;
+        break;
+    default:
+        ALOGE("ERROR: can't compress map with format=%d", format);
+        return NULL;
+    }
+
+    regWidth = dvmRegisterMapGetRegWidth(pMap);
+    numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    if (debug) {
+        ALOGI("COMPRESS: %s.%s aw=%d rw=%d ne=%d",
+            meth->clazz->descriptor, meth->name,
+            addrWidth, regWidth, numEntries);
+        dumpRegisterMap(pMap, -1);
+    }
+
+    if (numEntries <= 1) {
+        ALOGV("Can't compress map with 0 or 1 entries");
+        return NULL;
+    }
+
+    /*
+     * We don't know how large the compressed data will be.  It's possible
+     * for it to expand and become larger than the original.  The header
+     * itself is variable-sized, so we generate everything into a temporary
+     * buffer and then copy it to form-fitting storage once we know how big
+     * it will be (and that it's smaller than the original).
+     *
+     * If we use a size that is equal to the size of the input map plus
+     * a value longer than a single entry can possibly expand to, we need
+     * only check for overflow at the end of each entry.  The worst case
+     * for a single line is (1 + <ULEB8 address> + <full copy of vector>).
+     * Addresses are 16 bits, so that's (1 + 3 + regWidth).
+     *
+     * The initial address offset and bit vector will take up less than
+     * or equal to the amount of space required when uncompressed -- large
+     * initial offsets are rejected.
+     */
+    UniquePtr<u1[]> tmpBuf(new u1[origSize + (1 + 3 + regWidth)]);
+    if (tmpBuf.get() == NULL)
+        return NULL;
+
+    tmpPtr = tmpBuf.get();
+
+    const u1* mapData = pMap->data;
+    const u1* prevBits;
+    u2 addr, prevAddr;
+
+    addr = *mapData++;
+    if (addrWidth > 1)
+        addr |= (*mapData++) << 8;
+
+    if (addr >= 128) {
+        ALOGV("Can't compress map with starting address >= 128");
+        return NULL;
+    }
+
+    /*
+     * Start by writing the initial address and bit vector data.  The high
+     * bit of the initial address is used to indicate the required address
+     * width (which the decoder can't otherwise determine without parsing
+     * the compressed data).
+     */
+    *tmpPtr++ = addr | (addrWidth > 1 ? 0x80 : 0x00);
+    memcpy(tmpPtr, mapData, regWidth);
+
+    prevBits = mapData;
+    prevAddr = addr;
+
+    tmpPtr += regWidth;
+    mapData += regWidth;
+
+    /*
+     * Loop over all following entries.
+     */
+    for (int entry = 1; entry < numEntries; entry++) {
+        int addrDiff;
+        u1 key;
+
+        /*
+         * Pull out the address and figure out how to encode it.
+         */
+        addr = *mapData++;
+        if (addrWidth > 1)
+            addr |= (*mapData++) << 8;
+
+        if (debug)
+            ALOGI(" addr=0x%04x ent=%d (aw=%d)", addr, entry, addrWidth);
+
+        addrDiff = addr - prevAddr;
+        assert(addrDiff > 0);
+        if (addrDiff < 8) {
+            /* small difference, encode in 3 bits */
+            key = addrDiff -1;          /* set 00000AAA */
+            if (debug)
+                ALOGI(" : small %d, key=0x%02x", addrDiff, key);
+        } else {
+            /* large difference, output escape code */
+            key = 0x07;                 /* escape code for AAA */
+            if (debug)
+                ALOGI(" : large %d, key=0x%02x", addrDiff, key);
+        }
+
+        int numBitsChanged, firstBitChanged, lebSize;
+
+        lebSize = computeBitDiff(prevBits, mapData, regWidth,
+            &firstBitChanged, &numBitsChanged, NULL);
+
+        if (debug) {
+            ALOGI(" : diff fbc=%d nbc=%d ls=%d (rw=%d)",
+                firstBitChanged, numBitsChanged, lebSize, regWidth);
+        }
+
+        if (numBitsChanged == 0) {
+            /* set B to 1 and CCCC to zero to indicate no bits were changed */
+            key |= 0x08;
+            if (debug) ALOGI(" : no bits changed");
+        } else if (numBitsChanged == 1 && firstBitChanged < 16) {
+            /* set B to 0 and CCCC to the index of the changed bit */
+            key |= firstBitChanged << 4;
+            if (debug) ALOGI(" : 1 low bit changed");
+        } else if (numBitsChanged < 15 && lebSize < regWidth) {
+            /* set B to 1 and CCCC to the number of bits */
+            key |= 0x08 | (numBitsChanged << 4);
+            if (debug) ALOGI(" : some bits changed");
+        } else {
+            /* set B to 1 and CCCC to 0x0f so we store the entire vector */
+            key |= 0x08 | 0xf0;
+            if (debug) ALOGI(" : encode original");
+        }
+
+        /*
+         * Encode output.  Start with the key, follow with the address
+         * diff (if it didn't fit in 3 bits), then the changed bit info.
+         */
+        *tmpPtr++ = key;
+        if ((key & 0x07) == 0x07)
+            tmpPtr = writeUnsignedLeb128(tmpPtr, addrDiff);
+
+        if ((key & 0x08) != 0) {
+            int bitCount = key >> 4;
+            if (bitCount == 0) {
+                /* nothing changed, no additional output required */
+            } else if (bitCount == 15) {
+                /* full vector is most compact representation */
+                memcpy(tmpPtr, mapData, regWidth);
+                tmpPtr += regWidth;
+            } else {
+                /* write bit indices in LEB128 format */
+                (void) computeBitDiff(prevBits, mapData, regWidth,
+                    NULL, NULL, tmpPtr);
+                tmpPtr += lebSize;
+            }
+        } else {
+            /* single-bit changed, value encoded in key byte */
+        }
+
+        prevBits = mapData;
+        prevAddr = addr;
+        mapData += regWidth;
+
+        /*
+         * See if we've run past the original size.
+         */
+        if (tmpPtr - tmpBuf.get() >= origSize) {
+            if (debug) {
+                ALOGD("Compressed size >= original (%d vs %d): %s.%s",
+                    tmpPtr - tmpBuf.get(), origSize,
+                    meth->clazz->descriptor, meth->name);
+            }
+            return NULL;
+        }
+    }
+
+    /*
+     * Create a RegisterMap with the contents.
+     *
+     * TODO: consider using a threshold other than merely ">=".  We would
+     * get poorer compression but potentially use less native heap space.
+     */
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    int newDataSize = tmpPtr - tmpBuf.get();
+    int newMapSize;
+
+    newMapSize = kHeaderSize + unsignedLeb128Size(newDataSize) + newDataSize;
+    if (newMapSize >= origSize) {
+        if (debug) {
+            ALOGD("Final comp size >= original (%d vs %d): %s.%s",
+                newMapSize, origSize, meth->clazz->descriptor, meth->name);
+        }
+        return NULL;
+    }
+
+    pNewMap = (RegisterMap*) malloc(newMapSize);
+    if (pNewMap == NULL)
+        return NULL;
+    dvmRegisterMapSetFormat(pNewMap, kRegMapFormatDifferential);
+    dvmRegisterMapSetOnHeap(pNewMap, true);
+    dvmRegisterMapSetRegWidth(pNewMap, regWidth);
+    dvmRegisterMapSetNumEntries(pNewMap, numEntries);
+
+    tmpPtr = pNewMap->data;
+    tmpPtr = writeUnsignedLeb128(tmpPtr, newDataSize);
+    memcpy(tmpPtr, tmpBuf.get(), newDataSize);
+
+    if (REGISTER_MAP_VERBOSE) {
+        ALOGD("Compression successful (%d -> %d) from aw=%d rw=%d ne=%d",
+            computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap),
+            addrWidth, regWidth, numEntries);
+    }
+
+    return pNewMap;
+}
+
+/*
+ * Toggle the value of the "idx"th bit in "ptr".
+ */
+static inline void toggleBit(u1* ptr, int idx)
+{
+    ptr[idx >> 3] ^= 1 << (idx & 0x07);
+}
+
+/*
+ * Expand a compressed map to an uncompressed form.
+ *
+ * Returns a newly-allocated RegisterMap on success, or NULL on failure.
+ *
+ * TODO: consider using the linear allocator or a custom allocator with
+ * LRU replacement for these instead of the native heap.
+ */
+static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap)
+{
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    RegisterMapFormat newFormat;
+    int regWidth, numEntries, newAddrWidth, newMapSize;
+
+    if (format != kRegMapFormatDifferential) {
+        ALOGE("Not differential (%d)", format);
+        return NULL;
+    }
+
+    regWidth = dvmRegisterMapGetRegWidth(pMap);
+    numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    /* get the data size; we can check this at the end */
+    const u1* srcPtr = pMap->data;
+    int expectedSrcLen = readUnsignedLeb128(&srcPtr);
+    const u1* srcStart = srcPtr;
+
+    /* get the initial address and the 16-bit address flag */
+    int addr = *srcPtr & 0x7f;
+    if ((*srcPtr & 0x80) == 0) {
+        newFormat = kRegMapFormatCompact8;
+        newAddrWidth = 1;
+    } else {
+        newFormat = kRegMapFormatCompact16;
+        newAddrWidth = 2;
+    }
+    srcPtr++;
+
+    /* now we know enough to allocate the new map */
+    if (REGISTER_MAP_VERBOSE) {
+        ALOGI("Expanding to map aw=%d rw=%d ne=%d",
+            newAddrWidth, regWidth, numEntries);
+    }
+    newMapSize = kHeaderSize + (newAddrWidth + regWidth) * numEntries;
+    RegisterMap* pNewMap = (RegisterMap*) malloc(newMapSize);
+
+    if (pNewMap == NULL)
+      return NULL;
+
+    dvmRegisterMapSetFormat(pNewMap, newFormat);
+    dvmRegisterMapSetOnHeap(pNewMap, true);
+    dvmRegisterMapSetRegWidth(pNewMap, regWidth);
+    dvmRegisterMapSetNumEntries(pNewMap, numEntries);
+
+    /*
+     * Write the start address and initial bits to the new map.
+     */
+    u1* dstPtr = pNewMap->data;
+
+    *dstPtr++ = addr & 0xff;
+    if (newAddrWidth > 1)
+        *dstPtr++ = (u1) (addr >> 8);
+
+    memcpy(dstPtr, srcPtr, regWidth);
+
+    int prevAddr = addr;
+    const u1* prevBits = dstPtr;    /* point at uncompressed data */
+
+    dstPtr += regWidth;
+    srcPtr += regWidth;
+
+    /*
+     * Walk through, uncompressing one line at a time.
+     */
+    int entry;
+    for (entry = 1; entry < numEntries; entry++) {
+        int addrDiff;
+        u1 key;
+
+        key = *srcPtr++;
+
+        /* get the address */
+        if ((key & 0x07) == 7) {
+            /* address diff follows in ULEB128 */
+            addrDiff = readUnsignedLeb128(&srcPtr);
+        } else {
+            addrDiff = (key & 0x07) +1;
+        }
+
+        addr = prevAddr + addrDiff;
+        *dstPtr++ = addr & 0xff;
+        if (newAddrWidth > 1)
+            *dstPtr++ = (u1) (addr >> 8);
+
+        /* unpack the bits */
+        if ((key & 0x08) != 0) {
+            int bitCount = (key >> 4);
+            if (bitCount == 0) {
+                /* no bits changed, just copy previous */
+                memcpy(dstPtr, prevBits, regWidth);
+            } else if (bitCount == 15) {
+                /* full copy of bit vector is present; ignore prevBits */
+                memcpy(dstPtr, srcPtr, regWidth);
+                srcPtr += regWidth;
+            } else {
+                /* copy previous bits and modify listed indices */
+                memcpy(dstPtr, prevBits, regWidth);
+                while (bitCount--) {
+                    int bitIndex = readUnsignedLeb128(&srcPtr);
+                    toggleBit(dstPtr, bitIndex);
+                }
+            }
+        } else {
+            /* copy previous bits and modify the specified one */
+            memcpy(dstPtr, prevBits, regWidth);
+
+            /* one bit, from 0-15 inclusive, was changed */
+            toggleBit(dstPtr, key >> 4);
+        }
+
+        prevAddr = addr;
+        prevBits = dstPtr;
+        dstPtr += regWidth;
+    }
+
+    if (dstPtr - (u1*) pNewMap != newMapSize) {
+        ALOGE("ERROR: output %d bytes, expected %d",
+            dstPtr - (u1*) pNewMap, newMapSize);
+        free(pNewMap);
+        return NULL;
+    }
+
+    if (srcPtr - srcStart != expectedSrcLen) {
+        ALOGE("ERROR: consumed %d bytes, expected %d",
+            srcPtr - srcStart, expectedSrcLen);
+        free(pNewMap);
+        return NULL;
+    }
+
+    if (REGISTER_MAP_VERBOSE) {
+        ALOGD("Expansion successful (%d -> %d)",
+            computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap));
+    }
+
+    return pNewMap;
+}
diff --git a/vm/analysis/RegisterMap.h b/vm/analysis/RegisterMap.h
new file mode 100644
index 0000000..2046899
--- /dev/null
+++ b/vm/analysis/RegisterMap.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Declaration of register map data structure and related functions.
+ *
+ * These structures should be treated as opaque through most of the VM.
+ */
+#ifndef DALVIK_REGISTERMAP_H_
+#define DALVIK_REGISTERMAP_H_
+
+#include "analysis/VerifySubs.h"
+#include "analysis/CodeVerify.h"
+
+/*
+ * Format enumeration for RegisterMap data area.
+ */
+enum RegisterMapFormat {
+    kRegMapFormatUnknown = 0,
+    kRegMapFormatNone,          /* indicates no map data follows */
+    kRegMapFormatCompact8,      /* compact layout, 8-bit addresses */
+    kRegMapFormatCompact16,     /* compact layout, 16-bit addresses */
+    kRegMapFormatDifferential,  /* compressed, differential encoding */
+
+    kRegMapFormatOnHeap = 0x80, /* bit flag, indicates allocation on heap */
+};
+
+/*
+ * This is a single variable-size structure.  It may be allocated on the
+ * heap or mapped out of a (post-dexopt) DEX file.
+ *
+ * 32-bit alignment of the structure is NOT guaranteed.  This makes it a
+ * little awkward to deal with as a structure; to avoid accidents we use
+ * only byte types.  Multi-byte values are little-endian.
+ *
+ * Size of (format==FormatNone): 1 byte
+ * Size of (format==FormatCompact8): 4 + (1 + regWidth) * numEntries
+ * Size of (format==FormatCompact16): 4 + (2 + regWidth) * numEntries
+ */
+struct RegisterMap {
+    /* header */
+    u1      format;         /* enum RegisterMapFormat; MUST be first entry */
+    u1      regWidth;       /* bytes per register line, 1+ */
+    u1      numEntries[2];  /* number of entries */
+
+    /* raw data starts here; need not be aligned */
+    u1      data[1];
+};
+
+bool dvmRegisterMapStartup(void);
+void dvmRegisterMapShutdown(void);
+
+/*
+ * Get the format.
+ */
+INLINE RegisterMapFormat dvmRegisterMapGetFormat(const RegisterMap* pMap) {
+    return (RegisterMapFormat)(pMap->format & ~(kRegMapFormatOnHeap));
+}
+
+/*
+ * Set the format.
+ */
+INLINE void dvmRegisterMapSetFormat(RegisterMap* pMap, RegisterMapFormat format)
+{
+    pMap->format &= kRegMapFormatOnHeap;
+    pMap->format |= format;
+}
+
+/*
+ * Get the "on heap" flag.
+ */
+INLINE bool dvmRegisterMapGetOnHeap(const RegisterMap* pMap) {
+    return (pMap->format & kRegMapFormatOnHeap) != 0;
+}
+
+/*
+ * Get the register bit vector width, in bytes.
+ */
+INLINE u1 dvmRegisterMapGetRegWidth(const RegisterMap* pMap) {
+    return pMap->regWidth;
+}
+
+/*
+ * Set the register bit vector width, in bytes.
+ */
+INLINE void dvmRegisterMapSetRegWidth(RegisterMap* pMap, int regWidth) {
+    pMap->regWidth = regWidth;
+}
+
+/*
+ * Set the "on heap" flag.
+ */
+INLINE void dvmRegisterMapSetOnHeap(RegisterMap* pMap, bool val) {
+    if (val)
+        pMap->format |= kRegMapFormatOnHeap;
+    else
+        pMap->format &= ~(kRegMapFormatOnHeap);
+}
+
+/*
+ * Get the number of entries in this map.
+ */
+INLINE u2 dvmRegisterMapGetNumEntries(const RegisterMap* pMap) {
+    return pMap->numEntries[0] | (pMap->numEntries[1] << 8);
+}
+
+/*
+ * Set the number of entries in this map.
+ */
+INLINE void dvmRegisterMapSetNumEntries(RegisterMap* pMap, u2 numEntries) {
+    pMap->numEntries[0] = (u1) numEntries;
+    pMap->numEntries[1] = numEntries >> 8;
+}
+
+/*
+ * Retrieve the bit vector for the specified address.  This is a pointer
+ * to the bit data from an uncompressed map, or to a temporary copy of
+ * data from a compressed map.
+ *
+ * The caller must call dvmReleaseRegisterMapLine() with the result.
+ *
+ * Returns NULL if not found.
+ */
+const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr);
+
+/*
+ * Release "data".
+ *
+ * If "pMap" points to a compressed map from which we have expanded a
+ * single line onto the heap, this will free "data"; otherwise, it does
+ * nothing.
+ *
+ * TODO: decide if this is still a useful concept.
+ */
+INLINE void dvmReleaseRegisterMapLine(const RegisterMap* pMap, const u1* data)
+{}
+
+
+/*
+ * A pool of register maps for methods associated with a single class.
+ *
+ * Each entry is a 4-byte method index followed by the 32-bit-aligned
+ * RegisterMap.  The size of the RegisterMap is determined by parsing
+ * the map.  The lack of an index reduces random access speed, but we
+ * should be doing that rarely (during class load) and it saves space.
+ *
+ * These structures are 32-bit aligned.
+ */
+struct RegisterMapMethodPool {
+    u2      methodCount;            /* chiefly used as a sanity check */
+
+    /* stream of per-method data starts here */
+    u4      methodData[1];
+};
+
+/*
+ * Header for the memory-mapped RegisterMap pool in the DEX file.
+ *
+ * The classDataOffset table provides offsets from the start of the
+ * RegisterMapPool structure.  There is one entry per class (including
+ * interfaces, which can have static initializers).
+ *
+ * The offset points to a RegisterMapMethodPool.
+ *
+ * These structures are 32-bit aligned.
+ */
+struct RegisterMapClassPool {
+    u4      numClasses;
+
+    /* offset table starts here, 32-bit aligned; offset==0 means no data */
+    u4      classDataOffset[1];
+};
+
+/*
+ * Find the register maps for this class.  (Used during class loading.)
+ * If "pNumMaps" is non-NULL, it will return the number of maps in the set.
+ *
+ * Returns NULL if none is available.
+ */
+const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx,
+    u4* pNumMaps);
+
+/*
+ * Get the register map for the next method.  "*pPtr" will be advanced past
+ * the end of the map.  (Used during class loading.)
+ *
+ * This should initially be called with the result from
+ * dvmRegisterMapGetClassData().
+ */
+const RegisterMap* dvmRegisterMapGetNext(const void** pPtr);
+
+/*
+ * This holds some meta-data while we construct the set of register maps
+ * for a DEX file.
+ *
+ * In particular, it keeps track of our temporary mmap region so we can
+ * free it later.
+ */
+struct RegisterMapBuilder {
+    /* public */
+    void*       data;
+    size_t      size;
+
+    /* private */
+    MemMapping  memMap;
+};
+
+/*
+ * Generate a register map set for all verified classes in "pDvmDex".
+ */
+RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex);
+
+/*
+ * Free the builder.
+ */
+void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder);
+
+/*
+ * Generate the register map for a method that has just been verified
+ * (i.e. we're doing this as part of verification).
+ *
+ * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+ */
+RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata);
+
+/*
+ * Get the expanded form of the register map associated with the specified
+ * method.  May update method->registerMap, possibly freeing the previous
+ * map.
+ *
+ * Returns NULL on failure (e.g. unable to expand map).
+ *
+ * NOTE: this function is not synchronized; external locking is mandatory.
+ * (This is expected to be called at GC time.)
+ */
+const RegisterMap* dvmGetExpandedRegisterMap0(Method* method);
+INLINE const RegisterMap* dvmGetExpandedRegisterMap(Method* method)
+{
+    const RegisterMap* curMap = method->registerMap;
+    if (curMap == NULL)
+        return NULL;
+    RegisterMapFormat format = dvmRegisterMapGetFormat(curMap);
+    if (format == kRegMapFormatCompact8 || format == kRegMapFormatCompact16) {
+        return curMap;
+    } else {
+        return dvmGetExpandedRegisterMap0(method);
+    }
+}
+
+/* dump stats gathered during register map creation process */
+void dvmRegisterMapDumpStats(void);
+
+#endif  // DALVIK_REGISTERMAP_H_
diff --git a/vm/analysis/VerifySubs.cpp b/vm/analysis/VerifySubs.cpp
new file mode 100644
index 0000000..7240dd0
--- /dev/null
+++ b/vm/analysis/VerifySubs.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik verification subroutines.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "libdex/InstrUtils.h"
+
+
+/*
+ * This is used when debugging to apply a magnifying glass to the
+ * verification of a particular method.
+ */
+bool dvmWantVerboseVerification(const Method* meth)
+{
+    return false;       /* COMMENT OUT to enable verbose debugging */
+
+    const char* cd = "Lcom/android/server/am/ActivityManagerService;";
+    const char* mn = "trimApplications";
+    const char* sg = "()V";
+    return (strcmp(meth->clazz->descriptor, cd) == 0 &&
+            dvmCompareNameDescriptorAndMethod(mn, sg, meth) == 0);
+}
+
+/*
+ * Output a code verifier warning message.  For the pre-verifier it's not
+ * a big deal if something fails (and it may even be expected), but if
+ * we're doing just-in-time verification it's significant.
+ */
+void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
+{
+    va_list ap;
+    int logLevel;
+
+    if (gDvm.optimizing) {
+        return;
+        //logLevel = ANDROID_LOG_DEBUG;
+    } else {
+        logLevel = ANDROID_LOG_WARN;
+    }
+
+    va_start(ap, format);
+    LOG_PRI_VA(logLevel, LOG_TAG, format, ap);
+    if (meth != NULL) {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        LOG_PRI(logLevel, LOG_TAG, "VFY:  rejected %s.%s %s",
+            meth->clazz->descriptor, meth->name, desc);
+        free(desc);
+    }
+}
+
+/*
+ * Show a relatively human-readable message describing the failure to
+ * resolve a class.
+ *
+ * TODO: this is somewhat misleading when resolution fails because of
+ * illegal access rather than nonexistent class.
+ */
+void dvmLogUnableToResolveClass(const char* missingClassDescr,
+    const Method* meth)
+{
+    if (gDvm.optimizing) {
+        return;
+    }
+
+    std::string dotMissingClass = dvmHumanReadableDescriptor(missingClassDescr);
+    std::string dotFromClass = dvmHumanReadableDescriptor(meth->clazz->descriptor);
+    ALOGE("Could not find class '%s', referenced from method %s.%s",
+            dotMissingClass.c_str(), dotFromClass.c_str(), meth->name);
+}
+
+/*
+ * Extract the relative offset from a branch instruction.
+ *
+ * Returns "false" on failure (e.g. this isn't a branch instruction).
+ */
+bool dvmGetBranchOffset(const Method* meth, const InsnFlags* insnFlags,
+    int curOffset, s4* pOffset, bool* pConditional)
+{
+    const u2* insns = meth->insns + curOffset;
+
+    switch (*insns & 0xff) {
+    case OP_GOTO:
+        *pOffset = ((s2) *insns) >> 8;
+        *pConditional = false;
+        break;
+    case OP_GOTO_32:
+        *pOffset = insns[1] | (((u4) insns[2]) << 16);
+        *pConditional = false;
+        break;
+    case OP_GOTO_16:
+        *pOffset = (s2) insns[1];
+        *pConditional = false;
+        break;
+    case OP_IF_EQ:
+    case OP_IF_NE:
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+        *pOffset = (s2) insns[1];
+        *pConditional = true;
+        break;
+    default:
+        return false;
+        break;
+    }
+
+    return true;
+}
diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h
new file mode 100644
index 0000000..330be57
--- /dev/null
+++ b/vm/analysis/VerifySubs.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik bytecode verification subroutines.
+ */
+#ifndef DALVIK_VERIFYSUBS_H_
+#define DALVIK_VERIFYSUBS_H_
+
+/*
+ * InsnFlags is a 32-bit integer with the following layout:
+ *   0-15  instruction length (or 0 if this address doesn't hold an opcode)
+ *  16-31  single bit flags:
+ *    InTry: in "try" block; exceptions thrown here may be caught locally
+ *    BranchTarget: other instructions can branch to this instruction
+ *    GcPoint: this instruction is a GC safe point
+ *    Visited: verifier has examined this instruction at least once
+ *    Changed: set/cleared as bytecode verifier runs
+ */
+typedef u4 InsnFlags;
+
+#define kInsnFlagWidthMask      0x0000ffff
+#define kInsnFlagInTry          (1 << 16)
+#define kInsnFlagBranchTarget   (1 << 17)
+#define kInsnFlagGcPoint        (1 << 18)
+#define kInsnFlagVisited        (1 << 30)
+#define kInsnFlagChanged        (1 << 31)
+
+/* add opcode widths to InsnFlags */
+bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
+    int* pNewInstanceCount);
+
+/* set the "in try" flag for sections of code wrapped with a "try" block */
+bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags);
+
+/* verification failure reporting */
+#define LOG_VFY(...)                dvmLogVerifyFailure(NULL, __VA_ARGS__)
+#define LOG_VFY_METH(_meth, ...)    dvmLogVerifyFailure(_meth, __VA_ARGS__)
+
+/* log verification failure with optional method info */
+void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+
+/* log verification failure due to resolution trouble */
+void dvmLogUnableToResolveClass(const char* missingClassDescr,
+    const Method* meth);
+
+/* extract the relative branch offset from a branch instruction */
+bool dvmGetBranchOffset(const Method* meth, const InsnFlags* insnFlags,
+    int curOffset, s4* pOffset, bool* pConditional);
+
+/* return a RegType enumeration value that "value" just fits into */
+char dvmDetermineCat1Const(s4 value);
+
+/* debugging */
+bool dvmWantVerboseVerification(const Method* meth);
+
+#endif  // DALVIK_VERIFYSUBS_H_
diff --git a/vm/analysis/VfyBasicBlock.cpp b/vm/analysis/VfyBasicBlock.cpp
new file mode 100644
index 0000000..d6c4b79
--- /dev/null
+++ b/vm/analysis/VfyBasicBlock.cpp
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Verifier basic block functions.
+ */
+#include "Dalvik.h"
+#include "analysis/VfyBasicBlock.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/VerifySubs.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+
+/*
+ * Extract the list of catch handlers from "pTry" into "addrBuf".
+ *
+ * Returns the size of the catch handler list.  If the return value
+ * exceeds "addrBufSize", the items at the end of the list will not be
+ * represented in the output array, and this function should be called
+ * again with a larger buffer.
+ */
+static u4 extractCatchHandlers(const DexCode* pCode, const DexTry* pTry,
+    u4* addrBuf, size_t addrBufSize)
+{
+    DexCatchIterator iterator;
+    unsigned int idx = 0;
+
+    dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
+    while (true) {
+        DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+        if (handler == NULL)
+            break;
+
+        if (idx < addrBufSize) {
+            addrBuf[idx] = handler->address;
+        }
+        idx++;
+    }
+
+    return idx;
+}
+
+/*
+ * Returns "true" if the instruction represents a data chunk, such as a
+ * switch statement block.
+ */
+static bool isDataChunk(u2 insn)
+{
+    return (insn == kPackedSwitchSignature ||
+            insn == kSparseSwitchSignature ||
+            insn == kArrayDataSignature);
+}
+
+/*
+ * Alloc a basic block in the specified slot.  The storage will be
+ * initialized.
+ */
+static VfyBasicBlock* allocVfyBasicBlock(VerifierData* vdata, u4 idx)
+{
+    VfyBasicBlock* newBlock = (VfyBasicBlock*) calloc(1, sizeof(VfyBasicBlock));
+    if (newBlock == NULL)
+        return NULL;
+
+    /*
+     * TODO: there is no good default size here -- the problem is that most
+     * addresses will only have one predecessor, but a fair number will
+     * have 10+, and a few will have 100+ (e.g. the synthetic "finally"
+     * in a large synchronized method).  We probably want to use a small
+     * base allocation (perhaps two) and then have the first overflow
+     * allocation jump dramatically (to 32 or thereabouts).
+     */
+    newBlock->predecessors = dvmPointerSetAlloc(32);
+    if (newBlock->predecessors == NULL) {
+        free(newBlock);
+        return NULL;
+    }
+
+    newBlock->firstAddr = (u4) -1;      // DEBUG
+
+    newBlock->liveRegs = dvmAllocBitVector(vdata->insnRegCount, false);
+    if (newBlock->liveRegs == NULL) {
+        dvmPointerSetFree(newBlock->predecessors);
+        free(newBlock);
+        return NULL;
+    }
+
+    return newBlock;
+}
+
+/*
+ * Add "curBlock" to the predecessor list in "targetIdx".
+ */
+static bool addToPredecessor(VerifierData* vdata, VfyBasicBlock* curBlock,
+    u4 targetIdx)
+{
+    assert(targetIdx < vdata->insnsSize);
+
+    /*
+     * Allocate the target basic block if necessary.  This will happen
+     * on e.g. forward branches.
+     *
+     * We can't fill in all the fields, but that will happen automatically
+     * when we get to that part of the code.
+     */
+    VfyBasicBlock* targetBlock = vdata->basicBlocks[targetIdx];
+    if (targetBlock == NULL) {
+        targetBlock = allocVfyBasicBlock(vdata, targetIdx);
+        if (targetBlock == NULL)
+            return false;
+        vdata->basicBlocks[targetIdx] = targetBlock;
+    }
+
+    PointerSet* preds = targetBlock->predecessors;
+    bool added = dvmPointerSetAddEntry(preds, curBlock);
+    if (!added) {
+        /*
+         * This happens sometimes for packed-switch instructions, where
+         * the same target address appears more than once.  Also, a
+         * (pointless) conditional branch to the next instruction will
+         * trip over this.
+         */
+        ALOGV("ODD: point set for targ=0x%04x (%p) already had block "
+             "fir=0x%04x (%p)",
+            targetIdx, targetBlock, curBlock->firstAddr, curBlock);
+    }
+
+    return true;
+}
+
+/*
+ * Add ourselves to the predecessor list in all blocks we might transfer
+ * control to.
+ *
+ * There are four ways to proceed to a new instruction:
+ *  (1) continue to the following instruction
+ *  (2) [un]conditionally branch to a specific location
+ *  (3) conditionally branch through a "switch" statement
+ *  (4) throw an exception
+ *
+ * Returning from the method (via a return statement or an uncaught
+ * exception) are not interesting for liveness analysis.
+ */
+static bool setPredecessors(VerifierData* vdata, VfyBasicBlock* curBlock,
+    u4 curIdx, OpcodeFlags opFlags, u4 nextIdx, u4* handlerList,
+    size_t numHandlers)
+{
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const Method* meth = vdata->method;
+
+    unsigned int handlerIdx;
+    for (handlerIdx = 0; handlerIdx < numHandlers; handlerIdx++) {
+        if (!addToPredecessor(vdata, curBlock, handlerList[handlerIdx]))
+            return false;
+    }
+
+    if ((opFlags & kInstrCanContinue) != 0) {
+        if (!addToPredecessor(vdata, curBlock, nextIdx))
+            return false;
+    }
+    if ((opFlags & kInstrCanBranch) != 0) {
+        bool unused, gotBranch;
+        s4 branchOffset, absOffset;
+
+        gotBranch = dvmGetBranchOffset(meth, insnFlags, curIdx,
+                &branchOffset, &unused);
+        assert(gotBranch);
+        absOffset = curIdx + branchOffset;
+        assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
+
+        if (!addToPredecessor(vdata, curBlock, absOffset))
+            return false;
+    }
+
+    if ((opFlags & kInstrCanSwitch) != 0) {
+        const u2* curInsn = &meth->insns[curIdx];
+        const u2* dataPtr;
+
+        /* these values have already been verified, so we can trust them */
+        s4 offsetToData = curInsn[1] | ((s4) curInsn[2]) << 16;
+        dataPtr = curInsn + offsetToData;
+
+        /*
+         * dataPtr points to the start of the switch data.  The first
+         * item is the NOP+magic, the second is the number of entries in
+         * the switch table.
+         */
+        u2 switchCount = dataPtr[1];
+
+        /*
+         * Skip past the ident field, size field, and the first_key field
+         * (for packed) or the key list (for sparse).
+         */
+        if (dexOpcodeFromCodeUnit(meth->insns[curIdx]) == OP_PACKED_SWITCH) {
+            dataPtr += 4;
+        } else {
+            assert(dexOpcodeFromCodeUnit(meth->insns[curIdx]) ==
+                    OP_SPARSE_SWITCH);
+            dataPtr += 2 + 2 * switchCount;
+        }
+
+        u4 switchIdx;
+        for (switchIdx = 0; switchIdx < switchCount; switchIdx++) {
+            s4 offset, absOffset;
+
+            offset = (s4) dataPtr[switchIdx*2] |
+                     (s4) (dataPtr[switchIdx*2 +1] << 16);
+            absOffset = curIdx + offset;
+            assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
+
+            if (!addToPredecessor(vdata, curBlock, absOffset))
+                return false;
+        }
+    }
+
+    if (false) {
+        if (dvmPointerSetGetCount(curBlock->predecessors) > 256) {
+            ALOGI("Lots of preds at 0x%04x in %s.%s:%s", curIdx,
+                meth->clazz->descriptor, meth->name, meth->shorty);
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Dump the contents of the basic blocks.
+ */
+static void dumpBasicBlocks(const VerifierData* vdata)
+{
+    char printBuf[256];
+    unsigned int idx;
+    int count;
+
+    ALOGI("Basic blocks for %s.%s:%s", vdata->method->clazz->descriptor,
+        vdata->method->name, vdata->method->shorty);
+    for (idx = 0; idx < vdata->insnsSize; idx++) {
+        VfyBasicBlock* block = vdata->basicBlocks[idx];
+        if (block == NULL)
+            continue;
+
+        assert(block->firstAddr == idx);
+        count = snprintf(printBuf, sizeof(printBuf), " %04x-%04x ",
+            block->firstAddr, block->lastAddr);
+
+        PointerSet* preds = block->predecessors;
+        size_t numPreds = dvmPointerSetGetCount(preds);
+
+        if (numPreds > 0) {
+            count += snprintf(printBuf + count, sizeof(printBuf) - count,
+                    "preds:");
+
+            unsigned int predIdx;
+            for (predIdx = 0; predIdx < numPreds; predIdx++) {
+                if (count >= (int) sizeof(printBuf))
+                    break;
+                const VfyBasicBlock* pred =
+                    (const VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx);
+                count += snprintf(printBuf + count, sizeof(printBuf) - count,
+                        "%04x(%p),", pred->firstAddr, pred);
+            }
+        } else {
+            count += snprintf(printBuf + count, sizeof(printBuf) - count,
+                    "(no preds)");
+        }
+
+        printBuf[sizeof(printBuf)-2] = '!';
+        printBuf[sizeof(printBuf)-1] = '\0';
+
+        ALOGI("%s", printBuf);
+    }
+
+    usleep(100 * 1000);      /* ugh...let logcat catch up */
+}
+
+
+/*
+ * Generate a list of basic blocks and related information.
+ *
+ * On success, returns "true" with vdata->basicBlocks initialized.
+ */
+bool dvmComputeVfyBasicBlocks(VerifierData* vdata)
+{
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const Method* meth = vdata->method;
+    const u4 insnsSize = vdata->insnsSize;
+    const DexCode* pCode = dvmGetMethodCode(meth);
+    const DexTry* pTries = NULL;
+    const size_t kHandlerStackAllocSize = 16;   /* max seen so far is 7 */
+    u4 handlerAddrs[kHandlerStackAllocSize];
+    u4* handlerListAlloc = NULL;
+    u4* handlerList = NULL;
+    size_t numHandlers = 0;
+    u4 idx, blockStartAddr;
+    bool result = false;
+
+    bool verbose = false; //dvmWantVerboseVerification(meth);
+    if (verbose) {
+        ALOGI("Basic blocks for %s.%s:%s",
+            meth->clazz->descriptor, meth->name, meth->shorty);
+    }
+
+    /*
+     * Allocate a data structure that allows us to map from an address to
+     * the corresponding basic block.  Initially all pointers are NULL.
+     * They are populated on demand as we proceed (either when we reach a
+     * new BB, or when we need to add an item to the predecessor list in
+     * a not-yet-reached BB).
+     *
+     * Only the first instruction in the block points to the BB structure;
+     * the rest remain NULL.
+     */
+    vdata->basicBlocks =
+        (VfyBasicBlock**) calloc(insnsSize, sizeof(VfyBasicBlock*));
+    if (vdata->basicBlocks == NULL)
+      return false;
+
+    /*
+     * The "tries" list is a series of non-overlapping regions with a list
+     * of "catch" handlers.  Rather than do the "find a matching try block"
+     * computation at each step, we just walk the "try" list in parallel.
+     *
+     * Not all methods have "try" blocks.  If this one does, we init tryEnd
+     * to zero, so that the (exclusive bound) range check trips immediately.
+     */
+    u4 tryIndex = 0, tryStart = 0, tryEnd = 0;
+    if (pCode->triesSize != 0) {
+        pTries = dexGetTries(pCode);
+    }
+
+    u4 debugBBIndex = 0;
+
+    /*
+     * The address associated with a basic block is the start address.
+     */
+    blockStartAddr = 0;
+
+    for (idx = 0; idx < insnsSize; ) {
+        /*
+         * Make sure we're pointing at the right "try" block.  It should
+         * not be possible to "jump over" a block, so if we're no longer
+         * in the correct one we can just advance to the next.
+         */
+        if (pTries != NULL && idx >= tryEnd) {
+            if (tryIndex == pCode->triesSize) {
+                /* no more try blocks in this method */
+                pTries = NULL;
+                numHandlers = 0;
+            } else {
+                /*
+                 * Extract the set of handlers.  We want to avoid doing
+                 * this for each block, so we copy them to local storage.
+                 * If it doesn't fit in the small stack area, we'll use
+                 * the heap instead.
+                 *
+                 * It's rare to encounter a method with more than half a
+                 * dozen possible handlers.
+                 */
+                tryStart = pTries[tryIndex].startAddr;
+                tryEnd = tryStart + pTries[tryIndex].insnCount;
+
+                if (handlerListAlloc != NULL) {
+                    free(handlerListAlloc);
+                    handlerListAlloc = NULL;
+                }
+                numHandlers = extractCatchHandlers(pCode, &pTries[tryIndex],
+                    handlerAddrs, kHandlerStackAllocSize);
+                assert(numHandlers > 0);    // TODO make sure this is verified
+                if (numHandlers <= kHandlerStackAllocSize) {
+                    handlerList = handlerAddrs;
+                } else {
+                    ALOGD("overflow, numHandlers=%d", numHandlers);
+                    handlerListAlloc = (u4*) malloc(sizeof(u4) * numHandlers);
+                    if (handlerListAlloc == NULL)
+                        return false;
+                    extractCatchHandlers(pCode, &pTries[tryIndex],
+                        handlerListAlloc, numHandlers);
+                    handlerList = handlerListAlloc;
+                }
+
+                ALOGV("+++ start=%x end=%x numHan=%d",
+                    tryStart, tryEnd, numHandlers);
+
+                tryIndex++;
+            }
+        }
+
+        /*
+         * Check the current instruction, and possibly aspects of the
+         * next instruction, to see if this instruction ends the current
+         * basic block.
+         *
+         * Instructions that can throw only end the block if there is the
+         * possibility of a local handler catching the exception.
+         */
+        Opcode opcode = dexOpcodeFromCodeUnit(meth->insns[idx]);
+        OpcodeFlags opFlags = dexGetFlagsFromOpcode(opcode);
+        size_t nextIdx = idx + dexGetWidthFromInstruction(&meth->insns[idx]);
+        bool endBB = false;
+        bool ignoreInstr = false;
+
+        if ((opFlags & kInstrCanContinue) == 0) {
+            /* does not continue */
+            endBB = true;
+        } else if ((opFlags & (kInstrCanBranch | kInstrCanSwitch)) != 0) {
+            /* conditionally branches elsewhere */
+            endBB = true;
+        } else if ((opFlags & kInstrCanThrow) != 0 &&
+                dvmInsnIsInTry(insnFlags, idx))
+        {
+            /* throws an exception that might be caught locally */
+            endBB = true;
+        } else if (isDataChunk(meth->insns[idx])) {
+            /*
+             * If this is a data chunk (e.g. switch data) we want to skip
+             * over it entirely.  Set endBB so we don't carry this along as
+             * the start of a block, and ignoreInstr so we don't try to
+             * open a basic block for this instruction.
+             */
+            endBB = ignoreInstr = true;
+        } else if (dvmInsnIsBranchTarget(insnFlags, nextIdx)) {
+            /*
+             * We also need to end it if the next instruction is a branch
+             * target.  Note we've tagged exception catch blocks as such.
+             *
+             * If we're this far along in the "else" chain, we know that
+             * this isn't a data-chunk NOP, and control can continue to
+             * the next instruction, so we're okay examining "nextIdx".
+             */
+            assert(nextIdx < insnsSize);
+            endBB = true;
+        } else if (opcode == OP_NOP && isDataChunk(meth->insns[nextIdx])) {
+            /*
+             * Handle an odd special case: if this is NOP padding before a
+             * data chunk, also treat it as "ignore".  Otherwise it'll look
+             * like a block that starts and doesn't end.
+             */
+            endBB = ignoreInstr = true;
+        } else {
+            /* check: return ops should be caught by absence of can-continue */
+            assert((opFlags & kInstrCanReturn) == 0);
+        }
+
+        if (verbose) {
+            char btc = dvmInsnIsBranchTarget(insnFlags, idx) ? '>' : ' ';
+            char tryc =
+                (pTries != NULL && idx >= tryStart && idx < tryEnd) ? 't' : ' ';
+            bool startBB = (idx == blockStartAddr);
+            const char* startEnd;
+
+
+            if (ignoreInstr)
+                startEnd = "IGNORE";
+            else if (startBB && endBB)
+                startEnd = "START/END";
+            else if (startBB)
+                startEnd = "START";
+            else if (endBB)
+                startEnd = "END";
+            else
+                startEnd = "-";
+
+            ALOGI("%04x: %c%c%s #%d", idx, tryc, btc, startEnd, debugBBIndex);
+
+            if (pTries != NULL && idx == tryStart) {
+                assert(numHandlers > 0);
+                ALOGI("  EXC block: [%04x, %04x) %d:(%04x...)",
+                    tryStart, tryEnd, numHandlers, handlerList[0]);
+            }
+        }
+
+        if (idx != blockStartAddr) {
+            /* should not be a basic block struct associated with this addr */
+            assert(vdata->basicBlocks[idx] == NULL);
+        }
+        if (endBB) {
+            if (!ignoreInstr) {
+                /*
+                 * Create a new BB if one doesn't already exist.
+                 */
+                VfyBasicBlock* curBlock = vdata->basicBlocks[blockStartAddr];
+                if (curBlock == NULL) {
+                    curBlock = allocVfyBasicBlock(vdata, blockStartAddr);
+                    if (curBlock == NULL)
+                        return false;
+                    vdata->basicBlocks[blockStartAddr] = curBlock;
+                }
+
+                curBlock->firstAddr = blockStartAddr;
+                curBlock->lastAddr = idx;
+
+                if (!setPredecessors(vdata, curBlock, idx, opFlags, nextIdx,
+                        handlerList, numHandlers))
+                {
+                    goto bail;
+                }
+            }
+
+            blockStartAddr = nextIdx;
+            debugBBIndex++;
+        }
+
+        idx = nextIdx;
+    }
+
+    assert(idx == insnsSize);
+
+    result = true;
+
+    if (verbose)
+        dumpBasicBlocks(vdata);
+
+bail:
+    free(handlerListAlloc);
+    return result;
+}
+
+/*
+ * Free the storage used by basic blocks.
+ */
+void dvmFreeVfyBasicBlocks(VerifierData* vdata)
+{
+    unsigned int idx;
+
+    if (vdata->basicBlocks == NULL)
+        return;
+
+    for (idx = 0; idx < vdata->insnsSize; idx++) {
+        VfyBasicBlock* block = vdata->basicBlocks[idx];
+        if (block == NULL)
+            continue;
+
+        dvmPointerSetFree(block->predecessors);
+        free(block);
+    }
+}
diff --git a/vm/analysis/VfyBasicBlock.h b/vm/analysis/VfyBasicBlock.h
new file mode 100644
index 0000000..0fc7428
--- /dev/null
+++ b/vm/analysis/VfyBasicBlock.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Basic block functions, as used by the verifier.  (The names were chosen
+ * to avoid conflicts with similar structures used by the compiler.)
+ */
+#ifndef DALVIK_VFYBASICBLOCK_H_
+#define DALVIK_VFYBASICBLOCK_H_
+
+#include "PointerSet.h"
+
+struct VerifierData;
+
+
+/*
+ * Structure representing a basic block.
+ *
+ * This is used for liveness analysis, which is a reverse-flow algorithm,
+ * so we need to mantain a list of predecessors for each block.
+ *
+ * "liveRegs" indicates the set of registers that are live at the end of
+ * the basic block (after the last instruction has executed).  Successor
+ * blocks will compare their results with this to see if this block needs
+ * to be re-evaluated.  Note that this is not the same as the contents of
+ * the RegisterLine for the last instruction in the block (which reflects
+ * the state *before* the instruction has executed).
+ */
+struct VfyBasicBlock {
+    u4              firstAddr;      /* address of first instruction */
+    u4              lastAddr;       /* address of last instruction */
+    PointerSet*     predecessors;   /* set of basic blocks that can flow here */
+    BitVector*      liveRegs;       /* liveness for each register */
+    bool            changed;        /* input set has changed, must re-eval */
+    bool            visited;        /* block has been visited at least once */
+};
+
+/*
+ * Generate a list of basic blocks.
+ */
+bool dvmComputeVfyBasicBlocks(struct VerifierData* vdata);
+
+/*
+ * Free storage allocated by dvmComputeVfyBasicBlocks.
+ */
+void dvmFreeVfyBasicBlocks(struct VerifierData* vdata);
+
+#endif  // DALVIK_VFYBASICBLOCK_H_
diff --git a/vm/arch/arm/CallEABI.S b/vm/arch/arm/CallEABI.S
new file mode 100644
index 0000000..9971b5d
--- /dev/null
+++ b/vm/arch/arm/CallEABI.S
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the "new" ARM EABI.
+ */
+
+#include <machine/cpu-features.h>
+
+#ifdef __ARM_EABI__
+
+#ifdef EXTENDED_EABI_DEBUG
+# define DBG
+#else
+# define DBG @
+#endif
+
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+    -or-
+  return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two).  It's up to
+us to convert these into local calling conventions.
+*/
+
+/*
+ARM EABI notes:
+
+r0-r3 hold first 4 args to a method
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.  This means
+we have to scan the method signature, identify arguments that must be
+padded, and fix them up appropriately.
+*/
+
+    .text
+    .align  2
+    .global dvmPlatformInvoke
+    .type   dvmPlatformInvoke, %function
+
+/*
+ * On entry:
+ *   r0  JNIEnv (can be left alone)
+ *   r1  clazz (NULL for virtual method calls, non-NULL for static)
+ *   r2  arg info
+ *   r3  argc (number of 32-bit values in argv)
+ *   [sp]     argv
+ *   [sp,#4]  short signature
+ *   [sp,#8]  func
+ *   [sp,#12] pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ *   SRRRLLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   S - if set, do things the hard way (scan the signature)
+ *   R - return-type enumeration, really only important for "hard" FP ABI
+ *   L - number of double-words of storage required on stack (0-30 words)
+ *   F - pad flag -- if set, write a pad word to the stack
+ *
+ * With this arrangement we can efficiently push up to 24 words of arguments
+ * onto the stack.  Anything requiring more than that -- which should happen
+ * rarely to never -- can do the slow signature scan.
+ *
+ * (We could pack the Fs more efficiently -- we know we never push two pads
+ * in a row, and the first word can never be a pad -- but there's really
+ * no need for it.)
+ *
+ * NOTE: if the called function has more than 4 words of arguments, gdb
+ * will not be able to unwind the stack past this method.  The only way
+ * around this is to convince gdb to respect an explicit frame pointer.
+ * The stack unwinder in debuggerd *does* pay attention to fp if we set it
+ * up appropriately, so at least that will work.
+ */
+dvmPlatformInvoke:
+    .fnstart
+
+    /*
+     * Save regs.
+     *
+     * On entry to a function, "sp" must be 64-bit aligned.  This means
+     * we have to adjust sp manually if we push an odd number of regs here
+     * (both here and when exiting).
+     *
+     * The ARM spec doesn't specify anything about the frame pointer.  gcc
+     * points fp at the first saved argument, so our "full descending"
+     * stack looks like:
+     *
+     *  pReturn
+     *  func
+     *  shorty
+     *  argv        <-- sp on entry
+     *  lr          <-- fp
+     *  fp
+     *  r9...r7
+     *  r6          <-- sp after reg save
+     *
+     * Any arguments that need to be pushed on for the target method
+     * come after this.  The last argument is pushed first.
+     */
+SAVED_REG_COUNT = 6                     @ push 6 regs
+FP_STACK_OFFSET = (SAVED_REG_COUNT-1) * 4 @ offset between fp and post-save sp
+FP_ADJ = 4                              @ fp is initial sp +4
+
+    .save        {r6, r7, r8, r9, fp, lr}
+    stmfd   sp!, {r6, r7, r8, r9, fp, lr}
+
+    .setfp  fp, sp, #FP_STACK_OFFSET    @ point fp at first saved reg
+    add     fp, sp, #FP_STACK_OFFSET
+
+    @.pad    #4                          @ adjust for 64-bit align
+    @sub     sp, sp, #4                  @ (if we save odd number of regs)
+
+    @ Ensure 64-bit alignment.  EABI guarantees sp is aligned on entry, make
+    @ sure we're aligned properly now.
+DBG tst     sp, #4                      @ 64-bit aligned?
+DBG bne     dvmAbort                    @ no, fail
+
+    ldr     r9, [fp, #0+FP_ADJ]         @ r9<- argv
+    cmp     r1, #0                      @ calling a static method?
+
+    @ Not static, grab the "this" pointer.  Note "this" is not explicitly
+    @ described by the method signature.
+    subeq   r3, r3, #1                  @ argc--
+    ldreq   r1, [r9], #4                @ r1<- *argv++
+
+    @ Do we have arg padding flags in "argInfo"? (just need to check hi bit)
+    teq     r2, #0
+    bmi     .Lno_arg_info
+
+    /*
+     * "Fast" path.
+     *
+     * Make room on the stack for the arguments and copy them over,
+     * inserting pad words when appropriate.
+     *
+     * Currently:
+     *  r0  don't touch
+     *  r1  don't touch
+     *  r2  arg info
+     *  r3  argc
+     *  r4-r5  don't touch (not saved)
+     *  r6-r8 (available)
+     *  r9  argv
+     *  fp  frame pointer
+     */
+.Lhave_arg_info:
+    @ Expand the stack by the specified amount.  We want to extract the
+    @ count of double-words from r2, multiply it by 8, and subtract that
+    @ from the stack pointer.
+    and     ip, r2, #0x0f000000         @ ip<- double-words required
+    mov     r6, r2, lsr #28             @ r6<- return type
+    sub     sp, sp, ip, lsr #21         @ shift right 24, then left 3
+    mov     r8, sp                      @ r8<- sp  (arg copy dest)
+
+    @ Stick argv in r7 and advance it past the argv values that will be
+    @ held in r2-r3.  It's possible r3 will hold a pad, so check the
+    @ bit in r2.  We do this by ignoring the first bit (which would
+    @ indicate a pad in r2) and shifting the second into the carry flag.
+    @ If the carry is set, r3 will hold a pad, so we adjust argv less.
+    @
+    @ (This is harmless if argc==0)
+    mov     r7, r9
+    movs    r2, r2, lsr #2
+    addcc   r7, r7, #8                  @ skip past 2 words, for r2 and r3
+    subcc   r3, r3, #2
+    addcs   r7, r7, #4                  @ skip past 1 word, for r2
+    subcs   r3, r3, #1
+
+.Lfast_copy_loop:
+    @ if (--argc < 0) goto invoke
+    subs    r3, r3, #1
+    bmi     .Lcopy_done                 @ NOTE: expects original argv in r9
+
+.Lfast_copy_loop2:
+    @ Get pad flag into carry bit.  If it's set, we don't pull a value
+    @ out of argv.
+    movs    r2, r2, lsr #1
+    ldrcc   ip, [r7], #4                @ ip = *r7++ (pull from argv)
+    strcc   ip, [r8], #4                @ *r8++ = ip (write to stack)
+    bcc     .Lfast_copy_loop
+
+DBG movcs   ip, #-3                     @ DEBUG DEBUG - make pad word obvious
+DBG strcs   ip, [r8]                    @ DEBUG DEBUG
+    add     r8, r8, #4                  @ if pad, just advance ip without store
+    b       .Lfast_copy_loop2           @ don't adjust argc after writing pad
+
+
+.Lcopy_done:
+    /*
+     * Currently:
+     *  r0-r3  args (JNIEnv*, thisOrClass, arg0, arg1)
+     *  r6  return type (enum DalvikJniReturnType)
+     *  r9  original argv
+     *  fp  frame pointer
+     *
+     * The stack copy is complete.  Grab the first two words off of argv
+     * and tuck them into r2/r3.  If the first arg is 32-bit and the second
+     * arg is 64-bit, then r3 "holds" a pad word and the load is unnecessary
+     * but harmless.
+     *
+     * If there are 0 or 1 arg words in argv, we will be loading uninitialized
+     * data into the registers, but since nothing tries to use it it's also
+     * harmless (assuming argv[0] and argv[1] point to valid memory, which
+     * is a reasonable assumption for Dalvik's interpreted stacks).
+     */
+    ldmia   r9, {r2-r3}                 @ r2/r3<- argv[0]/argv[1]
+
+    ldr     ip, [fp, #8+FP_ADJ]         @ ip<- func
+#ifdef __ARM_HAVE_BLX
+    blx     ip                          @ call func
+#else
+    mov     lr, pc                      @ call func the old-fashioned way
+    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 and
+    @ "hard" fp calling conventions, all we need to do is copy r0-r1 into
+    @ the JValue union.
+    @
+    @ Thought: could redefine DalvikJniReturnType such that single-word
+    @ and double-word values occupy different ranges; simple comparison
+    @ allows us to choose between str and stm.  Probably not worthwhile.
+    @
+    cmp     r6, #0                      @ DALVIK_JNI_RETURN_VOID?
+    ldrne   ip, [fp, #12+FP_ADJ]        @ pReturn
+    sub     sp, fp, #FP_STACK_OFFSET    @ restore sp to post-reg-save offset
+    stmneia ip, {r0-r1}                 @ pReturn->j <- r0/r1
+
+    @ Restore the registers we saved and return.  On >= ARMv5TE we can
+    @ restore PC directly from the saved LR.
+#ifdef __ARM_HAVE_PC_INTERWORK
+    ldmfd   sp!, {r6, r7, r8, r9, fp, pc}
+#else
+    ldmfd   sp!, {r6, r7, r8, r9, fp, lr}
+    bx      lr
+#endif
+
+
+
+    /*
+     * "Slow" path.
+     * Walk through the argument list, counting up the number of 32-bit words
+     * required to contain it.  Then walk through it a second time, copying
+     * values out to the stack.  (We could pre-compute the size to save
+     * ourselves a trip, but we'd have to store that somewhere -- this is
+     * sufficiently unlikely that it's not worthwhile.)
+     *
+     * Try not to make any assumptions about the number of args -- I think
+     * the class file format allows up to 64K words (need to verify that).
+     *
+     * Currently:
+     *  r0  don't touch
+     *  r1  don't touch
+     *  r2  (available)
+     *  r3  argc
+     *  r4-r5 don't touch (not saved)
+     *  r6-r8 (available)
+     *  r9  argv
+     *  fp  frame pointer
+     */
+.Lno_arg_info:
+    mov     ip, r2, lsr #28             @ ip<- return type
+    ldr     r6, [fp, #4+FP_ADJ]         @ r6<- short signature
+    add     r6, r6, #1                  @ advance past return type
+    mov     r2, #0                      @ r2<- word count, init to zero
+
+.Lcount_loop:
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+    cmp     ip, #0                      @ end?
+    beq     .Lcount_done                @ all done, bail
+    add     r2, r2, #1                  @ count++
+    cmp     ip, #'D'                    @ look for 'D' or 'J', which are 64-bit
+    cmpne   ip, #'J'
+    bne     .Lcount_loop
+
+    @ 64-bit value, insert padding if we're not aligned
+    tst     r2, #1                      @ odd after initial incr?
+    addne   r2, #1                      @ no, add 1 more to cover 64 bits
+    addeq   r2, #2                      @ yes, treat prev as pad, incr 2 now
+    b       .Lcount_loop
+.Lcount_done:
+
+    @ We have the padded-out word count in r2.  We subtract 2 from it
+    @ because we don't push the first two arg words on the stack (they're
+    @ destined for r2/r3).  Pushing them on and popping them off would be
+    @ simpler but slower.
+    subs    r2, r2, #2                  @ subtract 2 (for contents of r2/r3)
+    movmis  r2, #0                      @ if negative, peg at zero, set Z-flag
+    beq     .Lcopy_done                 @ zero args, skip stack copy
+
+DBG tst     sp, #7                      @ DEBUG - make sure sp is aligned now
+DBG bne     dvmAbort                    @ DEBUG
+
+    @ Set up to copy from r7 to r8.  We copy from the second arg to the
+    @ last arg, which means reading and writing to ascending addresses.
+    sub     sp, sp, r2, asl #2          @ sp<- sp - r2*4
+    bic     sp, #4                      @ subtract another 4 ifn
+    mov     r7, r9                      @ r7<- argv
+    mov     r8, sp                      @ r8<- sp
+
+    @ We need to copy words from [r7] to [r8].  We walk forward through
+    @ the signature again, "copying" pad words when appropriate, storing
+    @ upward into the stack.
+    ldr     r6, [fp, #4+FP_ADJ]         @ r6<- signature
+    add     r6, r6, #1                  @ advance past return type
+    add     r7, r7, #8                  @ r7<- r7+8 (assume argv 0/1 in r2/r3)
+
+    @ Eat first arg or two, for the stuff that goes into r2/r3.
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+    cmp     ip, #'D'
+    cmpne   ip, #'J'
+    beq     .Lstack_copy_loop           @ 64-bit arg fills r2+r3
+
+    @ First arg was 32-bit, check the next
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+    cmp     ip, #'D'
+    cmpne   ip, #'J'
+    subeq   r7, #4                      @ r7<- r7-4 (take it back - pad word)
+    beq     .Lstack_copy_loop2          @ start with char we already have
+
+    @ Two 32-bit args, fall through and start with next arg
+
+.Lstack_copy_loop:
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+.Lstack_copy_loop2:
+    cmp     ip, #0                      @ end of shorty?
+    beq     .Lcopy_done                 @ yes
+
+    cmp     ip, #'D'
+    cmpne   ip, #'J'
+    beq     .Lcopy64
+
+    @ Copy a 32-bit value.  [r8] is initially at the end of the stack.  We
+    @ use "full descending" stacks, so we store into [r8] and incr as we
+    @ move toward the end of the arg list.
+.Lcopy32:
+    ldr     ip, [r7], #4
+    str     ip, [r8], #4
+    b       .Lstack_copy_loop
+
+.Lcopy64:
+    @ Copy a 64-bit value.  If necessary, leave a hole in the stack to
+    @ ensure alignment.  We know the [r8] output area is 64-bit aligned,
+    @ so we can just mask the address.
+    add     r8, r8, #7          @ r8<- (r8+7) & ~7
+    ldr     ip, [r7], #4
+    bic     r8, r8, #7
+    ldr     r2, [r7], #4
+    str     ip, [r8], #4
+    str     r2, [r8], #4
+    b       .Lstack_copy_loop
+
+    .fnend
+    .size   dvmPlatformInvoke, .-dvmPlatformInvoke
+
+#if 0
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+     .macro SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak
+    mov     r1, #\num
+    bl      printf
+#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.cpp b/vm/arch/arm/HintsEABI.cpp
new file mode 100644
index 0000000..3e27e5a
--- /dev/null
+++ b/vm/arch/arm/HintsEABI.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints (see below for details)
+ *
+ * This function produces arm-specific hints - specifically a description
+ * of padding required to keep all 64-bit parameters properly aligned.
+ *
+ * ARM JNI hint format
+ *
+ *       LLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   L - number of double-words of storage required on the stack (0-30 words)
+ *   F - pad flag -- if set, write a pad word to the stack before copying
+ *       the next 32 bits
+ *
+ * If there are too many arguments to construct valid hints, this function will
+ * return a result with the S bit set.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto)
+{
+    const char* sig = dexProtoGetShorty(proto);
+    int padFlags, jniHints;
+    char sigByte;
+    int stackOffset, padMask;
+
+    stackOffset = padFlags = 0;
+    padMask = 0x00000001;
+
+    /* Skip past the return type */
+    sig++;
+
+    while (true) {
+        sigByte = *(sig++);
+
+        if (sigByte == '\0')
+            break;
+
+        if (sigByte == 'D' || sigByte == 'J') {
+            if ((stackOffset & 1) != 0) {
+                padFlags |= padMask;
+                stackOffset++;
+                padMask <<= 1;
+            }
+            stackOffset += 2;
+            padMask <<= 2;
+        } else {
+            stackOffset++;
+            padMask <<= 1;
+        }
+    }
+
+    jniHints = 0;
+
+    if (stackOffset > DALVIK_JNI_COUNT_SHIFT) {
+        /* too big for "fast" version */
+        jniHints = DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        assert((padFlags & (0xffffffff << DALVIK_JNI_COUNT_SHIFT)) == 0);
+        stackOffset -= 2;           // r2/r3 holds first two items
+        if (stackOffset < 0)
+            stackOffset = 0;
+        jniHints |= ((stackOffset+1) / 2) << DALVIK_JNI_COUNT_SHIFT;
+        jniHints |= padFlags;
+    }
+
+    return jniHints;
+}
diff --git a/vm/arch/generic/Call.cpp b/vm/arch/generic/Call.cpp
new file mode 100644
index 0000000..3d7cbef
--- /dev/null
+++ b/vm/arch/generic/Call.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This uses the FFI (Foreign Function Interface) library to abstract away
+ * the system-dependent stuff.  The FFI code is slower than a custom
+ * assembly version, but has the distinct advantage of having been
+ * written already for several platforms.
+ */
+#include "Dalvik.h"
+#include "ffi.h"
+
+/*
+ * Convert a signature type character to an FFI type.
+ */
+static ffi_type* getFfiType(char sigType)
+{
+    switch (sigType) {
+    case 'V': return &ffi_type_void;
+    case 'Z': return &ffi_type_uint8;
+    case 'B': return &ffi_type_sint8;
+    case 'C': return &ffi_type_uint16;
+    case 'S': return &ffi_type_sint16;
+    case 'I': return &ffi_type_sint32;
+    case 'F': return &ffi_type_float;
+    case 'J': return &ffi_type_sint64;
+    case 'D': return &ffi_type_double;
+    case '[':
+    case 'L': return &ffi_type_pointer;
+    default:
+        ALOGE("bad ffitype 0x%02x", sigType);
+        dvmAbort();
+        return NULL;
+    }
+}
+
+/* We will call this generic function if there are no hints */
+#ifdef __mips__
+#define dvmPlatformInvoke dvmPlatformInvokeFFI
+
+extern "C" void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo,
+    int argc, const u4* argv, const char* signature, void* func, JValue* pResult);
+#endif
+
+/*
+ * Call "func" with the specified arguments.
+ *
+ * The second argument to JNI native functions is either the object (the
+ * "this" pointer) or, for static functions, a pointer to the class object.
+ * The Dalvik instructions will push "this" into argv[0], but it's up to
+ * us to insert the class object.
+ *
+ * Because there is no such thing in as a null "this" pointer, we use
+ * the non-NULL state of "clazz" to determine whether or not it's static.
+ *
+ * For maximum efficiency we should compute the CIF once and save it with
+ * the method.  However, this requires storing the data with every native
+ * method.  Since the goal is to have custom assembly versions of this
+ * on the platforms where performance matters, I'm recomputing the CIF on
+ * every call.
+ */
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* shorty, void* func, JValue* pReturn)
+{
+    const int kMaxArgs = argc+2;    /* +1 for env, maybe +1 for clazz */
+    ffi_cif cif;
+    ffi_type* types[kMaxArgs];
+    void* values[kMaxArgs];
+    ffi_type* retType;
+    char sigByte;
+    int srcArg, dstArg;
+
+    types[0] = &ffi_type_pointer;
+    values[0] = &pEnv;
+
+    types[1] = &ffi_type_pointer;
+    if (clazz != NULL) {
+        values[1] = &clazz;
+        srcArg = 0;
+    } else {
+        values[1] = (void*) argv++;
+        srcArg = 1;
+    }
+    dstArg = 2;
+
+    /*
+     * Scan the types out of the short signature.  Use them to fill out the
+     * "types" array.  Store the start address of the argument in "values".
+     */
+    retType = getFfiType(*shorty);
+    while ((sigByte = *++shorty) != '\0') {
+        types[dstArg] = getFfiType(sigByte);
+        values[dstArg++] = (void*) argv++;
+        if (sigByte == 'D' || sigByte == 'J')
+            argv++;
+    }
+
+    /*
+     * Prep the CIF (Call InterFace object).
+     */
+    if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, dstArg, retType, types) != FFI_OK) {
+        ALOGE("ffi_prep_cif failed");
+        dvmAbort();
+    }
+
+    ffi_call(&cif, FFI_FN(func), pReturn, values);
+}
diff --git a/vm/arch/generic/Hints.cpp b/vm/arch/generic/Hints.cpp
new file mode 100644
index 0000000..7b08aeb
--- /dev/null
+++ b/vm/arch/generic/Hints.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints
+ *
+ * This function is a placeholder/template and should be duplicated in the
+ * appropriate arch/<target>/ directory for new target ports.  The hints
+ * field should be defined and constructed in conjunction with
+ * dvmPlatformInvoke.
+
+ * If valid hints can't be constructed, this function should return a negative
+ * value.  In that case, the caller will set the S bit in the jniArgInfo word
+ * and convert the arguments the slow way.
+ */
+u4 dvmPlatformInvokeHints( const DexProto* proto)
+{
+    /* No hints for generic target - force argument walk at run-time */
+    return DALVIK_JNI_NO_ARG_INFO;
+}
diff --git a/vm/arch/mips/CallO32.S b/vm/arch/mips/CallO32.S
new file mode 100644
index 0000000..e436d1e
--- /dev/null
+++ b/vm/arch/mips/CallO32.S
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the MIPS O32 ABI.
+ */
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+    -or-
+  return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two).  It's up to
+us to convert these into local calling conventions.
+
+Please notice that argc in dvmPlatformInvoke does NOT include pEnv and clazz/this.
+*/
+
+    .text
+    .align  2
+    .globl dvmPlatformInvoke
+    .ent dvmPlatformInvoke
+/*
+ * On entry:
+ *   a0  JNIEnv (can be left alone)
+ *   a1  clazz (NULL for virtual method calls, non-NULL for static)
+ *   a2  argInfo
+ *   a3  argc (number of 32-bit values in argv)
+ *   MIPS reservers 16 bytes on stack even if the first 4 args are passed by
+ *   reg a0-a3. That's different from ARM.
+ *   [sp + 16]  argv
+ *   [sp + 20]  short signature
+ *   [sp + 24]  func
+ *   [sp + 28]  pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ *   SRRRLLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   S - if set, do things the hard way (scan the signature)
+ *   R - return type enumeration, really only important for hardware FP
+ *   L - number of double-words (64 bits!) of storage required on stack (0-30 words)
+ *   F - pad flag -- if set, write a pad word to the stack
+ *
+ * With this arrangement we can efficiently push up to 24 words of arguments
+ * onto the stack.  Anything requiring more than that -- which should happen
+ * rarely to never -- can do the slow signature scan.
+ *
+ * (We could pack the Fs more efficiently -- we know we never push two pads
+ * in a row, and the first word can never be a pad -- but there's really
+ * no need for it.)
+ *
+ * NOTE: if the called function has more than 4 words of arguments, gdb
+ * will not be able to unwind the stack past this method.  The only way
+ * around this is to convince gdb to respect an explicit frame pointer.
+ */
+
+ /* Stack:
+  *                     High
+  *                 ____________
+  *                 |__28______| pReturn
+  *                 |__24______| func
+  *                 |__20______| short signature
+  *                 |__16______| argv
+  *                 |__12______| reserved (a3: argc)
+  *                 |__8_______| reserved (a2: arg)
+  *                 |__4_______| reserved (a1: clazz)
+  *__sp on entry_->_|__0_______|_reserved (a0: JNIenv)
+  *                 |__________| saved ra
+  *                 |__________| saved fp
+  *                 |__________| saved s0
+  *                 |__________| spare
+  *                 |__________| saved s2
+  *"framepointer"->_|__________| pad for 8 bytes aligned
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| reserved for a3
+  *                 |__________| reserved for a2
+  *                 |__________| reserved for a1
+  *_____new sp___-> |__________| reserved for a0
+  * (new sp: sp when call native method)
+  */
+
+ /* Register usage:
+  *
+  *  s0: pReturn
+  *  s2: Return type
+  * These registers should be saved to and restored from stack.
+  *
+  *  t0: argv
+  *  t9: func
+  * These registers do not need to be saved.
+  *
+  * We put the stack size into register s1 because we can not know the size
+  * of stack at the beginning. This size can be calculated with the help
+  * of hints in jniarginfo.
+  *
+  */
+
+dvmPlatformInvoke:
+	.set noreorder
+	.cpload $t9
+	.set reorder
+
+	/*  Do we have arg padding flags in "argInfo"? Check bit 31 */
+	bltz	$a2,.Lno_arginfo
+
+	/* Fast path. We have hints. */
+	/* save fp and ra to stack */
+#define FSIZE 24
+	subu	$sp,FSIZE
+	sw	$ra,20($sp)
+	sw	$fp,16($sp)
+	sw	$s0,12($sp)
+	sw	$s2,4($sp)
+	move	$fp,$sp
+
+	lw	$t0,FSIZE+16($sp)	/* t0 <- argv */
+	lw	$t9,FSIZE+24($sp)	/* t9 <- func */
+	lw	$s0,FSIZE+28($sp)	/* s0 <- pReturn */
+
+	/* Is the method static? */
+	bnez	$a1,1f
+	/* Not static: a1 <- *argv++ ("this"), argc-- */
+	lw	$a1,($t0)
+	addiu	$t0,4
+	addiu	$a3,-1
+1:
+	/* expand the stack for args */
+	srl	$s2,$a2,28	/* s2 <- returnType */
+	srl	$t1,$a2,21
+	andi	$t1,0x78	/* t1 <- stackSize in bytes */
+
+	addiu	$t1,16		/* include space for a0/a1/a2/a3 */
+	subu	$sp,$t1
+	addiu	$t1,$sp,8
+
+	/*
+	 * t0 :argv
+	 * t1 :sp+8(first arg position in stack except pEnv and clazz/this)
+	 * a2 :argInfo
+	 * a3 :argc
+	 * sp :new stack bottom
+	 */
+
+	/* first two args or one args and pad */
+	blez	$a3,.Largs_done
+	lw	$t2,($t0)
+	addiu	$t0,4
+	addiu	$a3,-1
+	sw	$t2,($t1)
+	addiu	$t1,4
+	srl	$a2,1
+	blez	$a3,.Largs_done
+
+	andi	$t3,$a2,0x1	/* the second position is a pad? */
+	bnez	$t3,.Lpad0
+
+	lw	$t2,($t0)
+	addiu	$t0,4
+	addiu	$a3,-1
+	sw	$t2,($t1)
+.Lpad0:
+	addiu	$t1,4
+	srl	$a2,1
+	blez	$a3,.Largs_done
+
+.Lloop1:
+	/* copy other args
+	 * $fp: sp top for args
+	 * $t1: sp for next arg
+	 */
+	beq	$t1,$fp,.Largs_done
+	andi	$t3,$a2,0x1
+	srl	$a2,1
+	bnez	$t3,.Lpad
+	lw	$t2,($t0)
+	addiu	$t0,4
+	sw	$t2,($t1)
+.Lpad:
+	addiu	$t1,4
+	b	.Lloop1
+
+.Largs_done:
+
+	/*
+	 * We have copied args into stacks. Then copy argv[0]/argv[1] into
+	 * reg a2/a3. You may find that if argv[0] is 32 bits and argv[1]
+	 * is 64 bits, then we do not need to set reg a3 since it is a pad.
+	 * However, copy a3 from argv is harmless. We do not need to set
+	 * a0(pEnv)/a1(clazz/this) since they are already there.
+	 */
+
+	/*
+	 * sp: new stack
+	 * s0: pReturn
+	 * s2: Return type
+	 *
+	 */
+	lw	$a2,8($sp)
+	lw	$a3,12($sp)
+
+	/* Linux/PIC needs $t9 points to function address.
+	 * call the function
+	 */
+	jalr $t9
+
+	/* function call return */
+	/* 1. check the return type
+	 * 2. if the return type is not DALVIK_JNI_RETURN_VOID then copy v0/v1
+	 *    to pReturn
+	 */
+	beqz	$s2,.Lend	/* don't set result if return type is void */
+
+#ifdef __mips_hard_float
+	mfc1	$t0,$f0		/* Get float ($f0) or double ($f1$f0) result */
+	mfc1	$t1,$f1
+	sltiu	$t2,$s2,3	/* set t2 if return type is float or double */
+#ifdef HAVE_LITTLE_ENDIAN
+        /* Note: for little endian, the double result is in $v1:$v0 and float result is in $v0 */
+	movn	$v0,$t0,$t2	/* If the result type is float or double overwrite $v1/$v0 */
+	movn	$v1,$t1,$t2
+#else
+        /* Note: for big endian, the double result is in $v0:$v1 and float result is in $v0 */
+	movn	$v1,$t0,$t2	/* If the result type is float or double overwrite $v0/$v1 */
+	movn	$v0,$t1,$t2
+	sltiu	$t3,$s2,2	/* set t3 if return type is float */
+	movn	$v0,$t0,$t3	/* If the result type is float overwrite $v0 */
+#endif
+#endif
+
+	/* Store the result */
+	sw	$v0,0($s0)
+	sw	$v1,4($s0)
+
+.Lend:
+	/* restore saved registers */
+	move	$sp,$fp
+	lw	$ra,20($sp)
+	lw	$fp,16($sp)
+	lw	$s0,12($sp)
+	lw	$s2,4($sp)
+	addiu	$sp,FSIZE
+	jr	$ra
+
+/* Slow path - just tail call the generic routine */
+.Lno_arginfo:
+
+	la $t9,dvmPlatformInvokeFFI
+	j $t9
+
+.end dvmPlatformInvoke
diff --git a/vm/arch/mips/HintsO32.cpp b/vm/arch/mips/HintsO32.cpp
new file mode 100644
index 0000000..77fdfd4
--- /dev/null
+++ b/vm/arch/mips/HintsO32.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the MIPS O32 ABI.
+ */
+
+/* TODO: this is candidate for consolidation of similar code from ARM. */
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints (see below for details)
+ *
+ * This function produces mips-specific hints - specifically a description
+ * of padding required to keep all 64-bit parameters properly aligned.
+ *
+ * MIPS JNI hint format(Same as ARM)
+ *
+ *       LLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   L - number of double-words of storage required on the stack (0-30 words)
+ *   F - pad flag -- if set, the stack increases 8 bytes, else the stack increases 4 bytes
+ *                   after copying 32 bits args into stack. (little different from ARM)
+ *
+ * If there are too many arguments to construct valid hints, this function will
+ * return a result with the S bit set.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto)
+{
+
+    const char* sig = dexProtoGetShorty(proto);
+    int padFlags, jniHints;
+    char sigByte;
+    int stackOffset, padMask, hints;
+
+    stackOffset = padFlags = 0;
+    padMask = 0x00000001;
+
+    /* Skip past the return type */
+    sig++;
+
+    while (true) {
+        sigByte = *(sig++);
+
+        if (sigByte == '\0')
+            break;
+
+        if (sigByte == 'D' || sigByte == 'J') {
+            if ((stackOffset & 1) != 0) {
+                padFlags |= padMask;
+                stackOffset++;
+                padMask <<= 1;
+            }
+            stackOffset += 2;
+            padMask <<= 2;
+        } else {
+            stackOffset++;
+            padMask <<= 1;
+        }
+    }
+
+    jniHints = 0;
+
+    if (stackOffset > DALVIK_JNI_COUNT_SHIFT) {
+        /* too big for "fast" version */
+        jniHints = DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        assert((padFlags & (0xffffffff << DALVIK_JNI_COUNT_SHIFT)) == 0);
+        /*
+         * StackOffset includes the space for a2/a3. However we have reserved
+         * 16 bytes on stack in CallO32.S, so we should subtract 2 from stackOffset.
+         */
+        stackOffset -= 2;
+        if (stackOffset < 0)
+            stackOffset = 0;
+        jniHints |= ((stackOffset+1) / 2) << DALVIK_JNI_COUNT_SHIFT;
+        jniHints |= padFlags;
+    }
+
+    return jniHints;
+}
diff --git a/vm/arch/x86/Call386ABI.S b/vm/arch/x86/Call386ABI.S
new file mode 100644
index 0000000..766aff7
--- /dev/null
+++ b/vm/arch/x86/Call386ABI.S
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports 32-bit x86
+ */
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+    -or-
+  return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two).  It's up to
+us to convert these into local calling conventions.
+*/
+
+/*
+x86 notes:
+
+The native code expects arguments on the stack, pushed from right to left.
+This matches what Dalvik is passing here.
+
+EAX, EDX and ECX are scratch.
+
+4-byte alignment is required for long long and double, so we won't pad
+
+Non-FP return types <= 4 bytes come back in EAX
+Non-FP return types of 8 bytes come back in EAX:EDX, with lsw in EAX.
+Float and double returned on top of FP stack.
+
+*/
+
+    .text
+    .align  4
+    .global dvmPlatformInvoke
+    .type   dvmPlatformInvoke, @function
+
+/*
+ * On entry:
+ *  [ 8]  arg0  JNIEnv (can be left alone)
+ *  [12]  arg1  clazz (NULL for virtual method calls, non-NULL for static)
+ *  [16]  arg2  arg info
+ *  [20]  arg3  argc
+ *  [24]  arg4  argv
+ *  [28]  arg5  short signature
+ *  [32]  arg6  func
+ *  [36]  arg7  pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ *   SRRRZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ *   Z - reserved
+ *   S - if set, argInfo hints are invalid
+ *   R - return type enumeration (see jniInternal.h)
+ *       VOID   -> 0
+ *       FLOAT  -> 1
+ *       DOUBLE -> 2
+ *       S8     -> 3
+ *       S4     -> 4
+ *   A - size of the variable argument block in 32-bit words
+ *
+ */
+dvmPlatformInvoke:
+/* Establish the frame pointer, 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, add two more slots for the first
+ * two arguments and grow (preserving alignment)
+ */
+    movl     %ebx,%ecx
+    leal     20(,%ecx,4),%ecx
+    andl     $0x0003FFF0,%ecx
+    subl     %ecx,%esp
+/* Handle this/class */
+    movl     8(%ebp),%ecx
+    movl     12(%ebp),%eax
+    movl     24(%ebp),%esi
+    testl    %eax,%eax
+    jne      isClass
+    movl     (%esi),%eax
+    addl     $4,%esi
+isClass:
+    movl     %eax,4(%esp)
+    movl     %ecx,0(%esp)
+/* Now, copy the variable arguments region */
+    movl     %ebx,%ecx
+    andl     $0x0000FFFF,%ecx
+    leal     8(%esp),%edi
+    cld
+    rep
+    movsd
+/* Ready to go - call the native code */
+    call     *32(%ebp)
+/* Store the result. */
+    sarl      $28,%ebx
+    /* Is void? */
+    testl     %ebx,%ebx
+    je       cleanUpAndExit
+    movl     36(%ebp),%ecx
+    /* Is FP? */
+    cmpl     $2,%ebx
+    jle      isFP
+    cmpl     $4,%ebx  /* smaller than 32-bits? */
+    jg       isSmall
+storeRetval:
+    /* Blindly storing 64-bits won't hurt 32-bit case */
+    movl     %eax,(%ecx)
+    movl     %edx,4(%ecx)
+    jmp      cleanUpAndExit
+isSmall:
+    cmpl     $7,%ebx  /* S1? */
+    jne      checkShort
+    movsbl   %al,%eax
+    movl     %eax,(%ecx)
+    jmp      cleanUpAndExit
+checkShort:
+    cmpl     $6,%ebx  /* U2? */
+    jne      isSignedShort
+    movzwl   %ax,%eax
+    movl     %eax,(%ecx)
+    jmp      cleanUpAndExit
+isSignedShort:
+    /* Must be S2 */
+    movswl   %ax,%eax
+    movl     %eax,(%ecx)
+    jmp      cleanUpAndExit
+isFP:
+    /* Is Float? */
+    cmpl    $1,%ebx
+    je       saveFloat
+    fstpl    (%ecx)
+    jmp      cleanUpAndExit
+saveFloat:
+    fstps    (%ecx)
+cleanUpAndExit:
+    leal     -12(%ebp),%esp
+    pop      %ebx
+    pop      %esi
+    pop      %edi
+    pop      %ebp
+    ret
+    .size    dvmPlatformInvoke, .-dvmPlatformInvoke
+    .section .note.GNU-stack,"",@progbits
diff --git a/vm/arch/x86/Hints386ABI.cpp b/vm/arch/x86/Hints386ABI.cpp
new file mode 100644
index 0000000..2a0a1d8
--- /dev/null
+++ b/vm/arch/x86/Hints386ABI.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints (see below for details)
+ *
+ * This function produces x86-specific hints for the standard 32-bit 386 ABI.
+ * Note that the JNI requirements are very close to the 386 runtime model.  In
+ * particular, natural datatype alignments do not apply to passed arguments.
+ * All arguments have 32-bit alignment.  As a result, we don't have to worry
+ * about padding - just total size.  The only tricky bit is that floating point
+ * return values come back on the FP stack.
+ *
+ *
+ * 386 ABI JNI hint format
+ *
+ *       ZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ *   Z - reserved, must be 0
+ *   A - size of variable argument block in 32-bit words (note - does not
+ *       include JNIEnv or clazz)
+ *
+ * For the 386 ABI, valid hints should always be generated.
+ */
+u4 dvmPlatformInvokeHints( const DexProto* proto)
+{
+    const char* sig = dexProtoGetShorty(proto);
+    unsigned int jniHints, wordCount;
+    char sigByte;
+
+    wordCount = 0;
+    while (true) {
+        sigByte = *(sig++);
+
+        if (sigByte == '\0')
+            break;
+
+        wordCount++;
+
+        if (sigByte == 'D' || sigByte == 'J') {
+            wordCount++;
+        }
+    }
+
+    if (wordCount > 0xFFFF) {
+        /* Invalid - Dex file limitation */
+        jniHints = DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        jniHints = wordCount;
+    }
+
+    return jniHints;
+}
diff --git a/vm/compiler/Compiler.cpp b/vm/compiler/Compiler.cpp
new file mode 100644
index 0000000..cdd62cc
--- /dev/null
+++ b/vm/compiler/Compiler.cpp
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+#include <errno.h>
+#include <cutils/ashmem.h>
+
+#include "Dalvik.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+#ifdef ARCH_IA32
+#include "codegen/x86/Translator.h"
+#include "codegen/x86/Lower.h"
+#endif
+
+extern "C" void dvmCompilerTemplateStart(void);
+extern "C" void dmvCompilerTemplateEnd(void);
+
+static inline bool workQueueLength(void)
+{
+    return gDvmJit.compilerQueueLength;
+}
+
+static CompilerWorkOrder workDequeue(void)
+{
+    assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind
+           != kWorkOrderInvalid);
+    CompilerWorkOrder work =
+        gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex];
+    gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind =
+        kWorkOrderInvalid;
+    if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) {
+        gDvmJit.compilerWorkDequeueIndex = 0;
+    }
+    gDvmJit.compilerQueueLength--;
+    if (gDvmJit.compilerQueueLength == 0) {
+        dvmSignalCond(&gDvmJit.compilerQueueEmpty);
+    }
+
+    /* Remember the high water mark of the queue length */
+    if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued)
+        gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength;
+
+    return work;
+}
+
+/*
+ * Enqueue a work order - retrying until successful.  If attempt to enqueue
+ * is repeatedly unsuccessful, assume the JIT is in a bad state and force a
+ * code cache reset.
+ */
+#define ENQUEUE_MAX_RETRIES 20
+void dvmCompilerForceWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
+{
+    bool success;
+    int retries = 0;
+    do {
+        success = dvmCompilerWorkEnqueue(pc, kind, info);
+        if (!success) {
+            retries++;
+            if (retries > ENQUEUE_MAX_RETRIES) {
+                ALOGE("JIT: compiler queue wedged - forcing reset");
+                gDvmJit.codeCacheFull = true;  // Force reset
+                success = true;  // Because we'll drop the order now anyway
+            } else {
+                dvmLockMutex(&gDvmJit.compilerLock);
+                pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+                                  &gDvmJit.compilerLock);
+                dvmUnlockMutex(&gDvmJit.compilerLock);
+
+            }
+        }
+    } while (!success);
+}
+
+/*
+ * Attempt to enqueue a work order, returning true if successful.
+ *
+ * NOTE: Make sure that the caller frees the info pointer if the return value
+ * is false.
+ */
+bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
+{
+    int cc;
+    int i;
+    int numWork;
+    bool result = true;
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+
+    /*
+     * Return if queue or code cache is full.
+     */
+    if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
+        gDvmJit.codeCacheFull == true) {
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        return false;
+    }
+
+    for (numWork = gDvmJit.compilerQueueLength,
+           i = gDvmJit.compilerWorkDequeueIndex;
+         numWork > 0;
+         numWork--) {
+        /* Already enqueued */
+        if (gDvmJit.compilerWorkQueue[i++].pc == pc) {
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+            return true;
+        }
+        /* Wrap around */
+        if (i == COMPILER_WORK_QUEUE_SIZE)
+            i = 0;
+    }
+
+    CompilerWorkOrder *newOrder =
+        &gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex];
+    newOrder->pc = pc;
+    newOrder->kind = kind;
+    newOrder->info = info;
+    newOrder->result.methodCompilationAborted = NULL;
+    newOrder->result.codeAddress = NULL;
+    newOrder->result.discardResult =
+        (kind == kWorkOrderTraceDebug) ? true : false;
+    newOrder->result.cacheVersion = gDvmJit.cacheVersion;
+    newOrder->result.requestingThread = dvmThreadSelf();
+
+    gDvmJit.compilerWorkEnqueueIndex++;
+    if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
+        gDvmJit.compilerWorkEnqueueIndex = 0;
+    gDvmJit.compilerQueueLength++;
+    cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+    assert(cc == 0);
+
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+    return result;
+}
+
+/* Block until the queue length is 0, or there is a pending suspend request */
+void dvmCompilerDrainQueue(void)
+{
+    Thread *self = dvmThreadSelf();
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+    while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread &&
+           self->suspendCount == 0) {
+        /*
+         * Use timed wait here - more than one mutator threads may be blocked
+         * but the compiler thread will only signal once when the queue is
+         * emptied. Furthermore, the compiler thread may have been shutdown
+         * so the blocked thread may never get the wakeup signal.
+         */
+        dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock,                             1000, 0);
+    }
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+}
+
+bool dvmCompilerSetupCodeCache(void)
+{
+    int fd;
+
+    /* Allocate the code cache */
+    fd = ashmem_create_region("dalvik-jit-code-cache", gDvmJit.codeCacheSize);
+    if (fd < 0) {
+        ALOGE("Could not create %u-byte ashmem region for the JIT code cache",
+             gDvmJit.codeCacheSize);
+        return false;
+    }
+    gDvmJit.codeCache = mmap(NULL, gDvmJit.codeCacheSize,
+                             PROT_READ | PROT_WRITE | PROT_EXEC,
+                             MAP_PRIVATE , fd, 0);
+    close(fd);
+    if (gDvmJit.codeCache == MAP_FAILED) {
+        ALOGE("Failed to mmap the JIT code cache: %s", strerror(errno));
+        return false;
+    }
+
+    gDvmJit.pageSizeMask = getpagesize() - 1;
+
+    /* This can be found through "dalvik-jit-code-cache" in /proc/<pid>/maps */
+    // ALOGD("Code cache starts at %p", gDvmJit.codeCache);
+
+#ifndef ARCH_IA32
+    /* Copy the template code into the beginning of the code cache */
+    int templateSize = (intptr_t) 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 */
+    dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
+                          (intptr_t) gDvmJit.codeCache + templateSize, 0);
+
+    int result = mprotect(gDvmJit.codeCache, gDvmJit.codeCacheSize,
+                          PROTECT_CODE_CACHE_ATTRS);
+
+    if (result == -1) {
+        ALOGE("Failed to remove the write permission for the code cache");
+        dvmAbort();
+    }
+#else
+    gDvmJit.codeCacheByteUsed = 0;
+    stream = (char*)gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+    ALOGV("codeCache = %p stream = %p before initJIT", gDvmJit.codeCache, stream);
+    streamStart = stream;
+    initJIT(NULL, NULL);
+    gDvmJit.templateSize = (stream - streamStart);
+    gDvmJit.codeCacheByteUsed = (stream - streamStart);
+    ALOGV("stream = %p after initJIT", stream);
+#endif
+
+    return true;
+}
+
+static void crawlDalvikStack(Thread *thread, bool print)
+{
+    void *fp = thread->interpSave.curFrame;
+    StackSaveArea* saveArea = NULL;
+    int stackLevel = 0;
+
+    if (print) {
+        ALOGD("Crawling tid %d (%s / %p %s)", thread->systemTid,
+             dvmGetThreadStatusStr(thread->status),
+             thread->inJitCodeCache,
+             thread->inJitCodeCache ? "jit" : "interp");
+    }
+    /* Crawl the Dalvik stack frames to clear the returnAddr field */
+    while (fp != NULL) {
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+        if (print) {
+            if (dvmIsBreakFrame((u4*)fp)) {
+                ALOGD("  #%d: break frame (%p)",
+                     stackLevel, saveArea->returnAddr);
+            }
+            else {
+                ALOGD("  #%d: %s.%s%s (%p)",
+                     stackLevel,
+                     saveArea->method->clazz->descriptor,
+                     saveArea->method->name,
+                     dvmIsNativeMethod(saveArea->method) ?
+                         " (native)" : "",
+                     saveArea->returnAddr);
+            }
+        }
+        stackLevel++;
+        saveArea->returnAddr = NULL;
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    /* Make sure the stack is fully unwound to the bottom */
+    assert(saveArea == NULL ||
+           (u1 *) (saveArea+1) == thread->interpStackStart);
+}
+
+static void resetCodeCache(void)
+{
+    Thread* thread;
+    u8 startTime = dvmGetRelativeTimeUsec();
+    int inJit = 0;
+    int byteUsed = gDvmJit.codeCacheByteUsed;
+
+    /* If any thread is found stuck in the JIT state, don't reset the cache  */
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        /*
+         * Crawl the stack to wipe out the returnAddr field so that
+         * 1) the soon-to-be-deleted code in the JIT cache won't be used
+         * 2) or the thread stuck in the JIT land will soon return
+         *    to the interpreter land
+         */
+        crawlDalvikStack(thread, false);
+        if (thread->inJitCodeCache) {
+            inJit++;
+        }
+        /* Cancel any ongoing trace selection */
+        dvmDisableSubMode(thread, kSubModeJitTraceBuild);
+    }
+    dvmUnlockThreadList();
+
+    if (inJit) {
+        ALOGD("JIT code cache reset delayed (%d bytes %d/%d)",
+             gDvmJit.codeCacheByteUsed, gDvmJit.numCodeCacheReset,
+             ++gDvmJit.numCodeCacheResetDelayed);
+        return;
+    }
+
+    /* Lock the mutex to clean up the work queue */
+    dvmLockMutex(&gDvmJit.compilerLock);
+
+    /* Update the translation cache version */
+    gDvmJit.cacheVersion++;
+
+    /* Drain the work queue to free the work orders */
+    while (workQueueLength()) {
+        CompilerWorkOrder work = workDequeue();
+        free(work.info);
+    }
+
+    /* Reset the JitEntry table contents to the initial unpopulated state */
+    dvmJitResetTable();
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+    /*
+     * Wipe out the code cache content to force immediate crashes if
+     * stale JIT'ed code is invoked.
+     */
+    dvmCompilerCacheClear((char *) gDvmJit.codeCache + gDvmJit.templateSize,
+                          gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+
+    dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
+                          (intptr_t) gDvmJit.codeCache +
+                          gDvmJit.codeCacheByteUsed, 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);
+
+    /*
+     * Reset the inflight compilation address (can only be done in safe points
+     * or by the compiler thread when its thread state is RUNNING).
+     */
+    gDvmJit.inflightBaseAddr = NULL;
+
+    /* All clear now */
+    gDvmJit.codeCacheFull = false;
+
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    ALOGD("JIT code cache reset in %lld ms (%d bytes %d/%d)",
+         (dvmGetRelativeTimeUsec() - startTime) / 1000,
+         byteUsed, ++gDvmJit.numCodeCacheReset,
+         gDvmJit.numCodeCacheResetDelayed);
+}
+
+/*
+ * Perform actions that are only safe when all threads are suspended. Currently
+ * we do:
+ * 1) Check if the code cache is full. If so reset it and restart populating it
+ *    from scratch.
+ * 2) Patch predicted chaining cells by consuming recorded work orders.
+ */
+void dvmCompilerPerformSafePointChecks(void)
+{
+    if (gDvmJit.codeCacheFull) {
+        resetCodeCache();
+    }
+    dvmCompilerPatchInlineCache();
+}
+
+static bool compilerThreadStartup(void)
+{
+    JitEntry *pJitTable = NULL;
+    unsigned char *pJitProfTable = NULL;
+    JitTraceProfCounters *pJitTraceProfCounters = NULL;
+    unsigned int i;
+
+    if (!dvmCompilerArchInit())
+        goto fail;
+
+    /*
+     * Setup the code cache if we have not inherited a valid code cache
+     * from the zygote.
+     */
+    if (gDvmJit.codeCache == NULL) {
+        if (!dvmCompilerSetupCodeCache())
+            goto fail;
+    }
+
+    /* Allocate the initial arena block */
+    if (dvmCompilerHeapInit() == false) {
+        goto fail;
+    }
+
+    /* Cache the thread pointer */
+    gDvmJit.compilerThread = dvmThreadSelf();
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+
+    /* Track method-level compilation statistics */
+    gDvmJit.methodStatsTable =  dvmHashTableCreate(32, NULL);
+
+#if defined(WITH_JIT_TUNING)
+    gDvm.verboseShutdown = true;
+#endif
+
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /* Set up the JitTable */
+
+    /* Power of 2? */
+    assert(gDvmJit.jitTableSize &&
+           !(gDvmJit.jitTableSize & (gDvmJit.jitTableSize - 1)));
+
+    dvmInitMutex(&gDvmJit.tableLock);
+    dvmLockMutex(&gDvmJit.tableLock);
+    pJitTable = (JitEntry*)
+                calloc(gDvmJit.jitTableSize, sizeof(*pJitTable));
+    if (!pJitTable) {
+        ALOGE("jit table allocation failed");
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        goto fail;
+    }
+    /*
+     * NOTE: the profile table must only be allocated once, globally.
+     * Profiling is turned on and off by nulling out gDvm.pJitProfTable
+     * and then restoring its original value.  However, this action
+     * is not synchronized for speed so threads may continue to hold
+     * and update the profile table after profiling has been turned
+     * off by null'ng the global pointer.  Be aware.
+     */
+    pJitProfTable = (unsigned char *)malloc(JIT_PROF_SIZE);
+    if (!pJitProfTable) {
+        ALOGE("jit prof table allocation failed");
+        free(pJitProfTable);
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        goto fail;
+    }
+    memset(pJitProfTable, gDvmJit.threshold, JIT_PROF_SIZE);
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+       pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
+    }
+    /* Is chain field wide enough for termination pattern? */
+    assert(pJitTable[0].u.info.chain == gDvmJit.jitTableSize);
+
+    /* Allocate the trace profiling structure */
+    pJitTraceProfCounters = (JitTraceProfCounters*)
+                             calloc(1, sizeof(*pJitTraceProfCounters));
+    if (!pJitTraceProfCounters) {
+        ALOGE("jit trace prof counters allocation failed");
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        goto fail;
+    }
+
+    gDvmJit.pJitEntryTable = pJitTable;
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    gDvmJit.jitTableEntriesUsed = 0;
+    gDvmJit.compilerHighWater =
+        COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
+    /*
+     * If the VM is launched with wait-on-the-debugger, we will need to hide
+     * the profile table here
+     */
+    gDvmJit.pProfTable = dvmDebuggerOrProfilerActive() ? NULL : pJitProfTable;
+    gDvmJit.pProfTableCopy = pJitProfTable;
+    gDvmJit.pJitTraceProfCounters = pJitTraceProfCounters;
+    dvmJitUpdateThreadStateAll();
+    dvmUnlockMutex(&gDvmJit.tableLock);
+
+    /* Signal running threads to refresh their cached pJitTable pointers */
+    dvmSuspendAllThreads(SUSPEND_FOR_REFRESH);
+    dvmResumeAllThreads(SUSPEND_FOR_REFRESH);
+
+    /* Enable signature breakpoints by customizing the following code */
+#if defined(SIGNATURE_BREAKPOINT)
+    /*
+     * Suppose one sees the following native crash in the bugreport:
+     * I/DEBUG   ( 1638): Build fingerprint: 'unknown'
+     * I/DEBUG   ( 1638): pid: 2468, tid: 2507  >>> com.google.android.gallery3d
+     * I/DEBUG   ( 1638): signal 11 (SIGSEGV), fault addr 00001400
+     * I/DEBUG   ( 1638):  r0 44ea7190  r1 44e4f7b8  r2 44ebc710  r3 00000000
+     * I/DEBUG   ( 1638):  r4 00000a00  r5 41862dec  r6 4710dc10  r7 00000280
+     * I/DEBUG   ( 1638):  r8 ad010f40  r9 46a37a12  10 001116b0  fp 42a78208
+     * I/DEBUG   ( 1638):  ip 00000090  sp 4710dbc8  lr ad060e67  pc 46b90682
+     * cpsr 00000030
+     * I/DEBUG   ( 1638):  #00  pc 46b90682 /dev/ashmem/dalvik-jit-code-cache
+     * I/DEBUG   ( 1638):  #01  pc 00060e62  /system/lib/libdvm.so
+     *
+     * I/DEBUG   ( 1638): code around pc:
+     * I/DEBUG   ( 1638): 46b90660 6888d01c 34091dcc d2174287 4a186b68
+     * I/DEBUG   ( 1638): 46b90670 d0052800 68006809 28004790 6b68d00e
+     * I/DEBUG   ( 1638): 46b90680 512000bc 37016eaf 6ea866af 6f696028
+     * I/DEBUG   ( 1638): 46b90690 682a6069 429a686b e003da08 6df1480b
+     * I/DEBUG   ( 1638): 46b906a0 1c2d4788 47806d70 46a378fa 47806d70
+     *
+     * Clearly it is a JIT bug. To find out which translation contains the
+     * offending code, the content of the memory dump around the faulting PC
+     * can be pasted into the gDvmJit.signatureBreakpoint[] array and next time
+     * when a similar compilation is being created, the JIT compiler replay the
+     * trace in the verbose mode and one can investigate the instruction
+     * sequence in details.
+     *
+     * The length of the signature may need additional experiments to determine.
+     * The rule of thumb is don't include PC-relative instructions in the
+     * signature since it may be affected by the alignment of the compiled code.
+     * However, a signature that's too short might increase the chance of false
+     * positive matches. Using gdbjithelper to disassembly the memory content
+     * first might be a good companion approach.
+     *
+     * For example, if the next 4 words starting from 46b90680 is pasted into
+     * the data structure:
+     */
+
+    gDvmJit.signatureBreakpointSize = 4;
+    gDvmJit.signatureBreakpoint =
+        malloc(sizeof(u4) * gDvmJit.signatureBreakpointSize);
+    gDvmJit.signatureBreakpoint[0] = 0x512000bc;
+    gDvmJit.signatureBreakpoint[1] = 0x37016eaf;
+    gDvmJit.signatureBreakpoint[2] = 0x6ea866af;
+    gDvmJit.signatureBreakpoint[3] = 0x6f696028;
+
+    /*
+     * The following log will be printed when a match is found in subsequent
+     * testings:
+     *
+     * D/dalvikvm( 2468): Signature match starting from offset 0x34 (4 words)
+     * D/dalvikvm( 2468): --------
+     * D/dalvikvm( 2468): Compiler: Building trace for computeVisibleItems,
+     * offset 0x1f7
+     * D/dalvikvm( 2468): 0x46a37a12: 0x0090 add-int v42, v5, v26
+     * D/dalvikvm( 2468): 0x46a37a16: 0x004d aput-object v13, v14, v42
+     * D/dalvikvm( 2468): 0x46a37a1a: 0x0028 goto, (#0), (#0)
+     * D/dalvikvm( 2468): 0x46a3794e: 0x00d8 add-int/lit8 v26, v26, (#1)
+     * D/dalvikvm( 2468): 0x46a37952: 0x0028 goto, (#0), (#0)
+     * D/dalvikvm( 2468): 0x46a378ee: 0x0002 move/from16 v0, v26, (#0)
+     * D/dalvikvm( 2468): 0x46a378f2: 0x0002 move/from16 v1, v29, (#0)
+     * D/dalvikvm( 2468): 0x46a378f6: 0x0035 if-ge v0, v1, (#10)
+     * D/dalvikvm( 2468): TRACEINFO (554): 0x46a37624
+     * Lcom/cooliris/media/GridLayer;computeVisibleItems 0x1f7 14 of 934, 8
+     * blocks
+     *     :
+     *     :
+     * D/dalvikvm( 2468): 0x20 (0020): ldr     r0, [r5, #52]
+     * D/dalvikvm( 2468): 0x22 (0022): ldr     r2, [pc, #96]
+     * D/dalvikvm( 2468): 0x24 (0024): cmp     r0, #0
+     * D/dalvikvm( 2468): 0x26 (0026): beq     0x00000034
+     * D/dalvikvm( 2468): 0x28 (0028): ldr     r1, [r1, #0]
+     * D/dalvikvm( 2468): 0x2a (002a): ldr     r0, [r0, #0]
+     * D/dalvikvm( 2468): 0x2c (002c): blx     r2
+     * D/dalvikvm( 2468): 0x2e (002e): cmp     r0, #0
+     * D/dalvikvm( 2468): 0x30 (0030): beq     0x00000050
+     * D/dalvikvm( 2468): 0x32 (0032): ldr     r0, [r5, #52]
+     * D/dalvikvm( 2468): 0x34 (0034): lsls    r4, r7, #2
+     * D/dalvikvm( 2468): 0x36 (0036): str     r0, [r4, r4]
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x01fb @ goto, (#0), (#0)
+     * D/dalvikvm( 2468): L0x0195:
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x0195 @ add-int/lit8 v26,
+     * v26, (#1)
+     * D/dalvikvm( 2468): 0x38 (0038): ldr     r7, [r5, #104]
+     * D/dalvikvm( 2468): 0x3a (003a): adds    r7, r7, #1
+     * D/dalvikvm( 2468): 0x3c (003c): str     r7, [r5, #104]
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x0197 @ goto, (#0), (#0)
+     * D/dalvikvm( 2468): L0x0165:
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x0165 @ move/from16 v0, v26,
+     * (#0)
+     * D/dalvikvm( 2468): 0x3e (003e): ldr     r0, [r5, #104]
+     * D/dalvikvm( 2468): 0x40 (0040): str     r0, [r5, #0]
+     *
+     * The "str r0, [r4, r4]" is indeed the culprit of the native crash.
+     */
+#endif
+
+    return true;
+
+fail:
+    return false;
+
+}
+
+static void *compilerThreadStart(void *arg)
+{
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+    /*
+     * If we're not running stand-alone, wait a little before
+     * recieving translation requests on the assumption that process start
+     * up code isn't worth compiling.  We'll resume when the framework
+     * signals us that the first screen draw has happened, or the timer
+     * below expires (to catch daemons).
+     *
+     * There is a theoretical race between the callback to
+     * VMRuntime.startJitCompiation and when the compiler thread reaches this
+     * point. In case the callback happens earlier, in order not to permanently
+     * hold the system_server (which is not using the timed wait) in
+     * interpreter-only mode we bypass the delay here.
+     */
+    if (gDvmJit.runningInAndroidFramework &&
+        !gDvmJit.alreadyEnabledViaFramework) {
+        /*
+         * If the current VM instance is the system server (detected by having
+         * 0 in gDvm.systemServerPid), we will use the indefinite wait on the
+         * conditional variable to determine whether to start the JIT or not.
+         * If the system server detects that the whole system is booted in
+         * safe mode, the conditional variable will never be signaled and the
+         * system server will remain in the interpreter-only mode. All
+         * subsequent apps will be started with the --enable-safemode flag
+         * explicitly appended.
+         */
+        if (gDvm.systemServerPid == 0) {
+            dvmLockMutex(&gDvmJit.compilerLock);
+            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+                              &gDvmJit.compilerLock);
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+            ALOGD("JIT started for system_server");
+        } else {
+            dvmLockMutex(&gDvmJit.compilerLock);
+            /*
+             * TUNING: experiment with the delay & perhaps make it
+             * target-specific
+             */
+            dvmRelativeCondWait(&gDvmJit.compilerQueueActivity,
+                                 &gDvmJit.compilerLock, 3000, 0);
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+        }
+        if (gDvmJit.haltCompilerThread) {
+             return NULL;
+        }
+    }
+
+    compilerThreadStartup();
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+    /*
+     * Since the compiler thread will not touch any objects on the heap once
+     * being created, we just fake its state as VMWAIT so that it can be a
+     * bit late when there is suspend request pending.
+     */
+    while (!gDvmJit.haltCompilerThread) {
+        if (workQueueLength() == 0) {
+            int cc;
+            cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
+            assert(cc == 0);
+            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+                              &gDvmJit.compilerLock);
+            continue;
+        } else {
+            do {
+                CompilerWorkOrder work = workDequeue();
+                dvmUnlockMutex(&gDvmJit.compilerLock);
+#if defined(WITH_JIT_TUNING)
+                /*
+                 * This is live across setjmp().  Mark it volatile to suppress
+                 * a gcc warning.  We should not need this since it is assigned
+                 * only once but gcc is not smart enough.
+                 */
+                volatile u8 startTime = dvmGetRelativeTimeUsec();
+#endif
+                /*
+                 * Check whether there is a suspend request on me.  This
+                 * is necessary to allow a clean shutdown.
+                 *
+                 * However, in the blocking stress testing mode, let the
+                 * compiler thread continue doing compilations to unblock
+                 * other requesting threads. This may occasionally cause
+                 * shutdown from proceeding cleanly in the standalone invocation
+                 * of the vm but this should be acceptable.
+                 */
+                if (!gDvmJit.blockingMode)
+                    dvmCheckSuspendPending(dvmThreadSelf());
+                /* Is JitTable filling up? */
+                if (gDvmJit.jitTableEntriesUsed >
+                    (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
+                    bool resizeFail =
+                        dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
+                    /*
+                     * If the jit table is full, consider it's time to reset
+                     * the code cache too.
+                     */
+                    gDvmJit.codeCacheFull |= resizeFail;
+                }
+                if (gDvmJit.haltCompilerThread) {
+                    ALOGD("Compiler shutdown in progress - discarding request");
+                } else if (!gDvmJit.codeCacheFull) {
+                    jmp_buf jmpBuf;
+                    work.bailPtr = &jmpBuf;
+                    bool aborted = setjmp(jmpBuf);
+                    if (!aborted) {
+                        bool codeCompiled = dvmCompilerDoWork(&work);
+                        /*
+                         * Make sure we are still operating with the
+                         * same translation cache version.  See
+                         * Issue 4271784 for details.
+                         */
+                        dvmLockMutex(&gDvmJit.compilerLock);
+                        if ((work.result.cacheVersion ==
+                             gDvmJit.cacheVersion) &&
+                             codeCompiled &&
+                             !work.result.discardResult &&
+                             work.result.codeAddress) {
+                            dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
+                                              work.result.instructionSet,
+                                              false, /* not method entry */
+                                              work.result.profileCodeSize);
+                        }
+                        dvmUnlockMutex(&gDvmJit.compilerLock);
+                    }
+                    dvmCompilerArenaReset();
+                }
+                free(work.info);
+#if defined(WITH_JIT_TUNING)
+                gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime;
+#endif
+                dvmLockMutex(&gDvmJit.compilerLock);
+            } while (workQueueLength() != 0);
+        }
+    }
+    pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /*
+     * As part of detaching the thread we need to call into Java code to update
+     * the ThreadGroup, and we should not be in VMWAIT state while executing
+     * interpreted code.
+     */
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+
+    if (gDvm.verboseShutdown)
+        ALOGD("Compiler thread shutting down");
+    return NULL;
+}
+
+bool dvmCompilerStartup(void)
+{
+
+    dvmInitMutex(&gDvmJit.compilerLock);
+    dvmInitMutex(&gDvmJit.compilerICPatchLock);
+    dvmInitMutex(&gDvmJit.codeCacheProtectionLock);
+    dvmLockMutex(&gDvmJit.compilerLock);
+    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;
+    dvmJitUpdateThreadStateAll();
+
+    if (gDvm.verboseShutdown ||
+            gDvmJit.profileMode == kTraceProfilingContinuous) {
+        dvmCompilerDumpStats();
+        while (gDvmJit.compilerQueueLength)
+          sleep(5);
+    }
+
+    if (gDvmJit.compilerHandle) {
+
+        gDvmJit.haltCompilerThread = true;
+
+        dvmLockMutex(&gDvmJit.compilerLock);
+        pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+
+        if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
+            ALOGW("Compiler thread join failed");
+        else if (gDvm.verboseShutdown)
+            ALOGD("Compiler thread has shut down");
+    }
+
+    /* Break loops within the translation cache */
+    dvmJitUnchainAll();
+
+    /*
+     * NOTE: our current implementatation doesn't allow for the compiler
+     * thread to be restarted after it exits here.  We aren't freeing
+     * the JitTable or the ProfTable because threads which still may be
+     * running or in the process of shutting down may hold references to
+     * them.
+     */
+}
+
+void dvmCompilerUpdateGlobalState()
+{
+    bool jitActive;
+    bool jitActivate;
+    bool needUnchain = false;
+
+    /*
+     * The tableLock might not be initialized yet by the compiler thread if
+     * debugger is attached from the very beginning of the VM launch. If
+     * pProfTableCopy is NULL, the lock is not initialized yet and we don't
+     * need to refresh anything either.
+     */
+    if (gDvmJit.pProfTableCopy == NULL) {
+        return;
+    }
+
+    /*
+     * On the first enabling of method tracing, switch the compiler
+     * into a mode that includes trace support for invokes and returns.
+     * If there are any existing translations, flush them.  NOTE:  we
+     * can't blindly flush the translation cache because this code
+     * may be executed before the compiler thread has finished
+     * initialization.
+     */
+    if ((gDvm.activeProfilers != 0) &&
+        !gDvmJit.methodTraceSupport) {
+        bool resetRequired;
+        /*
+         * compilerLock will prevent new compilations from being
+         * installed while we are working.
+         */
+        dvmLockMutex(&gDvmJit.compilerLock);
+        gDvmJit.cacheVersion++; // invalidate compilations in flight
+        gDvmJit.methodTraceSupport = true;
+        resetRequired = (gDvmJit.numCompilations != 0);
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        if (resetRequired) {
+            dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
+            resetCodeCache();
+            dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
+        }
+    }
+
+    dvmLockMutex(&gDvmJit.tableLock);
+    jitActive = gDvmJit.pProfTable != NULL;
+    jitActivate = !dvmDebuggerOrProfilerActive();
+
+    if (jitActivate && !jitActive) {
+        gDvmJit.pProfTable = gDvmJit.pProfTableCopy;
+    } else if (!jitActivate && jitActive) {
+        gDvmJit.pProfTable = NULL;
+        needUnchain = true;
+    }
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    if (needUnchain)
+        dvmJitUnchainAll();
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+}
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
new file mode 100644
index 0000000..7af2809
--- /dev/null
+++ b/vm/compiler/Compiler.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_H_
+#define DALVIK_VM_COMPILER_H_
+
+#include <setjmp.h>
+#include "Thread.h"
+
+/*
+ * Uncomment the following to enable JIT signature breakpoint
+ * #define SIGNATURE_BREAKPOINT
+ */
+
+#define COMPILER_WORK_QUEUE_SIZE        100
+#define COMPILER_IC_PATCH_QUEUE_SIZE    64
+#define COMPILER_PC_OFFSET_SIZE         100
+
+/* Architectural-independent parameters for predicted chains */
+#define PREDICTED_CHAIN_CLAZZ_INIT       0
+#define PREDICTED_CHAIN_METHOD_INIT      0
+#define PREDICTED_CHAIN_COUNTER_INIT     0
+/* A fake value which will avoid initialization and won't match any class */
+#define PREDICTED_CHAIN_FAKE_CLAZZ       0xdeadc001
+/* Has to be positive */
+#define PREDICTED_CHAIN_COUNTER_AVOID    0x7fffffff
+/* Rechain after this many misses - shared globally and has to be positive */
+#define PREDICTED_CHAIN_COUNTER_RECHAIN  8192
+
+#define COMPILER_TRACED(X)
+#define COMPILER_TRACEE(X)
+#define COMPILER_TRACE_CHAINING(X)
+
+/* Macro to change the permissions applied to a chunk of the code cache */
+#define PROTECT_CODE_CACHE_ATTRS       (PROT_READ | PROT_EXEC)
+#define UNPROTECT_CODE_CACHE_ATTRS     (PROT_READ | PROT_EXEC | PROT_WRITE)
+
+/* Acquire the lock before removing PROT_WRITE from the specified mem region */
+#define UNPROTECT_CODE_CACHE(addr, size)                                       \
+    {                                                                          \
+        dvmLockMutex(&gDvmJit.codeCacheProtectionLock);                        \
+        mprotect((void *) (((intptr_t) (addr)) & ~gDvmJit.pageSizeMask),       \
+                 (size) + (((intptr_t) (addr)) & gDvmJit.pageSizeMask),        \
+                 (UNPROTECT_CODE_CACHE_ATTRS));                                \
+    }
+
+/* Add the PROT_WRITE to the specified memory region then release the lock */
+#define PROTECT_CODE_CACHE(addr, size)                                         \
+    {                                                                          \
+        mprotect((void *) (((intptr_t) (addr)) & ~gDvmJit.pageSizeMask),       \
+                 (size) + (((intptr_t) (addr)) & gDvmJit.pageSizeMask),        \
+                 (PROTECT_CODE_CACHE_ATTRS));                                  \
+        dvmUnlockMutex(&gDvmJit.codeCacheProtectionLock);                      \
+    }
+
+#define SINGLE_STEP_OP(opcode)                                                 \
+    (gDvmJit.includeSelectedOp !=                                              \
+     ((gDvmJit.opList[opcode >> 3] & (1 << (opcode & 0x7))) != 0))
+
+typedef enum JitInstructionSetType {
+    DALVIK_JIT_NONE = 0,
+    DALVIK_JIT_ARM,
+    DALVIK_JIT_THUMB,
+    DALVIK_JIT_THUMB2,
+    DALVIK_JIT_IA32,
+    DALVIK_JIT_MIPS
+} JitInstructionSetType;
+
+/* Description of a compiled trace. */
+typedef struct JitTranslationInfo {
+    void *codeAddress;
+    JitInstructionSetType instructionSet;
+    int profileCodeSize;
+    bool discardResult;         // Used for debugging divergence and IC patching
+    bool methodCompilationAborted;  // Cannot compile the whole method
+    Thread *requestingThread;   // For debugging purpose
+    int cacheVersion;           // Used to identify stale trace requests
+} JitTranslationInfo;
+
+typedef enum WorkOrderKind {
+    kWorkOrderInvalid = 0,      // Should never see by the backend
+    kWorkOrderMethod = 1,       // Work is to compile a whole method
+    kWorkOrderTrace = 2,        // Work is to compile code fragment(s)
+    kWorkOrderTraceDebug = 3,   // Work is to compile/debug code fragment(s)
+    kWorkOrderProfileMode = 4,  // Change profiling mode
+} WorkOrderKind;
+
+typedef struct CompilerWorkOrder {
+    const u2* pc;
+    WorkOrderKind kind;
+    void* info;
+    JitTranslationInfo result;
+    jmp_buf *bailPtr;
+} CompilerWorkOrder;
+
+/* Chain cell for predicted method invocation */
+typedef struct PredictedChainingCell {
+    u4 branch;                  /* Branch to chained destination */
+#ifdef __mips__
+    u4 delay_slot;              /* nop goes here */
+#elif defined(ARCH_IA32)
+    u4 branch2;                 /* IA32 branch instr may be > 32 bits */
+#endif
+    const ClassObject *clazz;   /* key for prediction */
+    const Method *method;       /* to lookup native PC from dalvik PC */
+    const ClassObject *stagedClazz;   /* possible next key for prediction */
+} PredictedChainingCell;
+
+/* Work order for inline cache patching */
+typedef struct ICPatchWorkOrder {
+    PredictedChainingCell *cellAddr;    /* Address to be patched */
+    PredictedChainingCell cellContent;  /* content of the new cell */
+    const char *classDescriptor;        /* Descriptor of the class object */
+    Object *classLoader;                /* Class loader */
+    u4 serialNumber;                    /* Serial # (for verification only) */
+} ICPatchWorkOrder;
+
+/*
+ * Trace description as will appear in the translation cache.  Note
+ * flexible array at end, as these will be of variable size.  To
+ * conserve space in the translation cache, total length of JitTraceRun
+ * array must be recomputed via seqential scan if needed.
+ */
+typedef struct {
+    const Method* method;
+    JitTraceRun trace[0];       // Variable-length trace descriptors
+} JitTraceDescription;
+
+typedef enum JitMethodAttributes {
+    kIsCallee = 0,      /* Code is part of a callee (invoked by a hot trace) */
+    kIsHot,             /* Code is part of a hot trace */
+    kIsLeaf,            /* Method is leaf */
+    kIsEmpty,           /* Method is empty */
+    kIsThrowFree,       /* Method doesn't throw */
+    kIsGetter,          /* Method fits the getter pattern */
+    kIsSetter,          /* Method fits the setter pattern */
+    kCannotCompile,     /* Method cannot be compiled */
+} JitMethodAttributes;
+
+#define METHOD_IS_CALLEE        (1 << kIsCallee)
+#define METHOD_IS_HOT           (1 << kIsHot)
+#define METHOD_IS_LEAF          (1 << kIsLeaf)
+#define METHOD_IS_EMPTY         (1 << kIsEmpty)
+#define METHOD_IS_THROW_FREE    (1 << kIsThrowFree)
+#define METHOD_IS_GETTER        (1 << kIsGetter)
+#define METHOD_IS_SETTER        (1 << kIsSetter)
+#define METHOD_CANNOT_COMPILE   (1 << kCannotCompile)
+
+/* Vectors to provide optimization hints */
+typedef enum JitOptimizationHints {
+    kJitOptNoLoop = 0,          // Disable loop formation/optimization
+} JitOptimizationHints;
+
+#define JIT_OPT_NO_LOOP         (1 << kJitOptNoLoop)
+
+/* Customized node traversal orders for different needs */
+typedef enum DataFlowAnalysisMode {
+    kAllNodes = 0,              // All nodes
+    kReachableNodes,            // All reachable nodes
+    kPreOrderDFSTraversal,      // Depth-First-Search / Pre-Order
+    kPostOrderDFSTraversal,     // Depth-First-Search / Post-Order
+    kPostOrderDOMTraversal,     // Dominator tree / Post-Order
+} DataFlowAnalysisMode;
+
+typedef struct CompilerMethodStats {
+    const Method *method;       // Used as hash entry signature
+    int dalvikSize;             // # of bytes for dalvik bytecodes
+    int compiledDalvikSize;     // # of compiled dalvik bytecodes
+    int nativeSize;             // # of bytes for produced native code
+    int attributes;             // attribute vector
+} CompilerMethodStats;
+
+struct CompilationUnit;
+struct BasicBlock;
+struct SSARepresentation;
+struct GrowableList;
+struct JitEntry;
+struct MIR;
+
+bool dvmCompilerSetupCodeCache(void);
+bool dvmCompilerArchInit(void);
+void dvmCompilerArchDump(void);
+bool dvmCompilerStartup(void);
+void dvmCompilerShutdown(void);
+void dvmCompilerForceWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info);
+bool dvmCompilerWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info);
+void *dvmCheckCodeCache(void *method);
+CompilerMethodStats *dvmCompilerAnalyzeMethodBody(const Method *method,
+                                                  bool isCallee);
+bool dvmCompilerCanIncludeThisInstruction(const Method *method,
+                                          const DecodedInstruction *insn);
+bool dvmCompileMethod(const Method *method, JitTranslationInfo *info);
+bool dvmCompileTrace(JitTraceDescription *trace, int numMaxInsts,
+                     JitTranslationInfo *info, jmp_buf *bailPtr, int optHints);
+void dvmCompilerDumpStats(void);
+void dvmCompilerDrainQueue(void);
+void dvmJitUnchainAll(void);
+void dvmJitScanAllClassPointers(void (*callback)(void *ptr));
+void dvmCompilerSortAndPrintTraceProfiles(void);
+void dvmCompilerPerformSafePointChecks(void);
+void dvmCompilerInlineMIR(struct CompilationUnit *cUnit,
+                          JitTranslationInfo *info);
+void dvmInitializeSSAConversion(struct CompilationUnit *cUnit);
+int dvmConvertSSARegToDalvik(const struct CompilationUnit *cUnit, int ssaReg);
+bool dvmCompilerLoopOpt(struct CompilationUnit *cUnit);
+void dvmCompilerInsertBackwardChaining(struct CompilationUnit *cUnit);
+void dvmCompilerNonLoopAnalysis(struct CompilationUnit *cUnit);
+bool dvmCompilerFindLocalLiveIn(struct CompilationUnit *cUnit,
+                                struct BasicBlock *bb);
+bool dvmCompilerDoSSAConversion(struct CompilationUnit *cUnit,
+                                struct BasicBlock *bb);
+bool dvmCompilerDoConstantPropagation(struct CompilationUnit *cUnit,
+                                      struct BasicBlock *bb);
+bool dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+                                       struct BasicBlock *bb);
+/* Clear the visited flag for each BB */
+bool dvmCompilerClearVisitedFlag(struct CompilationUnit *cUnit,
+                                 struct BasicBlock *bb);
+char *dvmCompilerGetDalvikDisassembly(const DecodedInstruction *insn,
+                                      const char *note);
+char *dvmCompilerFullDisassembler(const struct CompilationUnit *cUnit,
+                                  const struct MIR *mir);
+char *dvmCompilerGetSSAString(struct CompilationUnit *cUnit,
+                              struct SSARepresentation *ssaRep);
+void dvmCompilerDataFlowAnalysisDispatcher(struct CompilationUnit *cUnit,
+                bool (*func)(struct CompilationUnit *, struct BasicBlock *),
+                DataFlowAnalysisMode dfaMode,
+                bool isIterative);
+void dvmCompilerMethodSSATransformation(struct CompilationUnit *cUnit);
+bool dvmCompilerBuildLoop(struct CompilationUnit *cUnit);
+void dvmCompilerUpdateGlobalState(void);
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const struct JitEntry *desc);
+extern "C" void *dvmCompilerGetInterpretTemplate();
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet();
+u8 dvmGetRegResourceMask(int reg);
+void dvmDumpCFG(struct CompilationUnit *cUnit, const char *dirPrefix);
+bool dvmIsOpcodeSupportedByJit(Opcode opcode);
+
+#endif  // DALVIK_VM_COMPILER_H_
diff --git a/vm/compiler/CompilerIR.h b/vm/compiler/CompilerIR.h
new file mode 100644
index 0000000..73efad8
--- /dev/null
+++ b/vm/compiler/CompilerIR.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_IR_H_
+#define DALVIK_VM_COMPILER_IR_H_
+
+#include "codegen/Optimizer.h"
+#ifdef ARCH_IA32
+#include "CompilerUtility.h"
+#endif
+
+typedef enum RegisterClass {
+    kCoreReg,
+    kFPReg,
+    kAnyReg,
+} RegisterClass;
+
+typedef enum RegLocationType {
+    kLocDalvikFrame = 0,
+    kLocPhysReg,
+    kLocRetval,          // Return region in interpState
+    kLocSpill,
+} RegLocationType;
+
+typedef struct RegLocation {
+    RegLocationType location:2;
+    unsigned wide:1;
+    unsigned fp:1;      // Hint for float/double
+    u1 lowReg:6;        // First physical register
+    u1 highReg:6;       // 2nd physical register (if wide)
+    s2 sRegLow;         // SSA name for low Dalvik word
+} RegLocation;
+
+#define INVALID_SREG (-1)
+#define INVALID_REG (0x3F)
+
+typedef enum BBType {
+    /* For coding convenience reasons chaining cell types should appear first */
+    kChainingCellNormal = 0,
+    kChainingCellHot,
+    kChainingCellInvokeSingleton,
+    kChainingCellInvokePredicted,
+    kChainingCellBackwardBranch,
+    kChainingCellGap,
+    /* Don't insert new fields between Gap and Last */
+    kChainingCellLast = kChainingCellGap + 1,
+    kEntryBlock,
+    kDalvikByteCode,
+    kExitBlock,
+    kPCReconstruction,
+    kExceptionHandling,
+    kCatchEntry,
+} BBType;
+
+typedef enum JitMode {
+    kJitTrace = 0, // Acyclic - all instructions come from the trace descriptor
+    kJitLoop,      // Cycle - trace descriptor is used as a hint
+    kJitMethod,    // Whole method
+} JitMode;
+
+typedef struct ChainCellCounts {
+    union {
+        u1 count[kChainingCellLast]; /* include one more space for the gap # */
+        u4 dummyForAlignment;
+    } u;
+} ChainCellCounts;
+
+typedef struct LIR {
+    int offset;
+    struct LIR *next;
+    struct LIR *prev;
+    struct LIR *target;
+} LIR;
+
+enum ExtendedMIROpcode {
+    kMirOpFirst = kNumPackedOpcodes,
+    kMirOpPhi = kMirOpFirst,
+    kMirOpNullNRangeUpCheck,
+    kMirOpNullNRangeDownCheck,
+    kMirOpLowerBound,
+    kMirOpPunt,
+    kMirOpCheckInlinePrediction,        // Gen checks for predicted inlining
+    kMirOpLast,
+};
+
+struct SSARepresentation;
+
+typedef enum {
+    kMIRIgnoreNullCheck = 0,
+    kMIRNullCheckOnly,
+    kMIRIgnoreRangeCheck,
+    kMIRRangeCheckOnly,
+    kMIRInlined,                        // Invoke is inlined (ie dead)
+    kMIRInlinedPred,                    // Invoke is inlined via prediction
+    kMIRCallee,                         // Instruction is inlined from callee
+    kMIRInvokeMethodJIT,                // Callee is JIT'ed as a whole method
+} MIROptimizationFlagPositons;
+
+#define MIR_IGNORE_NULL_CHECK           (1 << kMIRIgnoreNullCheck)
+#define MIR_NULL_CHECK_ONLY             (1 << kMIRNullCheckOnly)
+#define MIR_IGNORE_RANGE_CHECK          (1 << kMIRIgnoreRangeCheck)
+#define MIR_RANGE_CHECK_ONLY            (1 << kMIRRangeCheckOnly)
+#define MIR_INLINED                     (1 << kMIRInlined)
+#define MIR_INLINED_PRED                (1 << kMIRInlinedPred)
+#define MIR_CALLEE                      (1 << kMIRCallee)
+#define MIR_INVOKE_METHOD_JIT           (1 << kMIRInvokeMethodJIT)
+
+typedef struct CallsiteInfo {
+    const char *classDescriptor;
+    Object *classLoader;
+    const Method *method;
+    LIR *misPredBranchOver;
+} CallsiteInfo;
+
+typedef struct MIR {
+    DecodedInstruction dalvikInsn;
+    unsigned int width;
+    unsigned int offset;
+    struct MIR *prev;
+    struct MIR *next;
+    struct SSARepresentation *ssaRep;
+    int OptimizationFlags;
+    int seqNum;
+    union {
+        // Used by the inlined insn from the callee to find the mother method
+        const Method *calleeMethod;
+        // Used by the inlined invoke to find the class and method pointers
+        CallsiteInfo *callsiteInfo;
+    } meta;
+} MIR;
+
+struct BasicBlockDataFlow;
+
+/* For successorBlockList */
+typedef enum BlockListType {
+    kNotUsed = 0,
+    kCatch,
+    kPackedSwitch,
+    kSparseSwitch,
+} BlockListType;
+
+typedef struct BasicBlock {
+    int id;
+    bool visited;
+    bool hidden;
+    unsigned int startOffset;
+    const Method *containingMethod;     // For blocks from the callee
+    BBType blockType;
+    bool needFallThroughBranch;         // For blocks ended due to length limit
+    bool isFallThroughFromInvoke;       // True means the block needs alignment
+    MIR *firstMIRInsn;
+    MIR *lastMIRInsn;
+    struct BasicBlock *fallThrough;
+    struct BasicBlock *taken;
+    struct BasicBlock *iDom;            // Immediate dominator
+    struct BasicBlockDataFlow *dataFlowInfo;
+    BitVector *predecessors;
+    BitVector *dominators;
+    BitVector *iDominated;              // Set nodes being immediately dominated
+    BitVector *domFrontier;             // Dominance frontier
+    struct {                            // For one-to-many successors like
+        BlockListType blockListType;    // switch and exception handling
+        GrowableList blocks;
+    } successorBlockList;
+} BasicBlock;
+
+/*
+ * The "blocks" field in "successorBlockList" points to an array of
+ * elements with the type "SuccessorBlockInfo".
+ * For catch blocks, key is type index for the exception.
+ * For swtich blocks, key is the case value.
+ */
+typedef struct SuccessorBlockInfo {
+    BasicBlock *block;
+    int key;
+} SuccessorBlockInfo;
+
+struct LoopAnalysis;
+struct RegisterPool;
+
+typedef enum AssemblerStatus {
+    kSuccess,
+    kRetryAll,
+    kRetryHalve
+} AssemblerStatus;
+
+typedef struct CompilationUnit {
+    int numInsts;
+    int numBlocks;
+    GrowableList blockList;
+    const Method *method;
+#ifdef ARCH_IA32
+    int exceptionBlockId;               // the block corresponding to exception handling
+#endif
+    const JitTraceDescription *traceDesc;
+    LIR *firstLIRInsn;
+    LIR *lastLIRInsn;
+    LIR *literalList;                   // Constants
+    LIR *classPointerList;              // Relocatable
+    int numClassPointers;
+    LIR *chainCellOffsetLIR;
+    GrowableList pcReconstructionList;
+    int headerSize;                     // bytes before the first code ptr
+    int dataOffset;                     // starting offset of literal pool
+    int totalSize;                      // header + code size
+    AssemblerStatus assemblerStatus;    // Success or fix and retry
+    int assemblerRetries;               // How many times tried to fix assembly
+    unsigned char *codeBuffer;
+    void *baseAddr;
+    bool printMe;
+    bool allSingleStep;
+    bool hasClassLiterals;              // Contains class ptrs used as literals
+    bool hasLoop;                       // Contains a loop
+    bool hasInvoke;                     // Contains an invoke instruction
+    bool heapMemOp;                     // Mark mem ops for self verification
+    bool usesLinkRegister;              // For self-verification only
+    int profileCodeSize;                // Size of the profile prefix in bytes
+    int numChainingCells[kChainingCellGap];
+    LIR *firstChainingLIR[kChainingCellGap];
+    LIR *chainingCellBottom;
+    struct RegisterPool *regPool;
+    int optRound;                       // round number to tell an LIR's age
+    jmp_buf *bailPtr;
+    JitInstructionSetType instructionSet;
+    /* Number of total regs used in the whole cUnit after SSA transformation */
+    int numSSARegs;
+    /* Map SSA reg i to the Dalvik[15..0]/Sub[31..16] pair. */
+    GrowableList *ssaToDalvikMap;
+
+    /* The following are new data structures to support SSA representations */
+    /* Map original Dalvik reg i to the SSA[15..0]/Sub[31..16] pair */
+    int *dalvikToSSAMap;                // length == method->registersSize
+    BitVector *isConstantV;             // length == numSSAReg
+    int *constantValues;                // length == numSSAReg
+
+    /* Data structure for loop analysis and optimizations */
+    struct LoopAnalysis *loopAnalysis;
+
+    /* Map SSA names to location */
+    RegLocation *regLocation;
+    int sequenceNumber;
+
+    /*
+     * Set to the Dalvik PC of the switch instruction if it has more than
+     * MAX_CHAINED_SWITCH_CASES cases.
+     */
+    const u2 *switchOverflowPad;
+
+    JitMode jitMode;
+    int numReachableBlocks;
+    int numDalvikRegisters;             // method->registersSize + inlined
+    BasicBlock *entryBlock;
+    BasicBlock *exitBlock;
+    BasicBlock *puntBlock;              // punting to interp for exceptions
+    BasicBlock *backChainBlock;         // for loop-trace
+    BasicBlock *curBlock;
+    BasicBlock *nextCodegenBlock;       // for extended trace codegen
+    GrowableList dfsOrder;
+    GrowableList domPostOrderTraversal;
+    BitVector *tryBlockAddr;
+    BitVector **defBlockMatrix;         // numDalvikRegister x numBlocks
+    BitVector *tempBlockV;
+    BitVector *tempDalvikRegisterV;
+    BitVector *tempSSARegisterV;        // numSSARegs
+    bool printSSANames;
+    void *blockLabelList;
+    bool quitLoopMode;                  // cold path/complex bytecode
+} CompilationUnit;
+
+#if defined(WITH_SELF_VERIFICATION)
+#define HEAP_ACCESS_SHADOW(_state) cUnit->heapMemOp = _state
+#else
+#define HEAP_ACCESS_SHADOW(_state)
+#endif
+
+BasicBlock *dvmCompilerNewBB(BBType blockType, int blockId);
+
+void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir);
+
+void dvmCompilerPrependMIR(BasicBlock *bb, MIR *mir);
+
+void dvmCompilerInsertMIRAfter(BasicBlock *bb, MIR *currentMIR, MIR *newMIR);
+
+void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir);
+
+void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR);
+
+void dvmCompilerInsertLIRAfter(LIR *currentLIR, LIR *newLIR);
+
+void dvmCompilerAbort(CompilationUnit *cUnit);
+
+/* Debug Utilities */
+void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit);
+
+#endif  // DALVIK_VM_COMPILER_IR_H_
diff --git a/vm/compiler/CompilerInternals.h b/vm/compiler/CompilerInternals.h
new file mode 100644
index 0000000..d635286
--- /dev/null
+++ b/vm/compiler/CompilerInternals.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_INTERNAL_H_
+#define DALVIK_VM_COMPILER_INTERNAL_H_
+
+#include "Dalvik.h"
+#include "CompilerUtility.h"
+#include "codegen/CompilerCodegen.h"
+#include "interp/Jit.h"
+
+#endif  // DALVIK_VM_COMPILER_INTERNAL_H_
diff --git a/vm/compiler/CompilerUtility.h b/vm/compiler/CompilerUtility.h
new file mode 100644
index 0000000..faca03d
--- /dev/null
+++ b/vm/compiler/CompilerUtility.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_UTILITY_H_
+#define DALVIK_VM_COMPILER_UTILITY_H_
+
+#include "Dalvik.h"
+
+/* Each arena page has some overhead, so take a few bytes off 8k */
+#define ARENA_DEFAULT_SIZE 8100
+
+/* Allocate the initial memory block for arena-based allocation */
+bool dvmCompilerHeapInit(void);
+
+typedef struct ArenaMemBlock {
+    size_t blockSize;
+    size_t bytesAllocated;
+    struct ArenaMemBlock *next;
+    char ptr[0];
+} ArenaMemBlock;
+
+void *dvmCompilerNew(size_t size, bool zero);
+
+void dvmCompilerArenaReset(void);
+
+typedef struct GrowableList {
+    size_t numAllocated;
+    size_t numUsed;
+    intptr_t *elemList;
+} GrowableList;
+
+typedef struct GrowableListIterator {
+    GrowableList *list;
+    size_t idx;
+    size_t size;
+} GrowableListIterator;
+
+#define GET_ELEM_N(LIST, TYPE, N) (((TYPE*) LIST->elemList)[N])
+
+#define BLOCK_NAME_LEN 80
+
+/* Forward declarations */
+struct LIR;
+struct BasicBlock;
+
+void dvmInitGrowableList(GrowableList *gList, size_t initLength);
+void dvmInsertGrowableList(GrowableList *gList, intptr_t elem);
+void dvmGrowableListIteratorInit(GrowableList *gList,
+                                 GrowableListIterator *iterator);
+intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator);
+intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx);
+
+BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable);
+bool dvmCompilerSetBit(BitVector* pBits, unsigned int num);
+bool dvmCompilerClearBit(BitVector* pBits, unsigned int num);
+void dvmCompilerMarkAllBits(BitVector *pBits, bool set);
+void dvmDebugBitVector(char *msg, const BitVector *bv, int length);
+void dvmDumpLIRInsn(struct LIR *lir, unsigned char *baseAddr);
+void dvmDumpResourceMask(struct LIR *lir, u8 mask, const char *prefix);
+void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg,
+                           const BitVector *bv, int length);
+void dvmGetBlockName(struct BasicBlock *bb, char *name);
+void dvmCompilerCacheFlush(long start, long end, long flags);
+void dvmCompilerCacheClear(char *start, size_t size);
+
+
+#endif  // DALVIK_COMPILER_UTILITY_H_
diff --git a/vm/compiler/Dataflow.cpp b/vm/compiler/Dataflow.cpp
new file mode 100644
index 0000000..7bed839
--- /dev/null
+++ b/vm/compiler/Dataflow.cpp
@@ -0,0 +1,1746 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "Loop.h"
+#include "libdex/DexOpcodes.h"
+
+/*
+ * Main table containing data flow attributes for each bytecode. The
+ * first kNumPackedOpcodes entries are for Dalvik bytecode
+ * instructions, where extended opcode at the MIR level are appended
+ * afterwards.
+ *
+ * TODO - many optimization flags are incomplete - they will only limit the
+ * scope of optimizations but will not cause mis-optimizations.
+ */
+int dvmCompilerDataFlowAttributes[kMirOpLast] = {
+    // 00 OP_NOP
+    DF_NOP,
+
+    // 01 OP_MOVE vA, vB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 02 OP_MOVE_FROM16 vAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 03 OP_MOVE_16 vAAAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 04 OP_MOVE_WIDE vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+    // 05 OP_MOVE_WIDE_FROM16 vAA, vBBBB
+    DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+    // 06 OP_MOVE_WIDE_16 vAAAA, vBBBB
+    DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+    // 07 OP_MOVE_OBJECT vA, vB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 08 OP_MOVE_OBJECT_FROM16 vAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 09 OP_MOVE_OBJECT_16 vAAAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 0A OP_MOVE_RESULT vAA
+    DF_DA,
+
+    // 0B OP_MOVE_RESULT_WIDE vAA
+    DF_DA_WIDE,
+
+    // 0C OP_MOVE_RESULT_OBJECT vAA
+    DF_DA,
+
+    // 0D OP_MOVE_EXCEPTION vAA
+    DF_DA,
+
+    // 0E OP_RETURN_VOID
+    DF_NOP,
+
+    // 0F OP_RETURN vAA
+    DF_UA,
+
+    // 10 OP_RETURN_WIDE vAA
+    DF_UA_WIDE,
+
+    // 11 OP_RETURN_OBJECT vAA
+    DF_UA,
+
+    // 12 OP_CONST_4 vA, #+B
+    DF_DA | DF_SETS_CONST,
+
+    // 13 OP_CONST_16 vAA, #+BBBB
+    DF_DA | DF_SETS_CONST,
+
+    // 14 OP_CONST vAA, #+BBBBBBBB
+    DF_DA | DF_SETS_CONST,
+
+    // 15 OP_CONST_HIGH16 VAA, #+BBBB0000
+    DF_DA | DF_SETS_CONST,
+
+    // 16 OP_CONST_WIDE_16 vAA, #+BBBB
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 17 OP_CONST_WIDE_32 vAA, #+BBBBBBBB
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 18 OP_CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 19 OP_CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 1A OP_CONST_STRING vAA, string@BBBB
+    DF_DA,
+
+    // 1B OP_CONST_STRING_JUMBO vAA, string@BBBBBBBB
+    DF_DA,
+
+    // 1C OP_CONST_CLASS vAA, type@BBBB
+    DF_DA,
+
+    // 1D OP_MONITOR_ENTER vAA
+    DF_UA,
+
+    // 1E OP_MONITOR_EXIT vAA
+    DF_UA,
+
+    // 1F OP_CHECK_CAST vAA, type@BBBB
+    DF_UA,
+
+    // 20 OP_INSTANCE_OF vA, vB, type@CCCC
+    DF_DA | DF_UB,
+
+    // 21 OP_ARRAY_LENGTH vA, vB
+    DF_DA | DF_UB,
+
+    // 22 OP_NEW_INSTANCE vAA, type@BBBB
+    DF_DA,
+
+    // 23 OP_NEW_ARRAY vA, vB, type@CCCC
+    DF_DA | DF_UB,
+
+    // 24 OP_FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 25 OP_FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
+    DF_FORMAT_3RC,
+
+    // 26 OP_FILL_ARRAY_DATA vAA, +BBBBBBBB
+    DF_UA,
+
+    // 27 OP_THROW vAA
+    DF_UA,
+
+    // 28 OP_GOTO
+    DF_NOP,
+
+    // 29 OP_GOTO_16
+    DF_NOP,
+
+    // 2A OP_GOTO_32
+    DF_NOP,
+
+    // 2B OP_PACKED_SWITCH vAA, +BBBBBBBB
+    DF_UA,
+
+    // 2C OP_SPARSE_SWITCH vAA, +BBBBBBBB
+    DF_UA,
+
+    // 2D OP_CMPL_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C,
+
+    // 2E OP_CMPG_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C,
+
+    // 2F OP_CMPL_DOUBLE vAA, vBB, vCC
+    DF_DA | DF_UB_WIDE | DF_UC_WIDE | DF_FP_B | DF_FP_C,
+
+    // 30 OP_CMPG_DOUBLE vAA, vBB, vCC
+    DF_DA | DF_UB_WIDE | DF_UC_WIDE | DF_FP_B | DF_FP_C,
+
+    // 31 OP_CMP_LONG vAA, vBB, vCC
+    DF_DA | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 32 OP_IF_EQ vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 33 OP_IF_NE vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 34 OP_IF_LT vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 35 OP_IF_GE vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 36 OP_IF_GT vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 37 OP_IF_LE vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+
+    // 38 OP_IF_EQZ vAA, +BBBB
+    DF_UA,
+
+    // 39 OP_IF_NEZ vAA, +BBBB
+    DF_UA,
+
+    // 3A OP_IF_LTZ vAA, +BBBB
+    DF_UA,
+
+    // 3B OP_IF_GEZ vAA, +BBBB
+    DF_UA,
+
+    // 3C OP_IF_GTZ vAA, +BBBB
+    DF_UA,
+
+    // 3D OP_IF_LEZ vAA, +BBBB
+    DF_UA,
+
+    // 3E OP_UNUSED_3E
+    DF_NOP,
+
+    // 3F OP_UNUSED_3F
+    DF_NOP,
+
+    // 40 OP_UNUSED_40
+    DF_NOP,
+
+    // 41 OP_UNUSED_41
+    DF_NOP,
+
+    // 42 OP_UNUSED_42
+    DF_NOP,
+
+    // 43 OP_UNUSED_43
+    DF_NOP,
+
+    // 44 OP_AGET vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 45 OP_AGET_WIDE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 46 OP_AGET_OBJECT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 47 OP_AGET_BOOLEAN vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 48 OP_AGET_BYTE vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 49 OP_AGET_CHAR vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 4A OP_AGET_SHORT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 4B OP_APUT vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 4C OP_APUT_WIDE vAA, vBB, vCC
+    DF_UA_WIDE | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_2 | DF_IS_SETTER,
+
+    // 4D OP_APUT_OBJECT vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 4E OP_APUT_BOOLEAN vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 4F OP_APUT_BYTE vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 50 OP_APUT_CHAR vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 51 OP_APUT_SHORT vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 52 OP_IGET vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 53 OP_IGET_WIDE vA, vB, field@CCCC
+    DF_DA_WIDE | DF_UB | DF_IS_GETTER,
+
+    // 54 OP_IGET_OBJECT vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 55 OP_IGET_BOOLEAN vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 56 OP_IGET_BYTE vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 57 OP_IGET_CHAR vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 58 OP_IGET_SHORT vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 59 OP_IPUT vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5A OP_IPUT_WIDE vA, vB, field@CCCC
+    DF_UA_WIDE | DF_UB | DF_IS_SETTER,
+
+    // 5B OP_IPUT_OBJECT vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5C OP_IPUT_BOOLEAN vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5D OP_IPUT_BYTE vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5E OP_IPUT_CHAR vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5F OP_IPUT_SHORT vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 60 OP_SGET vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 61 OP_SGET_WIDE vAA, field@BBBB
+    DF_DA_WIDE | DF_IS_GETTER,
+
+    // 62 OP_SGET_OBJECT vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 63 OP_SGET_BOOLEAN vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 64 OP_SGET_BYTE vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 65 OP_SGET_CHAR vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 66 OP_SGET_SHORT vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 67 OP_SPUT vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 68 OP_SPUT_WIDE vAA, field@BBBB
+    DF_UA_WIDE | DF_IS_SETTER,
+
+    // 69 OP_SPUT_OBJECT vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6A OP_SPUT_BOOLEAN vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6B OP_SPUT_BYTE vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6C OP_SPUT_CHAR vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6D OP_SPUT_SHORT vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6E OP_INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 6F OP_INVOKE_SUPER {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 70 OP_INVOKE_DIRECT {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 71 OP_INVOKE_STATIC {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 72 OP_INVOKE_INTERFACE {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 73 OP_UNUSED_73
+    DF_NOP,
+
+    // 74 OP_INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 75 OP_INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 76 OP_INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 77 OP_INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 78 OP_INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 79 OP_UNUSED_79
+    DF_NOP,
+
+    // 7A OP_UNUSED_7A
+    DF_NOP,
+
+    // 7B OP_NEG_INT vA, vB
+    DF_DA | DF_UB,
+
+    // 7C OP_NOT_INT vA, vB
+    DF_DA | DF_UB,
+
+    // 7D OP_NEG_LONG vA, vB
+    DF_DA_WIDE | DF_UB_WIDE,
+
+    // 7E OP_NOT_LONG vA, vB
+    DF_DA_WIDE | DF_UB_WIDE,
+
+    // 7F OP_NEG_FLOAT vA, vB
+    DF_DA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // 80 OP_NEG_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // 81 OP_INT_TO_LONG vA, vB
+    DF_DA_WIDE | DF_UB,
+
+    // 82 OP_INT_TO_FLOAT vA, vB
+    DF_DA | DF_UB | DF_FP_A,
+
+    // 83 OP_INT_TO_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB | DF_FP_A,
+
+    // 84 OP_LONG_TO_INT vA, vB
+    DF_DA | DF_UB_WIDE,
+
+    // 85 OP_LONG_TO_FLOAT vA, vB
+    DF_DA | DF_UB_WIDE | DF_FP_A,
+
+    // 86 OP_LONG_TO_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_FP_A,
+
+    // 87 OP_FLOAT_TO_INT vA, vB
+    DF_DA | DF_UB | DF_FP_B,
+
+    // 88 OP_FLOAT_TO_LONG vA, vB
+    DF_DA_WIDE | DF_UB | DF_FP_B,
+
+    // 89 OP_FLOAT_TO_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB | DF_FP_A | DF_FP_B,
+
+    // 8A OP_DOUBLE_TO_INT vA, vB
+    DF_DA | DF_UB_WIDE | DF_FP_B,
+
+    // 8B OP_DOUBLE_TO_LONG vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_FP_B,
+
+    // 8C OP_DOUBLE_TO_FLOAT vA, vB
+    DF_DA | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // 8D OP_INT_TO_BYTE vA, vB
+    DF_DA | DF_UB,
+
+    // 8E OP_INT_TO_CHAR vA, vB
+    DF_DA | DF_UB,
+
+    // 8F OP_INT_TO_SHORT vA, vB
+    DF_DA | DF_UB,
+
+    // 90 OP_ADD_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_IS_LINEAR,
+
+    // 91 OP_SUB_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_IS_LINEAR,
+
+    // 92 OP_MUL_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 93 OP_DIV_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 94 OP_REM_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 95 OP_AND_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 96 OP_OR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 97 OP_XOR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 98 OP_SHL_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 99 OP_SHR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 9A OP_USHR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 9B OP_ADD_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9C OP_SUB_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9D OP_MUL_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9E OP_DIV_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9F OP_REM_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A0 OP_AND_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A1 OP_OR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A2 OP_XOR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A3 OP_SHL_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+    // A4 OP_SHR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+    // A5 OP_USHR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+    // A6 OP_ADD_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // A7 OP_SUB_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // A8 OP_MUL_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // A9 OP_DIV_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AA OP_REM_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AB OP_ADD_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AC OP_SUB_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AD OP_MUL_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AE OP_DIV_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AF OP_REM_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // B0 OP_ADD_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B1 OP_SUB_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B2 OP_MUL_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B3 OP_DIV_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B4 OP_REM_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B5 OP_AND_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B6 OP_OR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B7 OP_XOR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B8 OP_SHL_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B9 OP_SHR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // BA OP_USHR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // BB OP_ADD_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BC OP_SUB_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BD OP_MUL_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BE OP_DIV_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BF OP_REM_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C0 OP_AND_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C1 OP_OR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C2 OP_XOR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C3 OP_SHL_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+    // C4 OP_SHR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+    // C5 OP_USHR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+    // C6 OP_ADD_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // C7 OP_SUB_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // C8 OP_MUL_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // C9 OP_DIV_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // CA OP_REM_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // CB OP_ADD_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CC OP_SUB_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CD OP_MUL_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CE OP_DIV_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CF OP_REM_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // D0 OP_ADD_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D1 OP_RSUB_INT vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D2 OP_MUL_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D3 OP_DIV_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D4 OP_REM_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D5 OP_AND_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D6 OP_OR_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D7 OP_XOR_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D8 OP_ADD_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB | DF_IS_LINEAR,
+
+    // D9 OP_RSUB_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DA OP_MUL_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DB OP_DIV_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DC OP_REM_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DD OP_AND_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DE OP_OR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DF OP_XOR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E0 OP_SHL_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E1 OP_SHR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E2 OP_USHR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E3 OP_IGET_VOLATILE
+    DF_DA | DF_UB,
+
+    // E4 OP_IPUT_VOLATILE
+    DF_UA | DF_UB,
+
+    // E5 OP_SGET_VOLATILE
+    DF_DA,
+
+    // E6 OP_SPUT_VOLATILE
+    DF_UA,
+
+    // E7 OP_IGET_OBJECT_VOLATILE
+    DF_DA | DF_UB,
+
+    // E8 OP_IGET_WIDE_VOLATILE
+    DF_DA_WIDE | DF_UB,
+
+    // E9 OP_IPUT_WIDE_VOLATILE
+    DF_UA_WIDE | DF_UB,
+
+    // EA OP_SGET_WIDE_VOLATILE
+    DF_DA_WIDE,
+
+    // EB OP_SPUT_WIDE_VOLATILE
+    DF_UA_WIDE,
+
+    // EC OP_BREAKPOINT
+    DF_NOP,
+
+    // ED OP_THROW_VERIFICATION_ERROR
+    DF_NOP,
+
+    // EE OP_EXECUTE_INLINE
+    DF_FORMAT_35C,
+
+    // EF OP_EXECUTE_INLINE_RANGE
+    DF_FORMAT_3RC,
+
+    // F0 OP_INVOKE_OBJECT_INIT_RANGE
+    DF_NOP,
+
+    // F1 OP_RETURN_VOID_BARRIER
+    DF_NOP,
+
+    // F2 OP_IGET_QUICK
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // F3 OP_IGET_WIDE_QUICK
+    DF_DA_WIDE | DF_UB | DF_IS_GETTER,
+
+    // F4 OP_IGET_OBJECT_QUICK
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // F5 OP_IPUT_QUICK
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // F6 OP_IPUT_WIDE_QUICK
+    DF_UA_WIDE | DF_UB | DF_IS_SETTER,
+
+    // F7 OP_IPUT_OBJECT_QUICK
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // F8 OP_INVOKE_VIRTUAL_QUICK
+    DF_FORMAT_35C,
+
+    // F9 OP_INVOKE_VIRTUAL_QUICK_RANGE
+    DF_FORMAT_3RC,
+
+    // FA OP_INVOKE_SUPER_QUICK
+    DF_FORMAT_35C,
+
+    // FB OP_INVOKE_SUPER_QUICK_RANGE
+    DF_FORMAT_3RC,
+
+    // FC OP_IPUT_OBJECT_VOLATILE
+    DF_UA | DF_UB,
+
+    // FD OP_SGET_OBJECT_VOLATILE
+    DF_DA,
+
+    // FE OP_SPUT_OBJECT_VOLATILE
+    DF_UA,
+
+    // FF OP_UNUSED_FF
+    DF_NOP,
+
+    // Beginning of extended MIR opcodes
+    // 100 OP_MIR_PHI
+    DF_PHI | DF_DA,
+    /*
+     * For extended MIR inserted at the MIR2LIR stage, it is okay to have
+     * undefined values here.
+     */
+};
+
+/* Return the Dalvik register/subscript pair of a given SSA register */
+int dvmConvertSSARegToDalvik(const CompilationUnit *cUnit, int ssaReg)
+{
+      return GET_ELEM_N(cUnit->ssaToDalvikMap, int, ssaReg);
+}
+
+/*
+ * Utility function to convert encoded SSA register value into Dalvik register
+ * and subscript pair. Each SSA register can be used to index the
+ * ssaToDalvikMap list to get the subscript[31..16]/dalvik_reg[15..0] mapping.
+ */
+char *dvmCompilerGetDalvikDisassembly(const DecodedInstruction *insn,
+                                      const char *note)
+{
+    char buffer[256];
+    Opcode opcode = insn->opcode;
+    int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+    int flags;
+    char *ret;
+
+    buffer[0] = 0;
+    if ((int)opcode >= (int)kMirOpFirst) {
+        if ((int)opcode == (int)kMirOpPhi) {
+            strcpy(buffer, "PHI");
+        }
+        else {
+            sprintf(buffer, "Opcode %#x", opcode);
+        }
+        flags = 0;
+    } else {
+        strcpy(buffer, dexGetOpcodeName(opcode));
+        flags = dexGetFlagsFromOpcode(insn->opcode);
+    }
+
+    if (note)
+        strcat(buffer, note);
+
+    /* For branches, decode the instructions to print out the branch targets */
+    if (flags & kInstrCanBranch) {
+        InstructionFormat dalvikFormat = dexGetFormatFromOpcode(insn->opcode);
+        int offset = 0;
+        switch (dalvikFormat) {
+            case kFmt21t:
+                snprintf(buffer + strlen(buffer), 256, " v%d,", insn->vA);
+                offset = (int) insn->vB;
+                break;
+            case kFmt22t:
+                snprintf(buffer + strlen(buffer), 256, " v%d, v%d,",
+                         insn->vA, insn->vB);
+                offset = (int) insn->vC;
+                break;
+            case kFmt10t:
+            case kFmt20t:
+            case kFmt30t:
+                offset = (int) insn->vA;
+                break;
+            default:
+                ALOGE("Unexpected branch format %d / opcode %#x", dalvikFormat,
+                     opcode);
+                dvmAbort();
+                break;
+        }
+        snprintf(buffer + strlen(buffer), 256, " (%c%x)",
+                 offset > 0 ? '+' : '-',
+                 offset > 0 ? offset : -offset);
+    } else if (dfAttributes & DF_FORMAT_35C) {
+        unsigned int i;
+        for (i = 0; i < insn->vA; i++) {
+            if (i != 0) strcat(buffer, ",");
+            snprintf(buffer + strlen(buffer), 256, " v%d", insn->arg[i]);
+        }
+    }
+    else if (dfAttributes & DF_FORMAT_3RC) {
+        snprintf(buffer + strlen(buffer), 256,
+                 " v%d..v%d", insn->vC, insn->vC + insn->vA - 1);
+    }
+    else {
+        if (dfAttributes & DF_A_IS_REG) {
+            snprintf(buffer + strlen(buffer), 256, " v%d", insn->vA);
+        }
+        if (dfAttributes & DF_B_IS_REG) {
+            snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vB);
+        }
+        else if ((int)opcode < (int)kMirOpFirst) {
+            snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vB);
+        }
+        if (dfAttributes & DF_C_IS_REG) {
+            snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vC);
+        }
+        else if ((int)opcode < (int)kMirOpFirst) {
+            snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vC);
+        }
+    }
+    int length = strlen(buffer) + 1;
+    ret = (char *)dvmCompilerNew(length, false);
+    memcpy(ret, buffer, length);
+    return ret;
+}
+
+char *getSSAName(const CompilationUnit *cUnit, int ssaReg, char *name)
+{
+    int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaReg);
+
+    sprintf(name, "v%d_%d",
+            DECODE_REG(ssa2DalvikValue), DECODE_SUB(ssa2DalvikValue));
+    return name;
+}
+
+/*
+ * Dalvik instruction disassembler with optional SSA printing.
+ */
+char *dvmCompilerFullDisassembler(const CompilationUnit *cUnit,
+                                  const MIR *mir)
+{
+    char buffer[256];
+    char operand0[256], operand1[256];
+    const DecodedInstruction *insn = &mir->dalvikInsn;
+    int opcode = insn->opcode;
+    int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+    char *ret;
+    int length;
+    OpcodeFlags flags;
+
+    buffer[0] = 0;
+    if (opcode >= kMirOpFirst) {
+        if (opcode == kMirOpPhi) {
+            snprintf(buffer, 256, "PHI %s = (%s",
+                     getSSAName(cUnit, mir->ssaRep->defs[0], operand0),
+                     getSSAName(cUnit, mir->ssaRep->uses[0], operand1));
+            int i;
+            for (i = 1; i < mir->ssaRep->numUses; i++) {
+                snprintf(buffer + strlen(buffer), 256, ", %s",
+                         getSSAName(cUnit, mir->ssaRep->uses[i], operand0));
+            }
+            snprintf(buffer + strlen(buffer), 256, ")");
+        }
+        else {
+            sprintf(buffer, "Opcode %#x", opcode);
+        }
+        goto done;
+    } else {
+        strcpy(buffer, dexGetOpcodeName((Opcode)opcode));
+    }
+
+    flags = dexGetFlagsFromOpcode((Opcode)opcode);
+    /* For branches, decode the instructions to print out the branch targets */
+    if (flags & kInstrCanBranch) {
+        InstructionFormat dalvikFormat = dexGetFormatFromOpcode(insn->opcode);
+        int delta = 0;
+        switch (dalvikFormat) {
+            case kFmt21t:
+                snprintf(buffer + strlen(buffer), 256, " %s, ",
+                         getSSAName(cUnit, mir->ssaRep->uses[0], operand0));
+                delta = (int) insn->vB;
+                break;
+            case kFmt22t:
+                snprintf(buffer + strlen(buffer), 256, " %s, %s, ",
+                         getSSAName(cUnit, mir->ssaRep->uses[0], operand0),
+                         getSSAName(cUnit, mir->ssaRep->uses[1], operand1));
+                delta = (int) insn->vC;
+                break;
+            case kFmt10t:
+            case kFmt20t:
+            case kFmt30t:
+                delta = (int) insn->vA;
+                break;
+            default:
+                ALOGE("Unexpected branch format: %d", dalvikFormat);
+                dvmAbort();
+                break;
+        }
+        snprintf(buffer + strlen(buffer), 256, " %04x",
+                 mir->offset + delta);
+    } else if (dfAttributes & (DF_FORMAT_35C | DF_FORMAT_3RC)) {
+        unsigned int i;
+        for (i = 0; i < insn->vA; i++) {
+            if (i != 0) strcat(buffer, ",");
+            snprintf(buffer + strlen(buffer), 256, " %s",
+                     getSSAName(cUnit, mir->ssaRep->uses[i], operand0));
+        }
+    } else {
+        int udIdx;
+        if (mir->ssaRep->numDefs) {
+
+            for (udIdx = 0; udIdx < mir->ssaRep->numDefs; udIdx++) {
+                snprintf(buffer + strlen(buffer), 256, " %s",
+                         getSSAName(cUnit, mir->ssaRep->defs[udIdx], operand0));
+            }
+            strcat(buffer, ",");
+        }
+        if (mir->ssaRep->numUses) {
+            /* No leading ',' for the first use */
+            snprintf(buffer + strlen(buffer), 256, " %s",
+                     getSSAName(cUnit, mir->ssaRep->uses[0], operand0));
+            for (udIdx = 1; udIdx < mir->ssaRep->numUses; udIdx++) {
+                snprintf(buffer + strlen(buffer), 256, ", %s",
+                         getSSAName(cUnit, mir->ssaRep->uses[udIdx], operand0));
+            }
+        }
+        if (opcode < kMirOpFirst) {
+            InstructionFormat dalvikFormat =
+                dexGetFormatFromOpcode((Opcode)opcode);
+            switch (dalvikFormat) {
+                case kFmt11n:        // op vA, #+B
+                case kFmt21s:        // op vAA, #+BBBB
+                case kFmt21h:        // op vAA, #+BBBB00000[00000000]
+                case kFmt31i:        // op vAA, #+BBBBBBBB
+                case kFmt51l:        // op vAA, #+BBBBBBBBBBBBBBBB
+                    snprintf(buffer + strlen(buffer), 256, " #%#x", insn->vB);
+                    break;
+                case kFmt21c:        // op vAA, thing@BBBB
+                case kFmt31c:        // op vAA, thing@BBBBBBBB
+                    snprintf(buffer + strlen(buffer), 256, " @%#x", insn->vB);
+                    break;
+                case kFmt22b:        // op vAA, vBB, #+CC
+                case kFmt22s:        // op vA, vB, #+CCCC
+                    snprintf(buffer + strlen(buffer), 256, " #%#x", insn->vC);
+                    break;
+                case kFmt22c:        // op vA, vB, thing@CCCC
+                case kFmt22cs:       // [opt] op vA, vB, field offset CCCC
+                    snprintf(buffer + strlen(buffer), 256, " @%#x", insn->vC);
+                    break;
+                    /* No need for special printing */
+                default:
+                    break;
+            }
+        }
+    }
+
+done:
+    length = strlen(buffer) + 1;
+    ret = (char *) dvmCompilerNew(length, false);
+    memcpy(ret, buffer, length);
+    return ret;
+}
+
+/*
+ * Utility function to convert encoded SSA register value into Dalvik register
+ * and subscript pair. Each SSA register can be used to index the
+ * ssaToDalvikMap list to get the subscript[31..16]/dalvik_reg[15..0] mapping.
+ */
+char *dvmCompilerGetSSAString(CompilationUnit *cUnit, SSARepresentation *ssaRep)
+{
+    char buffer[256];
+    char *ret;
+    int i;
+
+    buffer[0] = 0;
+    for (i = 0; i < ssaRep->numDefs; i++) {
+        int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaRep->defs[i]);
+
+        sprintf(buffer + strlen(buffer), "s%d(v%d_%d) ",
+                ssaRep->defs[i], DECODE_REG(ssa2DalvikValue),
+                DECODE_SUB(ssa2DalvikValue));
+    }
+
+    if (ssaRep->numDefs) {
+        strcat(buffer, "<- ");
+    }
+
+    for (i = 0; i < ssaRep->numUses; i++) {
+        int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaRep->uses[i]);
+        int len = strlen(buffer);
+
+        if (snprintf(buffer + len, 250 - len, "s%d(v%d_%d) ",
+                     ssaRep->uses[i], DECODE_REG(ssa2DalvikValue),
+                     DECODE_SUB(ssa2DalvikValue)) >= (250 - len)) {
+            strcat(buffer, "...");
+            break;
+        }
+    }
+
+    int length = strlen(buffer) + 1;
+    ret = (char *)dvmCompilerNew(length, false);
+    memcpy(ret, buffer, length);
+    return ret;
+}
+
+/* Any register that is used before being defined is considered live-in */
+static inline void handleLiveInUse(BitVector *useV, BitVector *defV,
+                                   BitVector *liveInV, int dalvikRegId)
+{
+    dvmCompilerSetBit(useV, dalvikRegId);
+    if (!dvmIsBitSet(defV, dalvikRegId)) {
+        dvmCompilerSetBit(liveInV, dalvikRegId);
+    }
+}
+
+/* Mark a reg as being defined */
+static inline void handleDef(BitVector *defV, int dalvikRegId)
+{
+    dvmCompilerSetBit(defV, dalvikRegId);
+}
+
+/*
+ * Find out live-in variables for natural loops. Variables that are live-in in
+ * the main loop body are considered to be defined in the entry block.
+ */
+bool dvmCompilerFindLocalLiveIn(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    BitVector *useV, *defV, *liveInV;
+
+    if (bb->dataFlowInfo == NULL) return false;
+
+    useV = bb->dataFlowInfo->useV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+    defV = bb->dataFlowInfo->defV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+    liveInV = bb->dataFlowInfo->liveInV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+        if (dfAttributes & DF_HAS_USES) {
+            if (dfAttributes & DF_UA) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vA);
+            } else if (dfAttributes & DF_UA_WIDE) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vA);
+                handleLiveInUse(useV, defV, liveInV, dInsn->vA+1);
+            }
+            if (dfAttributes & DF_UB) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vB);
+            } else if (dfAttributes & DF_UB_WIDE) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vB);
+                handleLiveInUse(useV, defV, liveInV, dInsn->vB+1);
+            }
+            if (dfAttributes & DF_UC) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vC);
+            } else if (dfAttributes & DF_UC_WIDE) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vC);
+                handleLiveInUse(useV, defV, liveInV, dInsn->vC+1);
+            }
+        }
+        if (dfAttributes & DF_HAS_DEFS) {
+            handleDef(defV, dInsn->vA);
+            if (dfAttributes & DF_DA_WIDE) {
+                handleDef(defV, dInsn->vA+1);
+            }
+        }
+    }
+    return true;
+}
+
+/* Find out the latest SSA register for a given Dalvik register */
+static void handleSSAUse(CompilationUnit *cUnit, int *uses, int dalvikReg,
+                         int regIndex)
+{
+    int encodedValue = cUnit->dalvikToSSAMap[dalvikReg];
+    int ssaReg = DECODE_REG(encodedValue);
+    uses[regIndex] = ssaReg;
+}
+
+/* Setup a new SSA register for a given Dalvik register */
+static void handleSSADef(CompilationUnit *cUnit, int *defs, int dalvikReg,
+                         int regIndex)
+{
+    int encodedValue = cUnit->dalvikToSSAMap[dalvikReg];
+    int ssaReg = cUnit->numSSARegs++;
+    /* Bump up the subscript */
+    int dalvikSub = DECODE_SUB(encodedValue) + 1;
+    int newD2SMapping = ENCODE_REG_SUB(ssaReg, dalvikSub);
+
+    cUnit->dalvikToSSAMap[dalvikReg] = newD2SMapping;
+
+    int newS2DMapping = ENCODE_REG_SUB(dalvikReg, dalvikSub);
+    dvmInsertGrowableList(cUnit->ssaToDalvikMap, newS2DMapping);
+
+    defs[regIndex] = ssaReg;
+}
+
+/* Loop up new SSA names for format_35c instructions */
+static void dataFlowSSAFormat35C(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int numUses = dInsn->vA;
+    int i;
+
+    mir->ssaRep->numUses = numUses;
+    mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses, false);
+
+    for (i = 0; i < numUses; i++) {
+        handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->arg[i], i);
+    }
+}
+
+/* Loop up new SSA names for format_3rc instructions */
+static void dataFlowSSAFormat3RC(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int numUses = dInsn->vA;
+    int i;
+
+    mir->ssaRep->numUses = numUses;
+    mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses, false);
+
+    for (i = 0; i < numUses; i++) {
+        handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC+i, i);
+    }
+}
+
+/* Entry function to convert a block into SSA representation */
+bool dvmCompilerDoSSAConversion(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+
+    if (bb->dataFlowInfo == NULL) return false;
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        mir->ssaRep = (struct SSARepresentation *)
+            dvmCompilerNew(sizeof(SSARepresentation), true);
+
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        int numUses = 0;
+
+        if (dfAttributes & DF_FORMAT_35C) {
+            dataFlowSSAFormat35C(cUnit, mir);
+            continue;
+        }
+
+        if (dfAttributes & DF_FORMAT_3RC) {
+            dataFlowSSAFormat3RC(cUnit, mir);
+            continue;
+        }
+
+        if (dfAttributes & DF_HAS_USES) {
+            if (dfAttributes & DF_UA) {
+                numUses++;
+            } else if (dfAttributes & DF_UA_WIDE) {
+                numUses += 2;
+            }
+            if (dfAttributes & DF_UB) {
+                numUses++;
+            } else if (dfAttributes & DF_UB_WIDE) {
+                numUses += 2;
+            }
+            if (dfAttributes & DF_UC) {
+                numUses++;
+            } else if (dfAttributes & DF_UC_WIDE) {
+                numUses += 2;
+            }
+        }
+
+        if (numUses) {
+            mir->ssaRep->numUses = numUses;
+            mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses,
+                                                      false);
+            mir->ssaRep->fpUse = (bool *)dvmCompilerNew(sizeof(bool) * numUses,
+                                                false);
+        }
+
+        int numDefs = 0;
+
+        if (dfAttributes & DF_HAS_DEFS) {
+            numDefs++;
+            if (dfAttributes & DF_DA_WIDE) {
+                numDefs++;
+            }
+        }
+
+        if (numDefs) {
+            mir->ssaRep->numDefs = numDefs;
+            mir->ssaRep->defs = (int *)dvmCompilerNew(sizeof(int) * numDefs,
+                                                      false);
+            mir->ssaRep->fpDef = (bool *)dvmCompilerNew(sizeof(bool) * numDefs,
+                                                        false);
+        }
+
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+        if (dfAttributes & DF_HAS_USES) {
+            numUses = 0;
+            if (dfAttributes & DF_UA) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA, numUses++);
+            } else if (dfAttributes & DF_UA_WIDE) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA, numUses++);
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA+1, numUses++);
+            }
+            if (dfAttributes & DF_UB) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB, numUses++);
+            } else if (dfAttributes & DF_UB_WIDE) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB, numUses++);
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB+1, numUses++);
+            }
+            if (dfAttributes & DF_UC) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC, numUses++);
+            } else if (dfAttributes & DF_UC_WIDE) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC, numUses++);
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC+1, numUses++);
+            }
+        }
+        if (dfAttributes & DF_HAS_DEFS) {
+            mir->ssaRep->fpDef[0] = dfAttributes & DF_FP_A;
+            handleSSADef(cUnit, mir->ssaRep->defs, dInsn->vA, 0);
+            if (dfAttributes & DF_DA_WIDE) {
+                mir->ssaRep->fpDef[1] = dfAttributes & DF_FP_A;
+                handleSSADef(cUnit, mir->ssaRep->defs, dInsn->vA+1, 1);
+            }
+        }
+    }
+
+    /*
+     * Take a snapshot of Dalvik->SSA mapping at the end of each block. The
+     * input to PHI nodes can be derived from the snapshot of all predecessor
+     * blocks.
+     */
+    bb->dataFlowInfo->dalvikToSSAMap =
+        (int *)dvmCompilerNew(sizeof(int) * cUnit->method->registersSize,
+                              false);
+
+    memcpy(bb->dataFlowInfo->dalvikToSSAMap, cUnit->dalvikToSSAMap,
+           sizeof(int) * cUnit->method->registersSize);
+    return true;
+}
+
+/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */
+static void setConstant(CompilationUnit *cUnit, int ssaReg, int value)
+{
+    dvmSetBit(cUnit->isConstantV, ssaReg);
+    cUnit->constantValues[ssaReg] = value;
+}
+
+bool dvmCompilerDoConstantPropagation(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    BitVector *isConstantV = cUnit->isConstantV;
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+        if (!(dfAttributes & DF_HAS_DEFS)) continue;
+
+        /* Handle instructions that set up constants directly */
+        if (dfAttributes & DF_SETS_CONST) {
+            if (dfAttributes & DF_DA) {
+                switch (dInsn->opcode) {
+                    case OP_CONST_4:
+                    case OP_CONST_16:
+                    case OP_CONST:
+                        setConstant(cUnit, mir->ssaRep->defs[0], dInsn->vB);
+                        break;
+                    case OP_CONST_HIGH16:
+                        setConstant(cUnit, mir->ssaRep->defs[0],
+                                    dInsn->vB << 16);
+                        break;
+                    default:
+                        break;
+                }
+            } else if (dfAttributes & DF_DA_WIDE) {
+                switch (dInsn->opcode) {
+                    case OP_CONST_WIDE_16:
+                    case OP_CONST_WIDE_32:
+                        setConstant(cUnit, mir->ssaRep->defs[0], dInsn->vB);
+                        setConstant(cUnit, mir->ssaRep->defs[1], 0);
+                        break;
+                    case OP_CONST_WIDE:
+                        setConstant(cUnit, mir->ssaRep->defs[0],
+                                    (int) dInsn->vB_wide);
+                        setConstant(cUnit, mir->ssaRep->defs[1],
+                                    (int) (dInsn->vB_wide >> 32));
+                        break;
+                    case OP_CONST_WIDE_HIGH16:
+                        setConstant(cUnit, mir->ssaRep->defs[0], 0);
+                        setConstant(cUnit, mir->ssaRep->defs[1],
+                                    dInsn->vB << 16);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        /* Handle instructions that set up constants directly */
+        } else if (dfAttributes & DF_IS_MOVE) {
+            int i;
+
+            for (i = 0; i < mir->ssaRep->numUses; i++) {
+                if (!dvmIsBitSet(isConstantV, mir->ssaRep->uses[i])) break;
+            }
+            /* Move a register holding a constant to another register */
+            if (i == mir->ssaRep->numUses) {
+                setConstant(cUnit, mir->ssaRep->defs[0],
+                            cUnit->constantValues[mir->ssaRep->uses[0]]);
+                if (dfAttributes & DF_DA_WIDE) {
+                    setConstant(cUnit, mir->ssaRep->defs[1],
+                                cUnit->constantValues[mir->ssaRep->uses[1]]);
+                }
+            }
+        }
+    }
+    /* TODO: implement code to handle arithmetic operations */
+    return true;
+}
+
+bool dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+                                       struct BasicBlock *bb)
+{
+    BitVector *isIndVarV = cUnit->loopAnalysis->isIndVarV;
+    BitVector *isConstantV = cUnit->isConstantV;
+    GrowableList *ivList = cUnit->loopAnalysis->ivList;
+    MIR *mir;
+
+    if (bb->blockType != kDalvikByteCode && bb->blockType != kEntryBlock) {
+        return false;
+    }
+
+    /* If the bb doesn't have a phi it cannot contain an induction variable */
+    if (bb->firstMIRInsn == NULL ||
+        (int)bb->firstMIRInsn->dalvikInsn.opcode != (int)kMirOpPhi) {
+        return false;
+    }
+
+    /* Find basic induction variable first */
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        if (!(dfAttributes & DF_IS_LINEAR)) continue;
+
+        /*
+         * For a basic induction variable:
+         *   1) use[0] should belong to the output of a phi node
+         *   2) def[0] should belong to the input of the same phi node
+         *   3) the value added/subtracted is a constant
+         */
+        MIR *phi;
+        for (phi = bb->firstMIRInsn; phi; phi = phi->next) {
+            if ((int)phi->dalvikInsn.opcode != (int)kMirOpPhi) break;
+
+            if (phi->ssaRep->defs[0] == mir->ssaRep->uses[0] &&
+                phi->ssaRep->uses[1] == mir->ssaRep->defs[0]) {
+                bool deltaIsConstant = false;
+                int deltaValue;
+
+                switch (mir->dalvikInsn.opcode) {
+                    case OP_ADD_INT:
+                        if (dvmIsBitSet(isConstantV,
+                                        mir->ssaRep->uses[1])) {
+                            deltaValue =
+                                cUnit->constantValues[mir->ssaRep->uses[1]];
+                            deltaIsConstant = true;
+                        }
+                        break;
+                    case OP_SUB_INT:
+                        if (dvmIsBitSet(isConstantV,
+                                        mir->ssaRep->uses[1])) {
+                            deltaValue =
+                                -cUnit->constantValues[mir->ssaRep->uses[1]];
+                            deltaIsConstant = true;
+                        }
+                        break;
+                    case OP_ADD_INT_LIT8:
+                        deltaValue = mir->dalvikInsn.vC;
+                        deltaIsConstant = true;
+                        break;
+                    default:
+                        break;
+                }
+                if (deltaIsConstant) {
+                    dvmSetBit(isIndVarV, mir->ssaRep->uses[0]);
+                    InductionVariableInfo *ivInfo = (InductionVariableInfo *)
+                        dvmCompilerNew(sizeof(InductionVariableInfo),
+                                       false);
+
+                    ivInfo->ssaReg = mir->ssaRep->uses[0];
+                    ivInfo->basicSSAReg = mir->ssaRep->uses[0];
+                    ivInfo->m = 1;         // always 1 to basic iv
+                    ivInfo->c = 0;         // N/A to basic iv
+                    ivInfo->inc = deltaValue;
+                    dvmInsertGrowableList(ivList, (intptr_t) ivInfo);
+                    cUnit->loopAnalysis->numBasicIV++;
+                    break;
+                }
+            }
+        }
+    }
+
+    /* Find dependent induction variable now */
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        if (!(dfAttributes & DF_IS_LINEAR)) continue;
+
+        /* Skip already identified induction variables */
+        if (dvmIsBitSet(isIndVarV, mir->ssaRep->defs[0])) continue;
+
+        /*
+         * For a dependent induction variable:
+         *  1) use[0] should be an induction variable (basic/dependent)
+         *  2) operand2 should be a constant
+         */
+        if (dvmIsBitSet(isIndVarV, mir->ssaRep->uses[0])) {
+            int srcDalvikReg = dvmConvertSSARegToDalvik(cUnit,
+                                                        mir->ssaRep->uses[0]);
+            int dstDalvikReg = dvmConvertSSARegToDalvik(cUnit,
+                                                        mir->ssaRep->defs[0]);
+
+            bool cIsConstant = false;
+            int c = 0;
+
+            switch (mir->dalvikInsn.opcode) {
+                case OP_ADD_INT:
+                    if (dvmIsBitSet(isConstantV,
+                                    mir->ssaRep->uses[1])) {
+                        c = cUnit->constantValues[mir->ssaRep->uses[1]];
+                        cIsConstant = true;
+                    }
+                    break;
+                case OP_SUB_INT:
+                    if (dvmIsBitSet(isConstantV,
+                                    mir->ssaRep->uses[1])) {
+                        c = -cUnit->constantValues[mir->ssaRep->uses[1]];
+                        cIsConstant = true;
+                    }
+                    break;
+                case OP_ADD_INT_LIT8:
+                    c = mir->dalvikInsn.vC;
+                    cIsConstant = true;
+                    break;
+                default:
+                    break;
+            }
+
+            /* Ignore the update to the basic induction variable itself */
+            if (DECODE_REG(srcDalvikReg) == DECODE_REG(dstDalvikReg))  {
+                cUnit->loopAnalysis->ssaBIV = mir->ssaRep->defs[0];
+                cIsConstant = false;
+            }
+
+            if (cIsConstant) {
+                unsigned int i;
+                dvmSetBit(isIndVarV, mir->ssaRep->defs[0]);
+                InductionVariableInfo *ivInfo = (InductionVariableInfo *)
+                    dvmCompilerNew(sizeof(InductionVariableInfo),
+                                   false);
+                InductionVariableInfo *ivInfoOld = NULL ;
+
+                for (i = 0; i < ivList->numUsed; i++) {
+                    ivInfoOld = (InductionVariableInfo *) ivList->elemList[i];
+                    if (ivInfoOld->ssaReg == mir->ssaRep->uses[0]) break;
+                }
+
+                /* Guaranteed to find an element */
+                assert(i < ivList->numUsed);
+
+                ivInfo->ssaReg = mir->ssaRep->defs[0];
+                ivInfo->basicSSAReg = ivInfoOld->basicSSAReg;
+                ivInfo->m = ivInfoOld->m;
+                ivInfo->c = c + ivInfoOld->c;
+                ivInfo->inc = ivInfoOld->inc;
+                dvmInsertGrowableList(ivList, (intptr_t) ivInfo);
+            }
+        }
+    }
+    return true;
+}
+
+/* Setup the basic data structures for SSA conversion */
+void dvmInitializeSSAConversion(CompilationUnit *cUnit)
+{
+    int i;
+    int numDalvikReg = cUnit->method->registersSize;
+
+    cUnit->ssaToDalvikMap = (GrowableList *)dvmCompilerNew(sizeof(GrowableList),
+                                                           false);
+    dvmInitGrowableList(cUnit->ssaToDalvikMap, numDalvikReg);
+
+    /*
+     * Initial number of SSA registers is equal to the number of Dalvik
+     * registers.
+     */
+    cUnit->numSSARegs = numDalvikReg;
+
+    /*
+     * Initialize the SSA2Dalvik map list. For the first numDalvikReg elements,
+     * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
+     * into "(0 << 16) | i"
+     */
+    for (i = 0; i < numDalvikReg; i++) {
+        dvmInsertGrowableList(cUnit->ssaToDalvikMap, ENCODE_REG_SUB(i, 0));
+    }
+
+    /*
+     * Initialize the DalvikToSSAMap map. The low 16 bit is the SSA register id,
+     * while the high 16 bit is the current subscript. The original Dalvik
+     * register N is mapped to SSA register N with subscript 0.
+     */
+    cUnit->dalvikToSSAMap = (int *)dvmCompilerNew(sizeof(int) * numDalvikReg,
+                                                  false);
+    for (i = 0; i < numDalvikReg; i++) {
+        cUnit->dalvikToSSAMap[i] = i;
+    }
+
+    /*
+     * Allocate the BasicBlockDataFlow structure for the entry and code blocks
+     */
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->hidden == true) continue;
+        if (bb->blockType == kDalvikByteCode ||
+            bb->blockType == kEntryBlock ||
+            bb->blockType == kExitBlock) {
+            bb->dataFlowInfo = (BasicBlockDataFlow *)
+                dvmCompilerNew(sizeof(BasicBlockDataFlow),
+                               true);
+        }
+    }
+}
+
+/* Clear the visited flag for each BB */
+bool dvmCompilerClearVisitedFlag(struct CompilationUnit *cUnit,
+                                 struct BasicBlock *bb)
+{
+    bb->visited = false;
+    return true;
+}
+
+void dvmCompilerDataFlowAnalysisDispatcher(CompilationUnit *cUnit,
+                bool (*func)(CompilationUnit *, BasicBlock *),
+                DataFlowAnalysisMode dfaMode,
+                bool isIterative)
+{
+    bool change = true;
+
+    while (change) {
+        change = false;
+
+        /* Scan all blocks and perform the operations specified in func */
+        if (dfaMode == kAllNodes) {
+            GrowableListIterator iterator;
+            dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+            while (true) {
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+                if (bb == NULL) break;
+                if (bb->hidden == true) continue;
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks and perform the operations specified in
+         * func.
+         */
+        else if (dfaMode == kReachableNodes) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = 0; idx < numReachableBlocks; idx++) {
+                int blockIdx = cUnit->dfsOrder.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList,
+                                                             blockIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks by the pre-order in the depth-first-search
+         * CFG and perform the operations specified in func.
+         */
+        else if (dfaMode == kPreOrderDFSTraversal) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = 0; idx < numReachableBlocks; idx++) {
+                int dfsIdx = cUnit->dfsOrder.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, dfsIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks by the post-order in the depth-first-search
+         * CFG and perform the operations specified in func.
+         */
+        else if (dfaMode == kPostOrderDFSTraversal) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = numReachableBlocks - 1; idx >= 0; idx--) {
+                int dfsIdx = cUnit->dfsOrder.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, dfsIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks by the post-order in the dominator tree
+         * and perform the operations specified in func.
+         */
+        else if (dfaMode == kPostOrderDOMTraversal) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = 0; idx < numReachableBlocks; idx++) {
+                int domIdx = cUnit->domPostOrderTraversal.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, domIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /* If isIterative is false, exit the loop after the first iteration */
+        change &= isIterative;
+    }
+}
+
+/* Main entry point to do SSA conversion for non-loop traces */
+void dvmCompilerNonLoopAnalysis(CompilationUnit *cUnit)
+{
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+                                          kAllNodes,
+                                          false /* isIterative */);
+}
diff --git a/vm/compiler/Dataflow.h b/vm/compiler/Dataflow.h
new file mode 100644
index 0000000..f04c91c
--- /dev/null
+++ b/vm/compiler/Dataflow.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_DATAFLOW_H_
+#define DALVIK_VM_DATAFLOW_H_
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+typedef enum DataFlowAttributePos {
+    kUA = 0,
+    kUB,
+    kUC,
+    kUAWide,
+    kUBWide,
+    kUCWide,
+    kDA,
+    kDAWide,
+    kIsMove,
+    kIsLinear,
+    kSetsConst,
+    kFormat35c,
+    kFormat3rc,
+    kPhi,
+    kNullNRangeCheck0,
+    kNullNRangeCheck1,
+    kNullNRangeCheck2,
+    kFPA,
+    kFPB,
+    kFPC,
+    kGetter,
+    kSetter,
+} DataFlowAttributes;
+
+#define DF_NOP                  0
+#define DF_UA                   (1 << kUA)
+#define DF_UB                   (1 << kUB)
+#define DF_UC                   (1 << kUC)
+#define DF_UA_WIDE              (1 << kUAWide)
+#define DF_UB_WIDE              (1 << kUBWide)
+#define DF_UC_WIDE              (1 << kUCWide)
+#define DF_DA                   (1 << kDA)
+#define DF_DA_WIDE              (1 << kDAWide)
+#define DF_IS_MOVE              (1 << kIsMove)
+#define DF_IS_LINEAR            (1 << kIsLinear)
+#define DF_SETS_CONST           (1 << kSetsConst)
+#define DF_FORMAT_35C           (1 << kFormat35c)
+#define DF_FORMAT_3RC           (1 << kFormat3rc)
+#define DF_PHI                  (1 << kPhi)
+#define DF_NULL_N_RANGE_CHECK_0 (1 << kNullNRangeCheck0)
+#define DF_NULL_N_RANGE_CHECK_1 (1 << kNullNRangeCheck1)
+#define DF_NULL_N_RANGE_CHECK_2 (1 << kNullNRangeCheck2)
+#define DF_FP_A                 (1 << kFPA)
+#define DF_FP_B                 (1 << kFPB)
+#define DF_FP_C                 (1 << kFPC)
+#define DF_IS_GETTER            (1 << kGetter)
+#define DF_IS_SETTER            (1 << kSetter)
+
+#define DF_HAS_USES             (DF_UA | DF_UB | DF_UC | DF_UA_WIDE | \
+                                 DF_UB_WIDE | DF_UC_WIDE)
+
+#define DF_HAS_DEFS             (DF_DA | DF_DA_WIDE)
+
+#define DF_HAS_NR_CHECKS        (DF_NULL_N_RANGE_CHECK_0 | \
+                                 DF_NULL_N_RANGE_CHECK_1 | \
+                                 DF_NULL_N_RANGE_CHECK_2)
+
+#define DF_A_IS_REG             (DF_UA | DF_UA_WIDE | DF_DA | DF_DA_WIDE)
+#define DF_B_IS_REG             (DF_UB | DF_UB_WIDE)
+#define DF_C_IS_REG             (DF_UC | DF_UC_WIDE)
+#define DF_IS_GETTER_OR_SETTER  (DF_IS_GETTER | DF_IS_SETTER)
+
+extern int dvmCompilerDataFlowAttributes[kMirOpLast];
+
+typedef struct BasicBlockDataFlow {
+    BitVector *useV;
+    BitVector *defV;
+    BitVector *liveInV;
+    BitVector *phiV;
+    int *dalvikToSSAMap;
+} BasicBlockDataFlow;
+
+typedef struct SSARepresentation {
+    int numUses;
+    int *uses;
+    bool *fpUse;
+    int numDefs;
+    int *defs;
+    bool *fpDef;
+} SSARepresentation;
+
+/*
+ * An induction variable is represented by "m*i + c", where i is a basic
+ * induction variable.
+ */
+typedef struct InductionVariableInfo {
+    int ssaReg;
+    int basicSSAReg;
+    int m;      // multiplier
+    int c;      // constant
+    int inc;    // loop incriment
+} InductionVariableInfo;
+
+typedef struct ArrayAccessInfo {
+    int arrayReg;
+    int ivReg;
+    int maxC;                   // For DIV - will affect upper bound checking
+    int minC;                   // For DIV - will affect lower bound checking
+} ArrayAccessInfo;
+
+#define ENCODE_REG_SUB(r,s)             ((s<<16) | r)
+#define DECODE_REG(v)                   (v & 0xffff)
+#define DECODE_SUB(v)                   (((unsigned int) v) >> 16)
+
+#endif  // DALVIK_VM_DATAFLOW_H_
diff --git a/vm/compiler/Frontend.cpp b/vm/compiler/Frontend.cpp
new file mode 100644
index 0000000..47c1898
--- /dev/null
+++ b/vm/compiler/Frontend.cpp
@@ -0,0 +1,2177 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexCatch.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+
+static inline bool contentIsInsn(const u2 *codePtr) {
+    u2 instr = *codePtr;
+    Opcode opcode = (Opcode)(instr & 0xff);
+
+    /*
+     * Since the low 8-bit in metadata may look like OP_NOP, we need to check
+     * both the low and whole sub-word to determine whether it is code or data.
+     */
+    return (opcode != OP_NOP || instr == 0);
+}
+
+/*
+ * Parse an instruction, return the length of the instruction
+ */
+static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn,
+                            bool printMe)
+{
+    // Don't parse instruction data
+    if (!contentIsInsn(codePtr)) {
+        return 0;
+    }
+
+    u2 instr = *codePtr;
+    Opcode opcode = dexOpcodeFromCodeUnit(instr);
+
+    dexDecodeInstruction(codePtr, decInsn);
+    if (printMe) {
+        char *decodedString = dvmCompilerGetDalvikDisassembly(decInsn, NULL);
+        ALOGD("%p: %#06x %s", codePtr, opcode, decodedString);
+    }
+    return dexGetWidthFromOpcode(opcode);
+}
+
+#define UNKNOWN_TARGET 0xffffffff
+
+/*
+ * Identify block-ending instructions and collect supplemental information
+ * regarding the following instructions.
+ */
+static inline bool findBlockBoundary(const Method *caller, MIR *insn,
+                                     unsigned int curOffset,
+                                     unsigned int *target, bool *isInvoke,
+                                     const Method **callee)
+{
+    switch (insn->dalvikInsn.opcode) {
+        /* Target is not compile-time constant */
+        case OP_RETURN_VOID:
+        case OP_RETURN:
+        case OP_RETURN_WIDE:
+        case OP_RETURN_OBJECT:
+        case OP_THROW:
+          *target = UNKNOWN_TARGET;
+          break;
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_VIRTUAL_RANGE:
+        case OP_INVOKE_INTERFACE:
+        case OP_INVOKE_INTERFACE_RANGE:
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+            *isInvoke = true;
+            break;
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            int mIndex = caller->clazz->pDvmDex->
+                pResMethods[insn->dalvikInsn.vB]->methodIndex;
+            const Method *calleeMethod =
+                caller->clazz->super->vtable[mIndex];
+
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE: {
+            const Method *calleeMethod =
+                caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
+
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            const Method *calleeMethod =
+                caller->clazz->super->vtable[insn->dalvikInsn.vB];
+
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            const Method *calleeMethod =
+                caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_GOTO_32:
+            *target = curOffset + (int) insn->dalvikInsn.vA;
+            break;
+
+        case OP_IF_EQ:
+        case OP_IF_NE:
+        case OP_IF_LT:
+        case OP_IF_GE:
+        case OP_IF_GT:
+        case OP_IF_LE:
+            *target = curOffset + (int) insn->dalvikInsn.vC;
+            break;
+
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+            *target = curOffset + (int) insn->dalvikInsn.vB;
+            break;
+
+        default:
+            return false;
+    }
+    return true;
+}
+
+static inline bool isGoto(MIR *insn)
+{
+    switch (insn->dalvikInsn.opcode) {
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_GOTO_32:
+            return true;
+        default:
+            return false;
+    }
+}
+
+/*
+ * Identify unconditional branch instructions
+ */
+static inline bool isUnconditionalBranch(MIR *insn)
+{
+    switch (insn->dalvikInsn.opcode) {
+        case OP_RETURN_VOID:
+        case OP_RETURN:
+        case OP_RETURN_WIDE:
+        case OP_RETURN_OBJECT:
+            return true;
+        default:
+            return isGoto(insn);
+    }
+}
+
+/*
+ * dvmHashTableLookup() callback
+ */
+static int compareMethod(const CompilerMethodStats *m1,
+                         const CompilerMethodStats *m2)
+{
+    return (int) m1->method - (int) m2->method;
+}
+
+/*
+ * Analyze the body of the method to collect high-level information regarding
+ * inlining:
+ * - is empty method?
+ * - is getter/setter?
+ * - can throw exception?
+ *
+ * Currently the inliner only handles getters and setters. When its capability
+ * becomes more sophisticated more information will be retrieved here.
+ */
+static int analyzeInlineTarget(DecodedInstruction *dalvikInsn, int attributes,
+                               int offset)
+{
+    int flags = dexGetFlagsFromOpcode(dalvikInsn->opcode);
+    int dalvikOpcode = dalvikInsn->opcode;
+
+    if (flags & kInstrInvoke) {
+        attributes &= ~METHOD_IS_LEAF;
+    }
+
+    if (!(flags & kInstrCanReturn)) {
+        if (!(dvmCompilerDataFlowAttributes[dalvikOpcode] &
+              DF_IS_GETTER)) {
+            attributes &= ~METHOD_IS_GETTER;
+        }
+        if (!(dvmCompilerDataFlowAttributes[dalvikOpcode] &
+              DF_IS_SETTER)) {
+            attributes &= ~METHOD_IS_SETTER;
+        }
+    }
+
+    /*
+     * The expected instruction sequence is setter will never return value and
+     * getter will also do. Clear the bits if the behavior is discovered
+     * otherwise.
+     */
+    if (flags & kInstrCanReturn) {
+        if (dalvikOpcode == OP_RETURN_VOID) {
+            attributes &= ~METHOD_IS_GETTER;
+        }
+        else {
+            attributes &= ~METHOD_IS_SETTER;
+        }
+    }
+
+    if (flags & kInstrCanThrow) {
+        attributes &= ~METHOD_IS_THROW_FREE;
+    }
+
+    if (offset == 0 && dalvikOpcode == OP_RETURN_VOID) {
+        attributes |= METHOD_IS_EMPTY;
+    }
+
+    /*
+     * Check if this opcode is selected for single stepping.
+     * If so, don't inline the callee as there is no stack frame for the
+     * interpreter to single-step through the instruction.
+     */
+    if (SINGLE_STEP_OP(dalvikOpcode)) {
+        attributes &= ~(METHOD_IS_GETTER | METHOD_IS_SETTER);
+    }
+
+    return attributes;
+}
+
+/*
+ * Analyze each method whose traces are ever compiled. Collect a variety of
+ * statistics like the ratio of exercised vs overall code and code bloat
+ * ratios. If isCallee is true, also analyze each instruction in more details
+ * to see if it is suitable for inlining.
+ */
+CompilerMethodStats *dvmCompilerAnalyzeMethodBody(const Method *method,
+                                                  bool isCallee)
+{
+    const DexCode *dexCode = dvmGetMethodCode(method);
+    const u2 *codePtr = dexCode->insns;
+    const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+    int insnSize = 0;
+    int hashValue = dvmComputeUtf8Hash(method->name);
+
+    CompilerMethodStats dummyMethodEntry; // For hash table lookup
+    CompilerMethodStats *realMethodEntry; // For hash table storage
+
+    /* For lookup only */
+    dummyMethodEntry.method = method;
+    realMethodEntry = (CompilerMethodStats *)
+        dvmHashTableLookup(gDvmJit.methodStatsTable,
+                           hashValue,
+                           &dummyMethodEntry,
+                           (HashCompareFunc) compareMethod,
+                           false);
+
+    /* This method has never been analyzed before - create an entry */
+    if (realMethodEntry == NULL) {
+        realMethodEntry =
+            (CompilerMethodStats *) calloc(1, sizeof(CompilerMethodStats));
+        realMethodEntry->method = method;
+
+        dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
+                           realMethodEntry,
+                           (HashCompareFunc) compareMethod,
+                           true);
+    }
+
+    /* This method is invoked as a callee and has been analyzed - just return */
+    if ((isCallee == true) && (realMethodEntry->attributes & METHOD_IS_CALLEE))
+        return realMethodEntry;
+
+    /*
+     * Similarly, return if this method has been compiled before as a hot
+     * method already.
+     */
+    if ((isCallee == false) &&
+        (realMethodEntry->attributes & METHOD_IS_HOT))
+        return realMethodEntry;
+
+    int attributes;
+
+    /* Method hasn't been analyzed for the desired purpose yet */
+    if (isCallee) {
+        /* Aggressively set the attributes until proven otherwise */
+        attributes = METHOD_IS_LEAF | METHOD_IS_THROW_FREE | METHOD_IS_CALLEE |
+                     METHOD_IS_GETTER | METHOD_IS_SETTER;
+    } else {
+        attributes = METHOD_IS_HOT;
+    }
+
+    /* Count the number of instructions */
+    while (codePtr < codeEnd) {
+        DecodedInstruction dalvikInsn;
+        int width = parseInsn(codePtr, &dalvikInsn, false);
+
+        /* Terminate when the data section is seen */
+        if (width == 0)
+            break;
+
+        if (isCallee) {
+            attributes = analyzeInlineTarget(&dalvikInsn, attributes, insnSize);
+        }
+
+        insnSize += width;
+        codePtr += width;
+    }
+
+    /*
+     * Only handle simple getters/setters with one instruction followed by
+     * return
+     */
+    if ((attributes & (METHOD_IS_GETTER | METHOD_IS_SETTER)) &&
+        (insnSize != 3)) {
+        attributes &= ~(METHOD_IS_GETTER | METHOD_IS_SETTER);
+    }
+
+    realMethodEntry->dalvikSize = insnSize * 2;
+    realMethodEntry->attributes |= attributes;
+
+#if 0
+    /* Uncomment the following to explore various callee patterns */
+    if (attributes & METHOD_IS_THROW_FREE) {
+        ALOGE("%s%s is inlinable%s", method->clazz->descriptor, method->name,
+             (attributes & METHOD_IS_EMPTY) ? " empty" : "");
+    }
+
+    if (attributes & METHOD_IS_LEAF) {
+        ALOGE("%s%s is leaf %d%s", method->clazz->descriptor, method->name,
+             insnSize, insnSize < 5 ? " (small)" : "");
+    }
+
+    if (attributes & (METHOD_IS_GETTER | METHOD_IS_SETTER)) {
+        ALOGE("%s%s is %s", method->clazz->descriptor, method->name,
+             attributes & METHOD_IS_GETTER ? "getter": "setter");
+    }
+    if (attributes ==
+        (METHOD_IS_LEAF | METHOD_IS_THROW_FREE | METHOD_IS_CALLEE)) {
+        ALOGE("%s%s is inlinable non setter/getter", method->clazz->descriptor,
+             method->name);
+    }
+#endif
+
+    return realMethodEntry;
+}
+
+/*
+ * Crawl the stack of the thread that requesed compilation to see if any of the
+ * ancestors are on the blacklist.
+ */
+static bool filterMethodByCallGraph(Thread *thread, const char *curMethodName)
+{
+    /* Crawl the Dalvik stack frames and compare the method name*/
+    StackSaveArea *ssaPtr = ((StackSaveArea *) thread->interpSave.curFrame) - 1;
+    while (ssaPtr != ((StackSaveArea *) NULL) - 1) {
+        const Method *method = ssaPtr->method;
+        if (method) {
+            int hashValue = dvmComputeUtf8Hash(method->name);
+            bool found =
+                dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               (char *) method->name,
+                               (HashCompareFunc) strcmp, false) !=
+                NULL;
+            if (found) {
+                ALOGD("Method %s (--> %s) found on the JIT %s list",
+                     method->name, curMethodName,
+                     gDvmJit.includeSelectedMethod ? "white" : "black");
+                return true;
+            }
+
+        }
+        ssaPtr = ((StackSaveArea *) ssaPtr->prevFrame) - 1;
+    };
+    return false;
+}
+
+/*
+ * Since we are including instructions from possibly a cold method into the
+ * current trace, we need to make sure that all the associated information
+ * with the callee is properly initialized. If not, we punt on this inline
+ * target.
+ *
+ * TODO: volatile instructions will be handled later.
+ */
+bool dvmCompilerCanIncludeThisInstruction(const Method *method,
+                                          const DecodedInstruction *insn)
+{
+    switch (insn->opcode) {
+        case OP_NEW_INSTANCE:
+        case OP_CHECK_CAST: {
+            ClassObject *classPtr = (ClassObject *)(void*)
+              (method->clazz->pDvmDex->pResClasses[insn->vB]);
+
+            /* Class hasn't been initialized yet */
+            if (classPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_SGET:
+        case OP_SGET_WIDE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+        case OP_SPUT:
+        case OP_SPUT_WIDE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT: {
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[insn->vB]);
+
+            if (fieldPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            int mIndex = method->clazz->pDvmDex->
+                pResMethods[insn->vB]->methodIndex;
+            const Method *calleeMethod = method->clazz->super->vtable[mIndex];
+            if (calleeMethod == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            const Method *calleeMethod = method->clazz->super->vtable[insn->vB];
+            if (calleeMethod == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE:
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            const Method *calleeMethod =
+                method->clazz->pDvmDex->pResMethods[insn->vB];
+            if (calleeMethod == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_CONST_CLASS: {
+            void *classPtr = (void*)
+                (method->clazz->pDvmDex->pResClasses[insn->vB]);
+
+            if (classPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_CONST_STRING_JUMBO:
+        case OP_CONST_STRING: {
+            void *strPtr = (void*)
+                (method->clazz->pDvmDex->pResStrings[insn->vB]);
+
+            if (strPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        default:
+            return true;
+    }
+}
+
+/* Split an existing block from the specified code offset into two */
+static BasicBlock *splitBlock(CompilationUnit *cUnit,
+                              unsigned int codeOffset,
+                              BasicBlock *origBlock,
+                              BasicBlock **immedPredBlockP)
+{
+    MIR *insn = origBlock->firstMIRInsn;
+    while (insn) {
+        if (insn->offset == codeOffset) break;
+        insn = insn->next;
+    }
+    if (insn == NULL) {
+        ALOGE("Break split failed");
+        dvmAbort();
+    }
+    BasicBlock *bottomBlock = dvmCompilerNewBB(kDalvikByteCode,
+                                               cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bottomBlock);
+
+    bottomBlock->startOffset = codeOffset;
+    bottomBlock->firstMIRInsn = insn;
+    bottomBlock->lastMIRInsn = origBlock->lastMIRInsn;
+
+    /* Handle the taken path */
+    bottomBlock->taken = origBlock->taken;
+    if (bottomBlock->taken) {
+        origBlock->taken = NULL;
+        dvmCompilerClearBit(bottomBlock->taken->predecessors, origBlock->id);
+        dvmCompilerSetBit(bottomBlock->taken->predecessors, bottomBlock->id);
+    }
+
+    /* Handle the fallthrough path */
+    bottomBlock->needFallThroughBranch = origBlock->needFallThroughBranch;
+    bottomBlock->fallThrough = origBlock->fallThrough;
+    origBlock->fallThrough = bottomBlock;
+    origBlock->needFallThroughBranch = true;
+    dvmCompilerSetBit(bottomBlock->predecessors, origBlock->id);
+    if (bottomBlock->fallThrough) {
+        dvmCompilerClearBit(bottomBlock->fallThrough->predecessors,
+                            origBlock->id);
+        dvmCompilerSetBit(bottomBlock->fallThrough->predecessors,
+                          bottomBlock->id);
+    }
+
+    /* Handle the successor list */
+    if (origBlock->successorBlockList.blockListType != kNotUsed) {
+        bottomBlock->successorBlockList = origBlock->successorBlockList;
+        origBlock->successorBlockList.blockListType = kNotUsed;
+        GrowableListIterator iterator;
+
+        dvmGrowableListIteratorInit(&bottomBlock->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *bb = successorBlockInfo->block;
+            dvmCompilerClearBit(bb->predecessors, origBlock->id);
+            dvmCompilerSetBit(bb->predecessors, bottomBlock->id);
+        }
+    }
+
+    origBlock->lastMIRInsn = insn->prev;
+
+    insn->prev->next = NULL;
+    insn->prev = NULL;
+
+    /*
+     * Update the immediate predecessor block pointer so that outgoing edges
+     * can be applied to the proper block.
+     */
+    if (immedPredBlockP) {
+        assert(*immedPredBlockP == origBlock);
+        *immedPredBlockP = bottomBlock;
+    }
+    return bottomBlock;
+}
+
+/*
+ * Given a code offset, find out the block that starts with it. If the offset
+ * is in the middle of an existing block, split it into two. If immedPredBlockP
+ * is non-null and is the block being split, update *immedPredBlockP to point
+ * to the bottom block so that outgoing edges can be setup properly (by the
+ * caller).
+ */
+static BasicBlock *findBlock(CompilationUnit *cUnit,
+                             unsigned int codeOffset,
+                             bool split, bool create,
+                             BasicBlock **immedPredBlockP)
+{
+    GrowableList *blockList = &cUnit->blockList;
+    BasicBlock *bb;
+    unsigned int i;
+
+    for (i = 0; i < blockList->numUsed; i++) {
+        bb = (BasicBlock *) blockList->elemList[i];
+        if (bb->blockType != kDalvikByteCode) continue;
+        if (bb->startOffset == codeOffset) return bb;
+        /* Check if a branch jumps into the middle of an existing block */
+        if ((split == true) && (codeOffset > bb->startOffset) &&
+            (bb->lastMIRInsn != NULL) &&
+            (codeOffset <= bb->lastMIRInsn->offset)) {
+            BasicBlock *newBB = splitBlock(cUnit, codeOffset, bb,
+                                           bb == *immedPredBlockP ?
+                                               immedPredBlockP : NULL);
+            return newBB;
+        }
+    }
+    if (create) {
+          bb = dvmCompilerNewBB(kDalvikByteCode, cUnit->numBlocks++);
+          dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+          bb->startOffset = codeOffset;
+          return bb;
+    }
+    return NULL;
+}
+
+/* Dump the CFG into a DOT graph */
+void dvmDumpCFG(CompilationUnit *cUnit, const char *dirPrefix)
+{
+    const Method *method = cUnit->method;
+    FILE *file;
+    char *signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    char startOffset[80];
+    sprintf(startOffset, "_%x", cUnit->entryBlock->fallThrough->startOffset);
+    char *fileName = (char *) dvmCompilerNew(
+                                  strlen(dirPrefix) +
+                                  strlen(method->clazz->descriptor) +
+                                  strlen(method->name) +
+                                  strlen(signature) +
+                                  strlen(startOffset) +
+                                  strlen(".dot") + 1, true);
+    sprintf(fileName, "%s%s%s%s%s.dot", dirPrefix,
+            method->clazz->descriptor, method->name, signature, startOffset);
+    free(signature);
+
+    /*
+     * Convert the special characters into a filesystem- and shell-friendly
+     * format.
+     */
+    int i;
+    for (i = strlen(dirPrefix); fileName[i]; i++) {
+        if (fileName[i] == '/') {
+            fileName[i] = '_';
+        } else if (fileName[i] == ';') {
+            fileName[i] = '#';
+        } else if (fileName[i] == '$') {
+            fileName[i] = '+';
+        } else if (fileName[i] == '(' || fileName[i] == ')') {
+            fileName[i] = '@';
+        } else if (fileName[i] == '<' || fileName[i] == '>') {
+            fileName[i] = '=';
+        }
+    }
+    file = fopen(fileName, "w");
+    if (file == NULL) {
+        return;
+    }
+    fprintf(file, "digraph G {\n");
+
+    fprintf(file, "  rankdir=TB\n");
+
+    int numReachableBlocks = cUnit->numReachableBlocks;
+    int idx;
+    const GrowableList *blockList = &cUnit->blockList;
+
+    for (idx = 0; idx < numReachableBlocks; idx++) {
+        int blockIdx = cUnit->dfsOrder.elemList[idx];
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListGetElement(blockList,
+                                                                  blockIdx);
+        if (bb == NULL) break;
+        if (bb->blockType == kEntryBlock) {
+            fprintf(file, "  entry [shape=Mdiamond];\n");
+        } else if (bb->blockType == kExitBlock) {
+            fprintf(file, "  exit [shape=Mdiamond];\n");
+        } else if (bb->blockType == kDalvikByteCode) {
+            fprintf(file, "  block%04x [shape=record,label = \"{ \\\n",
+                    bb->startOffset);
+            const MIR *mir;
+            fprintf(file, "    {block id %d\\l}%s\\\n", bb->id,
+                    bb->firstMIRInsn ? " | " : " ");
+            for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+                fprintf(file, "    {%04x %s\\l}%s\\\n", mir->offset,
+                        mir->ssaRep ?
+                            dvmCompilerFullDisassembler(cUnit, mir) :
+                            dexGetOpcodeName(mir->dalvikInsn.opcode),
+                        mir->next ? " | " : " ");
+            }
+            fprintf(file, "  }\"];\n\n");
+        } else if (bb->blockType == kExceptionHandling) {
+            char blockName[BLOCK_NAME_LEN];
+
+            dvmGetBlockName(bb, blockName);
+            fprintf(file, "  %s [shape=invhouse];\n", blockName);
+        }
+
+        char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
+
+        if (bb->taken) {
+            dvmGetBlockName(bb, blockName1);
+            dvmGetBlockName(bb->taken, blockName2);
+            fprintf(file, "  %s:s -> %s:n [style=dotted]\n",
+                    blockName1, blockName2);
+        }
+        if (bb->fallThrough) {
+            dvmGetBlockName(bb, blockName1);
+            dvmGetBlockName(bb->fallThrough, blockName2);
+            fprintf(file, "  %s:s -> %s:n\n", blockName1, blockName2);
+        }
+
+        if (bb->successorBlockList.blockListType != kNotUsed) {
+            fprintf(file, "  succ%04x [shape=%s,label = \"{ \\\n",
+                    bb->startOffset,
+                    (bb->successorBlockList.blockListType == kCatch) ?
+                        "Mrecord" : "record");
+            GrowableListIterator iterator;
+            dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                        &iterator);
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+
+            int succId = 0;
+            while (true) {
+                if (successorBlockInfo == NULL) break;
+
+                BasicBlock *destBlock = successorBlockInfo->block;
+                SuccessorBlockInfo *nextSuccessorBlockInfo =
+                  (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+
+                fprintf(file, "    {<f%d> %04x: %04x\\l}%s\\\n",
+                        succId++,
+                        successorBlockInfo->key,
+                        destBlock->startOffset,
+                        (nextSuccessorBlockInfo != NULL) ? " | " : " ");
+
+                successorBlockInfo = nextSuccessorBlockInfo;
+            }
+            fprintf(file, "  }\"];\n\n");
+
+            dvmGetBlockName(bb, blockName1);
+            fprintf(file, "  %s:s -> succ%04x:n [style=dashed]\n",
+                    blockName1, bb->startOffset);
+
+            if (bb->successorBlockList.blockListType == kPackedSwitch ||
+                bb->successorBlockList.blockListType == kSparseSwitch) {
+
+                dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                            &iterator);
+
+                succId = 0;
+                while (true) {
+                    SuccessorBlockInfo *successorBlockInfo =
+                        (SuccessorBlockInfo *)
+                            dvmGrowableListIteratorNext(&iterator);
+                    if (successorBlockInfo == NULL) break;
+
+                    BasicBlock *destBlock = successorBlockInfo->block;
+
+                    dvmGetBlockName(destBlock, blockName2);
+                    fprintf(file, "  succ%04x:f%d:e -> %s:n\n",
+                            bb->startOffset, succId++,
+                            blockName2);
+                }
+            }
+        }
+        fprintf(file, "\n");
+
+        /*
+         * If we need to debug the dominator tree, uncomment the following code
+         */
+#if 1
+        dvmGetBlockName(bb, blockName1);
+        fprintf(file, "  cfg%s [label=\"%s\", shape=none];\n",
+                blockName1, blockName1);
+        if (bb->iDom) {
+            dvmGetBlockName(bb->iDom, blockName2);
+            fprintf(file, "  cfg%s:s -> cfg%s:n\n\n",
+                    blockName2, blockName1);
+        }
+#endif
+    }
+    fprintf(file, "}\n");
+    fclose(file);
+}
+
+/* Verify if all the successor is connected with all the claimed predecessors */
+static bool verifyPredInfo(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVectorIterator bvIterator;
+
+    dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+    while (true) {
+        int blockIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (blockIdx == -1) break;
+        BasicBlock *predBB = (BasicBlock *)
+            dvmGrowableListGetElement(&cUnit->blockList, blockIdx);
+        bool found = false;
+        if (predBB->taken == bb) {
+            found = true;
+        } else if (predBB->fallThrough == bb) {
+            found = true;
+        } else if (predBB->successorBlockList.blockListType != kNotUsed) {
+            GrowableListIterator iterator;
+            dvmGrowableListIteratorInit(&predBB->successorBlockList.blocks,
+                                        &iterator);
+            while (true) {
+                SuccessorBlockInfo *successorBlockInfo =
+                    (SuccessorBlockInfo *)
+                        dvmGrowableListIteratorNext(&iterator);
+                if (successorBlockInfo == NULL) break;
+                BasicBlock *succBB = successorBlockInfo->block;
+                if (succBB == bb) {
+                    found = true;
+                    break;
+                }
+            }
+        }
+        if (found == false) {
+            char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
+            dvmGetBlockName(bb, blockName1);
+            dvmGetBlockName(predBB, blockName2);
+            dvmDumpCFG(cUnit, "/sdcard/cfg/");
+            ALOGE("Successor %s not found from %s",
+                 blockName1, blockName2);
+            dvmAbort();
+        }
+    }
+    return true;
+}
+
+/* Identify code range in try blocks and set up the empty catch blocks */
+static void processTryCatchBlocks(CompilationUnit *cUnit)
+{
+    const Method *meth = cUnit->method;
+    const DexCode *pCode = dvmGetMethodCode(meth);
+    int triesSize = pCode->triesSize;
+    int i;
+    int offset;
+
+    if (triesSize == 0) {
+        return;
+    }
+
+    const DexTry *pTries = dexGetTries(pCode);
+    BitVector *tryBlockAddr = cUnit->tryBlockAddr;
+
+    /* Mark all the insn offsets in Try blocks */
+    for (i = 0; i < triesSize; i++) {
+        const DexTry* pTry = &pTries[i];
+        /* all in 16-bit units */
+        int startOffset = pTry->startAddr;
+        int endOffset = startOffset + pTry->insnCount;
+
+        for (offset = startOffset; offset < endOffset; offset++) {
+            dvmCompilerSetBit(tryBlockAddr, offset);
+        }
+    }
+
+    /* Iterate over each of the handlers to enqueue the empty Catch blocks */
+    offset = dexGetFirstHandlerOffset(pCode);
+    int handlersSize = dexGetHandlersSize(pCode);
+
+    for (i = 0; i < handlersSize; i++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            /*
+             * Create dummy catch blocks first. Since these are created before
+             * other blocks are processed, "split" is specified as false.
+             */
+            findBlock(cUnit, handler->address,
+                      /* split */
+                      false,
+                      /* create */
+                      true,
+                      /* immedPredBlockP */
+                      NULL);
+        }
+
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+}
+
+/* Process instructions with the kInstrCanBranch flag */
+static void processCanBranch(CompilationUnit *cUnit, BasicBlock *curBlock,
+                             MIR *insn, int curOffset, int width, int flags,
+                             const u2* codePtr, const u2* codeEnd)
+{
+    int target = curOffset;
+    switch (insn->dalvikInsn.opcode) {
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_GOTO_32:
+            target += (int) insn->dalvikInsn.vA;
+            break;
+        case OP_IF_EQ:
+        case OP_IF_NE:
+        case OP_IF_LT:
+        case OP_IF_GE:
+        case OP_IF_GT:
+        case OP_IF_LE:
+            target += (int) insn->dalvikInsn.vC;
+            break;
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+            target += (int) insn->dalvikInsn.vB;
+            break;
+        default:
+            ALOGE("Unexpected opcode(%d) with kInstrCanBranch set",
+                 insn->dalvikInsn.opcode);
+            dvmAbort();
+    }
+    BasicBlock *takenBlock = findBlock(cUnit, target,
+                                       /* split */
+                                       true,
+                                       /* create */
+                                       true,
+                                       /* immedPredBlockP */
+                                       &curBlock);
+    curBlock->taken = takenBlock;
+    dvmCompilerSetBit(takenBlock->predecessors, curBlock->id);
+
+    /* Always terminate the current block for conditional branches */
+    if (flags & kInstrCanContinue) {
+        BasicBlock *fallthroughBlock = findBlock(cUnit,
+                                                 curOffset +  width,
+                                                 /*
+                                                  * If the method is processed
+                                                  * in sequential order from the
+                                                  * beginning, we don't need to
+                                                  * specify split for continue
+                                                  * blocks. However, this
+                                                  * routine can be called by
+                                                  * compileLoop, which starts
+                                                  * parsing the method from an
+                                                  * arbitrary address in the
+                                                  * method body.
+                                                  */
+                                                 true,
+                                                 /* create */
+                                                 true,
+                                                 /* immedPredBlockP */
+                                                 &curBlock);
+        curBlock->fallThrough = fallthroughBlock;
+        dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+    } else if (codePtr < codeEnd) {
+        /* Create a fallthrough block for real instructions (incl. OP_NOP) */
+        if (contentIsInsn(codePtr)) {
+            findBlock(cUnit, curOffset + width,
+                      /* split */
+                      false,
+                      /* create */
+                      true,
+                      /* immedPredBlockP */
+                      NULL);
+        }
+    }
+}
+
+/* Process instructions with the kInstrCanSwitch flag */
+static void processCanSwitch(CompilationUnit *cUnit, BasicBlock *curBlock,
+                             MIR *insn, int curOffset, int width, int flags)
+{
+    u2 *switchData= (u2 *) (cUnit->method->insns + curOffset +
+                            insn->dalvikInsn.vB);
+    int size;
+    int *keyTable;
+    int *targetTable;
+    int i;
+    int firstKey;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    if (insn->dalvikInsn.opcode == OP_PACKED_SWITCH) {
+        assert(switchData[0] == kPackedSwitchSignature);
+        size = switchData[1];
+        firstKey = switchData[2] | (switchData[3] << 16);
+        targetTable = (int *) &switchData[4];
+        keyTable = NULL;        // Make the compiler happy
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+    } else {
+        assert(switchData[0] == kSparseSwitchSignature);
+        size = switchData[1];
+        keyTable = (int *) &switchData[2];
+        targetTable = (int *) &switchData[2 + size*2];
+        firstKey = 0;   // To make the compiler happy
+    }
+
+    if (curBlock->successorBlockList.blockListType != kNotUsed) {
+        ALOGE("Successor block list already in use: %d",
+             curBlock->successorBlockList.blockListType);
+        dvmAbort();
+    }
+    curBlock->successorBlockList.blockListType =
+        (insn->dalvikInsn.opcode == OP_PACKED_SWITCH) ?
+        kPackedSwitch : kSparseSwitch;
+    dvmInitGrowableList(&curBlock->successorBlockList.blocks, size);
+
+    for (i = 0; i < size; i++) {
+        BasicBlock *caseBlock = findBlock(cUnit, curOffset + targetTable[i],
+                                          /* split */
+                                          true,
+                                          /* create */
+                                          true,
+                                          /* immedPredBlockP */
+                                          &curBlock);
+        SuccessorBlockInfo *successorBlockInfo =
+            (SuccessorBlockInfo *) dvmCompilerNew(sizeof(SuccessorBlockInfo),
+                                                  false);
+        successorBlockInfo->block = caseBlock;
+        successorBlockInfo->key = (insn->dalvikInsn.opcode == OP_PACKED_SWITCH)?
+                                  firstKey + i : keyTable[i];
+        dvmInsertGrowableList(&curBlock->successorBlockList.blocks,
+                              (intptr_t) successorBlockInfo);
+        dvmCompilerSetBit(caseBlock->predecessors, curBlock->id);
+    }
+
+    /* Fall-through case */
+    BasicBlock *fallthroughBlock = findBlock(cUnit,
+                                             curOffset +  width,
+                                             /* split */
+                                             false,
+                                             /* create */
+                                             true,
+                                             /* immedPredBlockP */
+                                             NULL);
+    curBlock->fallThrough = fallthroughBlock;
+    dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+}
+
+/* Process instructions with the kInstrCanThrow flag */
+static void processCanThrow(CompilationUnit *cUnit, BasicBlock *curBlock,
+                            MIR *insn, int curOffset, int width, int flags,
+                            BitVector *tryBlockAddr, const u2 *codePtr,
+                            const u2* codeEnd)
+{
+    const Method *method = cUnit->method;
+    const DexCode *dexCode = dvmGetMethodCode(method);
+
+    /* In try block */
+    if (dvmIsBitSet(tryBlockAddr, curOffset)) {
+        DexCatchIterator iterator;
+
+        if (!dexFindCatchHandler(&iterator, dexCode, curOffset)) {
+            ALOGE("Catch block not found in dexfile for insn %x in %s",
+                 curOffset, method->name);
+            dvmAbort();
+
+        }
+        if (curBlock->successorBlockList.blockListType != kNotUsed) {
+            ALOGE("Successor block list already in use: %d",
+                 curBlock->successorBlockList.blockListType);
+            dvmAbort();
+        }
+        curBlock->successorBlockList.blockListType = kCatch;
+        dvmInitGrowableList(&curBlock->successorBlockList.blocks, 2);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            BasicBlock *catchBlock = findBlock(cUnit, handler->address,
+                                               /* split */
+                                               false,
+                                               /* create */
+                                               false,
+                                               /* immedPredBlockP */
+                                               NULL);
+
+            SuccessorBlockInfo *successorBlockInfo =
+              (SuccessorBlockInfo *) dvmCompilerNew(sizeof(SuccessorBlockInfo),
+                                                    false);
+            successorBlockInfo->block = catchBlock;
+            successorBlockInfo->key = handler->typeIdx;
+            dvmInsertGrowableList(&curBlock->successorBlockList.blocks,
+                                  (intptr_t) successorBlockInfo);
+            dvmCompilerSetBit(catchBlock->predecessors, curBlock->id);
+        }
+    } else {
+        BasicBlock *ehBlock = dvmCompilerNewBB(kExceptionHandling,
+                                               cUnit->numBlocks++);
+        curBlock->taken = ehBlock;
+        dvmInsertGrowableList(&cUnit->blockList, (intptr_t) ehBlock);
+        ehBlock->startOffset = curOffset;
+        dvmCompilerSetBit(ehBlock->predecessors, curBlock->id);
+    }
+
+    /*
+     * Force the current block to terminate.
+     *
+     * Data may be present before codeEnd, so we need to parse it to know
+     * whether it is code or data.
+     */
+    if (codePtr < codeEnd) {
+        /* Create a fallthrough block for real instructions (incl. OP_NOP) */
+        if (contentIsInsn(codePtr)) {
+            BasicBlock *fallthroughBlock = findBlock(cUnit,
+                                                     curOffset + width,
+                                                     /* split */
+                                                     false,
+                                                     /* create */
+                                                     true,
+                                                     /* immedPredBlockP */
+                                                     NULL);
+            /*
+             * OP_THROW and OP_THROW_VERIFICATION_ERROR are unconditional
+             * branches.
+             */
+            if (insn->dalvikInsn.opcode != OP_THROW_VERIFICATION_ERROR &&
+                insn->dalvikInsn.opcode != OP_THROW) {
+                curBlock->fallThrough = fallthroughBlock;
+                dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+            }
+        }
+    }
+}
+
+/*
+ * Similar to dvmCompileTrace, but the entity processed here is the whole
+ * method.
+ *
+ * TODO: implementation will be revisited when the trace builder can provide
+ * whole-method traces.
+ */
+bool dvmCompileMethod(const Method *method, JitTranslationInfo *info)
+{
+    CompilationUnit cUnit;
+    const DexCode *dexCode = dvmGetMethodCode(method);
+    const u2 *codePtr = dexCode->insns;
+    const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+    int numBlocks = 0;
+    unsigned int curOffset = 0;
+
+    /* Method already compiled */
+    if (dvmJitGetMethodAddr(codePtr)) {
+        info->codeAddress = NULL;
+        return false;
+    }
+
+    memset(&cUnit, 0, sizeof(cUnit));
+    cUnit.method = method;
+
+    cUnit.jitMode = kJitMethod;
+
+    /* Initialize the block list */
+    dvmInitGrowableList(&cUnit.blockList, 4);
+
+    /*
+     * FIXME - PC reconstruction list won't be needed after the codegen routines
+     * are enhanced to true method mode.
+     */
+    /* Initialize the PC reconstruction list */
+    dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
+
+    /* Allocate the bit-vector to track the beginning of basic blocks */
+    BitVector *tryBlockAddr = dvmCompilerAllocBitVector(dexCode->insnsSize,
+                                                        true /* expandable */);
+    cUnit.tryBlockAddr = tryBlockAddr;
+
+    /* Create the default entry and exit blocks and enter them to the list */
+    BasicBlock *entryBlock = dvmCompilerNewBB(kEntryBlock, numBlocks++);
+    BasicBlock *exitBlock = dvmCompilerNewBB(kExitBlock, numBlocks++);
+
+    cUnit.entryBlock = entryBlock;
+    cUnit.exitBlock = exitBlock;
+
+    dvmInsertGrowableList(&cUnit.blockList, (intptr_t) entryBlock);
+    dvmInsertGrowableList(&cUnit.blockList, (intptr_t) exitBlock);
+
+    /* Current block to record parsed instructions */
+    BasicBlock *curBlock = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+    curBlock->startOffset = 0;
+    dvmInsertGrowableList(&cUnit.blockList, (intptr_t) curBlock);
+    entryBlock->fallThrough = curBlock;
+    dvmCompilerSetBit(curBlock->predecessors, entryBlock->id);
+
+    /*
+     * Store back the number of blocks since new blocks may be created of
+     * accessing cUnit.
+     */
+    cUnit.numBlocks = numBlocks;
+
+    /* Identify code range in try blocks and set up the empty catch blocks */
+    processTryCatchBlocks(&cUnit);
+
+    /* Parse all instructions and put them into containing basic blocks */
+    while (codePtr < codeEnd) {
+        MIR *insn = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+        insn->offset = curOffset;
+        int width = parseInsn(codePtr, &insn->dalvikInsn, false);
+        insn->width = width;
+
+        /* Terminate when the data section is seen */
+        if (width == 0)
+            break;
+
+        dvmCompilerAppendMIR(curBlock, insn);
+
+        codePtr += width;
+        int flags = dexGetFlagsFromOpcode(insn->dalvikInsn.opcode);
+
+        if (flags & kInstrCanBranch) {
+            processCanBranch(&cUnit, curBlock, insn, curOffset, width, flags,
+                             codePtr, codeEnd);
+        } else if (flags & kInstrCanReturn) {
+            curBlock->fallThrough = exitBlock;
+            dvmCompilerSetBit(exitBlock->predecessors, curBlock->id);
+            /*
+             * Terminate the current block if there are instructions
+             * afterwards.
+             */
+            if (codePtr < codeEnd) {
+                /*
+                 * Create a fallthrough block for real instructions
+                 * (incl. OP_NOP).
+                 */
+                if (contentIsInsn(codePtr)) {
+                    findBlock(&cUnit, curOffset + width,
+                              /* split */
+                              false,
+                              /* create */
+                              true,
+                              /* immedPredBlockP */
+                              NULL);
+                }
+            }
+        } else if (flags & kInstrCanThrow) {
+            processCanThrow(&cUnit, curBlock, insn, curOffset, width, flags,
+                            tryBlockAddr, codePtr, codeEnd);
+        } else if (flags & kInstrCanSwitch) {
+            processCanSwitch(&cUnit, curBlock, insn, curOffset, width, flags);
+        }
+        curOffset += width;
+        BasicBlock *nextBlock = findBlock(&cUnit, curOffset,
+                                          /* split */
+                                          false,
+                                          /* create */
+                                          false,
+                                          /* immedPredBlockP */
+                                          NULL);
+        if (nextBlock) {
+            /*
+             * The next instruction could be the target of a previously parsed
+             * forward branch so a block is already created. If the current
+             * instruction is not an unconditional branch, connect them through
+             * the fall-through link.
+             */
+            assert(curBlock->fallThrough == NULL ||
+                   curBlock->fallThrough == nextBlock ||
+                   curBlock->fallThrough == exitBlock);
+
+            if ((curBlock->fallThrough == NULL) &&
+                (flags & kInstrCanContinue)) {
+                curBlock->fallThrough = nextBlock;
+                dvmCompilerSetBit(nextBlock->predecessors, curBlock->id);
+            }
+            curBlock = nextBlock;
+        }
+    }
+
+    if (cUnit.printMe) {
+        dvmCompilerDumpCompilationUnit(&cUnit);
+    }
+
+    /* Adjust this value accordingly once inlining is performed */
+    cUnit.numDalvikRegisters = cUnit.method->registersSize;
+
+    /* Verify if all blocks are connected as claimed */
+    /* FIXME - to be disabled in the future */
+    dvmCompilerDataFlowAnalysisDispatcher(&cUnit, verifyPredInfo,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+
+    /* Perform SSA transformation for the whole method */
+    dvmCompilerMethodSSATransformation(&cUnit);
+
+#ifndef ARCH_IA32
+    dvmCompilerInitializeRegAlloc(&cUnit);  // Needs to happen after SSA naming
+
+    /* Allocate Registers using simple local allocation scheme */
+    dvmCompilerLocalRegAlloc(&cUnit);
+#endif
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMethodMIR2LIR(&cUnit);
+
+    // Debugging only
+    //dvmDumpCFG(&cUnit, "/sdcard/cfg/");
+
+    /* Method is not empty */
+    if (cUnit.firstLIRInsn) {
+        /* Convert LIR into machine code. Loop for recoverable retries */
+        do {
+            dvmCompilerAssembleLIR(&cUnit, info);
+            cUnit.assemblerRetries++;
+            if (cUnit.printMe && cUnit.assemblerStatus != kSuccess)
+                ALOGD("Assembler abort #%d on %d",cUnit.assemblerRetries,
+                      cUnit.assemblerStatus);
+        } while (cUnit.assemblerStatus == kRetryAll);
+
+        if (cUnit.printMe) {
+            dvmCompilerCodegenDump(&cUnit);
+        }
+
+        if (info->codeAddress) {
+            dvmJitSetCodeAddr(dexCode->insns, info->codeAddress,
+                              info->instructionSet, true, 0);
+            /*
+             * Clear the codeAddress for the enclosing trace to reuse the info
+             */
+            info->codeAddress = NULL;
+        }
+    }
+
+    return false;
+}
+
+/* Extending the trace by crawling the code from curBlock */
+static bool exhaustTrace(CompilationUnit *cUnit, BasicBlock *curBlock)
+{
+    unsigned int curOffset = curBlock->startOffset;
+    const u2 *codePtr = cUnit->method->insns + curOffset;
+
+    if (curBlock->visited == true) return false;
+
+    curBlock->visited = true;
+
+    if (curBlock->blockType == kEntryBlock ||
+        curBlock->blockType == kExitBlock) {
+        return false;
+    }
+
+    /*
+     * Block has been parsed - check the taken/fallThrough in case it is a split
+     * block.
+     */
+    if (curBlock->firstMIRInsn != NULL) {
+          bool changed = false;
+          if (curBlock->taken)
+              changed |= exhaustTrace(cUnit, curBlock->taken);
+          if (curBlock->fallThrough)
+              changed |= exhaustTrace(cUnit, curBlock->fallThrough);
+          return changed;
+    }
+    while (true) {
+        MIR *insn = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+        insn->offset = curOffset;
+        int width = parseInsn(codePtr, &insn->dalvikInsn, false);
+        insn->width = width;
+
+        /* Terminate when the data section is seen */
+        if (width == 0)
+            break;
+
+        dvmCompilerAppendMIR(curBlock, insn);
+
+        codePtr += width;
+        int flags = dexGetFlagsFromOpcode(insn->dalvikInsn.opcode);
+
+        /* Stop extending the trace after seeing these instructions */
+        if (flags & (kInstrCanReturn | kInstrCanSwitch | kInstrInvoke)) {
+            curBlock->fallThrough = cUnit->exitBlock;
+            dvmCompilerSetBit(cUnit->exitBlock->predecessors, curBlock->id);
+            break;
+        } else if (flags & kInstrCanBranch) {
+            processCanBranch(cUnit, curBlock, insn, curOffset, width, flags,
+                             codePtr, NULL);
+            if (curBlock->taken) {
+                exhaustTrace(cUnit, curBlock->taken);
+            }
+            if (curBlock->fallThrough) {
+                exhaustTrace(cUnit, curBlock->fallThrough);
+            }
+            break;
+        }
+        curOffset += width;
+        BasicBlock *nextBlock = findBlock(cUnit, curOffset,
+                                          /* split */
+                                          false,
+                                          /* create */
+                                          false,
+                                          /* immedPredBlockP */
+                                          NULL);
+        if (nextBlock) {
+            /*
+             * The next instruction could be the target of a previously parsed
+             * forward branch so a block is already created. If the current
+             * instruction is not an unconditional branch, connect them through
+             * the fall-through link.
+             */
+            assert(curBlock->fallThrough == NULL ||
+                   curBlock->fallThrough == nextBlock ||
+                   curBlock->fallThrough == cUnit->exitBlock);
+
+            if ((curBlock->fallThrough == NULL) &&
+                (flags & kInstrCanContinue)) {
+                curBlock->needFallThroughBranch = true;
+                curBlock->fallThrough = nextBlock;
+                dvmCompilerSetBit(nextBlock->predecessors, curBlock->id);
+            }
+            /* Block has been visited - no more parsing needed */
+            if (nextBlock->visited == true) {
+                return true;
+            }
+            curBlock = nextBlock;
+        }
+    }
+    return true;
+}
+
+/* Compile a loop */
+static bool compileLoop(CompilationUnit *cUnit, unsigned int startOffset,
+                        JitTraceDescription *desc, int numMaxInsts,
+                        JitTranslationInfo *info, jmp_buf *bailPtr,
+                        int optHints)
+{
+    int numBlocks = 0;
+    unsigned int curOffset = startOffset;
+    bool changed;
+    BasicBlock *bb;
+#if defined(WITH_JIT_TUNING)
+    CompilerMethodStats *methodStats;
+#endif
+
+    cUnit->jitMode = kJitLoop;
+
+    /* Initialize the block list */
+    dvmInitGrowableList(&cUnit->blockList, 4);
+
+    /* Initialize the PC reconstruction list */
+    dvmInitGrowableList(&cUnit->pcReconstructionList, 8);
+
+    /* Create the default entry and exit blocks and enter them to the list */
+    BasicBlock *entryBlock = dvmCompilerNewBB(kEntryBlock, numBlocks++);
+    entryBlock->startOffset = curOffset;
+    BasicBlock *exitBlock = dvmCompilerNewBB(kExitBlock, numBlocks++);
+
+    cUnit->entryBlock = entryBlock;
+    cUnit->exitBlock = exitBlock;
+
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) entryBlock);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) exitBlock);
+
+    /* Current block to record parsed instructions */
+    BasicBlock *curBlock = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+    curBlock->startOffset = curOffset;
+
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) curBlock);
+    entryBlock->fallThrough = curBlock;
+    dvmCompilerSetBit(curBlock->predecessors, entryBlock->id);
+
+    /*
+     * Store back the number of blocks since new blocks may be created of
+     * accessing cUnit.
+     */
+    cUnit->numBlocks = numBlocks;
+
+    do {
+        dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                              dvmCompilerClearVisitedFlag,
+                                              kAllNodes,
+                                              false /* isIterative */);
+        changed = exhaustTrace(cUnit, curBlock);
+    } while (changed);
+
+    /* Backward chaining block */
+    bb = dvmCompilerNewBB(kChainingCellBackwardBranch, cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+    cUnit->backChainBlock = bb;
+
+    /* A special block to host PC reconstruction code */
+    bb = dvmCompilerNewBB(kPCReconstruction, cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+
+    /* And one final block that publishes the PC and raises the exception */
+    bb = dvmCompilerNewBB(kExceptionHandling, cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+    cUnit->puntBlock = bb;
+
+    cUnit->numDalvikRegisters = cUnit->method->registersSize;
+
+    /* Verify if all blocks are connected as claimed */
+    /* FIXME - to be disabled in the future */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, verifyPredInfo,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+
+    /* Try to identify a loop */
+    if (!dvmCompilerBuildLoop(cUnit))
+        goto bail;
+
+    dvmCompilerLoopOpt(cUnit);
+
+    /*
+     * Change the backward branch to the backward chaining cell after dataflow
+     * analsys/optimizations are done.
+     */
+    dvmCompilerInsertBackwardChaining(cUnit);
+
+#if defined(ARCH_IA32)
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(cUnit, info);
+#else
+    dvmCompilerInitializeRegAlloc(cUnit);
+
+    /* Allocate Registers using simple local allocation scheme */
+    dvmCompilerLocalRegAlloc(cUnit);
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(cUnit);
+#endif
+
+    /* Loop contains never executed blocks / heavy instructions */
+    if (cUnit->quitLoopMode) {
+        if (cUnit->printMe || gDvmJit.receivedSIGUSR2) {
+            ALOGD("Loop trace @ offset %04x aborted due to unresolved code info",
+                 cUnit->entryBlock->startOffset);
+        }
+        goto bail;
+    }
+
+    /* Convert LIR into machine code. Loop for recoverable retries */
+    do {
+        dvmCompilerAssembleLIR(cUnit, info);
+        cUnit->assemblerRetries++;
+        if (cUnit->printMe && cUnit->assemblerStatus != kSuccess)
+            ALOGD("Assembler abort #%d on %d", cUnit->assemblerRetries,
+                  cUnit->assemblerStatus);
+    } while (cUnit->assemblerStatus == kRetryAll);
+
+    /* Loop is too big - bail out */
+    if (cUnit->assemblerStatus == kRetryHalve) {
+        goto bail;
+    }
+
+    if (cUnit->printMe || gDvmJit.receivedSIGUSR2) {
+        ALOGD("Loop trace @ offset %04x", cUnit->entryBlock->startOffset);
+        dvmCompilerCodegenDump(cUnit);
+    }
+
+    /*
+     * If this trace uses class objects as constants,
+     * dvmJitInstallClassObjectPointers will switch the thread state
+     * to running and look up the class pointers using the descriptor/loader
+     * tuple stored in the callsite info structure. We need to make this window
+     * as short as possible since it is blocking GC.
+     */
+    if (cUnit->hasClassLiterals && info->codeAddress) {
+        dvmJitInstallClassObjectPointers(cUnit, (char *) info->codeAddress);
+    }
+
+    /*
+     * Since callsiteinfo is allocated from the arena, delay the reset until
+     * class pointers are resolved.
+     */
+    dvmCompilerArenaReset();
+
+    assert(cUnit->assemblerStatus == kSuccess);
+#if defined(WITH_JIT_TUNING)
+    /* Locate the entry to store compilation statistics for this method */
+    methodStats = dvmCompilerAnalyzeMethodBody(desc->method, false);
+    methodStats->nativeSize += cUnit->totalSize;
+#endif
+    return info->codeAddress != NULL;
+
+bail:
+    /* Retry the original trace with JIT_OPT_NO_LOOP disabled */
+    dvmCompilerArenaReset();
+    return dvmCompileTrace(desc, numMaxInsts, info, bailPtr,
+                           optHints | JIT_OPT_NO_LOOP);
+}
+
+static bool searchClassTablePrefix(const Method* method) {
+    if (gDvmJit.classTable == NULL) {
+        return false;
+    }
+    HashIter iter;
+    HashTable* pTab = gDvmJit.classTable;
+    for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        const char* str = (const char*) dvmHashIterData(&iter);
+        if (strncmp(method->clazz->descriptor, str, strlen(str)) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Main entry point to start trace compilation. Basic blocks are constructed
+ * first and they will be passed to the codegen routines to convert Dalvik
+ * bytecode into machine code.
+ */
+bool dvmCompileTrace(JitTraceDescription *desc, int numMaxInsts,
+                     JitTranslationInfo *info, jmp_buf *bailPtr,
+                     int optHints)
+{
+    const DexCode *dexCode = dvmGetMethodCode(desc->method);
+    const JitTraceRun* currRun = &desc->trace[0];
+    unsigned int curOffset = currRun->info.frag.startOffset;
+    unsigned int startOffset = curOffset;
+    unsigned int numInsts = currRun->info.frag.numInsts;
+    const u2 *codePtr = dexCode->insns + curOffset;
+    int traceSize = 0;  // # of half-words
+    const u2 *startCodePtr = codePtr;
+    BasicBlock *curBB, *entryCodeBB;
+    int numBlocks = 0;
+    static int compilationId;
+    CompilationUnit cUnit;
+    GrowableList *blockList;
+#if defined(WITH_JIT_TUNING)
+    CompilerMethodStats *methodStats;
+#endif
+
+    /* If we've already compiled this trace, just return success */
+    if (dvmJitGetTraceAddr(startCodePtr) && !info->discardResult) {
+        /*
+         * Make sure the codeAddress is NULL so that it won't clobber the
+         * existing entry.
+         */
+        info->codeAddress = NULL;
+        return true;
+    }
+
+    /* If the work order is stale, discard it */
+    if (info->cacheVersion != gDvmJit.cacheVersion) {
+        return false;
+    }
+
+    compilationId++;
+    memset(&cUnit, 0, sizeof(CompilationUnit));
+
+#if defined(WITH_JIT_TUNING)
+    /* Locate the entry to store compilation statistics for this method */
+    methodStats = dvmCompilerAnalyzeMethodBody(desc->method, false);
+#endif
+
+    /* Set the recover buffer pointer */
+    cUnit.bailPtr = bailPtr;
+
+    /* Initialize the printMe flag */
+    cUnit.printMe = gDvmJit.printMe;
+
+    /* Setup the method */
+    cUnit.method = desc->method;
+
+    /* Store the trace descriptor and set the initial mode */
+    cUnit.traceDesc = desc;
+    cUnit.jitMode = kJitTrace;
+
+    /* Initialize the PC reconstruction list */
+    dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
+
+    /* Initialize the basic block list */
+    blockList = &cUnit.blockList;
+    dvmInitGrowableList(blockList, 8);
+
+    /* Identify traces that we don't want to compile */
+    if (gDvmJit.classTable) {
+        bool classFound = searchClassTablePrefix(desc->method);
+        if (gDvmJit.classTable && gDvmJit.includeSelectedMethod != classFound) {
+            return false;
+        }
+    }
+    if (gDvmJit.methodTable) {
+        int len = strlen(desc->method->clazz->descriptor) +
+                  strlen(desc->method->name) + 1;
+        char *fullSignature = (char *)dvmCompilerNew(len, true);
+        strcpy(fullSignature, desc->method->clazz->descriptor);
+        strcat(fullSignature, desc->method->name);
+
+        int hashValue = dvmComputeUtf8Hash(fullSignature);
+
+        /*
+         * Doing three levels of screening to see whether we want to skip
+         * compiling this method
+         */
+
+        /* First, check the full "class;method" signature */
+        bool methodFound =
+            dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               fullSignature, (HashCompareFunc) strcmp,
+                               false) !=
+            NULL;
+
+        /* Full signature not found - check the enclosing class */
+        if (methodFound == false) {
+            int hashValue = dvmComputeUtf8Hash(desc->method->clazz->descriptor);
+            methodFound =
+                dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               (char *) desc->method->clazz->descriptor,
+                               (HashCompareFunc) strcmp, false) !=
+                NULL;
+            /* Enclosing class not found - check the method name */
+            if (methodFound == false) {
+                int hashValue = dvmComputeUtf8Hash(desc->method->name);
+                methodFound =
+                    dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                                   (char *) desc->method->name,
+                                   (HashCompareFunc) strcmp, false) !=
+                    NULL;
+
+                /*
+                 * Debug by call-graph is enabled. Check if the debug list
+                 * covers any methods on the VM stack.
+                 */
+                if (methodFound == false && gDvmJit.checkCallGraph == true) {
+                    methodFound =
+                        filterMethodByCallGraph(info->requestingThread,
+                                                desc->method->name);
+                }
+            }
+        }
+
+        /*
+         * Under the following conditions, the trace will be *conservatively*
+         * compiled by only containing single-step instructions to and from the
+         * interpreter.
+         * 1) If includeSelectedMethod == false, the method matches the full or
+         *    partial signature stored in the hash table.
+         *
+         * 2) If includeSelectedMethod == true, the method does not match the
+         *    full and partial signature stored in the hash table.
+         */
+        if (gDvmJit.methodTable && gDvmJit.includeSelectedMethod != methodFound) {
+#ifdef ARCH_IA32
+            return false;
+#else
+            cUnit.allSingleStep = true;
+#endif
+        } else {
+            /* Compile the trace as normal */
+
+            /* Print the method we cherry picked */
+            if (gDvmJit.includeSelectedMethod == true) {
+                cUnit.printMe = true;
+            }
+        }
+    }
+
+    // Each pair is a range, check whether curOffset falls into a range.
+    bool includeOffset = (gDvmJit.num_entries_pcTable < 2);
+    for (int pcOff = 0; pcOff < gDvmJit.num_entries_pcTable; ) {
+        if (pcOff+1 >= gDvmJit.num_entries_pcTable) {
+          break;
+        }
+        if (curOffset >= gDvmJit.pcTable[pcOff] && curOffset <= gDvmJit.pcTable[pcOff+1]) {
+            includeOffset = true;
+            break;
+        }
+        pcOff += 2;
+    }
+    if (!includeOffset) {
+        return false;
+    }
+
+    /* Allocate the entry block */
+    curBB = dvmCompilerNewBB(kEntryBlock, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) curBB);
+    curBB->startOffset = curOffset;
+
+    entryCodeBB = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) entryCodeBB);
+    entryCodeBB->startOffset = curOffset;
+    curBB->fallThrough = entryCodeBB;
+    curBB = entryCodeBB;
+
+    if (cUnit.printMe) {
+        ALOGD("--------\nCompiler: Building trace for %s, offset %#x",
+             desc->method->name, curOffset);
+    }
+
+    /*
+     * Analyze the trace descriptor and include up to the maximal number
+     * of Dalvik instructions into the IR.
+     */
+    while (1) {
+        MIR *insn;
+        int width;
+        insn = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        insn->offset = curOffset;
+        width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe);
+
+        /* The trace should never incude instruction data */
+        assert(width);
+        insn->width = width;
+        traceSize += width;
+        dvmCompilerAppendMIR(curBB, insn);
+        cUnit.numInsts++;
+
+        int flags = dexGetFlagsFromOpcode(insn->dalvikInsn.opcode);
+
+        if (flags & kInstrInvoke) {
+            const Method *calleeMethod = (const Method *)
+                currRun[JIT_TRACE_CUR_METHOD].info.meta;
+            assert(numInsts == 1);
+            CallsiteInfo *callsiteInfo =
+                (CallsiteInfo *)dvmCompilerNew(sizeof(CallsiteInfo), true);
+            callsiteInfo->classDescriptor = (const char *)
+                currRun[JIT_TRACE_CLASS_DESC].info.meta;
+            callsiteInfo->classLoader = (Object *)
+                currRun[JIT_TRACE_CLASS_LOADER].info.meta;
+            callsiteInfo->method = calleeMethod;
+            insn->meta.callsiteInfo = callsiteInfo;
+        }
+
+        /* Instruction limit reached - terminate the trace here */
+        if (cUnit.numInsts >= numMaxInsts) {
+            break;
+        }
+        if (--numInsts == 0) {
+            if (currRun->info.frag.runEnd) {
+                break;
+            } else {
+                /* Advance to the next trace description (ie non-meta info) */
+                do {
+                    currRun++;
+                } while (!currRun->isCode);
+
+                /* Dummy end-of-run marker seen */
+                if (currRun->info.frag.numInsts == 0) {
+                    break;
+                }
+
+                curBB = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+                dvmInsertGrowableList(blockList, (intptr_t) curBB);
+                curOffset = currRun->info.frag.startOffset;
+                numInsts = currRun->info.frag.numInsts;
+                curBB->startOffset = curOffset;
+                codePtr = dexCode->insns + curOffset;
+            }
+        } else {
+            curOffset += width;
+            codePtr += width;
+        }
+    }
+
+#if defined(WITH_JIT_TUNING)
+    /* Convert # of half-word to bytes */
+    methodStats->compiledDalvikSize += traceSize * 2;
+#endif
+
+    /*
+     * Now scan basic blocks containing real code to connect the
+     * taken/fallthrough links. Also create chaining cells for code not included
+     * in the trace.
+     */
+    size_t blockId;
+    for (blockId = 0; blockId < blockList->numUsed; blockId++) {
+        curBB = (BasicBlock *) dvmGrowableListGetElement(blockList, blockId);
+        MIR *lastInsn = curBB->lastMIRInsn;
+        /* Skip empty blocks */
+        if (lastInsn == NULL) {
+            continue;
+        }
+        curOffset = lastInsn->offset;
+        unsigned int targetOffset = curOffset;
+        unsigned int fallThroughOffset = curOffset + lastInsn->width;
+        bool isInvoke = false;
+        const Method *callee = NULL;
+
+        findBlockBoundary(desc->method, curBB->lastMIRInsn, curOffset,
+                          &targetOffset, &isInvoke, &callee);
+
+        /* Link the taken and fallthrough blocks */
+        BasicBlock *searchBB;
+
+        int flags = dexGetFlagsFromOpcode(lastInsn->dalvikInsn.opcode);
+
+        if (flags & kInstrInvoke) {
+            cUnit.hasInvoke = true;
+        }
+
+        /* Backward branch seen */
+        if (isInvoke == false &&
+            (flags & kInstrCanBranch) != 0 &&
+            targetOffset < curOffset &&
+            (optHints & JIT_OPT_NO_LOOP) == 0) {
+            dvmCompilerArenaReset();
+            return compileLoop(&cUnit, startOffset, desc, numMaxInsts,
+                               info, bailPtr, optHints);
+        }
+
+        /* No backward branch in the trace - start searching the next BB */
+        size_t searchBlockId;
+        for (searchBlockId = blockId+1; searchBlockId < blockList->numUsed;
+             searchBlockId++) {
+            searchBB = (BasicBlock *) dvmGrowableListGetElement(blockList,
+                                                                searchBlockId);
+            if (targetOffset == searchBB->startOffset) {
+                curBB->taken = searchBB;
+                dvmCompilerSetBit(searchBB->predecessors, curBB->id);
+            }
+            if (fallThroughOffset == searchBB->startOffset) {
+                curBB->fallThrough = searchBB;
+                dvmCompilerSetBit(searchBB->predecessors, curBB->id);
+
+                /*
+                 * Fallthrough block of an invoke instruction needs to be
+                 * aligned to 4-byte boundary (alignment instruction to be
+                 * inserted later.
+                 */
+                if (flags & kInstrInvoke) {
+                    searchBB->isFallThroughFromInvoke = true;
+                }
+            }
+        }
+
+        /*
+         * Some blocks are ended by non-control-flow-change instructions,
+         * currently only due to trace length constraint. In this case we need
+         * to generate an explicit branch at the end of the block to jump to
+         * the chaining cell.
+         */
+        curBB->needFallThroughBranch =
+            ((flags & (kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+                       kInstrInvoke)) == 0);
+        if (lastInsn->dalvikInsn.opcode == OP_PACKED_SWITCH ||
+            lastInsn->dalvikInsn.opcode == OP_SPARSE_SWITCH) {
+            int i;
+            const u2 *switchData = desc->method->insns + lastInsn->offset +
+                             lastInsn->dalvikInsn.vB;
+            int size = switchData[1];
+            int maxChains = MIN(size, MAX_CHAINED_SWITCH_CASES);
+
+            /*
+             * Generate the landing pad for cases whose ranks are higher than
+             * MAX_CHAINED_SWITCH_CASES. The code will re-enter the interpreter
+             * through the NoChain point.
+             */
+            if (maxChains != size) {
+                cUnit.switchOverflowPad =
+                    desc->method->insns + lastInsn->offset;
+            }
+
+            s4 *targets = (s4 *) (switchData + 2 +
+                    (lastInsn->dalvikInsn.opcode == OP_PACKED_SWITCH ?
+                     2 : size * 2));
+
+            /* One chaining cell for the first MAX_CHAINED_SWITCH_CASES cases */
+            for (i = 0; i < maxChains; i++) {
+                BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal,
+                                                         numBlocks++);
+                dvmInsertGrowableList(blockList, (intptr_t) caseChain);
+                caseChain->startOffset = lastInsn->offset + targets[i];
+            }
+
+            /* One more chaining cell for the default case */
+            BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal,
+                                                     numBlocks++);
+            dvmInsertGrowableList(blockList, (intptr_t) caseChain);
+            caseChain->startOffset = lastInsn->offset + lastInsn->width;
+        /* Fallthrough block not included in the trace */
+        } else if (!isUnconditionalBranch(lastInsn) &&
+                   curBB->fallThrough == NULL) {
+            BasicBlock *fallThroughBB;
+            /*
+             * If the chaining cell is after an invoke or
+             * instruction that cannot change the control flow, request a hot
+             * chaining cell.
+             */
+            if (isInvoke || curBB->needFallThroughBranch) {
+                fallThroughBB = dvmCompilerNewBB(kChainingCellHot, numBlocks++);
+            } else {
+                fallThroughBB = dvmCompilerNewBB(kChainingCellNormal,
+                                                 numBlocks++);
+            }
+            dvmInsertGrowableList(blockList, (intptr_t) fallThroughBB);
+            fallThroughBB->startOffset = fallThroughOffset;
+            curBB->fallThrough = fallThroughBB;
+            dvmCompilerSetBit(fallThroughBB->predecessors, curBB->id);
+        }
+        /* Target block not included in the trace */
+        if (curBB->taken == NULL &&
+            (isGoto(lastInsn) || isInvoke ||
+            (targetOffset != UNKNOWN_TARGET && targetOffset != curOffset))) {
+            BasicBlock *newBB = NULL;
+            if (isInvoke) {
+                /* Monomorphic callee */
+                if (callee) {
+                    /* JNI call doesn't need a chaining cell */
+                    if (!dvmIsNativeMethod(callee)) {
+                        newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton,
+                                                 numBlocks++);
+                        newBB->startOffset = 0;
+                        newBB->containingMethod = callee;
+                    }
+                /* Will resolve at runtime */
+                } else {
+                    newBB = dvmCompilerNewBB(kChainingCellInvokePredicted,
+                                             numBlocks++);
+                    newBB->startOffset = 0;
+                }
+            /* For unconditional branches, request a hot chaining cell */
+            } else {
+#if !defined(WITH_SELF_VERIFICATION)
+                newBB = dvmCompilerNewBB(dexIsGoto(flags) ?
+                                                  kChainingCellHot :
+                                                  kChainingCellNormal,
+                                         numBlocks++);
+                newBB->startOffset = targetOffset;
+#else
+                /* Handle branches that branch back into the block */
+                if (targetOffset >= curBB->firstMIRInsn->offset &&
+                    targetOffset <= curBB->lastMIRInsn->offset) {
+                    newBB = dvmCompilerNewBB(kChainingCellBackwardBranch,
+                                             numBlocks++);
+                } else {
+                    newBB = dvmCompilerNewBB(dexIsGoto(flags) ?
+                                                      kChainingCellHot :
+                                                      kChainingCellNormal,
+                                             numBlocks++);
+                }
+                newBB->startOffset = targetOffset;
+#endif
+            }
+            if (newBB) {
+                curBB->taken = newBB;
+                dvmCompilerSetBit(newBB->predecessors, curBB->id);
+                dvmInsertGrowableList(blockList, (intptr_t) newBB);
+            }
+        }
+    }
+
+    /* Now create a special block to host PC reconstruction code */
+    curBB = dvmCompilerNewBB(kPCReconstruction, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) curBB);
+
+    /* And one final block that publishes the PC and raise the exception */
+    curBB = dvmCompilerNewBB(kExceptionHandling, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) curBB);
+    cUnit.puntBlock = curBB;
+
+    if (cUnit.printMe) {
+        char* signature =
+            dexProtoCopyMethodDescriptor(&desc->method->prototype);
+        ALOGD("TRACEINFO (%d): 0x%08x %s%s.%s %#x %d of %d, %d blocks",
+            compilationId,
+            (intptr_t) desc->method->insns,
+            desc->method->clazz->descriptor,
+            desc->method->name,
+            signature,
+            desc->trace[0].info.frag.startOffset,
+            traceSize,
+            dexCode->insnsSize,
+            numBlocks);
+        free(signature);
+    }
+
+    cUnit.numBlocks = numBlocks;
+
+    /* Set the instruction set to use (NOTE: later components may change it) */
+    cUnit.instructionSet = dvmCompilerInstructionSet();
+
+    /* Inline transformation @ the MIR level */
+    if (cUnit.hasInvoke && !(gDvmJit.disableOpt & (1 << kMethodInlining))) {
+        dvmCompilerInlineMIR(&cUnit, info);
+    }
+
+    cUnit.numDalvikRegisters = cUnit.method->registersSize;
+
+    /* Preparation for SSA conversion */
+    dvmInitializeSSAConversion(&cUnit);
+
+    dvmCompilerNonLoopAnalysis(&cUnit);
+
+#ifndef ARCH_IA32
+    dvmCompilerInitializeRegAlloc(&cUnit);  // Needs to happen after SSA naming
+#endif
+
+    if (cUnit.printMe) {
+        dvmCompilerDumpCompilationUnit(&cUnit);
+    }
+
+#ifndef ARCH_IA32
+    /* Allocate Registers using simple local allocation scheme */
+    dvmCompilerLocalRegAlloc(&cUnit);
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(&cUnit);
+#else /* ARCH_IA32 */
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(&cUnit, info);
+#endif
+
+    /* Convert LIR into machine code. Loop for recoverable retries */
+    do {
+        dvmCompilerAssembleLIR(&cUnit, info);
+        cUnit.assemblerRetries++;
+        if (cUnit.printMe && cUnit.assemblerStatus != kSuccess)
+            ALOGD("Assembler abort #%d on %d",cUnit.assemblerRetries,
+                  cUnit.assemblerStatus);
+    } while (cUnit.assemblerStatus == kRetryAll);
+
+    if (cUnit.printMe) {
+        ALOGD("Trace Dalvik PC: %p", startCodePtr);
+        dvmCompilerCodegenDump(&cUnit);
+        ALOGD("End %s%s, %d Dalvik instructions",
+             desc->method->clazz->descriptor, desc->method->name,
+             cUnit.numInsts);
+    }
+
+    if (cUnit.assemblerStatus == kRetryHalve) {
+        /* Reset the compiler resource pool before retry */
+        dvmCompilerArenaReset();
+
+        /* Halve the instruction count and start from the top */
+        return dvmCompileTrace(desc, cUnit.numInsts / 2, info, bailPtr,
+                               optHints);
+    }
+
+    /*
+     * If this trace uses class objects as constants,
+     * dvmJitInstallClassObjectPointers will switch the thread state
+     * to running and look up the class pointers using the descriptor/loader
+     * tuple stored in the callsite info structure. We need to make this window
+     * as short as possible since it is blocking GC.
+     */
+    if (cUnit.hasClassLiterals && info->codeAddress) {
+        dvmJitInstallClassObjectPointers(&cUnit, (char *) info->codeAddress);
+    }
+
+    /*
+     * Since callsiteinfo is allocated from the arena, delay the reset until
+     * class pointers are resolved.
+     */
+    dvmCompilerArenaReset();
+
+    assert(cUnit.assemblerStatus == kSuccess);
+#if defined(WITH_JIT_TUNING)
+    methodStats->nativeSize += cUnit.totalSize;
+#endif
+
+    return info->codeAddress != NULL;
+}
diff --git a/vm/compiler/InlineTransformation.cpp b/vm/compiler/InlineTransformation.cpp
new file mode 100644
index 0000000..650340c
--- /dev/null
+++ b/vm/compiler/InlineTransformation.cpp
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "libdex/DexOpcodes.h"
+
+/* Convert the reg id from the callee to the original id passed by the caller */
+static inline u4 convertRegId(const DecodedInstruction *invoke,
+                              const Method *calleeMethod,
+                              int calleeRegId, bool isRange)
+{
+    /* The order in the original arg passing list */
+    int rank = calleeRegId -
+               (calleeMethod->registersSize - calleeMethod->insSize);
+    assert(rank >= 0);
+    if (!isRange) {
+        return invoke->arg[rank];
+    } else {
+        return invoke->vC + rank;
+    }
+}
+
+static bool inlineGetter(CompilationUnit *cUnit,
+                         const Method *calleeMethod,
+                         MIR *invokeMIR,
+                         BasicBlock *invokeBB,
+                         bool isPredicted,
+                         bool isRange)
+{
+    BasicBlock *moveResultBB = invokeBB->fallThrough;
+    MIR *moveResultMIR = moveResultBB->firstMIRInsn;
+    MIR *newGetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+    DecodedInstruction getterInsn;
+
+    /*
+     * Not all getter instructions have vC but vC will be read by
+     * dvmCompilerGetDalvikDisassembly unconditionally.
+     * Initialize it here to get Valgrind happy.
+     */
+    getterInsn.vC = 0;
+
+    dexDecodeInstruction(calleeMethod->insns, &getterInsn);
+
+    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
+        return false;
+
+    /*
+     * Some getters (especially invoked through interface) are not followed
+     * by a move result.
+     */
+    if ((moveResultMIR == NULL) ||
+        (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT &&
+         moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT &&
+         moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) {
+        return false;
+    }
+
+    int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode];
+
+    /* Expecting vA to be the destination register */
+    if (dfFlags & (DF_UA | DF_UA_WIDE)) {
+        ALOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode);
+        dvmAbort();
+    }
+
+    if (dfFlags & DF_UB) {
+        getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     getterInsn.vB, isRange);
+    }
+
+    if (dfFlags & DF_UC) {
+        getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     getterInsn.vC, isRange);
+    }
+
+    getterInsn.vA = moveResultMIR->dalvikInsn.vA;
+
+    /* Now setup the Dalvik instruction with converted src/dst registers */
+    newGetterMIR->dalvikInsn = getterInsn;
+
+    newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode);
+
+    newGetterMIR->OptimizationFlags |= MIR_CALLEE;
+
+    /*
+     * If the getter instruction is about to raise any exception, punt to the
+     * interpreter and re-execute the invoke.
+     */
+    newGetterMIR->offset = invokeMIR->offset;
+
+    newGetterMIR->meta.calleeMethod = calleeMethod;
+
+    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
+
+    if (isPredicted) {
+        MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        *invokeMIRSlow = *invokeMIR;
+        invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
+
+        /* Use vC to denote the first argument (ie this) */
+        if (!isRange) {
+            invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
+        }
+
+        moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED;
+
+        dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow);
+        invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokePolyGetterInlined++;
+#endif
+    } else {
+        invokeMIR->OptimizationFlags |= MIR_INLINED;
+        moveResultMIR->OptimizationFlags |= MIR_INLINED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonoGetterInlined++;
+#endif
+    }
+
+    return true;
+}
+
+static bool inlineSetter(CompilationUnit *cUnit,
+                         const Method *calleeMethod,
+                         MIR *invokeMIR,
+                         BasicBlock *invokeBB,
+                         bool isPredicted,
+                         bool isRange)
+{
+    MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+    DecodedInstruction setterInsn;
+
+    /*
+     * Not all setter instructions have vC but vC will be read by
+     * dvmCompilerGetDalvikDisassembly unconditionally.
+     * Initialize it here to get Valgrind happy.
+     */
+    setterInsn.vC = 0;
+
+    dexDecodeInstruction(calleeMethod->insns, &setterInsn);
+
+    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
+        return false;
+
+    int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode];
+
+    if (dfFlags & (DF_UA | DF_UA_WIDE)) {
+        setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     setterInsn.vA, isRange);
+
+    }
+
+    if (dfFlags & DF_UB) {
+        setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     setterInsn.vB, isRange);
+
+    }
+
+    if (dfFlags & DF_UC) {
+        setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     setterInsn.vC, isRange);
+    }
+
+    /* Now setup the Dalvik instruction with converted src/dst registers */
+    newSetterMIR->dalvikInsn = setterInsn;
+
+    newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode);
+
+    newSetterMIR->OptimizationFlags |= MIR_CALLEE;
+
+    /*
+     * If the setter instruction is about to raise any exception, punt to the
+     * interpreter and re-execute the invoke.
+     */
+    newSetterMIR->offset = invokeMIR->offset;
+
+    newSetterMIR->meta.calleeMethod = calleeMethod;
+
+    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
+
+    if (isPredicted) {
+        MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        *invokeMIRSlow = *invokeMIR;
+        invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
+
+        /* Use vC to denote the first argument (ie this) */
+        if (!isRange) {
+            invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
+        }
+
+        dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow);
+        invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokePolySetterInlined++;
+#endif
+    } else {
+        /*
+         * The invoke becomes no-op so it needs an explicit branch to jump to
+         * the chaining cell.
+         */
+        invokeBB->needFallThroughBranch = true;
+        invokeMIR->OptimizationFlags |= MIR_INLINED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonoSetterInlined++;
+#endif
+    }
+
+    return true;
+}
+
+static bool tryInlineSingletonCallsite(CompilationUnit *cUnit,
+                                       const Method *calleeMethod,
+                                       MIR *invokeMIR,
+                                       BasicBlock *invokeBB,
+                                       bool isRange)
+{
+    /* Not a Java method */
+    if (dvmIsNativeMethod(calleeMethod)) return false;
+
+    CompilerMethodStats *methodStats =
+        dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+
+    /* Empty callee - do nothing */
+    if (methodStats->attributes & METHOD_IS_EMPTY) {
+        /* The original invoke instruction is effectively turned into NOP */
+        invokeMIR->OptimizationFlags |= MIR_INLINED;
+        /*
+         * Need to insert an explicit branch to catch the falling knife (into
+         * the PC reconstruction or chaining cell).
+         */
+        invokeBB->needFallThroughBranch = true;
+        return true;
+    }
+
+    if (methodStats->attributes & METHOD_IS_GETTER) {
+        return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
+                            isRange);
+    } else if (methodStats->attributes & METHOD_IS_SETTER) {
+        return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
+                            isRange);
+    }
+    return false;
+}
+
+static bool inlineEmptyVirtualCallee(CompilationUnit *cUnit,
+                                     const Method *calleeMethod,
+                                     MIR *invokeMIR,
+                                     BasicBlock *invokeBB)
+{
+    MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+    *invokeMIRSlow = *invokeMIR;
+    invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
+
+    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
+    invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+    return true;
+}
+
+static bool tryInlineVirtualCallsite(CompilationUnit *cUnit,
+                                     const Method *calleeMethod,
+                                     MIR *invokeMIR,
+                                     BasicBlock *invokeBB,
+                                     bool isRange)
+{
+    /* Not a Java method */
+    if (dvmIsNativeMethod(calleeMethod)) return false;
+
+    CompilerMethodStats *methodStats =
+        dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+
+    /* Empty callee - do nothing by checking the clazz pointer */
+    if (methodStats->attributes & METHOD_IS_EMPTY) {
+        return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR,
+                                        invokeBB);
+    }
+
+    if (methodStats->attributes & METHOD_IS_GETTER) {
+        return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
+                            isRange);
+    } else if (methodStats->attributes & METHOD_IS_SETTER) {
+        return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
+                            isRange);
+    }
+    return false;
+}
+
+
+void dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    bool isRange = false;
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+    /*
+     * Analyze the basic block containing an invoke to see if it can be inlined
+     */
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->blockType != kDalvikByteCode)
+            continue;
+        MIR *lastMIRInsn = bb->lastMIRInsn;
+        Opcode opcode = lastMIRInsn->dalvikInsn.opcode;
+        int flags = (int)dexGetFlagsFromOpcode(opcode);
+
+        /* No invoke - continue */
+        if ((flags & kInstrInvoke) == 0)
+            continue;
+
+        /* Disable inlining when doing method tracing */
+        if (gDvmJit.methodTraceSupport)
+            continue;
+
+        /*
+         * If the invoke itself is selected for single stepping, don't bother
+         * to inline it.
+         */
+        if (SINGLE_STEP_OP(opcode))
+            continue;
+
+        const Method *calleeMethod;
+
+        switch (opcode) {
+            case OP_INVOKE_SUPER:
+            case OP_INVOKE_DIRECT:
+            case OP_INVOKE_STATIC:
+            case OP_INVOKE_SUPER_QUICK:
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            case OP_INVOKE_SUPER_RANGE:
+            case OP_INVOKE_DIRECT_RANGE:
+            case OP_INVOKE_STATIC_RANGE:
+            case OP_INVOKE_SUPER_QUICK_RANGE:
+                isRange = true;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            default:
+                calleeMethod = NULL;
+                break;
+        }
+
+        if (calleeMethod) {
+            bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod,
+                                                      lastMIRInsn, bb, isRange);
+            if (!inlined &&
+                !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
+                !dvmIsNativeMethod(calleeMethod)) {
+                CompilerMethodStats *methodStats =
+                    dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+                if ((methodStats->attributes & METHOD_IS_LEAF) &&
+                    !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
+                    /* Callee has been previously compiled */
+                    if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                        lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
+                    } else {
+                        /* Compile the callee first */
+                        dvmCompileMethod(calleeMethod, info);
+                        if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                            lastMIRInsn->OptimizationFlags |=
+                                MIR_INVOKE_METHOD_JIT;
+                        } else {
+                            methodStats->attributes |= METHOD_CANNOT_COMPILE;
+                        }
+                    }
+                }
+            }
+            return;
+        }
+
+        switch (opcode) {
+            case OP_INVOKE_VIRTUAL:
+            case OP_INVOKE_VIRTUAL_QUICK:
+            case OP_INVOKE_INTERFACE:
+                isRange = false;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            case OP_INVOKE_VIRTUAL_RANGE:
+            case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+            case OP_INVOKE_INTERFACE_RANGE:
+                isRange = true;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            default:
+                break;
+        }
+
+        if (calleeMethod) {
+            bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod,
+                                                    lastMIRInsn, bb, isRange);
+            if (!inlined &&
+                !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
+                !dvmIsNativeMethod(calleeMethod)) {
+                CompilerMethodStats *methodStats =
+                    dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+                if ((methodStats->attributes & METHOD_IS_LEAF) &&
+                    !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
+                    /* Callee has been previously compiled */
+                    if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                        lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
+                    } else {
+                        /* Compile the callee first */
+                        dvmCompileMethod(calleeMethod, info);
+                        if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                            lastMIRInsn->OptimizationFlags |=
+                                MIR_INVOKE_METHOD_JIT;
+                        } else {
+                            methodStats->attributes |= METHOD_CANNOT_COMPILE;
+                        }
+                    }
+                }
+            }
+            return;
+        }
+    }
+}
diff --git a/vm/compiler/IntermediateRep.cpp b/vm/compiler/IntermediateRep.cpp
new file mode 100644
index 0000000..db68c3c
--- /dev/null
+++ b/vm/compiler/IntermediateRep.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+/* Allocate a new basic block */
+BasicBlock *dvmCompilerNewBB(BBType blockType, int blockId)
+{
+    BasicBlock *bb = (BasicBlock *)dvmCompilerNew(sizeof(BasicBlock), true);
+    bb->blockType = blockType;
+    bb->id = blockId;
+    bb->predecessors = dvmCompilerAllocBitVector(blockId > 32 ? blockId : 32,
+                                                 true /* expandable */);
+    return bb;
+}
+
+/* Insert an MIR instruction to the end of a basic block */
+void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir)
+{
+    if (bb->firstMIRInsn == NULL) {
+        assert(bb->lastMIRInsn == NULL);
+        bb->lastMIRInsn = bb->firstMIRInsn = mir;
+        mir->prev = mir->next = NULL;
+    } else {
+        bb->lastMIRInsn->next = mir;
+        mir->prev = bb->lastMIRInsn;
+        mir->next = NULL;
+        bb->lastMIRInsn = mir;
+    }
+}
+
+/* Insert an MIR instruction to the head of a basic block */
+void dvmCompilerPrependMIR(BasicBlock *bb, MIR *mir)
+{
+    if (bb->firstMIRInsn == NULL) {
+        assert(bb->lastMIRInsn == NULL);
+        bb->lastMIRInsn = bb->firstMIRInsn = mir;
+        mir->prev = mir->next = NULL;
+    } else {
+        bb->firstMIRInsn->prev = mir;
+        mir->next = bb->firstMIRInsn;
+        mir->prev = NULL;
+        bb->firstMIRInsn = mir;
+    }
+}
+
+/* Insert an MIR instruction after the specified MIR */
+void dvmCompilerInsertMIRAfter(BasicBlock *bb, MIR *currentMIR, MIR *newMIR)
+{
+    newMIR->prev = currentMIR;
+    newMIR->next = currentMIR->next;
+    currentMIR->next = newMIR;
+
+    if (newMIR->next) {
+        /* Is not the last MIR in the block */
+        newMIR->next->prev = newMIR;
+    } else {
+        /* Is the last MIR in the block */
+        bb->lastMIRInsn = newMIR;
+    }
+}
+
+/*
+ * Append an LIR instruction to the LIR list maintained by a compilation
+ * unit
+ */
+void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir)
+{
+    if (cUnit->firstLIRInsn == NULL) {
+        assert(cUnit->lastLIRInsn == NULL);
+        cUnit->lastLIRInsn = cUnit->firstLIRInsn = lir;
+        lir->prev = lir->next = NULL;
+    } else {
+        cUnit->lastLIRInsn->next = lir;
+        lir->prev = cUnit->lastLIRInsn;
+        lir->next = NULL;
+        cUnit->lastLIRInsn = lir;
+    }
+}
+
+/*
+ * Insert an LIR instruction before the current instruction, which cannot be the
+ * first instruction.
+ *
+ * prevLIR <-> newLIR <-> currentLIR
+ */
+void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR)
+{
+    assert(currentLIR->prev != NULL);
+    LIR *prevLIR = currentLIR->prev;
+
+    prevLIR->next = newLIR;
+    newLIR->prev = prevLIR;
+    newLIR->next = currentLIR;
+    currentLIR->prev = newLIR;
+}
+
+/*
+ * Insert an LIR instruction after the current instruction, which cannot be the
+ * first instruction.
+ *
+ * currentLIR -> newLIR -> oldNext
+ */
+void dvmCompilerInsertLIRAfter(LIR *currentLIR, LIR *newLIR)
+{
+    newLIR->prev = currentLIR;
+    newLIR->next = currentLIR->next;
+    currentLIR->next = newLIR;
+    newLIR->next->prev = newLIR;
+}
diff --git a/vm/compiler/Loop.cpp b/vm/compiler/Loop.cpp
new file mode 100644
index 0000000..f826686
--- /dev/null
+++ b/vm/compiler/Loop.cpp
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+#include "Loop.h"
+
+#define DEBUG_LOOP(X)
+
+#if 0
+/* Debugging routines */
+static void dumpConstants(CompilationUnit *cUnit)
+{
+    int i;
+    ALOGE("LOOP starting offset: %x", cUnit->entryBlock->startOffset);
+    for (i = 0; i < cUnit->numSSARegs; i++) {
+        if (dvmIsBitSet(cUnit->isConstantV, i)) {
+            int subNReg = dvmConvertSSARegToDalvik(cUnit, i);
+            ALOGE("CONST: s%d(v%d_%d) has %d", i,
+                 DECODE_REG(subNReg), DECODE_SUB(subNReg),
+                 cUnit->constantValues[i]);
+        }
+    }
+}
+
+static void dumpIVList(CompilationUnit *cUnit)
+{
+    unsigned int i;
+    GrowableList *ivList = cUnit->loopAnalysis->ivList;
+
+    for (i = 0; i < ivList->numUsed; i++) {
+        InductionVariableInfo *ivInfo =
+            (InductionVariableInfo *) ivList->elemList[i];
+        int iv = dvmConvertSSARegToDalvik(cUnit, ivInfo->ssaReg);
+        /* Basic IV */
+        if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
+            ALOGE("BIV %d: s%d(v%d_%d) + %d", i,
+                 ivInfo->ssaReg,
+                 DECODE_REG(iv), DECODE_SUB(iv),
+                 ivInfo->inc);
+        /* Dependent IV */
+        } else {
+            int biv = dvmConvertSSARegToDalvik(cUnit, ivInfo->basicSSAReg);
+
+            ALOGE("DIV %d: s%d(v%d_%d) = %d * s%d(v%d_%d) + %d", i,
+                 ivInfo->ssaReg,
+                 DECODE_REG(iv), DECODE_SUB(iv),
+                 ivInfo->m,
+                 ivInfo->basicSSAReg,
+                 DECODE_REG(biv), DECODE_SUB(biv),
+                 ivInfo->c);
+        }
+    }
+}
+
+static void dumpHoistedChecks(CompilationUnit *cUnit)
+{
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+    unsigned int i;
+
+    for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) {
+        ArrayAccessInfo *arrayAccessInfo =
+            GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+                       ArrayAccessInfo*, i);
+        int arrayReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg));
+        int idxReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg));
+        ALOGE("Array access %d", i);
+        ALOGE("  arrayReg %d", arrayReg);
+        ALOGE("  idxReg %d", idxReg);
+        ALOGE("  endReg %d", loopAnalysis->endConditionReg);
+        ALOGE("  maxC %d", arrayAccessInfo->maxC);
+        ALOGE("  minC %d", arrayAccessInfo->minC);
+        ALOGE("  opcode %d", loopAnalysis->loopBranchOpcode);
+    }
+}
+
+#endif
+
+static BasicBlock *findPredecessorBlock(const CompilationUnit *cUnit,
+                                        const BasicBlock *bb)
+{
+    int numPred = dvmCountSetBits(bb->predecessors);
+    BitVectorIterator bvIterator;
+    dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+
+    if (numPred == 1) {
+        int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+        return (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                        predIdx);
+    /* First loop block */
+    } else if ((numPred == 2) &&
+               dvmIsBitSet(bb->predecessors, cUnit->entryBlock->id)) {
+        while (true) {
+            int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+            if (predIdx == cUnit->entryBlock->id) continue;
+            return (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                            predIdx);
+        }
+    /* Doesn't support other shape of control flow yet */
+    } else {
+        return NULL;
+    }
+}
+
+/* Used for normalized loop exit condition checks */
+static Opcode negateOpcode(Opcode opcode)
+{
+    switch (opcode) {
+        /* reg/reg cmp */
+        case OP_IF_EQ:
+            return OP_IF_NE;
+        case OP_IF_NE:
+            return OP_IF_EQ;
+        case OP_IF_LT:
+            return OP_IF_GE;
+        case OP_IF_GE:
+            return OP_IF_LT;
+        case OP_IF_GT:
+            return OP_IF_LE;
+        case OP_IF_LE:
+            return OP_IF_GT;
+        /* reg/zero cmp */
+        case OP_IF_EQZ:
+            return OP_IF_NEZ;
+        case OP_IF_NEZ:
+            return OP_IF_EQZ;
+        case OP_IF_LTZ:
+            return OP_IF_GEZ;
+        case OP_IF_GEZ:
+            return OP_IF_LTZ;
+        case OP_IF_GTZ:
+            return OP_IF_LEZ;
+        case OP_IF_LEZ:
+            return OP_IF_GTZ;
+        default:
+            ALOGE("opcode %d cannot be negated", opcode);
+            dvmAbort();
+            break;
+    }
+    return (Opcode)-1;  // unreached
+}
+
+/*
+ * A loop is considered optimizable if:
+ * 1) It has one basic induction variable.
+ * 2) The loop back branch compares the BIV with a constant.
+ * 3) We need to normalize the loop exit condition so that the loop is exited
+ *    via the taken path.
+ * 4) If it is a count-up loop, the condition is GE/GT. Otherwise it is
+ *    LE/LT/LEZ/LTZ for a count-down loop.
+ *
+ * Return false for loops that fail the above tests.
+ */
+static bool isSimpleCountedLoop(CompilationUnit *cUnit)
+{
+    unsigned int i;
+    BasicBlock *loopBackBlock = cUnit->entryBlock->fallThrough;
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+
+    if (loopAnalysis->numBasicIV != 1) return false;
+    for (i = 0; i < loopAnalysis->ivList->numUsed; i++) {
+        InductionVariableInfo *ivInfo;
+
+        ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i);
+        /* Count up or down loop? */
+        if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
+            /* Infinite loop */
+            if (ivInfo->inc == 0) {
+                return false;
+            }
+            loopAnalysis->isCountUpLoop = ivInfo->inc > 0;
+            break;
+        }
+    }
+
+    /* Find the block that ends with a branch to exit the loop */
+    while (true) {
+        loopBackBlock = findPredecessorBlock(cUnit, loopBackBlock);
+        /* Loop structure not recognized as counted blocks */
+        if (loopBackBlock == NULL) {
+            return false;
+        }
+        /* Unconditional goto - continue to trace up the predecessor chain */
+        if (loopBackBlock->taken == NULL) {
+            continue;
+        }
+        break;
+    }
+
+    MIR *branch = loopBackBlock->lastMIRInsn;
+    Opcode opcode = branch->dalvikInsn.opcode;
+
+    /* Last instruction is not a conditional branch - bail */
+    if (dexGetFlagsFromOpcode(opcode) != (kInstrCanContinue|kInstrCanBranch)) {
+        return false;
+    }
+
+    int endSSAReg;
+    int endDalvikReg;
+
+    /* reg/reg comparison */
+    if (branch->ssaRep->numUses == 2) {
+        if (branch->ssaRep->uses[0] == loopAnalysis->ssaBIV) {
+            endSSAReg = branch->ssaRep->uses[1];
+        } else if (branch->ssaRep->uses[1] == loopAnalysis->ssaBIV) {
+            endSSAReg = branch->ssaRep->uses[0];
+            opcode = negateOpcode(opcode);
+        } else {
+            return false;
+        }
+        endDalvikReg = dvmConvertSSARegToDalvik(cUnit, endSSAReg);
+        /*
+         * If the comparison is not between the BIV and a loop invariant,
+         * return false. endDalvikReg is loop invariant if one of the
+         * following is true:
+         * - It is not defined in the loop (ie DECODE_SUB returns 0)
+         * - It is reloaded with a constant
+         */
+        if ((DECODE_SUB(endDalvikReg) != 0) &&
+            !dvmIsBitSet(cUnit->isConstantV, endSSAReg)) {
+            return false;
+        }
+    /* Compare against zero */
+    } else if (branch->ssaRep->numUses == 1) {
+        if (branch->ssaRep->uses[0] == loopAnalysis->ssaBIV) {
+            /* Keep the compiler happy */
+            endDalvikReg = -1;
+        } else {
+            return false;
+        }
+    } else {
+        return false;
+    }
+
+    /* Normalize the loop exit check as "if (iv op end) exit;" */
+    if (loopBackBlock->taken->blockType == kDalvikByteCode) {
+        opcode = negateOpcode(opcode);
+    }
+
+    if (loopAnalysis->isCountUpLoop) {
+        /*
+         * If the normalized condition op is not > or >=, this is not an
+         * optimization candidate.
+         */
+        switch (opcode) {
+            case OP_IF_GT:
+            case OP_IF_GE:
+                break;
+            default:
+                return false;
+        }
+        loopAnalysis->endConditionReg = DECODE_REG(endDalvikReg);
+    } else  {
+        /*
+         * If the normalized condition op is not < or <=, this is not an
+         * optimization candidate.
+         */
+        switch (opcode) {
+            case OP_IF_LT:
+            case OP_IF_LE:
+                loopAnalysis->endConditionReg = DECODE_REG(endDalvikReg);
+                break;
+            case OP_IF_LTZ:
+            case OP_IF_LEZ:
+                break;
+            default:
+                return false;
+        }
+    }
+    /*
+     * Remember the normalized opcode, which will be used to determine the end
+     * value used for the yanked range checks.
+     */
+    loopAnalysis->loopBranchOpcode = opcode;
+    return true;
+}
+
+/*
+ * Record the upper and lower bound information for range checks for each
+ * induction variable. If array A is accessed by index "i+5", the upper and
+ * lower bound will be len(A)-5 and -5, respectively.
+ */
+static void updateRangeCheckInfo(CompilationUnit *cUnit, int arrayReg,
+                                 int idxReg)
+{
+    InductionVariableInfo *ivInfo;
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+    unsigned int i, j;
+
+    for (i = 0; i < loopAnalysis->ivList->numUsed; i++) {
+        ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i);
+        if (ivInfo->ssaReg == idxReg) {
+            ArrayAccessInfo *arrayAccessInfo = NULL;
+            for (j = 0; j < loopAnalysis->arrayAccessInfo->numUsed; j++) {
+                ArrayAccessInfo *existingArrayAccessInfo =
+                    GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+                               ArrayAccessInfo*,
+                               j);
+                if (existingArrayAccessInfo->arrayReg == arrayReg) {
+                    if (ivInfo->c > existingArrayAccessInfo->maxC) {
+                        existingArrayAccessInfo->maxC = ivInfo->c;
+                    }
+                    if (ivInfo->c < existingArrayAccessInfo->minC) {
+                        existingArrayAccessInfo->minC = ivInfo->c;
+                    }
+                    arrayAccessInfo = existingArrayAccessInfo;
+                    break;
+                }
+            }
+            if (arrayAccessInfo == NULL) {
+                arrayAccessInfo =
+                    (ArrayAccessInfo *)dvmCompilerNew(sizeof(ArrayAccessInfo),
+                                                      false);
+                arrayAccessInfo->ivReg = ivInfo->basicSSAReg;
+                arrayAccessInfo->arrayReg = arrayReg;
+                arrayAccessInfo->maxC = (ivInfo->c > 0) ? ivInfo->c : 0;
+                arrayAccessInfo->minC = (ivInfo->c < 0) ? ivInfo->c : 0;
+                dvmInsertGrowableList(loopAnalysis->arrayAccessInfo,
+                                      (intptr_t) arrayAccessInfo);
+            }
+            break;
+        }
+    }
+}
+
+/* Returns true if the loop body cannot throw any exceptions */
+static bool doLoopBodyCodeMotion(CompilationUnit *cUnit)
+{
+    BasicBlock *loopBody = cUnit->entryBlock->fallThrough;
+    MIR *mir;
+    bool loopBodyCanThrow = false;
+
+    for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) {
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        /* Skip extended MIR instructions */
+        if (dInsn->opcode >= kNumPackedOpcodes) continue;
+
+        int instrFlags = dexGetFlagsFromOpcode(dInsn->opcode);
+
+        /* Instruction is clean */
+        if ((instrFlags & kInstrCanThrow) == 0) continue;
+
+        /*
+         * Currently we can only optimize away null and range checks. Punt on
+         * instructions that can throw due to other exceptions.
+         */
+        if (!(dfAttributes & DF_HAS_NR_CHECKS)) {
+            loopBodyCanThrow = true;
+            continue;
+        }
+
+        /*
+         * This comparison is redundant now, but we will have more than one
+         * group of flags to check soon.
+         */
+        if (dfAttributes & DF_HAS_NR_CHECKS) {
+            /*
+             * Check if the null check is applied on a loop invariant register?
+             * If the register's SSA id is less than the number of Dalvik
+             * registers, then it is loop invariant.
+             */
+            int refIdx;
+            switch (dfAttributes & DF_HAS_NR_CHECKS) {
+                case DF_NULL_N_RANGE_CHECK_0:
+                    refIdx = 0;
+                    break;
+                case DF_NULL_N_RANGE_CHECK_1:
+                    refIdx = 1;
+                    break;
+                case DF_NULL_N_RANGE_CHECK_2:
+                    refIdx = 2;
+                    break;
+                default:
+                    refIdx = 0;
+                    ALOGE("Jit: bad case in doLoopBodyCodeMotion");
+                    dvmCompilerAbort(cUnit);
+            }
+
+            int useIdx = refIdx + 1;
+            int subNRegArray =
+                dvmConvertSSARegToDalvik(cUnit, mir->ssaRep->uses[refIdx]);
+            int arraySub = DECODE_SUB(subNRegArray);
+
+            /*
+             * If the register is never updated in the loop (ie subscript == 0),
+             * it is an optimization candidate.
+             */
+            if (arraySub != 0) {
+                loopBodyCanThrow = true;
+                continue;
+            }
+
+            /*
+             * Then check if the range check can be hoisted out of the loop if
+             * it is basic or dependent induction variable.
+             */
+            if (dvmIsBitSet(cUnit->loopAnalysis->isIndVarV,
+                            mir->ssaRep->uses[useIdx])) {
+                mir->OptimizationFlags |=
+                    MIR_IGNORE_RANGE_CHECK | MIR_IGNORE_NULL_CHECK;
+                updateRangeCheckInfo(cUnit, mir->ssaRep->uses[refIdx],
+                                     mir->ssaRep->uses[useIdx]);
+            }
+        }
+    }
+
+    return !loopBodyCanThrow;
+}
+
+static void genHoistedChecks(CompilationUnit *cUnit)
+{
+    unsigned int i;
+    BasicBlock *entry = cUnit->entryBlock;
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+    int globalMaxC = 0;
+    int globalMinC = 0;
+    /* Should be loop invariant */
+    int idxReg = 0;
+
+    for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) {
+        ArrayAccessInfo *arrayAccessInfo =
+            GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+                       ArrayAccessInfo*, i);
+        int arrayReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg));
+        idxReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg));
+
+        MIR *rangeCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        rangeCheckMIR->dalvikInsn.opcode = (loopAnalysis->isCountUpLoop) ?
+            (Opcode)kMirOpNullNRangeUpCheck : (Opcode)kMirOpNullNRangeDownCheck;
+        rangeCheckMIR->dalvikInsn.vA = arrayReg;
+        rangeCheckMIR->dalvikInsn.vB = idxReg;
+        rangeCheckMIR->dalvikInsn.vC = loopAnalysis->endConditionReg;
+        rangeCheckMIR->dalvikInsn.arg[0] = arrayAccessInfo->maxC;
+        rangeCheckMIR->dalvikInsn.arg[1] = arrayAccessInfo->minC;
+        rangeCheckMIR->dalvikInsn.arg[2] = loopAnalysis->loopBranchOpcode;
+        dvmCompilerAppendMIR(entry, rangeCheckMIR);
+        if (arrayAccessInfo->maxC > globalMaxC) {
+            globalMaxC = arrayAccessInfo->maxC;
+        }
+        if (arrayAccessInfo->minC < globalMinC) {
+            globalMinC = arrayAccessInfo->minC;
+        }
+    }
+
+    if (loopAnalysis->arrayAccessInfo->numUsed != 0) {
+        if (loopAnalysis->isCountUpLoop) {
+            MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+            boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpLowerBound;
+            boundCheckMIR->dalvikInsn.vA = idxReg;
+            boundCheckMIR->dalvikInsn.vB = globalMinC;
+            dvmCompilerAppendMIR(entry, boundCheckMIR);
+        } else {
+            if (loopAnalysis->loopBranchOpcode == OP_IF_LT ||
+                loopAnalysis->loopBranchOpcode == OP_IF_LE) {
+                MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+                boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpLowerBound;
+                boundCheckMIR->dalvikInsn.vA = loopAnalysis->endConditionReg;
+                boundCheckMIR->dalvikInsn.vB = globalMinC;
+                /*
+                 * If the end condition is ">" in the source, the check in the
+                 * Dalvik bytecode is OP_IF_LE. In this case add 1 back to the
+                 * constant field to reflect the fact that the smallest index
+                 * value is "endValue + constant + 1".
+                 */
+                if (loopAnalysis->loopBranchOpcode == OP_IF_LE) {
+                    boundCheckMIR->dalvikInsn.vB++;
+                }
+                dvmCompilerAppendMIR(entry, boundCheckMIR);
+            } else if (loopAnalysis->loopBranchOpcode == OP_IF_LTZ) {
+                /* Array index will fall below 0 */
+                if (globalMinC < 0) {
+                    MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR),
+                                                               true);
+                    boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpPunt;
+                    dvmCompilerAppendMIR(entry, boundCheckMIR);
+                }
+            } else if (loopAnalysis->loopBranchOpcode == OP_IF_LEZ) {
+                /* Array index will fall below 0 */
+                if (globalMinC < -1) {
+                    MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR),
+                                                               true);
+                    boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpPunt;
+                    dvmCompilerAppendMIR(entry, boundCheckMIR);
+                }
+            } else {
+                ALOGE("Jit: bad case in genHoistedChecks");
+                dvmCompilerAbort(cUnit);
+            }
+        }
+    }
+}
+
+void resetBlockEdges(BasicBlock *bb)
+{
+    bb->taken = NULL;
+    bb->fallThrough = NULL;
+    bb->successorBlockList.blockListType = kNotUsed;
+}
+
+static bool clearPredecessorVector(struct CompilationUnit *cUnit,
+                                   struct BasicBlock *bb)
+{
+    dvmClearAllBits(bb->predecessors);
+    return false;
+}
+
+bool dvmCompilerFilterLoopBlocks(CompilationUnit *cUnit)
+{
+    BasicBlock *firstBB = cUnit->entryBlock->fallThrough;
+
+    int numPred = dvmCountSetBits(firstBB->predecessors);
+    /*
+     * A loop body should have at least two incoming edges.
+     */
+    if (numPred < 2) return false;
+
+    GrowableList *blockList = &cUnit->blockList;
+
+    /* Record blocks included in the loop */
+    dvmClearAllBits(cUnit->tempBlockV);
+
+    dvmCompilerSetBit(cUnit->tempBlockV, cUnit->entryBlock->id);
+    dvmCompilerSetBit(cUnit->tempBlockV, firstBB->id);
+
+    BasicBlock *bodyBB = firstBB;
+
+    /*
+     * First try to include the fall-through block in the loop, then the taken
+     * block. Stop loop formation on the first backward branch that enters the
+     * first block (ie only include the inner-most loop).
+     */
+    while (true) {
+        /* Loop formed */
+        if (bodyBB->taken == firstBB) {
+            /* Check if the fallThrough edge will cause a nested loop */
+            if (bodyBB->fallThrough &&
+                dvmIsBitSet(cUnit->tempBlockV, bodyBB->fallThrough->id)) {
+                return false;
+            }
+            /* Single loop formed */
+            break;
+        } else if (bodyBB->fallThrough == firstBB) {
+            /* Check if the taken edge will cause a nested loop */
+            if (bodyBB->taken &&
+                dvmIsBitSet(cUnit->tempBlockV, bodyBB->taken->id)) {
+                return false;
+            }
+            /* Single loop formed */
+            break;
+        }
+
+        /* Inner loops formed first - quit */
+        if (bodyBB->fallThrough &&
+            dvmIsBitSet(cUnit->tempBlockV, bodyBB->fallThrough->id)) {
+            return false;
+        }
+        if (bodyBB->taken &&
+            dvmIsBitSet(cUnit->tempBlockV, bodyBB->taken->id)) {
+            return false;
+        }
+
+        if (bodyBB->fallThrough) {
+            if (bodyBB->fallThrough->iDom == bodyBB) {
+                bodyBB = bodyBB->fallThrough;
+                dvmCompilerSetBit(cUnit->tempBlockV, bodyBB->id);
+                /*
+                 * Loop formation to be detected at the beginning of next
+                 * iteration.
+                 */
+                continue;
+            }
+        }
+        if (bodyBB->taken) {
+            if (bodyBB->taken->iDom == bodyBB) {
+                bodyBB = bodyBB->taken;
+                dvmCompilerSetBit(cUnit->tempBlockV, bodyBB->id);
+                /*
+                 * Loop formation to be detected at the beginning of next
+                 * iteration.
+                 */
+                continue;
+            }
+        }
+        /*
+         * Current block is not the immediate dominator of either fallthrough
+         * nor taken block - bail out of loop formation.
+         */
+        return false;
+    }
+
+
+    /* Now mark blocks not included in the loop as hidden */
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(blockList, &iterator);
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (!dvmIsBitSet(cUnit->tempBlockV, bb->id)) {
+            bb->hidden = true;
+            /* Clear the insn list */
+            bb->firstMIRInsn = bb->lastMIRInsn = NULL;
+            resetBlockEdges(bb);
+        }
+    }
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, clearPredecessorVector,
+                                          kAllNodes, false /* isIterative */);
+
+    dvmGrowableListIteratorInit(blockList, &iterator);
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (dvmIsBitSet(cUnit->tempBlockV, bb->id)) {
+            if (bb->taken) {
+                /*
+                 * exit block means we run into control-flow that we don't want
+                 * to handle.
+                 */
+                if (bb->taken == cUnit->exitBlock) {
+                    return false;
+                }
+                if (bb->taken->hidden) {
+                    bb->taken->blockType = kChainingCellNormal;
+                    bb->taken->hidden = false;
+                }
+                dvmCompilerSetBit(bb->taken->predecessors, bb->id);
+            }
+            if (bb->fallThrough) {
+                /*
+                 * exit block means we run into control-flow that we don't want
+                 * to handle.
+                 */
+                if (bb->fallThrough == cUnit->exitBlock) {
+                    return false;
+                }
+                if (bb->fallThrough->hidden) {
+                    bb->fallThrough->blockType = kChainingCellNormal;
+                    bb->fallThrough->hidden = false;
+                }
+                dvmCompilerSetBit(bb->fallThrough->predecessors, bb->id);
+            }
+            /* Loop blocks shouldn't contain any successor blocks (yet) */
+            assert(bb->successorBlockList.blockListType == kNotUsed);
+        }
+    }
+    return true;
+}
+
+/*
+ * Main entry point to do loop optimization.
+ * Return false if sanity checks for loop formation/optimization failed.
+ */
+bool dvmCompilerLoopOpt(CompilationUnit *cUnit)
+{
+    LoopAnalysis *loopAnalysis =
+        (LoopAnalysis *)dvmCompilerNew(sizeof(LoopAnalysis), true);
+    cUnit->loopAnalysis = loopAnalysis;
+
+    /* Constant propagation */
+    cUnit->isConstantV = dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+    cUnit->constantValues =
+        (int *)dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
+                              true);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                          dvmCompilerDoConstantPropagation,
+                                          kAllNodes,
+                                          false /* isIterative */);
+    DEBUG_LOOP(dumpConstants(cUnit);)
+
+    /* Find induction variables - basic and dependent */
+    loopAnalysis->ivList =
+        (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
+    dvmInitGrowableList(loopAnalysis->ivList, 4);
+    loopAnalysis->isIndVarV = dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                          dvmCompilerFindInductionVariables,
+                                          kAllNodes,
+                                          false /* isIterative */);
+    DEBUG_LOOP(dumpIVList(cUnit);)
+
+    /* Only optimize array accesses for simple counted loop for now */
+    if (!isSimpleCountedLoop(cUnit))
+        return false;
+
+    loopAnalysis->arrayAccessInfo =
+        (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
+    dvmInitGrowableList(loopAnalysis->arrayAccessInfo, 4);
+    loopAnalysis->bodyIsClean = doLoopBodyCodeMotion(cUnit);
+    DEBUG_LOOP(dumpHoistedChecks(cUnit);)
+
+    /*
+     * Convert the array access information into extended MIR code in the loop
+     * header.
+     */
+    genHoistedChecks(cUnit);
+    return true;
+}
+
+/*
+ * Select the target block of the backward branch.
+ */
+void dvmCompilerInsertBackwardChaining(CompilationUnit *cUnit)
+{
+    /*
+     * If we are not in self-verification or profiling mode, the backward
+     * branch can go to the entryBlock->fallThrough directly. Suspend polling
+     * code will be generated along the backward branch to honor the suspend
+     * requests.
+     */
+#ifndef ARCH_IA32
+#if !defined(WITH_SELF_VERIFICATION)
+    if (gDvmJit.profileMode != kTraceProfilingContinuous &&
+        gDvmJit.profileMode != kTraceProfilingPeriodicOn) {
+        return;
+    }
+#endif
+#endif
+
+    /*
+     * In self-verification or profiling mode, the backward branch is altered
+     * to go to the backward chaining cell. Without using the backward chaining
+     * cell we won't be able to do check-pointing on the target PC, or count the
+     * number of iterations accurately.
+     */
+    BasicBlock *firstBB = cUnit->entryBlock->fallThrough;
+    BasicBlock *backBranchBB = findPredecessorBlock(cUnit, firstBB);
+    if (backBranchBB->taken == firstBB) {
+        backBranchBB->taken = cUnit->backChainBlock;
+    } else {
+        assert(backBranchBB->fallThrough == firstBB);
+        backBranchBB->fallThrough = cUnit->backChainBlock;
+    }
+    cUnit->backChainBlock->startOffset = firstBB->startOffset;
+}
diff --git a/vm/compiler/Loop.h b/vm/compiler/Loop.h
new file mode 100644
index 0000000..8032093
--- /dev/null
+++ b/vm/compiler/Loop.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_LOOP_H_
+#define DALVIK_VM_LOOP_H_
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+typedef struct LoopAnalysis {
+    BitVector *isIndVarV;               // length == numSSAReg
+    GrowableList *ivList;               // induction variables
+    GrowableList *arrayAccessInfo;      // hoisted checks for array accesses
+    int numBasicIV;                     // number of basic induction variables
+    int ssaBIV;                         // basic IV in SSA name
+    bool isCountUpLoop;                 // count up or down loop
+    Opcode loopBranchOpcode;            // OP_IF_XXX for the loop back branch
+    int endConditionReg;                // vB in "vA op vB"
+    LIR *branchToBody;                  // branch over to the body from entry
+    LIR *branchToPCR;                   // branch over to the PCR cell
+    bool bodyIsClean;                   // loop body cannot throw any exceptions
+} LoopAnalysis;
+
+bool dvmCompilerFilterLoopBlocks(CompilationUnit *cUnit);
+
+/*
+ * An unexecuted code path may contain unresolved fields or classes. Before we
+ * have a quiet resolver we simply bail out of the loop compilation mode.
+ */
+#define BAIL_LOOP_COMPILATION() if (cUnit->jitMode == kJitLoop) {       \
+                                    cUnit->quitLoopMode = true;         \
+                                    return false;                       \
+                                }
+
+#endif  // DALVIK_VM_LOOP_H_
diff --git a/vm/compiler/Ralloc.cpp b/vm/compiler/Ralloc.cpp
new file mode 100644
index 0000000..e2752b1
--- /dev/null
+++ b/vm/compiler/Ralloc.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+
+/*
+ * Quick & dirty - make FP usage sticky.  This is strictly a hint - local
+ * code generation will handle misses.  It might be worthwhile to collaborate
+ * with dx/dexopt to avoid reusing the same Dalvik temp for values of
+ * different types.
+ */
+static void inferTypes(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    if (bb->blockType != kDalvikByteCode && bb->blockType != kEntryBlock)
+        return;
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        SSARepresentation *ssaRep = mir->ssaRep;
+        if (ssaRep) {
+            int i;
+            for (i=0; ssaRep->fpUse && i< ssaRep->numUses; i++) {
+                if (ssaRep->fpUse[i])
+                    cUnit->regLocation[ssaRep->uses[i]].fp = true;
+            }
+            for (i=0; ssaRep->fpDef && i< ssaRep->numDefs; i++) {
+                if (ssaRep->fpDef[i])
+                    cUnit->regLocation[ssaRep->defs[i]].fp = true;
+            }
+        }
+    }
+}
+
+static const RegLocation freshLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
+                                     INVALID_REG, INVALID_SREG};
+
+/*
+ * Local register allocation for simple traces.  Most of the work for
+ * local allocation is done on the fly.  Here we do some initialization
+ * and type inference.
+ */
+void dvmCompilerLocalRegAlloc(CompilationUnit *cUnit)
+{
+    int i;
+    RegLocation *loc;
+
+    /* Allocate the location map */
+    loc = (RegLocation*)dvmCompilerNew(cUnit->numSSARegs * sizeof(*loc), true);
+    for (i=0; i< cUnit->numSSARegs; i++) {
+        loc[i] = freshLoc;
+        loc[i].sRegLow = i;
+    }
+    cUnit->regLocation = loc;
+
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+    /* Do type inference pass */
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        inferTypes(cUnit, bb);
+    }
+
+    /* Remap SSA names back to original frame locations. */
+    for (i=0; i < cUnit->numSSARegs; i++) {
+        cUnit->regLocation[i].sRegLow =
+                DECODE_REG(dvmConvertSSARegToDalvik(cUnit, loc[i].sRegLow));
+    }
+}
diff --git a/vm/compiler/SSATransformation.cpp b/vm/compiler/SSATransformation.cpp
new file mode 100644
index 0000000..7dde594
--- /dev/null
+++ b/vm/compiler/SSATransformation.cpp
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "Loop.h"
+#include "libdex/DexOpcodes.h"
+
+/* Enter the node to the dfsOrder list then visit its successors */
+static void recordDFSPreOrder(CompilationUnit *cUnit, BasicBlock *block)
+{
+
+    if (block->visited || block->hidden) return;
+    block->visited = true;
+
+    /* Enqueue the block id */
+    dvmInsertGrowableList(&cUnit->dfsOrder, block->id);
+
+    if (block->fallThrough) recordDFSPreOrder(cUnit, block->fallThrough);
+    if (block->taken) recordDFSPreOrder(cUnit, block->taken);
+    if (block->successorBlockList.blockListType != kNotUsed) {
+        GrowableListIterator iterator;
+        dvmGrowableListIteratorInit(&block->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *succBB = successorBlockInfo->block;
+            recordDFSPreOrder(cUnit, succBB);
+        }
+    }
+    return;
+}
+
+/* Sort the blocks by the Depth-First-Search pre-order */
+static void computeDFSOrder(CompilationUnit *cUnit)
+{
+    /* Initialize or reset the DFS order list */
+    if (cUnit->dfsOrder.elemList == NULL) {
+        dvmInitGrowableList(&cUnit->dfsOrder, cUnit->numBlocks);
+    } else {
+        /* Just reset the used length on the counter */
+        cUnit->dfsOrder.numUsed = 0;
+    }
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+    recordDFSPreOrder(cUnit, cUnit->entryBlock);
+    cUnit->numReachableBlocks = cUnit->dfsOrder.numUsed;
+}
+
+/*
+ * Mark block bit on the per-Dalvik register vector to denote that Dalvik
+ * register idx is defined in BasicBlock bb.
+ */
+static bool fillDefBlockMatrix(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    if (bb->dataFlowInfo == NULL) return false;
+
+    BitVectorIterator iterator;
+
+    dvmBitVectorIteratorInit(bb->dataFlowInfo->defV, &iterator);
+    while (true) {
+        int idx = dvmBitVectorIteratorNext(&iterator);
+        if (idx == -1) break;
+        /* Block bb defines register idx */
+        dvmCompilerSetBit(cUnit->defBlockMatrix[idx], bb->id);
+    }
+    return true;
+}
+
+static void computeDefBlockMatrix(CompilationUnit *cUnit)
+{
+    int numRegisters = cUnit->numDalvikRegisters;
+    /* Allocate numDalvikRegisters bit vector pointers */
+    cUnit->defBlockMatrix = (BitVector **)
+        dvmCompilerNew(sizeof(BitVector *) * numRegisters, true);
+    int i;
+
+    /* Initialize numRegister vectors with numBlocks bits each */
+    for (i = 0; i < numRegisters; i++) {
+        cUnit->defBlockMatrix[i] = dvmCompilerAllocBitVector(cUnit->numBlocks,
+                                                             false);
+    }
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLocalLiveIn,
+                                          kAllNodes,
+                                          false /* isIterative */);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, fillDefBlockMatrix,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+    if (cUnit->jitMode == kJitMethod) {
+        /*
+         * Also set the incoming parameters as defs in the entry block.
+         * Only need to handle the parameters for the outer method.
+         */
+        int inReg = cUnit->method->registersSize - cUnit->method->insSize;
+        for (; inReg < cUnit->method->registersSize; inReg++) {
+            dvmCompilerSetBit(cUnit->defBlockMatrix[inReg],
+                              cUnit->entryBlock->id);
+        }
+    }
+}
+
+/* Compute the post-order traversal of the CFG */
+static void computeDomPostOrderTraversal(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVectorIterator bvIterator;
+    dvmBitVectorIteratorInit(bb->iDominated, &bvIterator);
+    GrowableList *blockList = &cUnit->blockList;
+
+    /* Iterate through the dominated blocks first */
+    while (true) {
+        int bbIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (bbIdx == -1) break;
+        BasicBlock *dominatedBB =
+            (BasicBlock *) dvmGrowableListGetElement(blockList, bbIdx);
+        computeDomPostOrderTraversal(cUnit, dominatedBB);
+    }
+
+    /* Enter the current block id */
+    dvmInsertGrowableList(&cUnit->domPostOrderTraversal, bb->id);
+
+    /* hacky loop detection */
+    if (bb->taken && dvmIsBitSet(bb->dominators, bb->taken->id)) {
+        cUnit->hasLoop = true;
+    }
+}
+
+static void checkForDominanceFrontier(BasicBlock *domBB,
+                                      const BasicBlock *succBB)
+{
+    /*
+     * TODO - evaluate whether phi will ever need to be inserted into exit
+     * blocks.
+     */
+    if (succBB->iDom != domBB &&
+        succBB->blockType == kDalvikByteCode &&
+        succBB->hidden == false) {
+        dvmSetBit(domBB->domFrontier, succBB->id);
+    }
+}
+
+/* Worker function to compute the dominance frontier */
+static bool computeDominanceFrontier(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    GrowableList *blockList = &cUnit->blockList;
+
+    /* Calculate DF_local */
+    if (bb->taken) {
+        checkForDominanceFrontier(bb, bb->taken);
+    }
+    if (bb->fallThrough) {
+        checkForDominanceFrontier(bb, bb->fallThrough);
+    }
+    if (bb->successorBlockList.blockListType != kNotUsed) {
+        GrowableListIterator iterator;
+        dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *succBB = successorBlockInfo->block;
+            checkForDominanceFrontier(bb, succBB);
+        }
+    }
+
+    /* Calculate DF_up */
+    BitVectorIterator bvIterator;
+    dvmBitVectorIteratorInit(bb->iDominated, &bvIterator);
+    while (true) {
+        int dominatedIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (dominatedIdx == -1) break;
+        BasicBlock *dominatedBB = (BasicBlock *)
+            dvmGrowableListGetElement(blockList, dominatedIdx);
+        BitVectorIterator dfIterator;
+        dvmBitVectorIteratorInit(dominatedBB->domFrontier, &dfIterator);
+        while (true) {
+            int dfUpIdx = dvmBitVectorIteratorNext(&dfIterator);
+            if (dfUpIdx == -1) break;
+            BasicBlock *dfUpBlock = (BasicBlock *)
+                dvmGrowableListGetElement(blockList, dfUpIdx);
+            checkForDominanceFrontier(bb, dfUpBlock);
+        }
+    }
+
+    return true;
+}
+
+/* Worker function for initializing domination-related data structures */
+static bool initializeDominationInfo(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    int numTotalBlocks = cUnit->blockList.numUsed;
+
+    if (bb->dominators == NULL ) {
+        bb->dominators = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                   false /* expandable */);
+        bb->iDominated = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                   false /* expandable */);
+        bb->domFrontier = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                   false /* expandable */);
+    } else {
+        dvmClearAllBits(bb->dominators);
+        dvmClearAllBits(bb->iDominated);
+        dvmClearAllBits(bb->domFrontier);
+    }
+    /* Set all bits in the dominator vector */
+    dvmSetInitialBits(bb->dominators, numTotalBlocks);
+
+    return true;
+}
+
+/* Worker function to compute each block's dominators */
+static bool computeBlockDominators(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    GrowableList *blockList = &cUnit->blockList;
+    int numTotalBlocks = blockList->numUsed;
+    BitVector *tempBlockV = cUnit->tempBlockV;
+    BitVectorIterator bvIterator;
+
+    /*
+     * The dominator of the entry block has been preset to itself and we need
+     * to skip the calculation here.
+     */
+    if (bb == cUnit->entryBlock) return false;
+
+    dvmSetInitialBits(tempBlockV, numTotalBlocks);
+
+    /* Iterate through the predecessors */
+    dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+    while (true) {
+        int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (predIdx == -1) break;
+        BasicBlock *predBB = (BasicBlock *) dvmGrowableListGetElement(
+                                 blockList, predIdx);
+        /* tempBlockV = tempBlockV ^ dominators */
+        dvmIntersectBitVectors(tempBlockV, tempBlockV, predBB->dominators);
+    }
+    dvmSetBit(tempBlockV, bb->id);
+    if (dvmCompareBitVectors(tempBlockV, bb->dominators)) {
+        dvmCopyBitVector(bb->dominators, tempBlockV);
+        return true;
+    }
+    return false;
+}
+
+/* Worker function to compute the idom */
+static bool computeImmediateDominator(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    GrowableList *blockList = &cUnit->blockList;
+    BitVector *tempBlockV = cUnit->tempBlockV;
+    BitVectorIterator bvIterator;
+    BasicBlock *iDom;
+
+    if (bb == cUnit->entryBlock) return false;
+
+    dvmCopyBitVector(tempBlockV, bb->dominators);
+    dvmClearBit(tempBlockV, bb->id);
+    dvmBitVectorIteratorInit(tempBlockV, &bvIterator);
+
+    /* Should not see any dead block */
+    assert(dvmCountSetBits(tempBlockV) != 0);
+    if (dvmCountSetBits(tempBlockV) == 1) {
+        iDom = (BasicBlock *) dvmGrowableListGetElement(
+                       blockList, dvmBitVectorIteratorNext(&bvIterator));
+        bb->iDom = iDom;
+    } else {
+        int iDomIdx = dvmBitVectorIteratorNext(&bvIterator);
+        assert(iDomIdx != -1);
+        while (true) {
+            int nextDom = dvmBitVectorIteratorNext(&bvIterator);
+            if (nextDom == -1) break;
+            BasicBlock *nextDomBB = (BasicBlock *)
+                dvmGrowableListGetElement(blockList, nextDom);
+            /* iDom dominates nextDom - set new iDom */
+            if (dvmIsBitSet(nextDomBB->dominators, iDomIdx)) {
+                iDomIdx = nextDom;
+            }
+
+        }
+        iDom = (BasicBlock *) dvmGrowableListGetElement(blockList, iDomIdx);
+        /* Set the immediate dominator block for bb */
+        bb->iDom = iDom;
+    }
+    /* Add bb to the iDominated set of the immediate dominator block */
+    dvmCompilerSetBit(iDom->iDominated, bb->id);
+    return true;
+}
+
+/* Compute dominators, immediate dominator, and dominance fronter */
+static void computeDominators(CompilationUnit *cUnit)
+{
+    int numReachableBlocks = cUnit->numReachableBlocks;
+    int numTotalBlocks = cUnit->blockList.numUsed;
+
+    /* Initialize domination-related data structures */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, initializeDominationInfo,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+
+    /* Set the dominator for the root node */
+    dvmClearAllBits(cUnit->entryBlock->dominators);
+    dvmSetBit(cUnit->entryBlock->dominators, cUnit->entryBlock->id);
+
+    if (cUnit->tempBlockV == NULL) {
+        cUnit->tempBlockV = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                  false /* expandable */);
+    } else {
+        dvmClearAllBits(cUnit->tempBlockV);
+    }
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeBlockDominators,
+                                          kPreOrderDFSTraversal,
+                                          true /* isIterative */);
+
+    cUnit->entryBlock->iDom = NULL;
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeImmediateDominator,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+
+    /*
+     * Now go ahead and compute the post order traversal based on the
+     * iDominated sets.
+     */
+    if (cUnit->domPostOrderTraversal.elemList == NULL) {
+        dvmInitGrowableList(&cUnit->domPostOrderTraversal, numReachableBlocks);
+    } else {
+        cUnit->domPostOrderTraversal.numUsed = 0;
+    }
+
+    computeDomPostOrderTraversal(cUnit, cUnit->entryBlock);
+    assert(cUnit->domPostOrderTraversal.numUsed ==
+           (unsigned) cUnit->numReachableBlocks);
+
+    /* Now compute the dominance frontier for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeDominanceFrontier,
+                                          kPostOrderDOMTraversal,
+                                          false /* isIterative */);
+}
+
+/*
+ * Perform dest U= src1 ^ ~src2
+ * This is probably not general enough to be placed in BitVector.[ch].
+ */
+static void computeSuccLiveIn(BitVector *dest,
+                              const BitVector *src1,
+                              const BitVector *src2)
+{
+    if (dest->storageSize != src1->storageSize ||
+        dest->storageSize != src2->storageSize ||
+        dest->expandable != src1->expandable ||
+        dest->expandable != src2->expandable) {
+        ALOGE("Incompatible set properties");
+        dvmAbort();
+    }
+
+    unsigned int idx;
+    for (idx = 0; idx < dest->storageSize; idx++) {
+        dest->storage[idx] |= src1->storage[idx] & ~src2->storage[idx];
+    }
+}
+
+/*
+ * Iterate through all successor blocks and propagate up the live-in sets.
+ * The calculated result is used for phi-node pruning - where we only need to
+ * insert a phi node if the variable is live-in to the block.
+ */
+static bool computeBlockLiveIns(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVector *tempDalvikRegisterV = cUnit->tempDalvikRegisterV;
+
+    if (bb->dataFlowInfo == NULL) return false;
+    dvmCopyBitVector(tempDalvikRegisterV, bb->dataFlowInfo->liveInV);
+    if (bb->taken && bb->taken->dataFlowInfo)
+        computeSuccLiveIn(tempDalvikRegisterV, bb->taken->dataFlowInfo->liveInV,
+                          bb->dataFlowInfo->defV);
+    if (bb->fallThrough && bb->fallThrough->dataFlowInfo)
+        computeSuccLiveIn(tempDalvikRegisterV,
+                          bb->fallThrough->dataFlowInfo->liveInV,
+                          bb->dataFlowInfo->defV);
+    if (bb->successorBlockList.blockListType != kNotUsed) {
+        GrowableListIterator iterator;
+        dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *succBB = successorBlockInfo->block;
+            if (succBB->dataFlowInfo) {
+                computeSuccLiveIn(tempDalvikRegisterV,
+                                  succBB->dataFlowInfo->liveInV,
+                                  bb->dataFlowInfo->defV);
+            }
+        }
+    }
+    if (dvmCompareBitVectors(tempDalvikRegisterV, bb->dataFlowInfo->liveInV)) {
+        dvmCopyBitVector(bb->dataFlowInfo->liveInV, tempDalvikRegisterV);
+        return true;
+    }
+    return false;
+}
+
+/* Insert phi nodes to for each variable to the dominance frontiers */
+static void insertPhiNodes(CompilationUnit *cUnit)
+{
+    int dalvikReg;
+    const GrowableList *blockList = &cUnit->blockList;
+    BitVector *phiBlocks =
+        dvmCompilerAllocBitVector(cUnit->numBlocks, false);
+    BitVector *tmpBlocks =
+        dvmCompilerAllocBitVector(cUnit->numBlocks, false);
+    BitVector *inputBlocks =
+        dvmCompilerAllocBitVector(cUnit->numBlocks, false);
+
+    cUnit->tempDalvikRegisterV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeBlockLiveIns,
+                                          kPostOrderDFSTraversal,
+                                          true /* isIterative */);
+
+    /* Iterate through each Dalvik register */
+    for (dalvikReg = 0; dalvikReg < cUnit->numDalvikRegisters; dalvikReg++) {
+        bool change;
+        BitVectorIterator iterator;
+
+        dvmCopyBitVector(inputBlocks, cUnit->defBlockMatrix[dalvikReg]);
+        dvmClearAllBits(phiBlocks);
+
+        /* Calculate the phi blocks for each Dalvik register */
+        do {
+            change = false;
+            dvmClearAllBits(tmpBlocks);
+            dvmBitVectorIteratorInit(inputBlocks, &iterator);
+
+            while (true) {
+                int idx = dvmBitVectorIteratorNext(&iterator);
+                if (idx == -1) break;
+                BasicBlock *defBB =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, idx);
+
+                /* Merge the dominance frontier to tmpBlocks */
+                dvmUnifyBitVectors(tmpBlocks, tmpBlocks, defBB->domFrontier);
+            }
+            if (dvmCompareBitVectors(phiBlocks, tmpBlocks)) {
+                change = true;
+                dvmCopyBitVector(phiBlocks, tmpBlocks);
+
+                /*
+                 * Iterate through the original blocks plus the new ones in
+                 * the dominance frontier.
+                 */
+                dvmCopyBitVector(inputBlocks, phiBlocks);
+                dvmUnifyBitVectors(inputBlocks, inputBlocks,
+                                   cUnit->defBlockMatrix[dalvikReg]);
+            }
+        } while (change);
+
+        /*
+         * Insert a phi node for dalvikReg in the phiBlocks if the Dalvik
+         * register is in the live-in set.
+         */
+        dvmBitVectorIteratorInit(phiBlocks, &iterator);
+        while (true) {
+            int idx = dvmBitVectorIteratorNext(&iterator);
+            if (idx == -1) break;
+            BasicBlock *phiBB =
+                (BasicBlock *) dvmGrowableListGetElement(blockList, idx);
+            /* Variable will be clobbered before being used - no need for phi */
+            if (!dvmIsBitSet(phiBB->dataFlowInfo->liveInV, dalvikReg)) continue;
+            MIR *phi = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+            phi->dalvikInsn.opcode = (Opcode)kMirOpPhi;
+            phi->dalvikInsn.vA = dalvikReg;
+            phi->offset = phiBB->startOffset;
+            dvmCompilerPrependMIR(phiBB, phi);
+        }
+    }
+}
+
+/*
+ * Worker function to insert phi-operands with latest SSA names from
+ * predecessor blocks
+ */
+static bool insertPhiNodeOperands(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVector *ssaRegV = cUnit->tempSSARegisterV;
+    BitVectorIterator bvIterator;
+    GrowableList *blockList = &cUnit->blockList;
+    MIR *mir;
+
+    /* Phi nodes are at the beginning of each block */
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        if (mir->dalvikInsn.opcode != (Opcode)kMirOpPhi)
+            return true;
+        int ssaReg = mir->ssaRep->defs[0];
+        int encodedDalvikValue =
+            (int) dvmGrowableListGetElement(cUnit->ssaToDalvikMap, ssaReg);
+        int dalvikReg = DECODE_REG(encodedDalvikValue);
+
+        dvmClearAllBits(ssaRegV);
+
+        /* Iterate through the predecessors */
+        dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+        while (true) {
+            int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+            if (predIdx == -1) break;
+            BasicBlock *predBB = (BasicBlock *) dvmGrowableListGetElement(
+                                     blockList, predIdx);
+            int encodedSSAValue =
+                predBB->dataFlowInfo->dalvikToSSAMap[dalvikReg];
+            int ssaReg = DECODE_REG(encodedSSAValue);
+            dvmSetBit(ssaRegV, ssaReg);
+        }
+
+        /* Count the number of SSA registers for a Dalvik register */
+        int numUses = dvmCountSetBits(ssaRegV);
+        mir->ssaRep->numUses = numUses;
+        mir->ssaRep->uses =
+            (int *) dvmCompilerNew(sizeof(int) * numUses, false);
+        mir->ssaRep->fpUse =
+            (bool *) dvmCompilerNew(sizeof(bool) * numUses, true);
+
+        BitVectorIterator phiIterator;
+
+        dvmBitVectorIteratorInit(ssaRegV, &phiIterator);
+        int *usePtr = mir->ssaRep->uses;
+
+        /* Set the uses array for the phi node */
+        while (true) {
+            int ssaRegIdx = dvmBitVectorIteratorNext(&phiIterator);
+            if (ssaRegIdx == -1) break;
+            *usePtr++ = ssaRegIdx;
+        }
+    }
+
+    return true;
+}
+
+/* Perform SSA transformation for the whole method */
+void dvmCompilerMethodSSATransformation(CompilationUnit *cUnit)
+{
+    /* Compute the DFS order */
+    computeDFSOrder(cUnit);
+
+    /* Compute the dominator info */
+    computeDominators(cUnit);
+
+    /* Allocate data structures in preparation for SSA conversion */
+    dvmInitializeSSAConversion(cUnit);
+
+    /* Find out the "Dalvik reg def x block" relation */
+    computeDefBlockMatrix(cUnit);
+
+    /* Insert phi nodes to dominance frontiers for all variables */
+    insertPhiNodes(cUnit);
+
+    /* Rename register names by local defs and phi nodes */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+                                          kPreOrderDFSTraversal,
+                                          false /* isIterative */);
+
+    /*
+     * Shared temp bit vector used by each block to count the number of defs
+     * from all the predecessor blocks.
+     */
+    cUnit->tempSSARegisterV = dvmCompilerAllocBitVector(cUnit->numSSARegs,
+                                                        false);
+
+    /* Insert phi-operands with latest SSA names from predecessor blocks */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, insertPhiNodeOperands,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+}
+
+/* Build a loop. Return true if a loop structure is successfully identified. */
+bool dvmCompilerBuildLoop(CompilationUnit *cUnit)
+{
+    /* Compute the DFS order */
+    computeDFSOrder(cUnit);
+
+    /* Compute the dominator info */
+    computeDominators(cUnit);
+
+    /* Loop structure not recognized/supported - return false */
+    if (dvmCompilerFilterLoopBlocks(cUnit) == false)
+        return false;
+
+    /* Re-compute the DFS order just for the loop */
+    computeDFSOrder(cUnit);
+
+    /* Re-compute the dominator info just for the loop */
+    computeDominators(cUnit);
+
+    /* Allocate data structures in preparation for SSA conversion */
+    dvmInitializeSSAConversion(cUnit);
+
+    /* Find out the "Dalvik reg def x block" relation */
+    computeDefBlockMatrix(cUnit);
+
+    /* Insert phi nodes to dominance frontiers for all variables */
+    insertPhiNodes(cUnit);
+
+    /* Rename register names by local defs and phi nodes */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+                                          kPreOrderDFSTraversal,
+                                          false /* isIterative */);
+
+    /*
+     * Shared temp bit vector used by each block to count the number of defs
+     * from all the predecessor blocks.
+     */
+    cUnit->tempSSARegisterV = dvmCompilerAllocBitVector(cUnit->numSSARegs,
+                                                        false);
+
+    /* Insert phi-operands with latest SSA names from predecessor blocks */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, insertPhiNodeOperands,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+
+    if (gDvmJit.receivedSIGUSR2 || gDvmJit.printMe) {
+        dvmDumpCFG(cUnit, "/sdcard/cfg/");
+    }
+
+    return true;
+}
diff --git a/vm/compiler/Utility.cpp b/vm/compiler/Utility.cpp
new file mode 100644
index 0000000..2fe94d2
--- /dev/null
+++ b/vm/compiler/Utility.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+static ArenaMemBlock *arenaHead, *currentArena;
+static int numArenaBlocks;
+
+/* Allocate the initial memory block for arena-based allocation */
+bool dvmCompilerHeapInit(void)
+{
+    assert(arenaHead == NULL);
+    arenaHead =
+        (ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE);
+    if (arenaHead == NULL) {
+        ALOGE("No memory left to create compiler heap memory");
+        return false;
+    }
+    arenaHead->blockSize = ARENA_DEFAULT_SIZE;
+    currentArena = arenaHead;
+    currentArena->bytesAllocated = 0;
+    currentArena->next = NULL;
+    numArenaBlocks = 1;
+
+    return true;
+}
+
+/* Arena-based malloc for compilation tasks */
+void * dvmCompilerNew(size_t size, bool zero)
+{
+    size = (size + 3) & ~3;
+retry:
+    /* Normal case - space is available in the current page */
+    if (size + currentArena->bytesAllocated <= currentArena->blockSize) {
+        void *ptr;
+        ptr = &currentArena->ptr[currentArena->bytesAllocated];
+        currentArena->bytesAllocated += size;
+        if (zero) {
+            memset(ptr, 0, size);
+        }
+        return ptr;
+    } else {
+        /*
+         * See if there are previously allocated arena blocks before the last
+         * reset
+         */
+        if (currentArena->next) {
+            currentArena = currentArena->next;
+            goto retry;
+        }
+
+        size_t blockSize = (size < ARENA_DEFAULT_SIZE) ?
+                          ARENA_DEFAULT_SIZE : size;
+        /* Time to allocate a new arena */
+        ArenaMemBlock *newArena = (ArenaMemBlock *)
+            malloc(sizeof(ArenaMemBlock) + blockSize);
+        if (newArena == NULL) {
+            ALOGE("Arena allocation failure");
+            dvmAbort();
+        }
+        newArena->blockSize = blockSize;
+        newArena->bytesAllocated = 0;
+        newArena->next = NULL;
+        currentArena->next = newArena;
+        currentArena = newArena;
+        numArenaBlocks++;
+        if (numArenaBlocks > 10)
+            ALOGI("Total arena pages for JIT: %d", numArenaBlocks);
+        goto retry;
+    }
+    /* Should not reach here */
+    dvmAbort();
+}
+
+/* Reclaim all the arena blocks allocated so far */
+void dvmCompilerArenaReset(void)
+{
+    ArenaMemBlock *block;
+
+    for (block = arenaHead; block; block = block->next) {
+        block->bytesAllocated = 0;
+    }
+    currentArena = arenaHead;
+}
+
+/* Growable List initialization */
+void dvmInitGrowableList(GrowableList *gList, size_t initLength)
+{
+    gList->numAllocated = initLength;
+    gList->numUsed = 0;
+    gList->elemList = (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * initLength,
+                                                  true);
+}
+
+/* Expand the capacity of a growable list */
+static void expandGrowableList(GrowableList *gList)
+{
+    int newLength = gList->numAllocated;
+    if (newLength < 128) {
+        newLength <<= 1;
+    } else {
+        newLength += 128;
+    }
+    intptr_t *newArray =
+        (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * newLength, true);
+    memcpy(newArray, gList->elemList, sizeof(intptr_t) * gList->numAllocated);
+    gList->numAllocated = newLength;
+    gList->elemList = newArray;
+}
+
+/* Insert a new element into the growable list */
+void dvmInsertGrowableList(GrowableList *gList, intptr_t elem)
+{
+    assert(gList->numAllocated != 0);
+    if (gList->numUsed == gList->numAllocated) {
+        expandGrowableList(gList);
+    }
+    gList->elemList[gList->numUsed++] = elem;
+}
+
+void dvmGrowableListIteratorInit(GrowableList *gList,
+                                 GrowableListIterator *iterator)
+{
+    iterator->list = gList;
+    iterator->idx = 0;
+    iterator->size = gList->numUsed;
+}
+
+intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator)
+{
+    assert(iterator->size == iterator->list->numUsed);
+    if (iterator->idx == iterator->size) return 0;
+    return iterator->list->elemList[iterator->idx++];
+}
+
+intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx)
+{
+    assert(idx < gList->numUsed);
+    return gList->elemList[idx];
+}
+
+/* Debug Utility - dump a compilation unit */
+void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit)
+{
+    BasicBlock *bb;
+    const char *blockTypeNames[] = {
+        "Normal Chaining Cell",
+        "Hot Chaining Cell",
+        "Singleton Chaining Cell",
+        "Predicted Chaining Cell",
+        "Backward Branch",
+        "Chaining Cell Gap",
+        "N/A",
+        "Entry Block",
+        "Code Block",
+        "Exit Block",
+        "PC Reconstruction",
+        "Exception Handling",
+    };
+
+    ALOGD("Compiling %s %s", cUnit->method->clazz->descriptor,
+         cUnit->method->name);
+    ALOGD("%d insns", dvmGetMethodInsnsSize(cUnit->method));
+    ALOGD("%d blocks in total", cUnit->numBlocks);
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    while (true) {
+        bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        ALOGD("Block %d (%s) (insn %04x - %04x%s)",
+             bb->id,
+             blockTypeNames[bb->blockType],
+             bb->startOffset,
+             bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset,
+             bb->lastMIRInsn ? "" : " empty");
+        if (bb->taken) {
+            ALOGD("  Taken branch: block %d (%04x)",
+                 bb->taken->id, bb->taken->startOffset);
+        }
+        if (bb->fallThrough) {
+            ALOGD("  Fallthrough : block %d (%04x)",
+                 bb->fallThrough->id, bb->fallThrough->startOffset);
+        }
+    }
+}
+
+/*
+ * dvmHashForeach callback.
+ */
+static int dumpMethodStats(void *compilerMethodStats, void *totalMethodStats)
+{
+    CompilerMethodStats *methodStats =
+        (CompilerMethodStats *) compilerMethodStats;
+    CompilerMethodStats *totalStats =
+        (CompilerMethodStats *) totalMethodStats;
+
+    totalStats->dalvikSize += methodStats->dalvikSize;
+    totalStats->compiledDalvikSize += methodStats->compiledDalvikSize;
+    totalStats->nativeSize += methodStats->nativeSize;
+
+    /* Enable the following when fine-tuning the JIT performance */
+#if 0
+    int limit = (methodStats->dalvikSize >> 2) * 3;
+
+    /* If over 3/4 of the Dalvik code is compiled, print something */
+    if (methodStats->compiledDalvikSize >= limit) {
+        ALOGD("Method stats: %s%s, %d/%d (compiled/total Dalvik), %d (native)",
+             methodStats->method->clazz->descriptor,
+             methodStats->method->name,
+             methodStats->compiledDalvikSize,
+             methodStats->dalvikSize,
+             methodStats->nativeSize);
+    }
+#endif
+    return 0;
+}
+
+/*
+ * Dump the current stats of the compiler, including number of bytes used in
+ * the code cache, arena size, and work queue length, and various JIT stats.
+ */
+void dvmCompilerDumpStats(void)
+{
+    CompilerMethodStats totalMethodStats;
+
+    memset(&totalMethodStats, 0, sizeof(CompilerMethodStats));
+    ALOGD("%d compilations using %d + %d bytes",
+         gDvmJit.numCompilations,
+         gDvmJit.templateSize,
+         gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+    ALOGD("Compiler arena uses %d blocks (%d bytes each)",
+         numArenaBlocks, ARENA_DEFAULT_SIZE);
+    ALOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength,
+         gDvmJit.compilerMaxQueued);
+    dvmJitStats();
+    dvmCompilerArchDump();
+    if (gDvmJit.methodStatsTable) {
+        dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats,
+                       &totalMethodStats);
+        ALOGD("Code size stats: %d/%d (compiled/total Dalvik), %d (native)",
+             totalMethodStats.compiledDalvikSize,
+             totalMethodStats.dalvikSize,
+             totalMethodStats.nativeSize);
+    }
+}
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ *
+ * NOTE: this is the sister implementation of dvmAllocBitVector. In this version
+ * memory is allocated from the compiler arena.
+ */
+BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable)
+{
+    BitVector* bv;
+    unsigned int count;
+
+    assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
+
+    bv = (BitVector*) dvmCompilerNew(sizeof(BitVector), false);
+
+    count = (startBits + 31) >> 5;
+
+    bv->storageSize = count;
+    bv->expandable = expandable;
+    bv->storage = (u4*) dvmCompilerNew(count * sizeof(u4), true);
+    return bv;
+}
+
+/*
+ * Mark the specified bit as "set".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ *
+ * NOTE: this is the sister implementation of dvmSetBit. In this version
+ * memory is allocated from the compiler arena.
+ */
+bool dvmCompilerSetBit(BitVector *pBits, unsigned int num)
+{
+    if (num >= pBits->storageSize * sizeof(u4) * 8) {
+        if (!pBits->expandable)
+            dvmAbort();
+
+        /* Round up to word boundaries for "num+1" bits */
+        unsigned int newSize = (num + 1 + 31) >> 5;
+        assert(newSize > pBits->storageSize);
+        u4 *newStorage = (u4*)dvmCompilerNew(newSize * sizeof(u4), false);
+        memcpy(newStorage, pBits->storage, pBits->storageSize * sizeof(u4));
+        memset(&newStorage[pBits->storageSize], 0,
+               (newSize - pBits->storageSize) * sizeof(u4));
+        pBits->storage = newStorage;
+        pBits->storageSize = newSize;
+    }
+
+    pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+    return true;
+}
+
+/*
+ * Mark the specified bit as "unset".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ *
+ * NOTE: this is the sister implementation of dvmClearBit. In this version
+ * memory is allocated from the compiler arena.
+ */
+bool dvmCompilerClearBit(BitVector *pBits, unsigned int num)
+{
+    if (num >= pBits->storageSize * sizeof(u4) * 8) {
+        ALOGE("Trying to clear a bit that is not set in the vector yet!");
+        dvmAbort();
+    }
+
+    pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+    return true;
+}
+
+/*
+ * If set is true, mark all bits as 1. Otherwise mark all bits as 0.
+ */
+void dvmCompilerMarkAllBits(BitVector *pBits, bool set)
+{
+    int value = set ? -1 : 0;
+    memset(pBits->storage, value, pBits->storageSize * (int)sizeof(u4));
+}
+
+void dvmDebugBitVector(char *msg, const BitVector *bv, int length)
+{
+    int i;
+
+    ALOGE("%s", msg);
+    for (i = 0; i < length; i++) {
+        if (dvmIsBitSet(bv, i)) {
+            ALOGE("    Bit %d is set", i);
+        }
+    }
+}
+
+void dvmCompilerAbort(CompilationUnit *cUnit)
+{
+    ALOGE("Jit: aborting trace compilation, reverting to interpreter");
+    /* Force a traceback in debug builds */
+    assert(0);
+    /*
+     * Abort translation and force to interpret-only for this trace
+     * Matching setjmp in compiler thread work loop in Compiler.c.
+     */
+    longjmp(*cUnit->bailPtr, 1);
+}
+
+void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg,
+                           const BitVector *bv, int length)
+{
+    int i;
+
+    ALOGE("%s", msg);
+    for (i = 0; i < length; i++) {
+        if (dvmIsBitSet(bv, i)) {
+            BasicBlock *bb =
+                (BasicBlock *) dvmGrowableListGetElement(blocks, i);
+            char blockName[BLOCK_NAME_LEN];
+            dvmGetBlockName(bb, blockName);
+            ALOGE("Bit %d / %s is set", i, blockName);
+        }
+    }
+}
+
+void dvmGetBlockName(BasicBlock *bb, char *name)
+{
+    switch (bb->blockType) {
+        case kEntryBlock:
+            snprintf(name, BLOCK_NAME_LEN, "entry");
+            break;
+        case kExitBlock:
+            snprintf(name, BLOCK_NAME_LEN, "exit");
+            break;
+        case kDalvikByteCode:
+            snprintf(name, BLOCK_NAME_LEN, "block%04x", bb->startOffset);
+            break;
+        case kChainingCellNormal:
+            snprintf(name, BLOCK_NAME_LEN, "chain%04x", bb->startOffset);
+            break;
+        case kExceptionHandling:
+            snprintf(name, BLOCK_NAME_LEN, "exception%04x", bb->startOffset);
+            break;
+        default:
+            snprintf(name, BLOCK_NAME_LEN, "??");
+            break;
+    }
+}
diff --git a/vm/compiler/codegen/CodegenFactory.cpp b/vm/compiler/codegen/CodegenFactory.cpp
new file mode 100644
index 0000000..f42ae74
--- /dev/null
+++ b/vm/compiler/codegen/CodegenFactory.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains target-independent codegen and support, and is
+ * included by:
+ *
+ *        $(TARGET_ARCH)/Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directories below this one.
+ *
+ * Prior to including this file, TGT_LIR should be #defined.
+ * For example, for arm:
+ *    #define TGT_LIR ArmLIR
+ * and for x86:
+ *    #define TGT_LIR X86LIR
+ */
+
+
+/* Load a word at base + displacement.  Displacement must be word multiple */
+static TGT_LIR *loadWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rDest)
+{
+    return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
+                        INVALID_SREG);
+}
+
+static TGT_LIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc)
+{
+    return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord);
+}
+
+/*
+ * Load a Dalvik register into a physical register.  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+                            int reg1)
+{
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopy(cUnit, reg1, rlSrc.lowReg);
+    } else  if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+        loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                     reg1);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * register.  Should be used when loading to a fixed register (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 int reg1)
+{
+    dvmCompilerClobber(cUnit, reg1);
+    dvmCompilerMarkInUse(cUnit, reg1);
+    loadValueDirect(cUnit, rlSrc, reg1);
+}
+
+/*
+ * Load a Dalvik register pair into a physical register[s].  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                int regLo, int regHi)
+{
+    rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg);
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF,
+                         offsetof(Thread, interpSave.retval),
+                         regLo, regHi, INVALID_SREG);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+            loadBaseDispWide(cUnit, NULL, rFP,
+                             dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                             regLo, regHi, INVALID_SREG);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * registers.  Should be used when loading to a fixed registers (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                     int regLo, int regHi)
+{
+    dvmCompilerClobber(cUnit, regLo);
+    dvmCompilerClobber(cUnit, regHi);
+    dvmCompilerMarkInUse(cUnit, regLo);
+    dvmCompilerMarkInUse(cUnit, regHi);
+    loadValueDirectWide(cUnit, rlSrc, regLo, regHi);
+}
+
+static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc,
+                             RegisterClass opKind)
+{
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirect(cUnit, rlSrc, rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+    } else if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                     rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+    }
+    return rlSrc;
+}
+
+static void storeValue(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(!rlDest.wide);
+    assert(!rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirect(cUnit, rlSrc, rlDest.lowReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                      rlDest.lowReg, kWord);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    } else {
+        dvmCompilerResetDefLoc(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
+
+static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 RegisterClass opKind)
+{
+    assert(rlSrc.wide);
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+        dvmCompilerMarkLive(cUnit, rlSrc.highReg,
+                            dvmCompilerSRegHi(rlSrc.sRegLow));
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF,
+                         offsetof(Thread, interpSave.retval),
+                         rlSrc.lowReg, rlSrc.highReg, INVALID_SREG);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        dvmCompilerClobber(cUnit, rlSrc.highReg);
+    }
+    return rlSrc;
+}
+
+static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest,
+                           RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg));
+    assert(rlDest.wide);
+    assert(rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            dvmCompilerIsLive(cUnit, rlSrc.highReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg,
+                           rlSrc.lowReg, rlSrc.highReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            rlDest.highReg = rlSrc.highReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+            dvmCompilerClobber(cUnit, rlSrc.highReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg,
+                            rlDest.highReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkLive(cUnit, rlDest.highReg,
+                        dvmCompilerSRegHi(rlDest.sRegLow));
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+    dvmCompilerMarkDirty(cUnit, rlDest.highReg);
+    dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                          rlDest.lowReg, rlDest.highReg);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        dvmCompilerResetDefLocWide(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) ||
+            dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            assert((vReg+1) == dvmCompilerS2VReg(cUnit,
+                                     dvmCompilerSRegHi(rlDest.sRegLow)));
+            storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg,
+                              rlDest.highReg);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            dvmCompilerMarkClean(cUnit, rlDest.highReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h
new file mode 100644
index 0000000..f2b36a3
--- /dev/null
+++ b/vm/compiler/codegen/CompilerCodegen.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILERCODEGEN_H_
+#define DALVIK_VM_COMPILERCODEGEN_H_
+
+#include "compiler/CompilerIR.h"
+
+/* Maximal number of switch cases to have inline chains */
+#define MAX_CHAINED_SWITCH_CASES 64
+
+/* Work unit is architecture dependent */
+bool dvmCompilerDoWork(CompilerWorkOrder *work);
+
+/* Lower middle-level IR to low-level IR */
+#ifndef ARCH_IA32
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit);
+#else /* ARCH_IA32 */
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit, JitTranslationInfo* info);
+#endif
+
+/* Lower middle-level IR to low-level IR for the whole method */
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit);
+
+/* Assemble LIR into machine code */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info);
+
+/* Perform translation chain operation. */
+extern "C" void* dvmJitChain(void* tgtAddr, u4* branchAddr);
+
+/* Install class objects in the literal pool */
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit,
+                                      char *codeAddress);
+
+/* Patch inline cache content for polymorphic callsites */
+bool dvmJitPatchInlineCache(void *cellPtr, void *contentPtr);
+
+/* Implemented in the codegen/<target>/ArchUtility.c */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit);
+
+/* Implemented in the codegen/<target>/Assembler.c */
+void dvmCompilerPatchInlineCache(void);
+
+/* Implemented in codegen/<target>/Ralloc.c */
+void dvmCompilerLocalRegAlloc(CompilationUnit *cUnit);
+
+/* Implemented in codegen/<target>/Thumb<version>Util.c */
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+JitInstructionSetType dvmCompilerInstructionSet(void);
+
+/*
+ * Implemented in codegen/<target>/<target_variant>/ArchVariant.c
+ * Architecture-specific initializations and checks
+ */
+bool dvmCompilerArchVariantInit(void);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+int dvmCompilerTargetOptHint(int key);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind);
+
+#endif  // DALVIK_VM_COMPILERCODEGEN_H_
diff --git a/vm/compiler/codegen/Optimizer.h b/vm/compiler/codegen/Optimizer.h
new file mode 100644
index 0000000..43d98ed
--- /dev/null
+++ b/vm/compiler/codegen/Optimizer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_OPTIMIZATION_H_
+#define DALVIK_VM_COMPILER_OPTIMIZATION_H_
+
+#include "Dalvik.h"
+
+/*
+ * If the corresponding bit is set in gDvmJit.disableOpt, the selected
+ * optimization will be suppressed.
+ */
+enum optControlVector {
+    kLoadStoreElimination = 0,
+    kLoadHoisting,
+    kTrackLiveTemps,
+    kSuppressLoads,
+    kMethodInlining,
+    kMethodJit,
+};
+
+/* Forward declarations */
+struct CompilationUnit;
+struct LIR;
+
+void dvmCompilerApplyLocalOptimizations(struct CompilationUnit *cUnit,
+                                        struct LIR *head,
+                                        struct LIR *tail);
+
+void dvmCompilerApplyGlobalOptimizations(struct CompilationUnit *cUnit);
+
+#endif  // DALVIK_VM_COMPILER_OPTIMIZATION_H_
diff --git a/vm/compiler/codegen/Ralloc.h b/vm/compiler/codegen/Ralloc.h
new file mode 100644
index 0000000..2296fbc
--- /dev/null
+++ b/vm/compiler/codegen/Ralloc.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains target independent register alloction support.
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+
+/*
+ * Return most flexible allowed register class based on size.
+ * Bug: 2813841
+ * Must use a core register for data types narrower than word (due
+ * to possible unaligned load/store.
+ */
+static inline RegisterClass dvmCompilerRegClassBySize(OpSize size)
+{
+    return (size == kUnsignedHalf ||
+            size == kSignedHalf ||
+            size == kUnsignedByte ||
+            size == kSignedByte ) ? kCoreReg : kAnyReg;
+}
+
+static inline int dvmCompilerS2VReg(CompilationUnit *cUnit, int sReg)
+{
+    assert(sReg != INVALID_SREG);
+    return DECODE_REG(dvmConvertSSARegToDalvik(cUnit, sReg));
+}
+
+/* Reset the tracker to unknown state */
+static inline void dvmCompilerResetNullCheck(CompilationUnit *cUnit)
+{
+    dvmClearAllBits(cUnit->regPool->nullCheckedRegs);
+}
+
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+static inline int dvmCompilerSRegHi(int lowSreg) {
+    return (lowSreg == INVALID_SREG) ? INVALID_SREG : lowSreg + 1;
+}
+
+
+static inline bool dvmCompilerLiveOut(CompilationUnit *cUnit, int sReg)
+{
+    //TODO: fully implement
+    return true;
+}
+
+static inline int dvmCompilerSSASrc(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numUses > num);
+    return mir->ssaRep->uses[num];
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update);
+/* Mark a temp register as dead.  Does not affect allocation state. */
+extern void dvmCompilerClobber(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit,
+                                        RegLocation loc);
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc);
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg);
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg,
+                                int highReg);
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl);
+
+/* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num);
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                               LIR *start, LIR *finish);
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                                   LIR *start, LIR *finish);
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high);
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high);
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num);
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num);
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit);
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit);
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg);
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit);
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit);
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit);
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc);
+
+//FIXME - this needs to also check the preserved pool.
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg);
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit);
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit);
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg);
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit);
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl);
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit);
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerFlushRegWide(CompilationUnit *cUnit, int reg1, int reg2);
+
+extern void dvmCompilerFlushReg(CompilationUnit *cUnit, int reg);
+
+/*
+ * Architecture-dependent register allocation routines implemented in
+ * ${TARGET_ARCH}/${TARGET_ARCH_VARIANT}/Ralloc.c
+ */
+extern int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+                                         bool fpHint, int regClass);
+
+extern int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+                                     int regClass);
+
+extern ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+extern void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo,
+                                   int destHi, int srcLo, int srcHi);
+
+extern void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                                    int displacement, int rSrc, OpSize size);
+
+extern void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                        int displacement, int rSrcLo,
+                                        int rSrcHi);
diff --git a/vm/compiler/codegen/RallocUtil.cpp b/vm/compiler/codegen/RallocUtil.cpp
new file mode 100644
index 0000000..f4a46f0
--- /dev/null
+++ b/vm/compiler/codegen/RallocUtil.cpp
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "Ralloc.h"
+
+#define SREG(c, s) ((c)->regLocation[(s)].sRegLow)
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i < cUnit->regPool->numCoreTemps; i++) {
+        cUnit->regPool->coreTemps[i].inUse = false;
+    }
+    for (i=0; i < cUnit->regPool->numFPTemps; i++) {
+        cUnit->regPool->FPTemps[i].inUse = false;
+    }
+}
+
+ /* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num)
+{
+    int i;
+    for (i=0; i < num; i++) {
+        regs[i].reg = regNums[i];
+        regs[i].inUse = false;
+        regs[i].pair = false;
+        regs[i].live = false;
+        regs[i].dirty = false;
+        regs[i].sReg = INVALID_SREG;
+    }
+}
+
+static void dumpRegPool(RegisterInfo *p, int numRegs)
+{
+    int i;
+    ALOGE("================================================");
+    for (i=0; i < numRegs; i++ ){
+        ALOGE("R[%d]: U:%d, P:%d, part:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+           p[i].reg, p[i].inUse, p[i].pair, p[i].partner, p[i].live,
+           p[i].dirty, p[i].sReg,(int)p[i].defStart, (int)p[i].defEnd);
+    }
+    ALOGE("================================================");
+}
+
+static RegisterInfo *getRegInfo(CompilationUnit *cUnit, int reg)
+{
+    int numTemps = cUnit->regPool->numCoreTemps;
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    ALOGE("Tried to get info on a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+    return NULL;
+}
+
+void dvmCompilerFlushRegWide(CompilationUnit *cUnit, int reg1, int reg2)
+{
+    RegisterInfo *info1 = getRegInfo(cUnit, reg1);
+    RegisterInfo *info2 = getRegInfo(cUnit, reg2);
+    assert(info1 && info2 && info1->pair && info2->pair &&
+           (info1->partner == info2->reg) &&
+           (info2->partner == info1->reg));
+    if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+        info1->dirty = false;
+        info2->dirty = false;
+        if (dvmCompilerS2VReg(cUnit, info2->sReg) <
+            dvmCompilerS2VReg(cUnit, info1->sReg))
+            info1 = info2;
+        dvmCompilerFlushRegWideImpl(cUnit, rFP,
+                                    dvmCompilerS2VReg(cUnit, info1->sReg) << 2,
+                                    info1->reg, info1->partner);
+    }
+}
+
+void dvmCompilerFlushReg(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if (info->live && info->dirty) {
+        info->dirty = false;
+        dvmCompilerFlushRegImpl(cUnit, rFP,
+                                dvmCompilerS2VReg(cUnit, info->sReg) << 2,
+                                reg, kWord);
+    }
+}
+
+/* return true if found reg to clobber */
+static bool clobberRegBody(CompilationUnit *cUnit, RegisterInfo *p,
+                           int numTemps, int reg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            if (p[i].live && p[i].dirty) {
+                if (p[i].pair) {
+                    dvmCompilerFlushRegWide(cUnit, p[i].reg, p[i].partner);
+                } else {
+                    dvmCompilerFlushReg(cUnit, p[i].reg);
+                }
+            }
+            p[i].live = false;
+            p[i].sReg = INVALID_SREG;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+            if (p[i].pair) {
+                p[i].pair = false;
+                /* partners should be in same pool */
+                clobberRegBody(cUnit, p, numTemps, p[i].partner);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Mark a temp register as dead.  Does not affect allocation state. */
+void dvmCompilerClobber(CompilationUnit *cUnit, int reg)
+{
+    if (!clobberRegBody(cUnit, cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps, reg)) {
+        clobberRegBody(cUnit, cUnit->regPool->FPTemps,
+                       cUnit->regPool->numFPTemps, reg);
+    }
+}
+
+static void clobberSRegBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].sReg == sReg) {
+            p[i].live = false;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+        }
+    }
+}
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg)
+{
+    clobberSRegBody(cUnit->regPool->coreTemps, cUnit->regPool->numCoreTemps,
+                    sReg);
+    clobberSRegBody(cUnit->regPool->FPTemps, cUnit->regPool->numFPTemps,
+                    sReg);
+}
+
+static int allocTempBody(CompilationUnit *cUnit, RegisterInfo *p, int numTemps,
+                         int *nextTemp, bool required)
+{
+    int i;
+    int next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next].live) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    if (required) {
+        ALOGE("No free temp registers");
+        dvmCompilerAbort(cUnit);
+    }
+    return -1;  // No register available
+}
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit)
+{
+    RegisterInfo *p = cUnit->regPool->FPTemps;
+    int numTemps = cUnit->regPool->numFPTemps;
+    int next = cUnit->regPool->nextFPTemp;
+    int i;
+
+    for (i=0; i < numTemps; i+=2) {
+        /* Cleanup - not all targets need aligned regs */
+        if (next & 1)
+            next++;
+        if (next >= numTemps)
+            next = 0;
+        if ((!p[next].inUse && !p[next].live) &&
+            (!p[next+1].inUse && !p[next+1].live)) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    next = cUnit->regPool->nextFPTemp;
+    for (i=0; i < numTemps; i+=2) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next+1].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    ALOGE("No free temp registers");
+    dvmCompilerAbort(cUnit);
+    return -1;
+}
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->FPTemps,
+                         cUnit->regPool->numFPTemps,
+                         &cUnit->regPool->nextFPTemp, true);
+}
+
+static RegisterInfo *allocLiveBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    if (sReg == -1)
+        return NULL;
+    for (i=0; i < numTemps; i++) {
+        if (p[i].live && (p[i].sReg == sReg)) {
+            p[i].inUse = true;
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+static RegisterInfo *allocLive(CompilationUnit *cUnit, int sReg,
+                               int regClass)
+{
+    RegisterInfo *res = NULL;
+    switch(regClass) {
+        case kAnyReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            if (res)
+                break;
+            /* Intentional fallthrough */
+        case kCoreReg:
+            res = allocLiveBody(cUnit->regPool->coreTemps,
+                                cUnit->regPool->numCoreTemps, sReg);
+            break;
+        case kFPReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            break;
+        default:
+            ALOGE("Invalid register type");
+            dvmCompilerAbort(cUnit);
+    }
+    return res;
+}
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    ALOGE("Tried to free a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    return NULL;
+}
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    ALOGE("Tried to lock a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = getRegInfo(cUnit, reg);
+    p->defStart = NULL;
+    p->defEnd = NULL;
+}
+
+static void nullifyRange(CompilationUnit *cUnit, LIR *start, LIR *finish,
+                         int sReg1, int sReg2)
+{
+    if (start && finish) {
+        LIR *p;
+        assert(sReg1 == sReg2);
+        for (p = start; ;p = p->next) {
+            ((ArmLIR *)p)->flags.isNop = true;
+            if (p == finish)
+                break;
+        }
+    }
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                    LIR *start, LIR *finish)
+{
+    assert(!rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                        LIR *start, LIR *finish)
+{
+    assert(rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);  // Only track low of pair
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl)
+{
+    assert(rl.wide);
+    if (rl.location == kLocPhysReg) {
+        RegisterInfo *infoLo = getRegInfo(cUnit, rl.lowReg);
+        RegisterInfo *infoHi = getRegInfo(cUnit, rl.highReg);
+        if (!infoLo->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoLo->pair);
+        }
+        if (!infoHi->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoHi->pair);
+        }
+        assert(infoLo->pair);
+        assert(infoHi->pair);
+        assert(infoLo->partner == infoHi->reg);
+        assert(infoHi->partner == infoLo->reg);
+        infoLo->pair = false;
+        infoHi->pair = false;
+        infoLo->defStart = NULL;
+        infoLo->defEnd = NULL;
+        infoHi->defStart = NULL;
+        infoHi->defEnd = NULL;
+    }
+    rl.wide = false;
+    return rl;
+}
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(!rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(!p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+}
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);
+}
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerLockTemp(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+}
+
+// Make sure nothing is live and dirty
+static void flushAllRegsBody(CompilationUnit *cUnit, RegisterInfo *info,
+                             int numRegs)
+{
+    int i;
+    for (i=0; i < numRegs; i++) {
+        if (info[i].live && info[i].dirty) {
+            if (info[i].pair) {
+                dvmCompilerFlushRegWide(cUnit, info[i].reg, info[i].partner);
+            } else {
+                dvmCompilerFlushReg(cUnit, info[i].reg);
+            }
+        }
+    }
+}
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit)
+{
+    flushAllRegsBody(cUnit, cUnit->regPool->coreTemps,
+                     cUnit->regPool->numCoreTemps);
+    flushAllRegsBody(cUnit, cUnit->regPool->FPTemps,
+                     cUnit->regPool->numFPTemps);
+    dvmCompilerClobberAllRegs(cUnit);
+}
+
+
+//TUNING: rewrite all of this reg stuff.  Probably use an attribute table
+static bool regClassMatches(int regClass, int reg)
+{
+    if (regClass == kAnyReg) {
+        return true;
+    } else if (regClass == kCoreReg) {
+        return !FPREG(reg);
+    } else {
+        return FPREG(reg);
+    }
+}
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if ((info->reg == reg) && (info->sReg == sReg) && info->live) {
+        return;  /* already live */
+    } else if (sReg != INVALID_SREG) {
+        dvmCompilerClobberSReg(cUnit, sReg);
+        info->live = true;
+    } else {
+        /* Can't be live if no associated sReg */
+        info->live = false;
+    }
+    info->sReg = sReg;
+}
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg, int highReg)
+{
+    RegisterInfo *infoLo = getRegInfo(cUnit, lowReg);
+    RegisterInfo *infoHi = getRegInfo(cUnit, highReg);
+    infoLo->pair = infoHi->pair = true;
+    infoLo->partner = highReg;
+    infoHi->partner = lowReg;
+}
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = false;
+}
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = true;
+}
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg)
+{
+      RegisterInfo *info = getRegInfo(cUnit, reg);
+          info->inUse = true;
+}
+
+static void copyRegInfo(CompilationUnit *cUnit, int newReg, int oldReg)
+{
+    RegisterInfo *newInfo = getRegInfo(cUnit, newReg);
+    RegisterInfo *oldInfo = getRegInfo(cUnit, oldReg);
+    *newInfo = *oldInfo;
+    newInfo->reg = newReg;
+}
+
+/*
+ * Return an updated location record with current in-register status.
+ * If the value lives in live temps, reflect that fact.  No code
+ * is generated.  The the live value is part of an older pair,
+ * clobber both low and high.
+ * TUNING: clobbering both is a bit heavy-handed, but the alternative
+ * is a bit complex when dealing with FP regs.  Examine code to see
+ * if it's worthwhile trying to be more clever here.
+ */
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit, RegLocation loc)
+{
+    assert(!loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        if (infoLo) {
+            if (infoLo->pair) {
+                dvmCompilerClobber(cUnit, infoLo->reg);
+                dvmCompilerClobber(cUnit, infoLo->partner);
+            } else {
+                loc.lowReg = infoLo->reg;
+                loc.location = kLocPhysReg;
+            }
+        }
+    }
+
+    return loc;
+}
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc)
+{
+    assert(loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        // Are the dalvik regs already live in physical registers?
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        RegisterInfo *infoHi = allocLive(cUnit,
+              dvmCompilerSRegHi(loc.sRegLow), kAnyReg);
+        bool match = true;
+        match = match && (infoLo != NULL);
+        match = match && (infoHi != NULL);
+        // Are they both core or both FP?
+        match = match && (FPREG(infoLo->reg) == FPREG(infoHi->reg));
+        // If a pair of floating point singles, are they properly aligned?
+        if (match && FPREG(infoLo->reg)) {
+            match &= ((infoLo->reg & 0x1) == 0);
+            match &= ((infoHi->reg - infoLo->reg) == 1);
+        }
+        // If previously used as a pair, it is the same pair?
+        if (match && (infoLo->pair || infoHi->pair)) {
+            match = (infoLo->pair == infoHi->pair);
+            match &= ((infoLo->reg == infoHi->partner) &&
+                      (infoHi->reg == infoLo->partner));
+        }
+        if (match) {
+            // Can reuse - update the register usage info
+            loc.lowReg = infoLo->reg;
+            loc.highReg = infoHi->reg;
+            loc.location = kLocPhysReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+            return loc;
+        }
+        // Can't easily reuse - clobber any overlaps
+        if (infoLo) {
+            dvmCompilerClobber(cUnit, infoLo->reg);
+            if (infoLo->pair)
+                dvmCompilerClobber(cUnit, infoLo->partner);
+        }
+        if (infoHi) {
+            dvmCompilerClobber(cUnit, infoHi->reg);
+            if (infoHi->pair)
+                dvmCompilerClobber(cUnit, infoHi->partner);
+        }
+    }
+
+    return loc;
+}
+
+static RegLocation evalLocWide(CompilationUnit *cUnit, RegLocation loc,
+                               int regClass, bool update)
+{
+    assert(loc.wide);
+    int newRegs;
+    int lowReg;
+    int highReg;
+
+    loc = dvmCompilerUpdateLocWide(cUnit, loc);
+
+    /* If already in registers, we can assume proper form.  Right reg class? */
+    if (loc.location == kLocPhysReg) {
+        assert(FPREG(loc.lowReg) == FPREG(loc.highReg));
+        assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Reallocate and copy */
+            newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+            lowReg = newRegs & 0xff;
+            highReg = (newRegs >> 8) & 0xff;
+            dvmCompilerRegCopyWide(cUnit, lowReg, highReg, loc.lowReg,
+                                   loc.highReg);
+            copyRegInfo(cUnit, lowReg, loc.lowReg);
+            copyRegInfo(cUnit, highReg, loc.highReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.highReg);
+            loc.lowReg = lowReg;
+            loc.highReg = highReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+    assert((loc.location != kLocRetval) ||
+           (dvmCompilerSRegHi(loc.sRegLow) == INVALID_SREG));
+
+    newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+    loc.lowReg = newRegs & 0xff;
+    loc.highReg = (newRegs >> 8) & 0xff;
+
+    dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+        dvmCompilerMarkLive(cUnit, loc.highReg, dvmCompilerSRegHi(loc.sRegLow));
+    }
+    assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+    return loc;
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update)
+{
+    int newReg;
+    if (loc.wide)
+        return evalLocWide(cUnit, loc, regClass, update);
+    loc = dvmCompilerUpdateLoc(cUnit, loc);
+
+    if (loc.location == kLocPhysReg) {
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Realloc, copy and transfer ownership */
+            newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+            dvmCompilerRegCopy(cUnit, newReg, loc.lowReg);
+            copyRegInfo(cUnit, newReg, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            loc.lowReg = newReg;
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+
+    newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+    loc.lowReg = newReg;
+
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+    }
+    return loc;
+}
+
+static inline int getDestSSAName(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numDefs > num);
+    return mir->ssaRep->defs[num];
+}
+
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num)
+{
+    RegLocation loc = cUnit->regLocation[
+         SREG(cUnit, dvmCompilerSSASrc(mir, num))];
+    loc.fp = cUnit->regLocation[dvmCompilerSSASrc(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num)
+{
+    RegLocation loc = cUnit->regLocation[SREG(cUnit, getDestSSAName(mir, num))];
+    loc.fp = cUnit->regLocation[getDestSSAName(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+static RegLocation getLocWide(CompilationUnit *cUnit, MIR *mir,
+                              int low, int high, bool isSrc)
+{
+    RegLocation lowLoc;
+    RegLocation highLoc;
+    /* Copy loc record for low word and patch in data from high word */
+    if (isSrc) {
+        lowLoc = dvmCompilerGetSrc(cUnit, mir, low);
+        highLoc = dvmCompilerGetSrc(cUnit, mir, high);
+    } else {
+        lowLoc = dvmCompilerGetDest(cUnit, mir, low);
+        highLoc = dvmCompilerGetDest(cUnit, mir, high);
+    }
+    /* Avoid this case by either promoting both or neither. */
+    assert(lowLoc.location == highLoc.location);
+    if (lowLoc.location == kLocPhysReg) {
+        /* This case shouldn't happen if we've named correctly */
+        assert(lowLoc.fp == highLoc.fp);
+    }
+    lowLoc.wide = true;
+    lowLoc.highReg = highLoc.lowReg;
+    return lowLoc;
+}
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, false);
+}
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, true);
+}
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc)
+{
+    if (loc.location != kLocRetval) {
+        assert(loc.sRegLow != INVALID_SREG);
+        dvmClearBit(cUnit->regPool->nullCheckedRegs, loc.sRegLow);
+        if (loc.wide) {
+            assert(dvmCompilerSRegHi(loc.sRegLow) != INVALID_SREG);
+            dvmClearBit(cUnit->regPool->nullCheckedRegs,
+                        dvmCompilerSRegHi(loc.sRegLow));
+        }
+    }
+}
diff --git a/vm/compiler/codegen/arm/ArchFactory.cpp b/vm/compiler/codegen/arm/ArchFactory.cpp
new file mode 100644
index 0000000..2daa7bc
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArchFactory.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains arm-specific codegen factory support.
+ * It is included by
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static TGT_LIR *genRegImmCheck(CompilationUnit *cUnit,
+                               ArmConditionCode cond, int reg,
+                               int checkValue, int dOffset,
+                               TGT_LIR *pcrLabel)
+{
+    TGT_LIR *branch = genCmpImmBranch(cUnit, cond, reg, checkValue);
+    if (cUnit->jitMode == kJitMethod) {
+        BasicBlock *bb = cUnit->curBlock;
+        if (bb->taken) {
+            ArmLIR  *exceptionLabel = (ArmLIR *) cUnit->blockLabelList;
+            exceptionLabel += bb->taken->id;
+            branch->generic.target = (LIR *) exceptionLabel;
+            return exceptionLabel;
+        } else {
+            ALOGE("Catch blocks not handled yet");
+            dvmAbort();
+            return NULL;
+        }
+    } else {
+        return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    }
+}
+
+/*
+ * Perform null-check on a register. sReg is the ssa register being checked,
+ * and mReg is the machine register holding the actual value. If internal state
+ * indicates that sReg has been checked before the check request is ignored.
+ */
+static TGT_LIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
+                             int dOffset, TGT_LIR *pcrLabel)
+{
+    /* This particular Dalvik register has been null-checked */
+    if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
+        return pcrLabel;
+    }
+    dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg);
+    return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/*
+ * Perform a "reg cmp reg" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static TGT_LIR *genRegRegCheck(CompilationUnit *cUnit,
+                               ArmConditionCode cond,
+                               int reg1, int reg2, int dOffset,
+                               TGT_LIR *pcrLabel)
+{
+    TGT_LIR *res;
+    res = opRegReg(cUnit, kOpCmp, reg1, reg2);
+    TGT_LIR *branch = opCondBranch(cUnit, cond);
+    genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    return res;
+}
+
+/*
+ * Perform zero-check on a register. Similar to genNullCheck but the value being
+ * checked does not have a corresponding Dalvik register.
+ */
+static TGT_LIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
+                             int dOffset, TGT_LIR *pcrLabel)
+{
+    return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/* Perform bound check on two registers */
+static TGT_LIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+                               int rBound, int dOffset, TGT_LIR *pcrLabel)
+{
+    return genRegRegCheck(cUnit, kArmCondCs, rIndex, rBound, dOffset,
+                          pcrLabel);
+}
+
+/*
+ * Jump to the out-of-line handler in ARM mode to finish executing the
+ * remaining of more complex instructions.
+ */
+static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opcode)
+{
+    /*
+     * NOTE - In practice BLX only needs one operand, but since the assembler
+     * may abort itself and retry due to other out-of-range conditions we
+     * cannot really use operand[0] to store the absolute target address since
+     * it may get clobbered by the final relative offset. Therefore,
+     * we fake BLX_1 is a two operand instruction and the absolute target
+     * address is stored in operand[1].
+     */
+    dvmCompilerClobberHandlerRegs(cUnit);
+    newLIR2(cUnit, kThumbBlx1,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+    newLIR2(cUnit, kThumbBlx2,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+}
diff --git a/vm/compiler/codegen/arm/ArchUtility.cpp b/vm/compiler/codegen/arm/ArchUtility.cpp
new file mode 100644
index 0000000..9f87b7f
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArchUtility.cpp
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../../CompilerInternals.h"
+#include "libdex/DexOpcodes.h"
+#include "ArmLIR.h"
+
+static const char *shiftNames[4] = {
+    "lsl",
+    "lsr",
+    "asr",
+    "ror"};
+
+/* Decode and print a ARM register name */
+static char * decodeRegList(ArmOpcode opcode, int vector, char *buf)
+{
+    int i;
+    bool printed = false;
+    buf[0] = 0;
+    for (i = 0; i < 16; i++, vector >>= 1) {
+        if (vector & 0x1) {
+            int regId = i;
+            if (opcode == kThumbPush && i == 8) {
+                regId = r14lr;
+            } else if (opcode == kThumbPop && i == 8) {
+                regId = r15pc;
+            }
+            if (printed) {
+                sprintf(buf + strlen(buf), ", r%d", regId);
+            } else {
+                printed = true;
+                sprintf(buf, "r%d", regId);
+            }
+        }
+    }
+    return buf;
+}
+
+static int expandImmediate(int value)
+{
+    int mode = (value & 0xf00) >> 8;
+    u4 bits = value & 0xff;
+    switch(mode) {
+        case 0:
+            return bits;
+       case 1:
+            return (bits << 16) | bits;
+       case 2:
+            return (bits << 24) | (bits << 8);
+       case 3:
+            return (bits << 24) | (bits << 16) | (bits << 8) | bits;
+      default:
+            break;
+    }
+    bits = (bits | 0x80) << 24;
+    return bits >> (((value & 0xf80) >> 7) - 8);
+}
+
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+static void buildInsnString(const char *fmt, ArmLIR *lir, char* buf,
+                            unsigned char *baseAddr, int size)
+{
+    int i;
+    char *bufEnd = &buf[size-1];
+    const char *fmtEnd = &fmt[strlen(fmt)];
+    char tbuf[256];
+    const char *name;
+    char nc;
+    while (fmt < fmtEnd) {
+        int operand;
+        if (*fmt == '!') {
+            fmt++;
+            assert(fmt < fmtEnd);
+            nc = *fmt++;
+            if (nc=='!') {
+                strcpy(tbuf, "!");
+            } else {
+               assert(fmt < fmtEnd);
+               assert((unsigned)(nc-'0') < 4);
+               operand = lir->operands[nc-'0'];
+               switch(*fmt++) {
+                   case 'H':
+                       if (operand != 0) {
+                           sprintf(tbuf, ", %s %d",shiftNames[operand & 0x3],
+                                   operand >> 2);
+                       } else {
+                           strcpy(tbuf,"");
+                       }
+                       break;
+                   case 'B':
+                       switch (operand) {
+                           case kSY:
+                               name = "sy";
+                               break;
+                           case kST:
+                               name = "st";
+                               break;
+                           case kISH:
+                               name = "ish";
+                               break;
+                           case kISHST:
+                               name = "ishst";
+                               break;
+                           case kNSH:
+                               name = "nsh";
+                               break;
+                           case kNSHST:
+                               name = "shst";
+                               break;
+                           default:
+                               name = "DecodeError";
+                               break;
+                       }
+                       strcpy(tbuf, name);
+                       break;
+                   case 'b':
+                       strcpy(tbuf,"0000");
+                       for (i=3; i>= 0; i--) {
+                           tbuf[i] += operand & 1;
+                           operand >>= 1;
+                       }
+                       break;
+                   case 'n':
+                       operand = ~expandImmediate(operand);
+                       sprintf(tbuf,"%d [%#x]", operand, operand);
+                       break;
+                   case 'm':
+                       operand = expandImmediate(operand);
+                       sprintf(tbuf,"%d [%#x]", operand, operand);
+                       break;
+                   case 's':
+                       sprintf(tbuf,"s%d",operand & FP_REG_MASK);
+                       break;
+                   case 'S':
+                       sprintf(tbuf,"d%d",(operand & FP_REG_MASK) >> 1);
+                       break;
+                   case 'h':
+                       sprintf(tbuf,"%04x", operand);
+                       break;
+                   case 'M':
+                   case 'd':
+                       sprintf(tbuf,"%d", operand);
+                       break;
+                   case 'E':
+                       sprintf(tbuf,"%d", operand*4);
+                       break;
+                   case 'F':
+                       sprintf(tbuf,"%d", operand*2);
+                       break;
+                   case 'c':
+                       switch (operand) {
+                           case kArmCondEq:
+                               strcpy(tbuf, "eq");
+                               break;
+                           case kArmCondNe:
+                               strcpy(tbuf, "ne");
+                               break;
+                           case kArmCondLt:
+                               strcpy(tbuf, "lt");
+                               break;
+                           case kArmCondGe:
+                               strcpy(tbuf, "ge");
+                               break;
+                           case kArmCondGt:
+                               strcpy(tbuf, "gt");
+                               break;
+                           case kArmCondLe:
+                               strcpy(tbuf, "le");
+                               break;
+                           case kArmCondCs:
+                               strcpy(tbuf, "cs");
+                               break;
+                           case kArmCondMi:
+                               strcpy(tbuf, "mi");
+                               break;
+                           default:
+                               strcpy(tbuf, "");
+                               break;
+                       }
+                       break;
+                   case 't':
+                       sprintf(tbuf,"0x%08x (L%p)",
+                               (int) baseAddr + lir->generic.offset + 4 +
+                               (operand << 1),
+                               lir->generic.target);
+                       break;
+                   case 'u': {
+                       int offset_1 = lir->operands[0];
+                       int offset_2 = NEXT_LIR(lir)->operands[0];
+                       intptr_t target =
+                           ((((intptr_t) baseAddr + lir->generic.offset + 4) &
+                            ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
+                           0xfffffffc;
+                       sprintf(tbuf, "%p", (void *) target);
+                       break;
+                    }
+
+                   /* Nothing to print for BLX_2 */
+                   case 'v':
+                       strcpy(tbuf, "see above");
+                       break;
+                   case 'R':
+                       decodeRegList(lir->opcode, operand, tbuf);
+                       break;
+                   default:
+                       strcpy(tbuf,"DecodeError");
+                       break;
+               }
+               if (buf+strlen(tbuf) <= bufEnd) {
+                   strcpy(buf, tbuf);
+                   buf += strlen(tbuf);
+               } else {
+                   break;
+               }
+            }
+        } else {
+           *buf++ = *fmt++;
+        }
+        if (buf == bufEnd)
+            break;
+    }
+    *buf = 0;
+}
+
+void dvmDumpResourceMask(LIR *lir, u8 mask, const char *prefix)
+{
+    char buf[256];
+    buf[0] = 0;
+    ArmLIR *armLIR = (ArmLIR *) lir;
+
+    if (mask == ENCODE_ALL) {
+        strcpy(buf, "all");
+    } else {
+        char num[8];
+        int i;
+
+        for (i = 0; i < kRegEnd; i++) {
+            if (mask & (1ULL << i)) {
+                sprintf(num, "%d ", i);
+                strcat(buf, num);
+            }
+        }
+
+        if (mask & ENCODE_CCODE) {
+            strcat(buf, "cc ");
+        }
+        if (mask & ENCODE_FP_STATUS) {
+            strcat(buf, "fpcc ");
+        }
+
+        /* Memory bits */
+        if (armLIR && (mask & ENCODE_DALVIK_REG)) {
+            sprintf(buf + strlen(buf), "dr%d%s", armLIR->aliasInfo & 0xffff,
+                    (armLIR->aliasInfo & 0x80000000) ? "(+1)" : "");
+        }
+        if (mask & ENCODE_LITERAL) {
+            strcat(buf, "lit ");
+        }
+
+        if (mask & ENCODE_HEAP_REF) {
+            strcat(buf, "heap ");
+        }
+        if (mask & ENCODE_MUST_NOT_ALIAS) {
+            strcat(buf, "noalias ");
+        }
+    }
+    if (buf[0]) {
+        ALOGD("%s: %s", prefix, buf);
+    }
+}
+
+/*
+ * Debugging macros
+ */
+#define DUMP_RESOURCE_MASK(X)
+#define DUMP_SSA_REP(X)
+
+/* Pretty-print a LIR instruction */
+void dvmDumpLIRInsn(LIR *arg, unsigned char *baseAddr)
+{
+    ArmLIR *lir = (ArmLIR *) arg;
+    char buf[256];
+    char opName[256];
+    int offset = lir->generic.offset;
+    int dest = lir->operands[0];
+    const bool dumpNop = false;
+
+    /* Handle pseudo-ops individually, and all regular insns as a group */
+    switch(lir->opcode) {
+        case kArmChainingCellBottom:
+            ALOGD("-------- end of chaining cells (0x%04x)", offset);
+            break;
+        case kArmPseudoBarrier:
+            ALOGD("-------- BARRIER");
+            break;
+        case kArmPseudoExtended:
+            ALOGD("-------- %s", (char *) dest);
+            break;
+        case kArmPseudoSSARep:
+            DUMP_SSA_REP(LOGD("-------- %s", (char *) dest));
+            break;
+        case kArmPseudoChainingCellBackwardBranch:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (backward branch): 0x%04x", dest);
+            break;
+        case kArmPseudoChainingCellNormal:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (normal): 0x%04x", dest);
+            break;
+        case kArmPseudoChainingCellHot:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (hot): 0x%04x", dest);
+            break;
+        case kArmPseudoChainingCellInvokePredicted:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (predicted): %s%s",
+                 dest ? ((Method *) dest)->clazz->descriptor : "",
+                 dest ? ((Method *) dest)->name : "N/A");
+            break;
+        case kArmPseudoChainingCellInvokeSingleton:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (invoke singleton): %s%s/%p",
+                 ((Method *)dest)->clazz->descriptor,
+                 ((Method *)dest)->name,
+                 ((Method *)dest)->insns);
+            break;
+        case kArmPseudoEntryBlock:
+            ALOGD("-------- entry offset: 0x%04x", dest);
+            break;
+        case kArmPseudoDalvikByteCodeBoundary:
+            ALOGD("-------- dalvik offset: 0x%04x @ %s", dest,
+                 (char *) lir->operands[1]);
+            break;
+        case kArmPseudoExitBlock:
+            ALOGD("-------- exit offset: 0x%04x", dest);
+            break;
+        case kArmPseudoPseudoAlign4:
+            ALOGD("%p (%04x): .align4", baseAddr + offset, offset);
+            break;
+        case kArmPseudoPCReconstructionCell:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x", dest,
+                 lir->operands[1]);
+            break;
+        case kArmPseudoPCReconstructionBlockLabel:
+            /* Do nothing */
+            break;
+        case kArmPseudoEHBlockLabel:
+            ALOGD("Exception_Handling:");
+            break;
+        case kArmPseudoTargetLabel:
+        case kArmPseudoNormalBlockLabel:
+            ALOGD("L%p:", lir);
+            break;
+        default:
+            if (lir->flags.isNop && !dumpNop) {
+                break;
+            }
+            buildInsnString(EncodingMap[lir->opcode].name, lir, opName,
+                            baseAddr, 256);
+            buildInsnString(EncodingMap[lir->opcode].fmt, lir, buf, baseAddr,
+                            256);
+            ALOGD("%p (%04x): %-8s%s%s",
+                 baseAddr + offset, offset, opName, buf,
+                 lir->flags.isNop ? "(nop)" : "");
+            break;
+    }
+
+    if (lir->useMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->useMask, "use"));
+    }
+    if (lir->defMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->defMask, "def"));
+    }
+}
+
+/* Dump instructions and constant pool contents */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit)
+{
+    ALOGD("Dumping LIR insns");
+    LIR *lirInsn;
+    ArmLIR *armLIR;
+
+    ALOGD("installed code is at %p", cUnit->baseAddr);
+    ALOGD("total size is %d bytes", cUnit->totalSize);
+    for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
+        dvmDumpLIRInsn(lirInsn, (unsigned char *) cUnit->baseAddr);
+    }
+    for (lirInsn = cUnit->classPointerList; lirInsn; lirInsn = lirInsn->next) {
+        armLIR = (ArmLIR *) lirInsn;
+        ALOGD("%p (%04x): .class (%s)",
+             (char*)cUnit->baseAddr + armLIR->generic.offset,
+             armLIR->generic.offset,
+             ((CallsiteInfo *) armLIR->operands[0])->classDescriptor);
+    }
+    for (lirInsn = cUnit->literalList; lirInsn; lirInsn = lirInsn->next) {
+        armLIR = (ArmLIR *) lirInsn;
+        ALOGD("%p (%04x): .word (%#x)",
+             (char*)cUnit->baseAddr + armLIR->generic.offset,
+             armLIR->generic.offset,
+             armLIR->operands[0]);
+    }
+}
+
+/* Target-specific cache flushing */
+void dvmCompilerCacheFlush(long start, long end, long flags)
+{
+    cacheflush(start, end, flags);
+}
+
+/* Target-specific cache clearing */
+void dvmCompilerCacheClear(char *start, size_t size)
+{
+    /*
+     * de is an invalid opcode for arm.
+     * From gdb disassembly:  <UNDEFINED> instruction: 0xdede
+     */
+
+    memset(start, 0xde, size);
+}
diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h
new file mode 100644
index 0000000..cbd4c70
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArmLIR.h
@@ -0,0 +1,795 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H_
+
+#include "Dalvik.h"
+#include "compiler/CompilerInternals.h"
+
+/*
+ * r0, r1, r2, r3 are always scratch
+ * r4 (rPC) is scratch for Jit, but most be restored when resuming interp
+ * r5 (rFP) is reserved [holds Dalvik frame pointer]
+ * r6 (rSELF) is reserved [holds current &Thread]
+ * r7 (rINST) is scratch for Jit
+ * r8 (rIBASE) is scratch for Jit, but must be restored when resuming interp
+ * r9 is reserved
+ * r10 is always scratch
+ * r11 (fp) used by gcc unless -fomit-frame-pointer set [available for jit?]
+ * r12 is always scratch
+ * r13 (sp) is reserved
+ * r14 (lr) is scratch for Jit
+ * r15 (pc) is reserved
+ *
+ * Preserved across C calls: r4, r5, r6, r7, r8, r10, r11
+ * Trashed across C calls: r0, r1, r2, r3, r12, r14
+ *
+ * Floating pointer registers
+ * s0-s31
+ * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31}
+ *
+ * s16-s31 (d8-d15) preserved across C calls
+ * s0-s15 (d0-d7) trashed across C calls
+ *
+ * For Thumb code use:
+ *       r0, r1, r2, r3 to hold operands/results
+ *       r4, r7 for temps
+ *
+ * For Thumb2 code use:
+ *       r0, r1, r2, r3, r8, r9, r10, r11, r12, r14 for operands/results
+ *       r4, r7 for temps
+ *       s16-s31/d8-d15 for operands/results
+ *       s0-s15/d0-d7 for temps
+ *
+ * When transitioning from code cache to interp:
+ *       restore rIBASE
+ *       restore rPC
+ *       restore r11?
+ */
+
+/* Offset to distingish FP regs */
+#define FP_REG_OFFSET 32
+/* Offset to distinguish DP FP regs */
+#define FP_DOUBLE 64
+/* Reg types */
+#define REGTYPE(x) (x & (FP_REG_OFFSET | FP_DOUBLE))
+#define FPREG(x) ((x & FP_REG_OFFSET) == FP_REG_OFFSET)
+#define LOWREG(x) ((x & 0x7) == x)
+#define DOUBLEREG(x) ((x & FP_DOUBLE) == FP_DOUBLE)
+#define SINGLEREG(x) (FPREG(x) && !DOUBLEREG(x))
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area.  Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define S2D(x,y) ((x) | FP_DOUBLE)
+/* Mask to strip off fp flags */
+#define FP_REG_MASK (FP_REG_OFFSET-1)
+/* non-existent Dalvik register */
+#define vNone   (-1)
+/* non-existant physical register */
+#define rNone   (-1)
+
+/* RegisterLocation templates return values (r0, or r0/r1) */
+#define LOC_C_RETURN {kLocPhysReg, 0, 0, r0, 0, -1}
+#define LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, r0, r1, -1}
+/* RegisterLocation templates for interpState->retVal; */
+#define LOC_DALVIK_RETURN_VAL {kLocRetval, 0, 0, 0, 0, -1}
+#define LOC_DALVIK_RETURN_VAL_WIDE {kLocRetval, 1, 0, 0, 0, -1}
+
+ /*
+ * Data structure tracking the mapping between a Dalvik register (pair) and a
+ * native register (pair). The idea is to reuse the previously loaded value
+ * if possible, otherwise to keep the value in a native register as long as
+ * possible.
+ */
+typedef struct RegisterInfo {
+    int reg;                    // Reg number
+    bool inUse;                 // Has it been allocated?
+    bool pair;                  // Part of a register pair?
+    int partner;                // If pair, other reg of pair
+    bool live;                  // Is there an associated SSA name?
+    bool dirty;                 // If live, is it dirty?
+    int sReg;                   // Name of live value
+    struct LIR *defStart;       // Starting inst in last def sequence
+    struct LIR *defEnd;         // Ending inst in last def sequence
+} RegisterInfo;
+
+typedef struct RegisterPool {
+    BitVector *nullCheckedRegs; // Track which registers have been null-checked
+    int numCoreTemps;
+    RegisterInfo *coreTemps;
+    int nextCoreTemp;
+    int numFPTemps;
+    RegisterInfo *FPTemps;
+    int nextFPTemp;
+} RegisterPool;
+
+typedef enum ResourceEncodingPos {
+    kGPReg0     = 0,
+    kRegSP      = 13,
+    kRegLR      = 14,
+    kRegPC      = 15,
+    kFPReg0     = 16,
+    kRegEnd     = 48,
+    kCCode      = kRegEnd,
+    kFPStatus,          // FP status word
+    // The following four bits are for memory disambiguation
+    kDalvikReg,         // 1 Dalvik Frame (can be fully disambiguated)
+    kLiteral,           // 2 Literal pool (can be fully disambiguated)
+    kHeapRef,           // 3 Somewhere on the heap (alias with any other heap)
+    kMustNotAlias,      // 4 Guaranteed to be non-alias (eg *(r6+x))
+} ResourceEncodingPos;
+
+#define ENCODE_REG_LIST(N)      ((u8) N)
+#define ENCODE_REG_SP           (1ULL << kRegSP)
+#define ENCODE_REG_LR           (1ULL << kRegLR)
+#define ENCODE_REG_PC           (1ULL << kRegPC)
+#define ENCODE_CCODE            (1ULL << kCCode)
+#define ENCODE_FP_STATUS        (1ULL << kFPStatus)
+
+/* Abstract memory locations */
+#define ENCODE_DALVIK_REG       (1ULL << kDalvikReg)
+#define ENCODE_LITERAL          (1ULL << kLiteral)
+#define ENCODE_HEAP_REF         (1ULL << kHeapRef)
+#define ENCODE_MUST_NOT_ALIAS   (1ULL << kMustNotAlias)
+
+#define ENCODE_ALL              (~0ULL)
+#define ENCODE_MEM              (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
+                                 ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
+
+#define DECODE_ALIAS_INFO_REG(X)        (X & 0xffff)
+#define DECODE_ALIAS_INFO_WIDE(X)       ((X & 0x80000000) ? 1 : 0)
+
+typedef enum OpSize {
+    kWord,
+    kLong,
+    kSingle,
+    kDouble,
+    kUnsignedHalf,
+    kSignedHalf,
+    kUnsignedByte,
+    kSignedByte,
+} OpSize;
+
+typedef enum OpKind {
+    kOpMov,
+    kOpMvn,
+    kOpCmp,
+    kOpLsl,
+    kOpLsr,
+    kOpAsr,
+    kOpRor,
+    kOpNot,
+    kOpAnd,
+    kOpOr,
+    kOpXor,
+    kOpNeg,
+    kOpAdd,
+    kOpAdc,
+    kOpSub,
+    kOpSbc,
+    kOpRsub,
+    kOpMul,
+    kOpDiv,
+    kOpRem,
+    kOpBic,
+    kOpCmn,
+    kOpTst,
+    kOpBkpt,
+    kOpBlx,
+    kOpPush,
+    kOpPop,
+    kOp2Char,
+    kOp2Short,
+    kOp2Byte,
+    kOpCondBr,
+    kOpUncondBr,
+} OpKind;
+
+/*
+ * Annotate special-purpose core registers:
+ *   - VM: r4PC, r5FP, and r6SELF
+ *   - ARM architecture: r13sp, r14lr, and r15pc
+ *
+ * rPC, rFP, and rSELF are for architecture-independent code to use.
+ */
+typedef enum NativeRegisterPool {
+    r0     = 0,
+    r1     = 1,
+    r2     = 2,
+    r3     = 3,
+    rPC    = 4,
+    r4PC   = rPC,
+    rFP    = 5,
+    r5FP   = rFP,
+    rSELF  = 6,
+    r6SELF = rSELF,
+    r7     = 7,
+    r8     = 8,
+    r9     = 9,
+    r10    = 10,
+    r11    = 11,
+    r12    = 12,
+    r13sp  = 13,
+    r14lr  = 14,
+    r15pc  = 15,
+    fr0  =  0 + FP_REG_OFFSET,
+    fr1  =  1 + FP_REG_OFFSET,
+    fr2  =  2 + FP_REG_OFFSET,
+    fr3  =  3 + FP_REG_OFFSET,
+    fr4  =  4 + FP_REG_OFFSET,
+    fr5  =  5 + FP_REG_OFFSET,
+    fr6  =  6 + FP_REG_OFFSET,
+    fr7  =  7 + FP_REG_OFFSET,
+    fr8  =  8 + FP_REG_OFFSET,
+    fr9  =  9 + FP_REG_OFFSET,
+    fr10 = 10 + FP_REG_OFFSET,
+    fr11 = 11 + FP_REG_OFFSET,
+    fr12 = 12 + FP_REG_OFFSET,
+    fr13 = 13 + FP_REG_OFFSET,
+    fr14 = 14 + FP_REG_OFFSET,
+    fr15 = 15 + FP_REG_OFFSET,
+    fr16 = 16 + FP_REG_OFFSET,
+    fr17 = 17 + FP_REG_OFFSET,
+    fr18 = 18 + FP_REG_OFFSET,
+    fr19 = 19 + FP_REG_OFFSET,
+    fr20 = 20 + FP_REG_OFFSET,
+    fr21 = 21 + FP_REG_OFFSET,
+    fr22 = 22 + FP_REG_OFFSET,
+    fr23 = 23 + FP_REG_OFFSET,
+    fr24 = 24 + FP_REG_OFFSET,
+    fr25 = 25 + FP_REG_OFFSET,
+    fr26 = 26 + FP_REG_OFFSET,
+    fr27 = 27 + FP_REG_OFFSET,
+    fr28 = 28 + FP_REG_OFFSET,
+    fr29 = 29 + FP_REG_OFFSET,
+    fr30 = 30 + FP_REG_OFFSET,
+    fr31 = 31 + FP_REG_OFFSET,
+    dr0 = fr0 + FP_DOUBLE,
+    dr1 = fr2 + FP_DOUBLE,
+    dr2 = fr4 + FP_DOUBLE,
+    dr3 = fr6 + FP_DOUBLE,
+    dr4 = fr8 + FP_DOUBLE,
+    dr5 = fr10 + FP_DOUBLE,
+    dr6 = fr12 + FP_DOUBLE,
+    dr7 = fr14 + FP_DOUBLE,
+    dr8 = fr16 + FP_DOUBLE,
+    dr9 = fr18 + FP_DOUBLE,
+    dr10 = fr20 + FP_DOUBLE,
+    dr11 = fr22 + FP_DOUBLE,
+    dr12 = fr24 + FP_DOUBLE,
+    dr13 = fr26 + FP_DOUBLE,
+    dr14 = fr28 + FP_DOUBLE,
+    dr15 = fr30 + FP_DOUBLE,
+} NativeRegisterPool;
+
+/* Shift encodings */
+typedef enum ArmShiftEncodings {
+    kArmLsl = 0x0,
+    kArmLsr = 0x1,
+    kArmAsr = 0x2,
+    kArmRor = 0x3
+} ArmShiftEncodings;
+
+/* Thumb condition encodings */
+typedef enum ArmConditionCode {
+    kArmCondEq = 0x0,    /* 0000 */
+    kArmCondNe = 0x1,    /* 0001 */
+    kArmCondCs = 0x2,    /* 0010 */
+    kArmCondCc = 0x3,    /* 0011 */
+    kArmCondMi = 0x4,    /* 0100 */
+    kArmCondPl = 0x5,    /* 0101 */
+    kArmCondVs = 0x6,    /* 0110 */
+    kArmCondVc = 0x7,    /* 0111 */
+    kArmCondHi = 0x8,    /* 1000 */
+    kArmCondLs = 0x9,    /* 1001 */
+    kArmCondGe = 0xa,    /* 1010 */
+    kArmCondLt = 0xb,    /* 1011 */
+    kArmCondGt = 0xc,    /* 1100 */
+    kArmCondLe = 0xd,    /* 1101 */
+    kArmCondAl = 0xe,    /* 1110 */
+    kArmCondNv = 0xf,    /* 1111 */
+} ArmConditionCode;
+
+#define isPseudoOpcode(opcode) ((int)(opcode) < 0)
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding snippet positions will be defined in
+ * Assemble.c.
+ */
+typedef enum ArmOpcode {
+    kArmChainingCellBottom = -18,
+    kArmPseudoBarrier = -17,
+    kArmPseudoExtended = -16,
+    kArmPseudoSSARep = -15,
+    kArmPseudoEntryBlock = -14,
+    kArmPseudoExitBlock = -13,
+    kArmPseudoTargetLabel = -12,
+    kArmPseudoChainingCellBackwardBranch = -11,
+    kArmPseudoChainingCellHot = -10,
+    kArmPseudoChainingCellInvokePredicted = -9,
+    kArmPseudoChainingCellInvokeSingleton = -8,
+    kArmPseudoChainingCellNormal = -7,
+    kArmPseudoDalvikByteCodeBoundary = -6,
+    kArmPseudoPseudoAlign4 = -5,
+    kArmPseudoPCReconstructionCell = -4,
+    kArmPseudoPCReconstructionBlockLabel = -3,
+    kArmPseudoEHBlockLabel = -2,
+    kArmPseudoNormalBlockLabel = -1,
+    /************************************************************************/
+    kArm16BitData,       /* DATA   [0] rd[15..0] */
+    kThumbAdcRR,         /* adc     [0100000101] rm[5..3] rd[2..0] */
+    kThumbAddRRI3,       /* add(1)  [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/
+    kThumbAddRI8,        /* add(2)  [00110] rd[10..8] imm_8[7..0] */
+    kThumbAddRRR,        /* add(3)  [0001100] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbAddRRLH,       /* add(4)  [01000100] H12[01] rm[5..3] rd[2..0] */
+    kThumbAddRRHL,       /* add(4)  [01001000] H12[10] rm[5..3] rd[2..0] */
+    kThumbAddRRHH,       /* add(4)  [01001100] H12[11] rm[5..3] rd[2..0] */
+    kThumbAddPcRel,      /* add(5)  [10100] rd[10..8] imm_8[7..0] */
+    kThumbAddSpRel,      /* add(6)  [10101] rd[10..8] imm_8[7..0] */
+    kThumbAddSpI7,       /* add(7)  [101100000] imm_7[6..0] */
+    kThumbAndRR,         /* and     [0100000000] rm[5..3] rd[2..0] */
+    kThumbAsrRRI5,       /* asr(1)  [00010] imm_5[10..6] rm[5..3] rd[2..0] */
+    kThumbAsrRR,         /* asr(2)  [0100000100] rs[5..3] rd[2..0] */
+    kThumbBCond,         /* b(1)    [1101] cond[11..8] offset_8[7..0] */
+    kThumbBUncond,       /* b(2)    [11100] offset_11[10..0] */
+    kThumbBicRR,         /* bic     [0100001110] rm[5..3] rd[2..0] */
+    kThumbBkpt,          /* bkpt    [10111110] imm_8[7..0] */
+    kThumbBlx1,          /* blx(1)  [111] H[10] offset_11[10..0] */
+    kThumbBlx2,          /* blx(1)  [111] H[01] offset_11[10..0] */
+    kThumbBl1,           /* blx(1)  [111] H[10] offset_11[10..0] */
+    kThumbBl2,           /* blx(1)  [111] H[11] offset_11[10..0] */
+    kThumbBlxR,          /* blx(2)  [010001111] rm[6..3] [000] */
+    kThumbBx,            /* bx      [010001110] H2[6..6] rm[5..3] SBZ[000] */
+    kThumbCmnRR,         /* cmn     [0100001011] rm[5..3] rd[2..0] */
+    kThumbCmpRI8,        /* cmp(1)  [00101] rn[10..8] imm_8[7..0] */
+    kThumbCmpRR,         /* cmp(2)  [0100001010] rm[5..3] rd[2..0] */
+    kThumbCmpLH,         /* cmp(3)  [01000101] H12[01] rm[5..3] rd[2..0] */
+    kThumbCmpHL,         /* cmp(3)  [01000110] H12[10] rm[5..3] rd[2..0] */
+    kThumbCmpHH,         /* cmp(3)  [01000111] H12[11] rm[5..3] rd[2..0] */
+    kThumbEorRR,         /* eor     [0100000001] rm[5..3] rd[2..0] */
+    kThumbLdmia,         /* ldmia   [11001] rn[10..8] reglist [7..0] */
+    kThumbLdrRRI5,       /* ldr(1)  [01101] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbLdrRRR,        /* ldr(2)  [0101100] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrPcRel,      /* ldr(3)  [01001] rd[10..8] imm_8[7..0] */
+    kThumbLdrSpRel,      /* ldr(4)  [10011] rd[10..8] imm_8[7..0] */
+    kThumbLdrbRRI5,      /* ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbLdrbRRR,       /* ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrhRRI5,      /* ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbLdrhRRR,       /* ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrsbRRR,      /* ldrsb   [0101011] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrshRRR,      /* ldrsh   [0101111] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLslRRI5,       /* lsl(1)  [00000] imm_5[10..6] rm[5..3] rd[2..0] */
+    kThumbLslRR,         /* lsl(2)  [0100000010] rs[5..3] rd[2..0] */
+    kThumbLsrRRI5,       /* lsr(1)  [00001] imm_5[10..6] rm[5..3] rd[2..0] */
+    kThumbLsrRR,         /* lsr(2)  [0100000011] rs[5..3] rd[2..0] */
+    kThumbMovImm,        /* mov(1)  [00100] rd[10..8] imm_8[7..0] */
+    kThumbMovRR,         /* mov(2)  [0001110000] rn[5..3] rd[2..0] */
+    kThumbMovRR_H2H,     /* mov(3)  [01000111] H12[11] rm[5..3] rd[2..0] */
+    kThumbMovRR_H2L,     /* mov(3)  [01000110] H12[01] rm[5..3] rd[2..0] */
+    kThumbMovRR_L2H,     /* mov(3)  [01000101] H12[10] rm[5..3] rd[2..0] */
+    kThumbMul,           /* mul     [0100001101] rm[5..3] rd[2..0] */
+    kThumbMvn,           /* mvn     [0100001111] rm[5..3] rd[2..0] */
+    kThumbNeg,           /* neg     [0100001001] rm[5..3] rd[2..0] */
+    kThumbOrr,           /* orr     [0100001100] rm[5..3] rd[2..0] */
+    kThumbPop,           /* pop     [1011110] r[8..8] rl[7..0] */
+    kThumbPush,          /* push    [1011010] r[8..8] rl[7..0] */
+    kThumbRorRR,         /* ror     [0100000111] rs[5..3] rd[2..0] */
+    kThumbSbc,           /* sbc     [0100000110] rm[5..3] rd[2..0] */
+    kThumbStmia,         /* stmia   [11000] rn[10..8] reglist [7.. 0] */
+    kThumbStrRRI5,       /* str(1)  [01100] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbStrRRR,        /* str(2)  [0101000] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbStrSpRel,      /* str(3)  [10010] rd[10..8] imm_8[7..0] */
+    kThumbStrbRRI5,      /* strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbStrbRRR,       /* strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbStrhRRI5,      /* strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbStrhRRR,       /* strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbSubRRI3,       /* sub(1)  [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/
+    kThumbSubRI8,        /* sub(2)  [00111] rd[10..8] imm_8[7..0] */
+    kThumbSubRRR,        /* sub(3)  [0001101] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbSubSpI7,       /* sub(4)  [101100001] imm_7[6..0] */
+    kThumbSwi,           /* swi     [11011111] imm_8[7..0] */
+    kThumbTst,           /* tst     [0100001000] rm[5..3] rn[2..0] */
+    kThumb2Vldrs,        /* vldr low  sx [111011011001] rn[19..16] rd[15-12]
+                                    [1010] imm_8[7..0] */
+    kThumb2Vldrd,        /* vldr low  dx [111011011001] rn[19..16] rd[15-12]
+                                    [1011] imm_8[7..0] */
+    kThumb2Vmuls,        /* vmul vd, vn, vm [111011100010] rn[19..16]
+                                    rd[15-12] [10100000] rm[3..0] */
+    kThumb2Vmuld,        /* vmul vd, vn, vm [111011100010] rn[19..16]
+                                    rd[15-12] [10110000] rm[3..0] */
+    kThumb2Vstrs,        /* vstr low  sx [111011011000] rn[19..16] rd[15-12]
+                                    [1010] imm_8[7..0] */
+    kThumb2Vstrd,        /* vstr low  dx [111011011000] rn[19..16] rd[15-12]
+                                    [1011] imm_8[7..0] */
+    kThumb2Vsubs,        /* vsub vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10100040] rm[3..0] */
+    kThumb2Vsubd,        /* vsub vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10110040] rm[3..0] */
+    kThumb2Vadds,        /* vadd vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10100000] rm[3..0] */
+    kThumb2Vaddd,        /* vadd vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10110000] rm[3..0] */
+    kThumb2Vdivs,        /* vdiv vd, vn, vm [111011101000] rn[19..16]
+                                    rd[15-12] [10100000] rm[3..0] */
+    kThumb2Vdivd,        /* vdiv vd, vn, vm [111011101000] rn[19..16]
+                                    rd[15-12] [10110000] rm[3..0] */
+    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    [1110100100101101] list[15-0]*/
+    kThumb2CmpRI8,       /* cmp rn, #<const> [11110] i [011011] rn[19-16] [0]
+                                       imm3 [1111] imm8[7..0] */
+    kThumb2AdcRRR,       /* adc [111010110101] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2AndRRR,       /* and [111010100000] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2BicRRR,       /* bic [111010100010] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2CmnRR,        /* cmn [111010110001] rn[19..16] [0000] [1111]
+                                   [0000] rm[3..0] */
+    kThumb2EorRRR,       /* eor [111010101000] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2MulRRR,       /* mul [111110110000] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2MnvRR,        /* mvn [11101010011011110] rd[11-8] [0000]
+                                   rm[3..0] */
+    kThumb2RsubRRI8,     /* rsub [111100011100] rn[19..16] [0000] rd[11..8]
+                                   imm8[7..0] */
+    kThumb2NegRR,        /* actually rsub rd, rn, #0 */
+    kThumb2OrrRRR,       /* orr [111010100100] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2TstRR,        /* tst [111010100001] rn[19..16] [0000] [1111]
+                                   [0000] rm[3..0] */
+    kThumb2LslRRR,       /* lsl [111110100000] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2LsrRRR,       /* lsr [111110100010] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2AsrRRR,       /* asr [111110100100] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2RorRRR,       /* ror [111110100110] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2LslRRI5,      /* lsl [11101010010011110] imm[14.12] rd[11..8]
+                                   [00] rm[3..0] */
+    kThumb2LsrRRI5,      /* lsr [11101010010011110] imm[14.12] rd[11..8]
+                                   [01] rm[3..0] */
+    kThumb2AsrRRI5,      /* asr [11101010010011110] imm[14.12] rd[11..8]
+                                   [10] rm[3..0] */
+    kThumb2RorRRI5,      /* ror [11101010010011110] imm[14.12] rd[11..8]
+                                   [11] rm[3..0] */
+    kThumb2BicRRI8,      /* bic [111100000010] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2AndRRI8,      /* bic [111100000000] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2OrrRRI8,      /* orr [111100000100] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2EorRRI8,      /* eor [111100001000] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2AddRRI8,      /* add [111100001000] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2AdcRRI8,      /* adc [111100010101] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2SubRRI8,      /* sub [111100011011] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2SbcRRI8,      /* sbc [111100010111] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2It,           /* it [10111111] firstcond[7-4] mask[3-0] */
+    kThumb2Fmstat,       /* fmstat [11101110111100011111101000010000] */
+    kThumb2Vcmpd,        /* vcmp [111011101] D [11011] rd[15-12] [1011]
+                                   E [1] M [0] rm[3-0] */
+    kThumb2Vcmps,        /* vcmp [111011101] D [11010] rd[15-12] [1011]
+                                   E [1] M [0] rm[3-0] */
+    kThumb2LdrPcRel12,   /* ldr rd,[pc,#imm12] [1111100011011111] rt[15-12]
+                                  imm12[11-0] */
+    kThumb2BCond,        /* b<c> [1110] S cond[25-22] imm6[21-16] [10]
+                                  J1 [0] J2 imm11[10..0] */
+    kThumb2Vmovd_RR,     /* vmov [111011101] D [110000] vd[15-12 [101101]
+                                  M [0] vm[3-0] */
+    kThumb2Vmovs_RR,     /* vmov [111011101] D [110000] vd[15-12 [101001]
+                                  M [0] vm[3-0] */
+    kThumb2Fmrs,         /* vmov [111011100000] vn[19-16] rt[15-12] [1010]
+                                  N [0010000] */
+    kThumb2Fmsr,         /* vmov [111011100001] vn[19-16] rt[15-12] [1010]
+                                  N [0010000] */
+    kThumb2Fmrrd,        /* vmov [111011000100] rt2[19-16] rt[15-12]
+                                  [101100] M [1] vm[3-0] */
+    kThumb2Fmdrr,        /* vmov [111011000101] rt2[19-16] rt[15-12]
+                                  [101100] M [1] vm[3-0] */
+    kThumb2Vabsd,        /* vabs.f64 [111011101] D [110000] rd[15-12]
+                                  [1011110] M [0] vm[3-0] */
+    kThumb2Vabss,        /* vabs.f32 [111011101] D [110000] rd[15-12]
+                                  [1010110] M [0] vm[3-0] */
+    kThumb2Vnegd,        /* vneg.f64 [111011101] D [110000] rd[15-12]
+                                  [1011110] M [0] vm[3-0] */
+    kThumb2Vnegs,        /* vneg.f32 [111011101] D [110000] rd[15-12]
+                                 [1010110] M [0] vm[3-0] */
+    kThumb2Vmovs_IMM8,   /* vmov.f32 [111011101] D [11] imm4h[19-16] vd[15-12]
+                                  [10100000] imm4l[3-0] */
+    kThumb2Vmovd_IMM8,   /* vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12]
+                                  [10110000] imm4l[3-0] */
+    kThumb2Mla,          /* mla [111110110000] rn[19-16] ra[15-12] rd[7-4]
+                                  [0000] rm[3-0] */
+    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] */
+    kThumb2LdrPcReln12,  /* ldr rd,[pc,-#imm12] [1111100011011111] rt[15-12]
+                                  imm12[11-0] */
+    kThumbUndefined,     /* undefined [11011110xxxxxxxx] */
+    kArmLast,
+} ArmOpcode;
+
+/* DMB option encodings */
+typedef enum ArmOpDmbOptions {
+    kSY = 0xf,
+    kST = 0xe,
+    kISH = 0xb,
+    kISHST = 0xa,
+    kNSH = 0x7,
+    kNSHST = 0x6
+} ArmOpDmbOptions;
+
+/* Bit flags describing the behavior of each native opcode */
+typedef enum ArmOpFeatureFlags {
+    kIsBranch = 0,
+    kRegDef0,
+    kRegDef1,
+    kRegDefSP,
+    kRegDefLR,
+    kRegDefList0,
+    kRegDefList1,
+    kRegUse0,
+    kRegUse1,
+    kRegUse2,
+    kRegUse3,
+    kRegUseSP,
+    kRegUsePC,
+    kRegUseList0,
+    kRegUseList1,
+    kNoOperand,
+    kIsUnaryOp,
+    kIsBinaryOp,
+    kIsTertiaryOp,
+    kIsQuadOp,
+    kIsIT,
+    kSetsCCodes,
+    kUsesCCodes,
+    kMemLoad,
+    kMemStore,
+} ArmOpFeatureFlags;
+
+#define IS_LOAD         (1 << kMemLoad)
+#define IS_STORE        (1 << kMemStore)
+#define IS_BRANCH       (1 << kIsBranch)
+#define REG_DEF0        (1 << kRegDef0)
+#define REG_DEF1        (1 << kRegDef1)
+#define REG_DEF_SP      (1 << kRegDefSP)
+#define REG_DEF_LR      (1 << kRegDefLR)
+#define REG_DEF_LIST0   (1 << kRegDefList0)
+#define REG_DEF_LIST1   (1 << kRegDefList1)
+#define REG_USE0        (1 << kRegUse0)
+#define REG_USE1        (1 << kRegUse1)
+#define REG_USE2        (1 << kRegUse2)
+#define REG_USE3        (1 << kRegUse3)
+#define REG_USE_SP      (1 << kRegUseSP)
+#define REG_USE_PC      (1 << kRegUsePC)
+#define REG_USE_LIST0   (1 << kRegUseList0)
+#define REG_USE_LIST1   (1 << kRegUseList1)
+#define NO_OPERAND      (1 << kNoOperand)
+#define IS_UNARY_OP     (1 << kIsUnaryOp)
+#define IS_BINARY_OP    (1 << kIsBinaryOp)
+#define IS_TERTIARY_OP  (1 << kIsTertiaryOp)
+#define IS_QUAD_OP      (1 << kIsQuadOp)
+#define IS_IT           (1 << kIsIT)
+#define SETS_CCODES     (1 << kSetsCCodes)
+#define USES_CCODES     (1 << kUsesCCodes)
+
+/* Common combo register usage patterns */
+#define REG_USE01       (REG_USE0 | REG_USE1)
+#define REG_USE012      (REG_USE01 | REG_USE2)
+#define REG_USE12       (REG_USE1 | REG_USE2)
+#define REG_DEF0_USE0   (REG_DEF0 | REG_USE0)
+#define REG_DEF0_USE1   (REG_DEF0 | REG_USE1)
+#define REG_DEF0_USE01  (REG_DEF0 | REG_USE01)
+#define REG_DEF0_USE12  (REG_DEF0 | REG_USE12)
+#define REG_DEF01_USE2  (REG_DEF0 | REG_DEF1 | REG_USE2)
+
+/* Instruction assembly fieldLoc kind */
+typedef enum ArmEncodingKind {
+    kFmtUnused,
+    kFmtBitBlt,        /* Bit string using end/start */
+    kFmtDfp,           /* Double FP reg */
+    kFmtSfp,           /* Single FP reg */
+    kFmtModImm,        /* Shifted 8-bit immed using [26,14..12,7..0] */
+    kFmtImm16,         /* Zero-extended immed using [26,19..16,14..12,7..0] */
+    kFmtImm6,          /* Encoded branch target using [9,7..3]0 */
+    kFmtImm12,         /* Zero-extended immediate using [26,14..12,7..0] */
+    kFmtShift,         /* Shift descriptor, [14..12,7..4] */
+    kFmtLsb,           /* least significant bit using [14..12][7..6] */
+    kFmtBWidth,        /* bit-field width, encoded as width-1 */
+    kFmtShift5,        /* Shift count, [14..12,7..6] */
+    kFmtBrOffset,      /* Signed extended [26,11,13,21-16,10-0]:0 */
+    kFmtFPImm,         /* Encoded floating point immediate */
+} ArmEncodingKind;
+
+/* Struct used to define the snippet positions for each Thumb opcode */
+typedef struct ArmEncodingMap {
+    u4 skeleton;
+    struct {
+        ArmEncodingKind kind;
+        int end;   /* end for kFmtBitBlt, 1-bit slice end for FP regs */
+        int start; /* start for kFmtBitBlt, 4-bit slice end for FP regs */
+    } fieldLoc[4];
+    ArmOpcode opcode;
+    int flags;
+    const char* name;
+    const char* fmt;
+    int size;
+} ArmEncodingMap;
+
+/* Keys for target-specific scheduling and other optimization hints */
+typedef enum ArmTargetOptHints {
+    kMaxHoistDistance,
+} ArmTargetOptHints;
+
+extern ArmEncodingMap EncodingMap[kArmLast];
+
+/*
+ * Each instance of this struct holds a pseudo or real LIR instruction:
+ * - pseudo ones (eg labels and marks) and will be discarded by the assembler.
+ * - real ones will be assembled into Thumb instructions.
+ *
+ * Machine resources are encoded into a 64-bit vector, where the encodings are
+ * as following:
+ * - [ 0..15]: general purpose registers including PC, SP, and LR
+ * - [16..47]: floating-point registers where d0 is expanded to s[01] and s0
+ *   starts at bit 16
+ * - [48]: IT block
+ * - [49]: integer condition code
+ * - [50]: floatint-point status word
+ */
+typedef struct ArmLIR {
+    LIR generic;
+    ArmOpcode opcode;
+    int operands[4];            // [0..3] = [dest, src1, src2, extra]
+    struct {
+        bool isNop:1;           // LIR is optimized away
+        bool insertWrapper:1;   // insert branch to emulate memory accesses
+        unsigned int age:4;     // default is 0, set lazily by the optimizer
+        unsigned int size:3;    // bytes (2 for thumb, 2/4 for thumb2)
+        unsigned int unused:23;
+    } flags;
+    int aliasInfo;              // For Dalvik register & litpool disambiguation
+    u8 useMask;                 // Resource mask for use
+    u8 defMask;                 // Resource mask for def
+} ArmLIR;
+
+/* Init values when a predicted chain is initially assembled */
+/* E7FE is branch to self */
+#define PREDICTED_CHAIN_BX_PAIR_INIT     0xe7fe
+
+/* Utility macros to traverse the LIR/ArmLIR list */
+#define NEXT_LIR(lir) ((ArmLIR *) lir->generic.next)
+#define PREV_LIR(lir) ((ArmLIR *) lir->generic.prev)
+
+#define NEXT_LIR_LVALUE(lir) (lir)->generic.next
+#define PREV_LIR_LVALUE(lir) (lir)->generic.prev
+
+#define CHAIN_CELL_OFFSET_TAG   0xcdab
+
+#define CHAIN_CELL_NORMAL_SIZE 12
+#define CHAIN_CELL_PREDICTED_SIZE 16
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H_
diff --git a/vm/compiler/codegen/arm/ArmRallocUtil.cpp b/vm/compiler/codegen/arm/ArmRallocUtil.cpp
new file mode 100644
index 0000000..3a5afa2
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArmRallocUtil.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains Arm-specific register alloction support.
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+#include "compiler/codegen/Ralloc.h"
+
+/*
+ * Register usage for 16-bit Thumb systems:
+ *     r0-r3: Temp/argument
+ *     lr(r14):      Temp for translations, return address for handlers
+ *     rSELF(r6):    Pointer to Thread
+ *     rFP(r5):      Dalvik frame pointer
+ *     r4, r7:       Temp for translations
+ *     r8, r9, r10:   Temp preserved across C calls
+ *     r11, ip(r12):  Temp not preserved across C calls
+ *
+ * Register usage for 32-bit Thumb systems:
+ *     r0-r3: Temp/argument
+ *     lr(r14):      Temp for translations, return address for handlers
+ *     rSELF(r6):    Pointer to Thread
+ *     rFP(r5):      Dalvik frame pointer
+ *     r4, r7:       Temp for translations
+ *     r8, r9, r10   Temp preserved across C calls
+ *     r11, ip(r12):      Temp not preserved across C calls
+ *     fp0-fp15:     Hot temps, not preserved across C calls
+ *     fp16-fp31:    Promotion pool
+ *
+ */
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit)
+{
+    dvmCompilerClobber(cUnit, r0);
+    dvmCompilerClobber(cUnit, r1);
+    dvmCompilerClobber(cUnit, r2);
+    dvmCompilerClobber(cUnit, r3);
+    dvmCompilerClobber(cUnit, r9); // Need to do this?, be conservative
+    dvmCompilerClobber(cUnit, r11);
+    dvmCompilerClobber(cUnit, r12);
+    dvmCompilerClobber(cUnit, r14lr);
+}
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit)
+{
+    //TUNING: reduce the set of regs used by handlers.  Only a few need lots.
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r4PC);
+    dvmCompilerClobber(cUnit, r7);
+    dvmCompilerClobber(cUnit, r8);
+    dvmCompilerClobber(cUnit, r9);
+    dvmCompilerClobber(cUnit, r10);
+}
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE;
+    dvmCompilerClobber(cUnit, r0);
+    dvmCompilerClobber(cUnit, r1);
+    dvmCompilerMarkInUse(cUnit, r0);
+    dvmCompilerMarkInUse(cUnit, r1);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE;
+    res.lowReg = r2;
+    res.highReg = r3;
+    dvmCompilerClobber(cUnit, r2);
+    dvmCompilerClobber(cUnit, r3);
+    dvmCompilerMarkInUse(cUnit, r2);
+    dvmCompilerMarkInUse(cUnit, r3);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN;
+    dvmCompilerClobber(cUnit, r0);
+    dvmCompilerMarkInUse(cUnit, r0);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN;
+    res.lowReg = r1;
+    dvmCompilerClobber(cUnit, r1);
+    dvmCompilerMarkInUse(cUnit, r1);
+    return res;
+}
diff --git a/vm/compiler/codegen/arm/Assemble.cpp b/vm/compiler/codegen/arm/Assemble.cpp
new file mode 100644
index 0000000..7406d3e
--- /dev/null
+++ b/vm/compiler/codegen/arm/Assemble.cpp
@@ -0,0 +1,2943 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+
+#include "../../CompilerInternals.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+#include <sys/mman.h>           /* for protection change */
+
+#define MAX_ASSEMBLER_RETRIES 10
+
+/*
+ * opcode: ArmOpcode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+                     k3, k3s, k3e, flags, name, fmt, size) \
+        {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+                    {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+
+/* Instruction dump string format keys: !pf, where "!" is the start
+ * of the key, "p" is which numeric operand to use and "f" is the
+ * print format.
+ *
+ * [p]ositions:
+ *     0 -> operands[0] (dest)
+ *     1 -> operands[1] (src1)
+ *     2 -> operands[2] (src2)
+ *     3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ *     h -> 4-digit hex
+ *     d -> decimal
+ *     E -> decimal*4
+ *     F -> decimal*2
+ *     c -> branch condition (beq, bne, etc.)
+ *     t -> pc-relative target
+ *     u -> 1st half of bl[x] target
+ *     v -> 2nd half ob bl[x] target
+ *     R -> register list
+ *     s -> single precision floating point register
+ *     S -> double precision floating point register
+ *     m -> Thumb2 modified immediate
+ *     n -> complimented Thumb2 modified immediate
+ *     M -> Thumb2 16-bit zero-extended immediate
+ *     b -> 4-digit binary
+ *     B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
+ *     H -> operand shift
+ *
+ *  [!] escape.  To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum ArmOpcode from ArmLIR.h */
+ArmEncodingMap EncodingMap[kArmLast] = {
+    ENCODING_MAP(kArm16BitData,    0x0000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 1),
+    ENCODING_MAP(kThumbAdcRR,        0x4140,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES,
+                 "adcs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddRRI3,      0x1c00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "adds", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbAddRI8,       0x3000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+                 "adds", "r!0d, r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbAddRRR,       0x1800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "adds", "r!0d, r!1d, r!2d", 1),
+    ENCODING_MAP(kThumbAddRRLH,     0x4440,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+                 "add", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddRRHL,     0x4480,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+                 "add", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddRRHH,     0x44c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+                 "add", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddPcRel,    0xa000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH,
+                 "add", "r!0d, pc, #!1E", 1),
+    ENCODING_MAP(kThumbAddSpRel,    0xa800,
+                 kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP,
+                 "add", "r!0d, sp, #!2E", 1),
+    ENCODING_MAP(kThumbAddSpI7,      0xb000,
+                 kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+                 "add", "sp, #!0d*4", 1),
+    ENCODING_MAP(kThumbAndRR,        0x4000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "ands", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAsrRRI5,      0x1000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "asrs", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbAsrRR,        0x4100,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "asrs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbBCond,        0xd000,
+                 kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES,
+                 "b!1c", "!0t", 1),
+    ENCODING_MAP(kThumbBUncond,      0xe000,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
+                 "b", "!0t", 1),
+    ENCODING_MAP(kThumbBicRR,        0x4380,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "bics", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbBkpt,          0xbe00,
+                 kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+                 "bkpt", "!0d", 1),
+    ENCODING_MAP(kThumbBlx1,         0xf000,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "blx_1", "!0u", 1),
+    ENCODING_MAP(kThumbBlx2,         0xe800,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "blx_2", "!0v", 1),
+    ENCODING_MAP(kThumbBl1,          0xf000,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "bl_1", "!0u", 1),
+    ENCODING_MAP(kThumbBl2,          0xf800,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "bl_2", "!0v", 1),
+    ENCODING_MAP(kThumbBlxR,         0x4780,
+                 kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR,
+                 "blx", "r!0d", 1),
+    ENCODING_MAP(kThumbBx,            0x4700,
+                 kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+                 "bx", "r!0d", 1),
+    ENCODING_MAP(kThumbCmnRR,        0x42c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmn", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpRI8,       0x2800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+                 "cmp", "r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbCmpRR,        0x4280,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpLH,        0x4540,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpHL,        0x4580,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpHH,        0x45c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbEorRR,        0x4040,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "eors", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbLdmia,         0xc800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+                 "ldmia", "r!0d!!, <!1R>", 1),
+    ENCODING_MAP(kThumbLdrRRI5,      0x6800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldr", "r!0d, [r!1d, #!2E]", 1),
+    ENCODING_MAP(kThumbLdrRRR,       0x5800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldr", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrPcRel,    0x4800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC
+                 | IS_LOAD, "ldr", "r!0d, [pc, #!1E]", 1),
+    ENCODING_MAP(kThumbLdrSpRel,    0x9800,
+                 kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP
+                 | IS_LOAD, "ldr", "r!0d, [sp, #!2E]", 1),
+    ENCODING_MAP(kThumbLdrbRRI5,     0x7800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrb", "r!0d, [r!1d, #2d]", 1),
+    ENCODING_MAP(kThumbLdrbRRR,      0x5c00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrb", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrhRRI5,     0x8800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrh", "r!0d, [r!1d, #!2F]", 1),
+    ENCODING_MAP(kThumbLdrhRRR,      0x5a00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrh", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrsbRRR,     0x5600,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrsb", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrshRRR,     0x5e00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrsh", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLslRRI5,      0x0000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "lsls", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbLslRR,        0x4080,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "lsls", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbLsrRRI5,      0x0800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "lsrs", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbLsrRR,        0x40c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "lsrs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovImm,       0x2000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0 | SETS_CCODES,
+                 "movs", "r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbMovRR,        0x1c00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "movs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovRR_H2H,    0x46c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovRR_H2L,    0x4640,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovRR_L2H,    0x4680,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMul,           0x4340,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "muls", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMvn,           0x43c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "mvns", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbNeg,           0x4240,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "negs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbOrr,           0x4300,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "orrs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbPop,           0xbc00,
+                 kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+                 | IS_LOAD, "pop", "<!0R>", 1),
+    ENCODING_MAP(kThumbPush,          0xb400,
+                 kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+                 | IS_STORE, "push", "<!0R>", 1),
+    ENCODING_MAP(kThumbRorRR,        0x41c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "rors", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbSbc,           0x4180,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES,
+                 "sbcs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbStmia,         0xc000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE,
+                 "stmia", "r!0d!!, <!1R>", 1),
+    ENCODING_MAP(kThumbStrRRI5,      0x6000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "str", "r!0d, [r!1d, #!2E]", 1),
+    ENCODING_MAP(kThumbStrRRR,       0x5000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+                 "str", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbStrSpRel,    0x9000,
+                 kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP
+                 | IS_STORE, "str", "r!0d, [sp, #!2E]", 1),
+    ENCODING_MAP(kThumbStrbRRI5,     0x7000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "strb", "r!0d, [r!1d, #!2d]", 1),
+    ENCODING_MAP(kThumbStrbRRR,      0x5400,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+                 "strb", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbStrhRRI5,     0x8000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "strh", "r!0d, [r!1d, #!2F]", 1),
+    ENCODING_MAP(kThumbStrhRRR,      0x5200,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+                 "strh", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbSubRRI3,      0x1e00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "subs", "r!0d, r!1d, #!2d]", 1),
+    ENCODING_MAP(kThumbSubRI8,       0x3800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+                 "subs", "r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbSubRRR,       0x1a00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "subs", "r!0d, r!1d, r!2d", 1),
+    ENCODING_MAP(kThumbSubSpI7,      0xb080,
+                 kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+                 "sub", "sp, #!0d", 1),
+    ENCODING_MAP(kThumbSwi,           0xdf00,
+                 kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,                       kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+                 "swi", "!0d", 1),
+    ENCODING_MAP(kThumbTst,           0x4200,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES,
+                 "tst", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumb2Vldrs,       0xed900a00,
+                 kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "vldr", "!0s, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vldrd,       0xed900b00,
+                 kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "vldr", "!0S, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vmuls,        0xee200a00,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vmuls", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vmuld,        0xee200b00,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vmuld", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2Vstrs,       0xed800a00,
+                 kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "vstr", "!0s, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vstrd,       0xed800b00,
+                 kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "vstr", "!0S, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vsubs,        0xee300a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vsub", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vsubd,        0xee300b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vsub", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2Vadds,        0xee300a00,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vadd", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vaddd,        0xee300b00,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vadd", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2Vdivs,        0xee800a00,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vdivs", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vdivd,        0xee800b00,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vdivd", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(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,          0xe92d0000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+                 | IS_STORE, "push", "<!0R>", 2),
+    ENCODING_MAP(kThumb2CmpRI8, 0xf1b00f00,
+                 kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+                 "cmp", "r!0d, #!1m", 2),
+    ENCODING_MAP(kThumb2AdcRRR,  0xeb500000, /* setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1,
+                 IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "adcs", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2AndRRR,  0xea000000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "and", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2BicRRR,  0xea200000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "bic", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2CmnRR,  0xeb000000,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "cmn", "r!0d, r!1d, shift !2d", 2),
+    ENCODING_MAP(kThumb2EorRRR,  0xea800000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "eor", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2MulRRR,  0xfb00f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2MnvRR,  0xea6f0000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "mvn", "r!0d, r!1d, shift !2d", 2),
+    ENCODING_MAP(kThumb2RsubRRI8,       0xf1d00000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "rsb", "r!0d,r!1d,#!2m", 2),
+    ENCODING_MAP(kThumb2NegRR,       0xf1d00000, /* instance of rsub */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "neg", "r!0d,r!1d", 2),
+    ENCODING_MAP(kThumb2OrrRRR,  0xea400000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "orr", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2TstRR,       0xea100f00,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+                 "tst", "r!0d, r!1d, shift !2d", 2),
+    ENCODING_MAP(kThumb2LslRRR,  0xfa00f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "lsl", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2LsrRRR,  0xfa20f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "lsr", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2AsrRRR,  0xfa40f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "asr", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2RorRRR,  0xfa60f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "ror", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2LslRRI5,  0xea4f0000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "lsl", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2LsrRRI5,  0xea4f0010,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "lsr", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2AsrRRI5,  0xea4f0020,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "asr", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2RorRRI5,  0xea4f0030,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "ror", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2BicRRI8,  0xf0200000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "bic", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2AndRRI8,  0xf0000000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "and", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2OrrRRI8,  0xf0400000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "orr", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2EorRRI8,  0xf0800000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "eor", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2AddRRI8,  0xf1100000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "adds", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2AdcRRI8,  0xf1500000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+                 "adcs", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2SubRRI8,  0xf1b00000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "subs", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2SbcRRI8,  0xf1700000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+                 "sbcs", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2It,  0xbf00,
+                 kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES,
+                 "it:!1b", "!0c", 1),
+    ENCODING_MAP(kThumb2Fmstat,  0xeef1fa10,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES,
+                 "fmstat", "", 2),
+    ENCODING_MAP(kThumb2Vcmpd,        0xeeb40b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+                 "vcmp.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vcmps,        0xeeb40a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+                 "vcmp.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2LdrPcRel12,       0xf8df0000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+                 "ldr", "r!0d, [r15pc, #!1d]", 2),
+    ENCODING_MAP(kThumb2BCond,        0xf0008000,
+                 kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | IS_BRANCH | USES_CCODES,
+                 "b!1c", "!0t", 2),
+    ENCODING_MAP(kThumb2Vmovd_RR,       0xeeb00b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vmov.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vmovs_RR,       0xeeb00a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vmov.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2Fmrs,       0xee100a10,
+                 kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "fmrs", "r!0d, !1s", 2),
+    ENCODING_MAP(kThumb2Fmsr,       0xee000a10,
+                 kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "fmsr", "!0s, r!1d", 2),
+    ENCODING_MAP(kThumb2Fmrrd,       0xec500b10,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2,
+                 "fmrrd", "r!0d, r!1d, !2S", 2),
+    ENCODING_MAP(kThumb2Fmdrr,       0xec400b10,
+                 kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "fmdrr", "!0S, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2Vabsd,       0xeeb00bc0,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vabs.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vabss,       0xeeb00ac0,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vabs.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2Vnegd,       0xeeb10b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vneg.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vnegs,       0xeeb10a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vneg.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2Vmovs_IMM8,       0xeeb00a00,
+                 kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "vmov.f32", "!0s, #0x!1h", 2),
+    ENCODING_MAP(kThumb2Vmovd_IMM8,       0xeeb00b00,
+                 kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "vmov.f64", "!0S, #0x!1h", 2),
+    ENCODING_MAP(kThumb2Mla,  0xfb000000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 15, 12,
+                 IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
+                 "mla", "r!0d, r!1d, r!2d, r!3d", 2),
+    ENCODING_MAP(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),
+    ENCODING_MAP(kThumb2LdrPcReln12,       0xf85f0000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+                 "ldr", "r!0d, [r15pc, -#!1d]", 2),
+    ENCODING_MAP(kThumbUndefined,       0xde00,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND,
+                 "undefined", "", 1),
+};
+
+/*
+ * The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
+ * not ready. Since r5FP is not updated often, it is less likely to
+ * generate unnecessary stall cycles.
+ */
+#define PADDING_MOV_R5_R5               0x1C2D
+
+/* Track the number of times that the code cache is patched */
+#if defined(WITH_JIT_TUNING)
+#define UPDATE_CODE_CACHE_PATCHES()    (gDvmJit.codeCachePatches++)
+#else
+#define UPDATE_CODE_CACHE_PATCHES()
+#endif
+
+/* Write the numbers in the constant and class pool to the output stream */
+static void installLiteralPools(CompilationUnit *cUnit)
+{
+    int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset);
+    /* Install number of class pointer literals */
+    *dataPtr++ = cUnit->numClassPointers;
+    ArmLIR *dataLIR = (ArmLIR *) cUnit->classPointerList;
+    while (dataLIR) {
+        /*
+         * Install the callsiteinfo pointers into the cells for now. They will
+         * be converted into real pointers in dvmJitInstallClassObjectPointers.
+         */
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+    dataLIR = (ArmLIR *) cUnit->literalList;
+    while (dataLIR) {
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+}
+
+/*
+ * Assemble the LIR into binary instruction format.  Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction.  In those cases we will try to substitute a new code
+ * sequence or request that the trace be shortened and retried.
+ */
+static AssemblerStatus assembleInstructions(CompilationUnit *cUnit,
+                                            intptr_t startAddr)
+{
+    short *bufferAddr = (short *) cUnit->codeBuffer;
+    ArmLIR *lir;
+
+    for (lir = (ArmLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
+        if (lir->opcode < 0) {
+            if ((lir->opcode == kArmPseudoPseudoAlign4) &&
+                /* 1 means padding is needed */
+                (lir->operands[0] == 1)) {
+                *bufferAddr++ = PADDING_MOV_R5_R5;
+            }
+            continue;
+        }
+
+        if (lir->flags.isNop) {
+            continue;
+        }
+
+        if (lir->opcode == kThumbLdrPcRel ||
+            lir->opcode == kThumb2LdrPcRel12 ||
+            lir->opcode == kThumbAddPcRel ||
+            ((lir->opcode == kThumb2Vldrd) && (lir->operands[1] == r15pc)) ||
+            ((lir->opcode == kThumb2Vldrs) && (lir->operands[1] == r15pc))) {
+            ArmLIR *lirTarget = (ArmLIR *) lir->generic.target;
+            intptr_t pc = (lir->generic.offset + 4) & ~3;
+            intptr_t target = lirTarget->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiples of 4: %d", delta);
+                dvmCompilerAbort(cUnit);
+            }
+            if ((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) {
+                if (cUnit->printMe) {
+                    ALOGD("kThumb2LdrPcRel12@%x: delta=%d", lir->generic.offset,
+                         delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryHalve;
+            } else if (delta > 1020) {
+                if (cUnit->printMe) {
+                    ALOGD("kThumbLdrPcRel@%x: delta=%d", lir->generic.offset,
+                         delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryHalve;
+            }
+            if ((lir->opcode == kThumb2Vldrs) || (lir->opcode == kThumb2Vldrd)) {
+                lir->operands[2] = delta >> 2;
+            } else {
+                lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ?
+                                    delta : delta >> 2;
+            }
+        } else if (lir->opcode == kThumb2Cbnz || lir->opcode == kThumb2Cbz) {
+            ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta > 126 || delta < 0) {
+                /* Convert to cmp rx,#0 / b[eq/ne] tgt pair */
+                ArmLIR *newInst =
+                    (ArmLIR *)dvmCompilerNew(sizeof(ArmLIR), true);
+                /* Make new branch instruction and insert after */
+                newInst->opcode = kThumbBCond;
+                newInst->operands[0] = 0;
+                newInst->operands[1] = (lir->opcode == kThumb2Cbz) ?
+                                        kArmCondEq : kArmCondNe;
+                newInst->generic.target = lir->generic.target;
+                dvmCompilerSetupResourceMasks(newInst);
+                dvmCompilerInsertLIRAfter((LIR *)lir, (LIR *)newInst);
+                /* Convert the cb[n]z to a cmp rx, #0 ] */
+                lir->opcode = kThumbCmpRI8;
+                /* operand[0] is src1 in both cb[n]z & CmpRI8 */
+                lir->operands[1] = 0;
+                lir->generic.target = 0;
+                dvmCompilerSetupResourceMasks(lir);
+                if (cUnit->printMe) {
+                    ALOGD("kThumb2Cbnz/kThumb2Cbz@%x: delta=%d",
+                         lir->generic.offset, delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryAll;
+            } else {
+                lir->operands[1] = delta >> 1;
+            }
+        } else if (lir->opcode == kThumbBCond ||
+                   lir->opcode == kThumb2BCond) {
+            ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
+                if (cUnit->printMe) {
+                    ALOGD("kThumbBCond@%x: delta=%d", lir->generic.offset,
+                         delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryHalve;
+            }
+            lir->operands[0] = delta >> 1;
+        } else if (lir->opcode == kThumbBUncond) {
+            ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta > 2046 || delta < -2048) {
+                ALOGE("Unconditional branch distance out of range: %d", delta);
+                dvmCompilerAbort(cUnit);
+            }
+            lir->operands[0] = delta >> 1;
+        } else if (lir->opcode == kThumbBlx1) {
+            assert(NEXT_LIR(lir)->opcode == kThumbBlx2);
+            /* curPC is Thumb */
+            intptr_t curPC = (startAddr + lir->generic.offset + 4) & ~3;
+            intptr_t target = lir->operands[1];
+
+            /* Match bit[1] in target with base */
+            if (curPC & 0x2) {
+                target |= 0x2;
+            }
+            int delta = target - curPC;
+            assert((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+            lir->operands[0] = (delta >> 12) & 0x7ff;
+            NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+        } else if (lir->opcode == kThumbBl1) {
+            assert(NEXT_LIR(lir)->opcode == kThumbBl2);
+            /* Both curPC and target are Thumb */
+            intptr_t curPC = startAddr + lir->generic.offset + 4;
+            intptr_t target = lir->operands[1];
+
+            int delta = target - curPC;
+            assert((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+            lir->operands[0] = (delta >> 12) & 0x7ff;
+            NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+        }
+
+        ArmEncodingMap *encoder = &EncodingMap[lir->opcode];
+        u4 bits = encoder->skeleton;
+        int i;
+        for (i = 0; i < 4; i++) {
+            u4 operand;
+            u4 value;
+            operand = lir->operands[i];
+            switch(encoder->fieldLoc[i].kind) {
+                case kFmtUnused:
+                    break;
+                case kFmtFPImm:
+                    value = ((operand & 0xF0) >> 4) << encoder->fieldLoc[i].end;
+                    value |= (operand & 0x0F) << encoder->fieldLoc[i].start;
+                    bits |= value;
+                    break;
+                case kFmtBrOffset:
+                    value = ((operand  & 0x80000) >> 19) << 26;
+                    value |= ((operand & 0x40000) >> 18) << 11;
+                    value |= ((operand & 0x20000) >> 17) << 13;
+                    value |= ((operand & 0x1f800) >> 11) << 16;
+                    value |= (operand  & 0x007ff);
+                    bits |= value;
+                    break;
+                case kFmtShift5:
+                    value = ((operand & 0x1c) >> 2) << 12;
+                    value |= (operand & 0x03) << 6;
+                    bits |= value;
+                    break;
+                case kFmtShift:
+                    value = ((operand & 0x70) >> 4) << 12;
+                    value |= (operand & 0x0f) << 4;
+                    bits |= value;
+                    break;
+                case kFmtBWidth:
+                    value = operand - 1;
+                    bits |= value;
+                    break;
+                case kFmtLsb:
+                    value = ((operand & 0x1c) >> 2) << 12;
+                    value |= (operand & 0x03) << 6;
+                    bits |= value;
+                    break;
+                case kFmtImm6:
+                    value = ((operand & 0x20) >> 5) << 9;
+                    value |= (operand & 0x1f) << 3;
+                    bits |= value;
+                    break;
+                case kFmtBitBlt:
+                    value = (operand << encoder->fieldLoc[i].start) &
+                            ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    bits |= value;
+                    break;
+                case kFmtDfp: {
+                    assert(DOUBLEREG(operand));
+                    assert((operand & 0x1) == 0);
+                    int regName = (operand & FP_REG_MASK) >> 1;
+                    /* Snag the 1-bit slice and position it */
+                    value = ((regName & 0x10) >> 4) <<
+                            encoder->fieldLoc[i].end;
+                    /* Extract and position the 4-bit slice */
+                    value |= (regName & 0x0f) <<
+                            encoder->fieldLoc[i].start;
+                    bits |= value;
+                    break;
+                }
+                case kFmtSfp:
+                    assert(SINGLEREG(operand));
+                    /* Snag the 1-bit slice and position it */
+                    value = (operand & 0x1) <<
+                            encoder->fieldLoc[i].end;
+                    /* Extract and position the 4-bit slice */
+                    value |= ((operand & 0x1e) >> 1) <<
+                            encoder->fieldLoc[i].start;
+                    bits |= value;
+                    break;
+                case kFmtImm12:
+                case kFmtModImm:
+                    value = ((operand & 0x800) >> 11) << 26;
+                    value |= ((operand & 0x700) >> 8) << 12;
+                    value |= operand & 0x0ff;
+                    bits |= value;
+                    break;
+                case kFmtImm16:
+                    value = ((operand & 0x0800) >> 11) << 26;
+                    value |= ((operand & 0xf000) >> 12) << 16;
+                    value |= ((operand & 0x0700) >> 8) << 12;
+                    value |= operand & 0x0ff;
+                    bits |= value;
+                    break;
+                default:
+                    assert(0);
+            }
+        }
+        if (encoder->size == 2) {
+            *bufferAddr++ = (bits >> 16) & 0xffff;
+        }
+        *bufferAddr++ = bits & 0xffff;
+    }
+    return kSuccess;
+}
+
+static int assignLiteralOffsetCommon(LIR *lir, int offset)
+{
+    for (;lir != NULL; lir = lir->next) {
+        lir->offset = offset;
+        offset += 4;
+    }
+    return offset;
+}
+
+/* Determine the offset of each literal field */
+static int assignLiteralOffset(CompilationUnit *cUnit, int offset)
+{
+    /* Reserved for the size field of class pointer pool */
+    offset += 4;
+    offset = assignLiteralOffsetCommon(cUnit->classPointerList, offset);
+    offset = assignLiteralOffsetCommon(cUnit->literalList, offset);
+    return offset;
+}
+
+/*
+ * Translation layout in the code cache.  Note that the codeAddress pointer
+ * in JitTable will point directly to the code body (field codeAddress).  The
+ * chain cell offset codeAddress - 2, and the address of the trace profile
+ * counter is at codeAddress - 6.
+ *
+ *      +----------------------------+
+ *      | Trace Profile Counter addr |  -> 4 bytes (PROF_COUNTER_ADDR_SIZE)
+ *      +----------------------------+
+ *   +--| Offset to chain cell counts|  -> 2 bytes (CHAIN_CELL_OFFSET_SIZE)
+ *   |  +----------------------------+
+ *   |  | Trace profile code         |  <- entry point when profiling
+ *   |  .  -   -   -   -   -   -   - .
+ *   |  | Code body                  |  <- entry point when not profiling
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Chaining Cells             |  -> 12/16 bytes, 4 byte aligned
+ *   |  .                            .
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Gap for large switch stmt  |  -> # cases >= MAX_CHAINED_SWITCH_CASES
+ *   |  +----------------------------+
+ *   +->| Chaining cell counts       |  -> 8 bytes, chain cell counts by type
+ *      +----------------------------+
+ *      | Trace description          |  -> variable sized
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | # Class pointer pool size  |  -> 4 bytes
+ *      +----------------------------+
+ *      | Class pointer pool         |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | Literal pool               |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *
+ */
+
+#define PROF_COUNTER_ADDR_SIZE 4
+#define CHAIN_CELL_OFFSET_SIZE 2
+
+/*
+ * Utility functions to navigate various parts in a trace. If we change the
+ * layout/offset in the future, we just modify these functions and we don't need
+ * to propagate the changes to all the use cases.
+ */
+static inline char *getTraceBase(const JitEntry *p)
+{
+    return (char*)p->codeAddress -
+        (PROF_COUNTER_ADDR_SIZE + CHAIN_CELL_OFFSET_SIZE +
+         (p->u.info.instructionSet == DALVIK_JIT_ARM ? 0 : 1));
+}
+
+/* Handy function to retrieve the profile count */
+static inline JitTraceCounter_t getProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return 0;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    return **p;
+}
+
+/* Handy function to reset the profile count */
+static inline void resetProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    **p = 0;
+}
+
+/* Get the pointer of the chain cell count */
+static inline ChainCellCounts* getChainCellCountsPointer(const char *base)
+{
+    /* 4 is the size of the profile count */
+    u2 *chainCellOffsetP = (u2 *) (base + PROF_COUNTER_ADDR_SIZE);
+    u2 chainCellOffset = *chainCellOffsetP;
+    return (ChainCellCounts *) ((char *) chainCellOffsetP + chainCellOffset);
+}
+
+/* Get the size of all chaining cells */
+static inline u4 getChainCellSize(const ChainCellCounts* pChainCellCounts)
+{
+    int cellSize = 0;
+    int i;
+
+    /* Get total count of chain cells */
+    for (i = 0; i < kChainingCellGap; i++) {
+        if (i != kChainingCellInvokePredicted) {
+            cellSize += pChainCellCounts->u.count[i] *
+                        (CHAIN_CELL_NORMAL_SIZE >> 2);
+        } else {
+            cellSize += pChainCellCounts->u.count[i] *
+                (CHAIN_CELL_PREDICTED_SIZE >> 2);
+        }
+    }
+    return cellSize;
+}
+
+/* Get the starting pointer of the trace description section */
+static JitTraceDescription* getTraceDescriptionPointer(const char *base)
+{
+    ChainCellCounts* pCellCounts = getChainCellCountsPointer(base);
+    return (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+}
+
+/* Get the size of a trace description */
+static int getTraceDescriptionSize(const JitTraceDescription *desc)
+{
+    int runCount;
+    /* Trace end is always of non-meta type (ie isCode == true) */
+    for (runCount = 0; ; runCount++) {
+        if (desc->trace[runCount].isCode &&
+            desc->trace[runCount].info.frag.runEnd)
+           break;
+    }
+    return sizeof(JitTraceDescription) + ((runCount+1) * sizeof(JitTraceRun));
+}
+
+#if defined(SIGNATURE_BREAKPOINT)
+/* Inspect the assembled instruction stream to find potential matches */
+static void matchSignatureBreakpoint(const CompilationUnit *cUnit,
+                                     unsigned int size)
+{
+    unsigned int i, j;
+    u4 *ptr = (u4 *) cUnit->codeBuffer;
+
+    for (i = 0; i < size - gDvmJit.signatureBreakpointSize + 1; i++) {
+        if (ptr[i] == gDvmJit.signatureBreakpoint[0]) {
+            for (j = 1; j < gDvmJit.signatureBreakpointSize; j++) {
+                if (ptr[i+j] != gDvmJit.signatureBreakpoint[j]) {
+                    break;
+                }
+            }
+            if (j == gDvmJit.signatureBreakpointSize) {
+                ALOGD("Signature match starting from offset %#x (%d words)",
+                     i*4, gDvmJit.signatureBreakpointSize);
+                int descSize = getTraceDescriptionSize(cUnit->traceDesc);
+                JitTraceDescription *newCopy =
+                    (JitTraceDescription *) malloc(descSize);
+                memcpy(newCopy, cUnit->traceDesc, descSize);
+                dvmCompilerWorkEnqueue(NULL, kWorkOrderTraceDebug, newCopy);
+                break;
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    ArmLIR *armLIR;
+    int offset = 0;
+    int i;
+    ChainCellCounts chainCellCounts;
+    int descSize = (cUnit->jitMode == kJitMethod) ?
+        0 : getTraceDescriptionSize(cUnit->traceDesc);
+    int chainingCellGap = 0;
+
+    info->instructionSet = cUnit->instructionSet;
+
+    /* Beginning offset needs to allow space for chain cell offset */
+    for (armLIR = (ArmLIR *) cUnit->firstLIRInsn;
+         armLIR;
+         armLIR = NEXT_LIR(armLIR)) {
+        armLIR->generic.offset = offset;
+        if (armLIR->opcode >= 0 && !armLIR->flags.isNop) {
+            armLIR->flags.size = EncodingMap[armLIR->opcode].size * 2;
+            offset += armLIR->flags.size;
+        } else if (armLIR->opcode == kArmPseudoPseudoAlign4) {
+            if (offset & 0x2) {
+                offset += 2;
+                armLIR->operands[0] = 1;
+            } else {
+                armLIR->operands[0] = 0;
+            }
+        }
+        /* Pseudo opcodes don't consume space */
+    }
+
+    /* Const values have to be word aligned */
+    offset = (offset + 3) & ~3;
+
+    u4 chainCellOffset = offset;
+    ArmLIR *chainCellOffsetLIR = NULL;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /*
+         * Get the gap (# of u4) between the offset of chaining cell count and
+         * the bottom of real chaining cells. If the translation has chaining
+         * cells, the gap is guaranteed to be multiples of 4.
+         */
+        chainingCellGap = (offset - cUnit->chainingCellBottom->offset) >> 2;
+
+        /* Add space for chain cell counts & trace description */
+        chainCellOffsetLIR = (ArmLIR *) cUnit->chainCellOffsetLIR;
+        assert(chainCellOffsetLIR);
+        assert(chainCellOffset < 0x10000);
+        assert(chainCellOffsetLIR->opcode == kArm16BitData &&
+               chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG);
+
+        /*
+         * Adjust the CHAIN_CELL_OFFSET_TAG LIR's offset to remove the
+         * space occupied by the pointer to the trace profiling counter.
+         */
+        chainCellOffsetLIR->operands[0] = chainCellOffset - 4;
+
+        offset += sizeof(chainCellCounts) + descSize;
+
+        assert((offset & 0x3) == 0);  /* Should still be word aligned */
+    }
+
+    /* Set up offsets for literals */
+    cUnit->dataOffset = offset;
+
+    /*
+     * Assign each class pointer/constant an offset from the beginning of the
+     * compilation unit.
+     */
+    offset = assignLiteralOffset(cUnit, offset);
+
+    cUnit->totalSize = offset;
+
+    if (gDvmJit.codeCacheByteUsed + cUnit->totalSize > gDvmJit.codeCacheSize) {
+        gDvmJit.codeCacheFull = true;
+        info->discardResult = true;
+        return;
+    }
+
+    /* Allocate enough space for the code block */
+    cUnit->codeBuffer = (unsigned char *)dvmCompilerNew(chainCellOffset, true);
+    if (cUnit->codeBuffer == NULL) {
+        ALOGE("Code buffer allocation failure");
+        info->discardResult = true;
+        return;
+    }
+
+    /*
+     * Attempt to assemble the trace.  Note that assembleInstructions
+     * may rewrite the code sequence and request a retry.
+     */
+    cUnit->assemblerStatus = assembleInstructions(cUnit,
+          (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed);
+
+    switch(cUnit->assemblerStatus) {
+        case kSuccess:
+            break;
+        case kRetryAll:
+            if (cUnit->assemblerRetries < MAX_ASSEMBLER_RETRIES) {
+                if (cUnit->jitMode != kJitMethod) {
+                    /* Restore pristine chain cell marker on retry */
+                    chainCellOffsetLIR->operands[0] = CHAIN_CELL_OFFSET_TAG;
+                }
+                return;
+            }
+            /* Too many retries - reset and try cutting the trace in half */
+            cUnit->assemblerRetries = 0;
+            cUnit->assemblerStatus = kRetryHalve;
+            return;
+        case kRetryHalve:
+            return;
+        default:
+             ALOGE("Unexpected assembler status: %d", cUnit->assemblerStatus);
+             dvmAbort();
+    }
+
+#if defined(SIGNATURE_BREAKPOINT)
+    if (info->discardResult == false && gDvmJit.signatureBreakpoint != NULL &&
+        chainCellOffset/4 >= gDvmJit.signatureBreakpointSize) {
+        matchSignatureBreakpoint(cUnit, chainCellOffset/4);
+    }
+#endif
+
+    /* Don't go all the way if the goal is just to get the verbose output */
+    if (info->discardResult) return;
+
+    /*
+     * The cache might disappear - acquire lock and check version
+     * Continue holding lock until translation cache update is complete.
+     * These actions are required here in the compiler thread because
+     * it is unaffected by suspend requests and doesn't know if a
+     * translation cache flush is in progress.
+     */
+    dvmLockMutex(&gDvmJit.compilerLock);
+    if (info->cacheVersion != gDvmJit.cacheVersion) {
+        /* Cache changed - discard current translation */
+        info->discardResult = true;
+        info->codeAddress = NULL;
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        return;
+    }
+
+    cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+    gDvmJit.codeCacheByteUsed += offset;
+
+    UNPROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Install the code block */
+    memcpy((char*)cUnit->baseAddr, cUnit->codeBuffer, chainCellOffset);
+    gDvmJit.numCompilations++;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /* Install the chaining cell counts */
+        for (i=0; i< kChainingCellGap; i++) {
+            chainCellCounts.u.count[i] = cUnit->numChainingCells[i];
+        }
+
+        /* Set the gap number in the chaining cell count structure */
+        chainCellCounts.u.count[kChainingCellGap] = chainingCellGap;
+
+        memcpy((char*)cUnit->baseAddr + chainCellOffset, &chainCellCounts,
+               sizeof(chainCellCounts));
+
+        /* Install the trace description */
+        memcpy((char*) cUnit->baseAddr + chainCellOffset +
+                       sizeof(chainCellCounts),
+               cUnit->traceDesc, descSize);
+    }
+
+    /* Write the literals directly into the code cache */
+    installLiteralPools(cUnit);
+
+    /* Flush dcache and invalidate the icache to maintain coherence */
+    dvmCompilerCacheFlush((long)cUnit->baseAddr,
+                          (long)((char *) cUnit->baseAddr + offset), 0);
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Translation cache update complete - release lock */
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /* Record code entry point and instruction set */
+    info->codeAddress = (char*)cUnit->baseAddr + cUnit->headerSize;
+    /* If applicable, mark low bit to denote thumb */
+    if (info->instructionSet != DALVIK_JIT_ARM)
+        info->codeAddress = (char*)info->codeAddress + 1;
+    /* transfer the size of the profiling code */
+    info->profileCodeSize = cUnit->profileCodeSize;
+}
+
+/*
+ * Returns the skeleton bit pattern associated with an opcode.  All
+ * variable fields are zeroed.
+ */
+static u4 getSkeleton(ArmOpcode op)
+{
+    return EncodingMap[op].skeleton;
+}
+
+static u4 assembleChainingBranch(int branchOffset, bool thumbTarget)
+{
+    u4 thumb1, thumb2;
+
+    if (!thumbTarget) {
+        thumb1 =  (getSkeleton(kThumbBlx1) | ((branchOffset>>12) & 0x7ff));
+        thumb2 =  (getSkeleton(kThumbBlx2) | ((branchOffset>> 1) & 0x7ff));
+    } else if ((branchOffset < -2048) | (branchOffset > 2046)) {
+        thumb1 =  (getSkeleton(kThumbBl1) | ((branchOffset>>12) & 0x7ff));
+        thumb2 =  (getSkeleton(kThumbBl2) | ((branchOffset>> 1) & 0x7ff));
+    } else {
+        thumb1 =  (getSkeleton(kThumbBUncond) | ((branchOffset>> 1) & 0x7ff));
+        thumb2 =  getSkeleton(kThumbOrr);  /* nop -> or r0, r0 */
+    }
+
+    return thumb2<<16 | thumb1;
+}
+
+/*
+ * Perform translation chain operation.
+ * For ARM, we'll use a pair of thumb instructions to generate
+ * an unconditional chaining branch of up to 4MB in distance.
+ * Use a BL, because the generic "interpret" translation needs
+ * the link register to find the dalvik pc of teh target.
+ *     111HHooooooooooo
+ * Where HH is 10 for the 1st inst, and 11 for the second and
+ * the "o" field is each instruction's 11-bit contribution to the
+ * 22-bit branch offset.
+ * If the target is nearby, use a single-instruction bl.
+ * If one or more threads is suspended, don't chain.
+ */
+void* dvmJitChain(void* tgtAddr, u4* branchAddr)
+{
+    int baseAddr = (u4) branchAddr + 4;
+    int branchOffset = (int) tgtAddr - baseAddr;
+    u4 newInst;
+    bool thumbTarget;
+
+    /*
+     * Only chain translations when there is no urge to ask all threads to
+     * suspend themselves via the interpreter.
+     */
+    if ((gDvmJit.pProfTable != NULL) && (gDvm.sumThreadSuspendCount == 0) &&
+        (gDvmJit.codeCacheFull == false)) {
+        assert((branchOffset >= -(1<<22)) && (branchOffset <= ((1<<22)-2)));
+
+        gDvmJit.translationChains++;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: chaining %#x to %#x",
+                 (int) branchAddr, (int) tgtAddr & -2));
+
+        /*
+         * NOTE: normally, all translations are Thumb[2] mode, with
+         * a single exception: the default TEMPLATE_INTERPRET
+         * pseudo-translation.  If the need ever arises to
+         * mix Arm & Thumb[2] translations, the following code should be
+         * generalized.
+         */
+        thumbTarget = (tgtAddr != dvmCompilerGetInterpretTemplate());
+
+        newInst = assembleChainingBranch(branchOffset, thumbTarget);
+
+        /*
+         * The second half-word instruction of the chaining cell must
+         * either be a nop (which represents initial state), or is the
+         * same exact branch halfword that we are trying to install.
+         */
+        assert( ((*branchAddr >> 16) == getSkeleton(kThumbOrr)) ||
+                ((*branchAddr >> 16) == (newInst >> 16)));
+
+        UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        *branchAddr = newInst;
+        dvmCompilerCacheFlush((long)branchAddr, (long)branchAddr + 4, 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,
+            (volatile int32_t *)(void *)&cellAddr->clazz);
+        dvmCompilerCacheFlush((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++;
+        const ClassObject *clazz = newContent->clazz;
+
+        gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+        gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+        gDvmJit.compilerICPatchQueue[index].classDescriptor = clazz->descriptor;
+        gDvmJit.compilerICPatchQueue[index].classLoader = clazz->classLoader;
+        /* For verification purpose only */
+        gDvmJit.compilerICPatchQueue[index].serialNumber = clazz->serialNumber;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchQueued++;
+#endif
+    } else {
+    /* Queue is full - just drop this patch request */
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchDropped++;
+#endif
+    }
+
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+#endif
+
+/*
+ * This method is called from the invoke templates for virtual and interface
+ * methods to speculatively setup a chain to the callee. The templates are
+ * written in assembly and have setup method, cell, and clazz at r0, r2, and
+ * r3 respectively, so there is a unused argument in the list. Upon return one
+ * of the following three results may happen:
+ *   1) Chain is not setup because the callee is native. Reset the rechain
+ *      count to a big number so that it will take a long time before the next
+ *      rechain attempt to happen.
+ *   2) Chain is not setup because the callee has not been created yet. Reset
+ *      the rechain count to a small number and retry in the near future.
+ *   3) Enqueue the new content for the chaining cell which will be appled in
+ *      next safe point.
+ */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz)
+{
+    int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#if defined(WITH_SELF_VERIFICATION)
+    newRechainCount = PREDICTED_CHAIN_COUNTER_AVOID;
+    goto done;
+#else
+    PredictedChainingCell newCell;
+    int baseAddr, branchOffset, tgtAddr;
+    if (dvmIsNativeMethod(method)) {
+        UNPROTECT_CODE_CACHE(cell, sizeof(*cell));
+
+        /*
+         * Put a non-zero/bogus value in the clazz field so that it won't
+         * trigger immediate patching and will continue to fail to match with
+         * a real clazz pointer.
+         */
+        cell->clazz = (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cell, sizeof(*cell));
+        goto done;
+    }
+    tgtAddr = (int) dvmJitGetTraceAddr(method->insns);
+
+    /*
+     * Compilation not made yet for the callee. Reset the counter to a small
+     * value and come back to check soon.
+     */
+    if ((tgtAddr == 0) ||
+        ((void*)tgtAddr == dvmCompilerGetInterpretTemplate())) {
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p to method %s%s delayed",
+                 cell, method->clazz->descriptor, method->name));
+        goto done;
+    }
+
+    if (cell->clazz == NULL) {
+        newRechainCount = self->icRechainCount;
+    }
+
+    baseAddr = (int) cell + 4;   // PC is cur_addr + 4
+    branchOffset = tgtAddr - baseAddr;
+
+    newCell.branch = assembleChainingBranch(branchOffset, true);
+    newCell.clazz = clazz;
+    newCell.method = method;
+    newCell.stagedClazz = NULL;
+
+    /*
+     * Enter the work order to the queue and the chaining cell will be patched
+     * the next time a safe point is entered.
+     *
+     * If the enqueuing fails reset the rechain count to a normal value so that
+     * it won't get indefinitely delayed.
+     */
+    inlineCachePatchEnqueue(cell, &newCell);
+#endif
+done:
+    self->icRechainCount = newRechainCount;
+    return method;
+}
+
+/*
+ * Patch the inline cache content based on the content passed from the work
+ * order.
+ */
+void dvmCompilerPatchInlineCache(void)
+{
+    int i;
+    PredictedChainingCell *minAddr, *maxAddr;
+
+    /* Nothing to be done */
+    if (gDvmJit.compilerICPatchIndex == 0) return;
+
+    /*
+     * Since all threads are already stopped we don't really need to acquire
+     * the lock. But race condition can be easily introduced in the future w/o
+     * paying attention so we still acquire the lock here.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    //ALOGD("Number of IC patch work orders: %d", gDvmJit.compilerICPatchIndex);
+
+    /* Initialize the min/max address range */
+    minAddr = (PredictedChainingCell *)
+        ((char *) gDvmJit.codeCache + gDvmJit.codeCacheSize);
+    maxAddr = (PredictedChainingCell *) gDvmJit.codeCache;
+
+    for (i = 0; i < gDvmJit.compilerICPatchIndex; i++) {
+        ICPatchWorkOrder *workOrder = &gDvmJit.compilerICPatchQueue[i];
+        PredictedChainingCell *cellAddr = workOrder->cellAddr;
+        PredictedChainingCell *cellContent = &workOrder->cellContent;
+        ClassObject *clazz = dvmFindClassNoInit(workOrder->classDescriptor,
+                                                workOrder->classLoader);
+
+        assert(clazz->serialNumber == workOrder->serialNumber);
+
+        /* Use the newly resolved clazz pointer */
+        cellContent->clazz = clazz;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p from %s to %s (%s) "
+                 "patched",
+                 cellAddr,
+                 cellAddr->clazz->descriptor,
+                 cellContent->clazz->descriptor,
+                 cellContent->method->name));
+
+        /* Patch the chaining cell */
+        *cellAddr = *cellContent;
+        minAddr = (cellAddr < minAddr) ? cellAddr : minAddr;
+        maxAddr = (cellAddr > maxAddr) ? cellAddr : maxAddr;
+    }
+
+    /* Then synchronize the I/D cache */
+    dvmCompilerCacheFlush((long) minAddr, (long) (maxAddr+1), 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.
+ */
+static u4* unchainSingle(JitEntry *trace)
+{
+    const char *base = getTraceBase(trace);
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    u4* pChainCells;
+    u4 newInst;
+    int i,j;
+    PredictedChainingCell *predChainCell;
+
+    if (cellSize == 0)
+        return (u4 *) pChainCellCounts;
+
+    /* Locate the beginning of the chain cell region */
+    pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+                  pChainCellCounts->u.count[kChainingCellGap];
+
+    /* The cells are sorted in order - walk through them and reset */
+    for (i = 0; i < kChainingCellGap; i++) {
+        int elemSize = CHAIN_CELL_NORMAL_SIZE >> 2;  /* In 32-bit words */
+        if (i == kChainingCellInvokePredicted) {
+            elemSize = CHAIN_CELL_PREDICTED_SIZE >> 2;
+        }
+
+        for (j = 0; j < pChainCellCounts->u.count[i]; j++) {
+            switch(i) {
+                case kChainingCellNormal:
+                case kChainingCellHot:
+                case kChainingCellInvokeSingleton:
+                case kChainingCellBackwardBranch:
+                    /*
+                     * Replace the 1st half-word of the cell with an
+                     * unconditional branch, leaving the 2nd half-word
+                     * untouched.  This avoids problems with a thread
+                     * that is suspended between the two halves when
+                     * this unchaining takes place.
+                     */
+                    newInst = *pChainCells;
+                    newInst &= 0xFFFF0000;
+                    newInst |= getSkeleton(kThumbBUncond); /* b offset is 0 */
+                    *pChainCells = newInst;
+                    break;
+                case kChainingCellInvokePredicted:
+                    predChainCell = (PredictedChainingCell *) pChainCells;
+                    /*
+                     * There could be a race on another mutator thread to use
+                     * this particular predicted cell and the check has passed
+                     * the clazz comparison. So we cannot safely wipe the
+                     * method and branch but it is safe to clear the clazz,
+                     * which serves as the key.
+                     */
+                    predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT;
+                    break;
+                default:
+                    ALOGE("Unexpected chaining type: %d", i);
+                    dvmAbort();  // dvmAbort OK here - can't safely recover
+            }
+            COMPILER_TRACE_CHAINING(
+                ALOGD("Jit Runtime: unchaining %#x", (int)pChainCells));
+            pChainCells += elemSize;  /* Advance by a fixed number of words */
+        }
+    }
+    return pChainCells;
+}
+
+/* Unchain all translation in the cache. */
+void dvmJitUnchainAll()
+{
+    u4* lowAddress = NULL;
+    u4* highAddress = NULL;
+    if (gDvmJit.pJitEntryTable != NULL) {
+        COMPILER_TRACE_CHAINING(LOGD("Jit Runtime: unchaining all"));
+        dvmLockMutex(&gDvmJit.tableLock);
+
+        UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        for (size_t i = 0; i < gDvmJit.jitTableSize; i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC &&
+                !gDvmJit.pJitEntryTable[i].u.info.isMethodEntry &&
+                gDvmJit.pJitEntryTable[i].codeAddress &&
+                (gDvmJit.pJitEntryTable[i].codeAddress !=
+                 dvmCompilerGetInterpretTemplate())) {
+                u4* lastAddress;
+                lastAddress = unchainSingle(&gDvmJit.pJitEntryTable[i]);
+                if (lowAddress == NULL ||
+                      (u4*)gDvmJit.pJitEntryTable[i].codeAddress <
+                      lowAddress)
+                    lowAddress = lastAddress;
+                if (lastAddress > highAddress)
+                    highAddress = lastAddress;
+            }
+        }
+        dvmCompilerCacheFlush((long)lowAddress, (long)highAddress, 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;
+}
+
+/* Dumps profile info for a single trace */
+static int dumpTraceProfile(JitEntry *p, bool silent, bool reset,
+                            unsigned long sum)
+{
+    int idx;
+
+    if (p->codeAddress == NULL) {
+        if (!silent)
+            ALOGD("TRACEPROFILE NULL");
+        return 0;
+    }
+    if (p->codeAddress == dvmCompilerGetInterpretTemplate()) {
+        if (!silent)
+            ALOGD("TRACEPROFILE INTERPRET_ONLY");
+        return 0;
+    }
+    JitTraceCounter_t count = getProfileCount(p);
+    if (reset) {
+        resetProfileCount(p);
+    }
+    if (silent) {
+        return count;
+    }
+    JitTraceDescription *desc = getTraceDescriptionPointer(getTraceBase(p));
+    const Method *method = desc->method;
+    char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+    jitProfileAddrToLine addrToLine = {0, desc->trace[0].info.frag.startOffset};
+
+    /*
+     * We may end up decoding the debug information for the same method
+     * multiple times, but the tradeoff is we don't need to allocate extra
+     * space to store the addr/line mapping. Since this is a debugging feature
+     * and done infrequently so the slower but simpler mechanism should work
+     * just fine.
+     */
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+                       dvmGetMethodCode(method),
+                       method->clazz->descriptor,
+                       method->prototype.protoIdx,
+                       method->accessFlags,
+                       addrToLineCb, NULL, &addrToLine);
+
+    ALOGD("TRACEPROFILE 0x%08x % 10d %5.2f%% [%#x(+%d), %d] %s%s;%s",
+         (int) getTraceBase(p),
+         count,
+         ((float ) count) / sum * 100.0,
+         desc->trace[0].info.frag.startOffset,
+         desc->trace[0].info.frag.numInsts,
+         addrToLine.lineNum,
+         method->clazz->descriptor, method->name, methodDesc);
+    free(methodDesc);
+
+    /* Find the last fragment (ie runEnd is set) */
+    for (idx = 0;
+         desc->trace[idx].isCode && !desc->trace[idx].info.frag.runEnd;
+         idx++) {
+    }
+
+    /*
+     * runEnd must comes with a JitCodeDesc frag. If isCode is false it must
+     * be a meta info field (only used by callsite info for now).
+     */
+    if (!desc->trace[idx].isCode) {
+        const Method *method = (const Method *)
+            desc->trace[idx+JIT_TRACE_CUR_METHOD-1].info.meta;
+        char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+        /* Print the callee info in the trace */
+        ALOGD("    -> %s%s;%s", method->clazz->descriptor, method->name,
+             methodDesc);
+    }
+
+    return count;
+}
+
+/* Create a copy of the trace descriptor of an existing compilation */
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const JitEntry *knownEntry)
+{
+    const JitEntry *jitEntry = knownEntry ? knownEntry
+                                          : dvmJitFindEntry(pc, false);
+    if ((jitEntry == NULL) || (jitEntry->codeAddress == 0))
+        return NULL;
+
+    JitTraceDescription *desc =
+        getTraceDescriptionPointer(getTraceBase(jitEntry));
+
+    /* Now make a copy and return */
+    int descSize = getTraceDescriptionSize(desc);
+    JitTraceDescription *newCopy = (JitTraceDescription *) malloc(descSize);
+    memcpy(newCopy, desc, descSize);
+    return newCopy;
+}
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+    const JitEntry *jitEntry1 = (const JitEntry *)entry1;
+    const JitEntry *jitEntry2 = (const JitEntry *)entry2;
+
+    JitTraceCounter_t count1 = getProfileCount(jitEntry1);
+    JitTraceCounter_t count2 = getProfileCount(jitEntry2);
+    return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles()
+{
+    JitEntry *sortedEntries;
+    int numTraces = 0;
+    unsigned long sum = 0;
+    unsigned int i;
+
+    /* Make sure that the table is not changing */
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Sort the entries by descending order */
+    sortedEntries = (JitEntry *)malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+    if (sortedEntries == NULL)
+        goto done;
+    memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+           sizeof(JitEntry) * gDvmJit.jitTableSize);
+    qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+          sortTraceProfileCount);
+
+    /* Analyze the sorted entries */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            sum += dumpTraceProfile(&sortedEntries[i],
+                                       true /* silent */,
+                                       false /* reset */,
+                                       0);
+            numTraces++;
+        }
+    }
+    if (numTraces == 0)
+        numTraces = 1;
+    if (sum == 0) {
+        sum = 1;
+    }
+
+    ALOGD("JIT: Average execution count -> %d",(int)(sum / numTraces));
+
+    /* Dump the sorted entries. The count of each trace will be reset to 0. */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            dumpTraceProfile(&sortedEntries[i],
+                             false /* silent */,
+                             true /* reset */,
+                             sum);
+        }
+    }
+
+    for (i=0; i < gDvmJit.jitTableSize && i < 10; i++) {
+        /* Stip interpreter stubs */
+        if (sortedEntries[i].codeAddress == dvmCompilerGetInterpretTemplate()) {
+            continue;
+        }
+        JitTraceDescription* desc =
+            dvmCopyTraceDescriptor(NULL, &sortedEntries[i]);
+        if (desc) {
+            dvmCompilerWorkEnqueue(sortedEntries[i].dPC,
+                                   kWorkOrderTraceDebug, desc);
+        }
+    }
+
+    free(sortedEntries);
+done:
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    return;
+}
+
+static void findClassPointersSingleTrace(char *base, void (*callback)(void *))
+{
+    unsigned int chainTypeIdx, chainIdx;
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    /* Scan the chaining cells */
+    if (cellSize) {
+        /* Locate the beginning of the chain cell region */
+        u4 *pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+            pChainCellCounts->u.count[kChainingCellGap];
+        /* The cells are sorted in order - walk through them */
+        for (chainTypeIdx = 0; chainTypeIdx < kChainingCellGap;
+             chainTypeIdx++) {
+            if (chainTypeIdx != kChainingCellInvokePredicted) {
+                /* In 32-bit words */
+                pChainCells += (CHAIN_CELL_NORMAL_SIZE >> 2) *
+                    pChainCellCounts->u.count[chainTypeIdx];
+                continue;
+            }
+            for (chainIdx = 0;
+                 chainIdx < pChainCellCounts->u.count[chainTypeIdx];
+                 chainIdx++) {
+                PredictedChainingCell *cell =
+                    (PredictedChainingCell *) pChainCells;
+                /*
+                 * Report the cell if it contains a sane class
+                 * pointer.
+                 */
+                if (cell->clazz != NULL &&
+                    cell->clazz !=
+                      (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ) {
+                    callback(&cell->clazz);
+                }
+                pChainCells += CHAIN_CELL_PREDICTED_SIZE >> 2;
+            }
+        }
+    }
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    int *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *classPointerP++;
+    for (; numClassPointers; numClassPointers--, classPointerP++) {
+        callback(classPointerP);
+    }
+}
+
+/*
+ * Scan class pointers in each translation and pass its address to the callback
+ * function. Currently such a pointers can be found in the pointer pool and the
+ * clazz field in the predicted chaining cells.
+ */
+void dvmJitScanAllClassPointers(void (*callback)(void *))
+{
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    /* Handle the inflight compilation first */
+    if (gDvmJit.inflightBaseAddr)
+        findClassPointersSingleTrace((char *) gDvmJit.inflightBaseAddr,
+                                     callback);
+
+    if (gDvmJit.pJitEntryTable != NULL) {
+        unsigned int traceIdx;
+        dvmLockMutex(&gDvmJit.tableLock);
+        for (traceIdx = 0; traceIdx < gDvmJit.jitTableSize; traceIdx++) {
+            const JitEntry *entry = &gDvmJit.pJitEntryTable[traceIdx];
+            if (entry->dPC &&
+                !entry->u.info.isMethodEntry &&
+                entry->codeAddress &&
+                (entry->codeAddress != dvmCompilerGetInterpretTemplate())) {
+                char *base = getTraceBase(entry);
+                findClassPointersSingleTrace(base, callback);
+            }
+        }
+        dvmUnlockMutex(&gDvmJit.tableLock);
+    }
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+}
+
+/*
+ * Provide the final touch on the class object pointer pool to install the
+ * actual pointers. The thread has to be in the running state.
+ */
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit, char *codeAddress)
+{
+    char *base = codeAddress - cUnit->headerSize -
+                 (cUnit->instructionSet == DALVIK_JIT_ARM ? 0 : 1);
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    intptr_t *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *(int *)classPointerP++;
+    intptr_t *startClassPointerP = classPointerP;
+
+    /*
+     * Change the thread state to VM_RUNNING so that GC won't be happening
+     * when the assembler looks up the class pointers. May suspend the current
+     * thread if there is a pending request before the state is actually
+     * changed to RUNNING.
+     */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_RUNNING);
+
+    /*
+     * Unprotecting the code cache will need to acquire the code cache
+     * protection lock first. Doing so after the state change may increase the
+     * time spent in the RUNNING state (which may delay the next GC request
+     * should there be contention on codeCacheProtectionLock). In practice
+     * this is probably not going to happen often since a GC is just served.
+     * More importantly, acquiring the lock before the state change will
+     * cause deadlock (b/4192964).
+     */
+    UNPROTECT_CODE_CACHE(startClassPointerP,
+                         numClassPointers * sizeof(intptr_t));
+#if defined(WITH_JIT_TUNING)
+    u8 startTime = dvmGetRelativeTimeUsec();
+#endif
+    for (;numClassPointers; numClassPointers--) {
+        CallsiteInfo *callsiteInfo = (CallsiteInfo *) *classPointerP;
+        ClassObject *clazz = dvmFindClassNoInit(
+            callsiteInfo->classDescriptor, callsiteInfo->classLoader);
+        assert(!strcmp(clazz->descriptor, callsiteInfo->classDescriptor));
+        *classPointerP++ = (intptr_t) clazz;
+    }
+
+    /*
+     * Register the base address so that if GC kicks in after the thread state
+     * has been changed to VMWAIT and before the compiled code is registered
+     * in the JIT table, its content can be patched if class objects are
+     * moved.
+     */
+    gDvmJit.inflightBaseAddr = base;
+
+#if defined(WITH_JIT_TUNING)
+    u8 blockTime = dvmGetRelativeTimeUsec() - startTime;
+    gDvmJit.compilerThreadBlockGCTime += blockTime;
+    if (blockTime > gDvmJit.maxCompilerThreadBlockGCTime)
+        gDvmJit.maxCompilerThreadBlockGCTime = blockTime;
+    gDvmJit.numCompilerThreadBlockGC++;
+#endif
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(startClassPointerP, numClassPointers * sizeof(intptr_t));
+
+    /* Change the thread state back to VMWAIT */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_VMWAIT);
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * The following are used to keep compiled loads and stores from modifying
+ * memory during self verification mode.
+ *
+ * Stores do not modify memory. Instead, the address and value pair are stored
+ * into heapSpace. Addresses within heapSpace are unique. For accesses smaller
+ * than a word, the word containing the address is loaded first before being
+ * updated.
+ *
+ * Loads check heapSpace first and return data from there if an entry exists.
+ * Otherwise, data is loaded from memory as usual.
+ */
+
+/* Used to specify sizes of memory operations */
+enum {
+    kSVByte,
+    kSVSignedByte,
+    kSVHalfword,
+    kSVSignedHalfword,
+    kSVWord,
+    kSVDoubleword,
+    kSVVariable,
+};
+
+/* Load the value of a decoded register from the stack */
+static int selfVerificationMemRegLoad(int* sp, int reg)
+{
+    return *(sp + reg);
+}
+
+/* Load the value of a decoded doubleword register from the stack */
+static s8 selfVerificationMemRegLoadDouble(int* sp, int reg)
+{
+    return *((s8*)(sp + reg));
+}
+
+/* Store the value of a decoded register out to the stack */
+static void selfVerificationMemRegStore(int* sp, int data, int reg)
+{
+    *(sp + reg) = data;
+}
+
+/* Store the value of a decoded doubleword register out to the stack */
+static void selfVerificationMemRegStoreDouble(int* sp, s8 data, int reg)
+{
+    *((s8*)(sp + reg)) = data;
+}
+
+/*
+ * Load the specified size of data from the specified address, checking
+ * heapSpace first if Self Verification mode wrote to it previously, and
+ * falling back to actual memory otherwise.
+ */
+static int selfVerificationLoad(int addr, int size)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int data;
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) {
+            addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+            break;
+        }
+    }
+
+    switch (size) {
+        case kSVByte:
+            data = *((u1*) addr);
+            break;
+        case kSVSignedByte:
+            data = *((s1*) addr);
+            break;
+        case kSVHalfword:
+            data = *((u2*) addr);
+            break;
+        case kSVSignedHalfword:
+            data = *((s2*) addr);
+            break;
+        case kSVWord:
+            data = *((u4*) addr);
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationLoad: %d", size);
+            data = 0;
+            dvmAbort();
+    }
+
+    //ALOGD("*** HEAP LOAD: Addr: %#x Data: %#x Size: %d", addr, data, size);
+    return data;
+}
+
+/* Like selfVerificationLoad, but specifically for doublewords */
+static s8 selfVerificationLoadDoubleword(int addr)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    ShadowHeap* heapSpacePtr;
+
+    int addr2 = addr+4;
+    unsigned int data = *((unsigned int*) addr);
+    unsigned int data2 = *((unsigned int*) addr2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            data = heapSpacePtr->data;
+        } else if (heapSpacePtr->addr == addr2) {
+            data2 = heapSpacePtr->data;
+        }
+    }
+
+    //ALOGD("*** HEAP LOAD DOUBLEWORD: Addr: %#x Data: %#x Data2: %#x",
+    //    addr, data, data2);
+    return (((s8) data2) << 32) | data;
+}
+
+/*
+ * Handles a store of a specified size of data to a specified address.
+ * This gets logged as an addr/data pair in heapSpace instead of modifying
+ * memory.  Addresses in heapSpace are unique, and accesses smaller than a
+ * word pull the entire word from memory first before updating.
+ */
+static void selfVerificationStore(int addr, int data, int size)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    //ALOGD("*** HEAP STORE: Addr: %#x Data: %#x Size: %d", addr, data, size);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) break;
+    }
+
+    if (heapSpacePtr == shadowSpace->heapSpaceTail) {
+        heapSpacePtr->addr = maskedAddr;
+        heapSpacePtr->data = *((unsigned int*) maskedAddr);
+        shadowSpace->heapSpaceTail++;
+    }
+
+    addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+    switch (size) {
+        case kSVByte:
+            *((u1*) addr) = data;
+            break;
+        case kSVSignedByte:
+            *((s1*) addr) = data;
+            break;
+        case kSVHalfword:
+            *((u2*) addr) = data;
+            break;
+        case kSVSignedHalfword:
+            *((s2*) addr) = data;
+            break;
+        case kSVWord:
+            *((u4*) addr) = data;
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationSave: %d", size);
+            dvmAbort();
+    }
+}
+
+/* Like selfVerificationStore, but specifically for doublewords */
+static void selfVerificationStoreDoubleword(int addr, s8 double_data)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int addr2 = addr+4;
+    int data = double_data;
+    int data2 = double_data >> 32;
+    bool store1 = false, store2 = false;
+
+    //ALOGD("*** HEAP STORE DOUBLEWORD: Addr: %#x Data: %#x, Data2: %#x",
+    //    addr, data, data2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            heapSpacePtr->data = data;
+            store1 = true;
+        } else if (heapSpacePtr->addr == addr2) {
+            heapSpacePtr->data = data2;
+            store2 = true;
+        }
+    }
+
+    if (!store1) {
+        shadowSpace->heapSpaceTail->addr = addr;
+        shadowSpace->heapSpaceTail->data = data;
+        shadowSpace->heapSpaceTail++;
+    }
+    if (!store2) {
+        shadowSpace->heapSpaceTail->addr = addr2;
+        shadowSpace->heapSpaceTail->data = data2;
+        shadowSpace->heapSpaceTail++;
+    }
+}
+
+/*
+ * Decodes the memory instruction at the address specified in the link
+ * register. All registers (r0-r12,lr) and fp registers (d0-d15) are stored
+ * consecutively on the stack beginning at the specified stack pointer.
+ * Calls the proper Self Verification handler for the memory instruction and
+ * updates the link register to point past the decoded memory instruction.
+ */
+void dvmSelfVerificationMemOpDecode(int lr, int* sp)
+{
+    enum {
+        kMemOpLdrPcRel = 0x09, // ldr(3)  [01001] rd[10..8] imm_8[7..0]
+        kMemOpRRR      = 0x0A, // Full opcode is 7 bits
+        kMemOp2Single  = 0x0A, // Used for Vstrs and Vldrs
+        kMemOpRRR2     = 0x0B, // Full opcode is 7 bits
+        kMemOp2Double  = 0x0B, // Used for Vstrd and Vldrd
+        kMemOpStrRRI5  = 0x0C, // str(1)  [01100] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRI5  = 0x0D, // ldr(1)  [01101] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRI5 = 0x0E, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRI5 = 0x0F, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRI5 = 0x10, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRI5 = 0x11, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrSpRel = 0x13, // ldr(4)  [10011] rd[10..8] imm_8[7..0]
+        kMemOpStmia    = 0x18, // stmia   [11000] rn[10..8] reglist [7..0]
+        kMemOpLdmia    = 0x19, // ldmia   [11001] rn[10..8] reglist [7..0]
+        kMemOpStrRRR   = 0x28, // str(2)  [0101000] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRR  = 0x29, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRR  = 0x2A, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrsbRRR = 0x2B, // ldrsb   [0101011] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRR   = 0x2C, // ldr(2)  [0101100] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRR  = 0x2D, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRR  = 0x2E, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrshRRR = 0x2F, // ldrsh   [0101111] rm[8..6] rn[5..3] rd[2..0]
+        kMemOp2Stmia   = 0xE88, // stmia  [111010001000[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia   = 0xE89, // ldmia  [111010001001[ rn[19..16] mask[15..0]
+        kMemOp2Stmia2  = 0xE8A, // stmia  [111010001010[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia2  = 0xE8B, // ldmia  [111010001011[ rn[19..16] mask[15..0]
+        kMemOp2Vstr    = 0xED8, // Used for Vstrs and Vstrd
+        kMemOp2Vldr    = 0xED9, // Used for Vldrs and Vldrd
+        kMemOp2Vstr2   = 0xEDC, // Used for Vstrs and Vstrd
+        kMemOp2Vldr2   = 0xEDD, // Used for Vstrs and Vstrd
+        kMemOp2StrbRRR = 0xF80, /* str rt,[rn,rm,LSL #imm] [111110000000]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrbRRR = 0xF81, /* ldrb rt,[rn,rm,LSL #imm] [111110000001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrhRRR = 0xF82, /* str rt,[rn,rm,LSL #imm] [111110000010]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrhRRR = 0xF83, /* ldrh rt,[rn,rm,LSL #imm] [111110000011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrRRR  = 0xF84, /* str rt,[rn,rm,LSL #imm] [111110000100]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrRRR  = 0xF85, /* ldr rt,[rn,rm,LSL #imm] [111110000101]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrbRRI12 = 0xF88, /* strb rt,[rn,#imm12] [111110001000]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrbRRI12 = 0xF89, /* ldrb rt,[rn,#imm12] [111110001001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrhRRI12 = 0xF8A, /* strh rt,[rn,#imm12] [111110001010]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrhRRI12 = 0xF8B, /* ldrh rt,[rn,#imm12] [111110001011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrRRI12 = 0xF8C, /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrRRI12 = 0xF8D, /* ldr(Imm,T3) rd,[rn,#imm12] [111110001101]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrsbRRR = 0xF91, /* ldrsb rt,[rn,rm,LSL #imm] [111110010001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrshRRR = 0xF93, /* ldrsh rt,[rn,rm,LSL #imm] [111110010011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrsbRRI12 = 0xF99, /* ldrsb rt,[rn,#imm12] [111110011001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrshRRI12 = 0xF9B, /* ldrsh rt,[rn,#imm12] [111110011011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2        = 0xE000, // top 3 bits set indicates Thumb2
+    };
+
+    int addr, offset, data;
+    long long double_data;
+    int size = kSVWord;
+    bool store = false;
+    unsigned int *lr_masked = (unsigned int *) (lr & 0xFFFFFFFE);
+    unsigned int insn = *lr_masked;
+
+    int old_lr;
+    old_lr = selfVerificationMemRegLoad(sp, 13);
+
+    if ((insn & kMemOp2) == kMemOp2) {
+        insn = (insn << 16) | (insn >> 16);
+        //ALOGD("*** THUMB2 - Addr: %#x Insn: %#x", lr, insn);
+
+        int opcode12 = (insn >> 20) & 0xFFF;
+        int opcode4 = (insn >> 8) & 0xF;
+        int imm2 = (insn >> 4) & 0x3;
+        int imm8 = insn & 0xFF;
+        int imm12 = insn & 0xFFF;
+        int rd = (insn >> 12) & 0xF;
+        int rm = insn & 0xF;
+        int rn = (insn >> 16) & 0xF;
+        int rt = (insn >> 12) & 0xF;
+        bool wBack = true;
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+4, 13);
+
+        // Determine whether the mem op is a store or load
+        switch (opcode12) {
+            case kMemOp2Stmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2StrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2StrRRI12:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode12) {
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+                size = kSVByte;
+                break;
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrsbRRI12:
+                size = kSVSignedByte;
+                break;
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+                size = kSVHalfword;
+                break;
+            case kMemOp2LdrshRRR:
+            case kMemOp2LdrshRRI12:
+                size = kSVSignedHalfword;
+                break;
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                if (opcode4 == kMemOp2Double) size = kSVDoubleword;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode12) {
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                offset = imm8 << 2;
+                if (opcode4 == kMemOp2Single) {
+                    rt = rd << 1;
+                    if (insn & 0x400000) rt |= 0x1;
+                } else if (opcode4 == kMemOp2Double) {
+                    if (insn & 0x400000) rt |= 0x10;
+                    rt = rt << 1;
+                } else {
+                    ALOGE("*** ERROR: UNRECOGNIZED VECTOR MEM OP: %x", opcode4);
+                    dvmAbort();
+                }
+                rt += 14;
+                break;
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2LdrRRR:
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrshRRR:
+                offset = selfVerificationMemRegLoad(sp, rm) << imm2;
+                break;
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+            case kMemOp2StrRRI12:
+            case kMemOp2LdrRRI12:
+            case kMemOp2LdrsbRRI12:
+            case kMemOp2LdrshRRI12:
+                offset = imm12;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+                wBack = false;
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB2 MEM OP: %x", opcode12);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 STMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationMemRegLoadDouble(sp, rt);
+                selfVerificationStoreDoubleword(addr+offset, double_data);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 LDMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationLoadDoubleword(addr+offset);
+                selfVerificationMemRegStoreDouble(sp, double_data, rt);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    } else {
+        //ALOGD("*** THUMB - Addr: %#x Insn: %#x", lr, insn);
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+2, 13);
+
+        int opcode5 = (insn >> 11) & 0x1F;
+        int opcode7 = (insn >> 9) & 0x7F;
+        int imm = (insn >> 6) & 0x1F;
+        int rd = (insn >> 8) & 0x7;
+        int rm = (insn >> 6) & 0x7;
+        int rn = (insn >> 3) & 0x7;
+        int rt = insn & 0x7;
+
+        // Determine whether the mem op is a store or load
+        switch (opcode5) {
+            case kMemOpRRR:
+                switch (opcode7) {
+                    case kMemOpStrRRR:
+                    case kMemOpStrhRRR:
+                    case kMemOpStrbRRR:
+                        store = true;
+                }
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpStrbRRI5:
+            case kMemOpStrhRRI5:
+            case kMemOpStmia:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode5) {
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                switch (opcode7) {
+                    case kMemOpStrbRRR:
+                    case kMemOpLdrbRRR:
+                        size = kSVByte;
+                        break;
+                    case kMemOpLdrsbRRR:
+                        size = kSVSignedByte;
+                        break;
+                    case kMemOpStrhRRR:
+                    case kMemOpLdrhRRR:
+                        size = kSVHalfword;
+                        break;
+                    case kMemOpLdrshRRR:
+                        size = kSVSignedHalfword;
+                        break;
+                }
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                size = kSVByte;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                size = kSVHalfword;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        if (opcode5 == kMemOpLdrPcRel)
+            addr = selfVerificationMemRegLoad(sp, 4);
+        else if (opcode5 == kMemOpStmia || opcode5 == kMemOpLdmia)
+            addr = selfVerificationMemRegLoad(sp, rd);
+        else
+            addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode5) {
+            case kMemOpLdrPcRel:
+                offset = (insn & 0xFF) << 2;
+                rt = rd;
+                break;
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                offset = selfVerificationMemRegLoad(sp, rm);
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpLdrRRI5:
+                offset = imm << 2;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                offset = imm << 1;
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                offset = imm;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB MEM OP: %x", opcode5);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                bool wBack = true;
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        if (i == rd) wBack = false;
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    }
+}
+#endif
diff --git a/vm/compiler/codegen/arm/CalloutHelper.h b/vm/compiler/codegen/arm/CalloutHelper.h
new file mode 100644
index 0000000..079c5f6
--- /dev/null
+++ b/vm/compiler/codegen/arm/CalloutHelper.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H_
+
+#include "Dalvik.h"
+
+/*
+ * Declare/comment prototypes of all native callout functions invoked by the
+ * JIT'ed code here and use the LOAD_FUNC_ADDR macro to load the address into
+ * a register. In this way we have a centralized place to find out all native
+ * helper functions and we can grep for LOAD_FUNC_ADDR to find out all the
+ * callsites.
+ */
+
+/* Load a statically compiled function address as a constant */
+#define LOAD_FUNC_ADDR(cUnit, reg, addr) loadConstant(cUnit, reg, addr)
+
+/* Conversions */
+extern "C" float __aeabi_i2f(int op1);             // OP_INT_TO_FLOAT
+extern "C" int __aeabi_f2iz(float op1);            // OP_FLOAT_TO_INT
+extern "C" float __aeabi_d2f(double op1);          // OP_DOUBLE_TO_FLOAT
+extern "C" double __aeabi_f2d(float op1);          // OP_FLOAT_TO_DOUBLE
+extern "C" double __aeabi_i2d(int op1);            // OP_INT_TO_DOUBLE
+extern "C" int __aeabi_d2iz(double op1);           // OP_DOUBLE_TO_INT
+extern "C" float __aeabi_l2f(long op1);            // OP_LONG_TO_FLOAT
+extern "C" double __aeabi_l2d(long op1);           // OP_LONG_TO_DOUBLE
+s8 dvmJitf2l(float op1);                // OP_FLOAT_TO_LONG
+s8 dvmJitd2l(double op1);               // OP_DOUBLE_TO_LONG
+
+/* Single-precision FP arithmetics */
+extern "C" float __aeabi_fadd(float a, float b);   // OP_ADD_FLOAT[_2ADDR]
+extern "C" float __aeabi_fsub(float a, float b);   // OP_SUB_FLOAT[_2ADDR]
+extern "C" float __aeabi_fdiv(float a, float b);   // OP_DIV_FLOAT[_2ADDR]
+extern "C" float __aeabi_fmul(float a, float b);   // OP_MUL_FLOAT[_2ADDR]
+extern "C" float fmodf(float a, float b);          // OP_REM_FLOAT[_2ADDR]
+
+/* Double-precision FP arithmetics */
+extern "C" double __aeabi_dadd(double a, double b); // OP_ADD_DOUBLE[_2ADDR]
+extern "C" double __aeabi_dsub(double a, double b); // OP_SUB_DOUBLE[_2ADDR]
+extern "C" double __aeabi_ddiv(double a, double b); // OP_DIV_DOUBLE[_2ADDR]
+extern "C" double __aeabi_dmul(double a, double b); // OP_MUL_DOUBLE[_2ADDR]
+extern "C" double fmod(double a, double b);         // OP_REM_DOUBLE[_2ADDR]
+
+/* Integer arithmetics */
+extern "C" int __aeabi_idivmod(int op1, int op2);  // OP_REM_INT[_2ADDR|_LIT8|_LIT16]
+extern "C" int __aeabi_idiv(int op1, int op2);     // OP_DIV_INT[_2ADDR|_LIT8|_LIT16]
+
+/* Long long arithmetics - OP_REM_LONG[_2ADDR] & OP_DIV_LONG[_2ADDR] */
+extern "C" long long __aeabi_ldivmod(long long op1, long long op2);
+
+/* Originally declared in Sync.h */
+bool dvmUnlockObject(struct Thread* self, struct Object* obj); //OP_MONITOR_EXIT
+
+/* Originally declared in oo/TypeCheck.h */
+bool dvmCanPutArrayElement(const ClassObject* elemClass,   // OP_APUT_OBJECT
+                           const ClassObject* arrayClass);
+int dvmInstanceofNonTrivial(const ClassObject* instance,   // OP_CHECK_CAST &&
+                            const ClassObject* clazz);     // OP_INSTANCE_OF
+
+/* Originally declared in oo/Array.h */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass, // OP_NEW_ARRAY
+                                  size_t length, int allocFlags);
+
+/* Originally declared in interp/InterpDefs.h */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,// OP_FILL_ARRAY_DATA
+                                  const u2* arrayData);
+
+/* Originally declared in compiler/codegen/arm/Assemble.c */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz);
+
+/*
+ * Resolve interface callsites - OP_INVOKE_INTERFACE & OP_INVOKE_INTERFACE_RANGE
+ *
+ * Originally declared in mterp/common/FindInterface.h and only comment it here
+ * due to the INLINE attribute.
+ *
+ * INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+ *  u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+ */
+
+/* Originally declared in alloc/Alloc.h */
+Object* dvmAllocObject(ClassObject* clazz, int flags);  // OP_NEW_INSTANCE
+
+/*
+ * Functions declared in gDvmInlineOpsTable[] are used for
+ * OP_EXECUTE_INLINE & OP_EXECUTE_INLINE_RANGE.
+ */
+extern "C" double sqrt(double x);  // INLINE_MATH_SQRT
+
+/*
+ * The following functions are invoked through the compiler templates (declared
+ * in compiler/template/armv5te/footer.S:
+ *
+ *      __aeabi_cdcmple         // CMPG_DOUBLE
+ *      __aeabi_cfcmple         // CMPG_FLOAT
+ *      dvmLockObject           // MONITOR_ENTER
+ */
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H_
diff --git a/vm/compiler/codegen/arm/Codegen.h b/vm/compiler/codegen/arm/Codegen.h
new file mode 100644
index 0000000..e67f3d8
--- /dev/null
+++ b/vm/compiler/codegen/arm/Codegen.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerIR.h"
+#include "CalloutHelper.h"
+
+#if defined(_CODEGEN_C)
+/*
+ * loadConstant() sometimes needs to add a small imm to a pre-existing constant
+ */
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value);
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2);
+
+/* Forward decalraton the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+#if defined(__ARM_ARCH_5__)
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir);
+#endif
+
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir);
+
+#endif
+
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Self Verification memory instruction decoder */
+extern "C" void dvmSelfVerificationMemOpDecode(int lr, int* sp);
+#endif
+
+extern void dvmCompilerSetupResourceMasks(ArmLIR *lir);
+
+extern ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest,
+                                          int rSrc);
diff --git a/vm/compiler/codegen/arm/CodegenCommon.cpp b/vm/compiler/codegen/arm/CodegenCommon.cpp
new file mode 100644
index 0000000..5c02678
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenCommon.cpp
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * ARM variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+#include "compiler/Loop.h"
+
+/* Array holding the entry offset of each template relative to the first one */
+static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK];
+
+/* Track exercised opcodes */
+static int opcodeCoverage[kNumPackedOpcodes];
+
+static void setMemRefType(ArmLIR *lir, bool isLoad, int memType)
+{
+    u8 *maskPtr;
+    u8 mask = ENCODE_MEM;;
+    assert(EncodingMap[lir->opcode].flags & (IS_LOAD | IS_STORE));
+    if (isLoad) {
+        maskPtr = &lir->useMask;
+    } else {
+        maskPtr = &lir->defMask;
+    }
+    /* Clear out the memref flags */
+    *maskPtr &= ~mask;
+    /* ..and then add back the one we need */
+    switch(memType) {
+        case kLiteral:
+            assert(isLoad);
+            *maskPtr |= ENCODE_LITERAL;
+            break;
+        case kDalvikReg:
+            *maskPtr |= ENCODE_DALVIK_REG;
+            break;
+        case kHeapRef:
+            *maskPtr |= ENCODE_HEAP_REF;
+            break;
+        case kMustNotAlias:
+            /* Currently only loads can be marked as kMustNotAlias */
+            assert(!(EncodingMap[lir->opcode].flags & IS_STORE));
+            *maskPtr |= ENCODE_MUST_NOT_ALIAS;
+            break;
+        default:
+            ALOGE("Jit: invalid memref kind - %d", memType);
+            assert(0);  // Bail if debug build, set worst-case in the field
+            *maskPtr |= ENCODE_ALL;
+    }
+}
+
+/*
+ * Mark load/store instructions that access Dalvik registers through r5FP +
+ * offset.
+ */
+static void annotateDalvikRegAccess(ArmLIR *lir, int regId, bool isLoad)
+{
+    setMemRefType(lir, isLoad, kDalvikReg);
+
+    /*
+     * Store the Dalvik register id in aliasInfo. Mark he MSB if it is a 64-bit
+     * access.
+     */
+    lir->aliasInfo = regId;
+    if (DOUBLEREG(lir->operands[0])) {
+        lir->aliasInfo |= 0x80000000;
+    }
+}
+
+/*
+ * Decode the register id.
+ */
+static inline u8 getRegMaskCommon(int reg)
+{
+    u8 seed;
+    int shift;
+    int regId = reg & 0x1f;
+
+    /*
+     * Each double register is equal to a pair of single-precision FP registers
+     */
+    seed = DOUBLEREG(reg) ? 3 : 1;
+    /* FP register starts at bit position 16 */
+    shift = FPREG(reg) ? kFPReg0 : 0;
+    /* Expand the double register id into single offset */
+    shift += regId;
+    return (seed << shift);
+}
+
+/* External version of getRegMaskCommon */
+u8 dvmGetRegResourceMask(int reg)
+{
+    return getRegMaskCommon(reg);
+}
+
+/*
+ * Mark the corresponding bit(s).
+ */
+static inline void setupRegMask(u8 *mask, int reg)
+{
+    *mask |= getRegMaskCommon(reg);
+}
+
+/*
+ * Set up the proper fields in the resource mask
+ */
+static void setupResourceMasks(ArmLIR *lir)
+{
+    int opcode = lir->opcode;
+    int flags;
+
+    if (opcode <= 0) {
+        lir->useMask = lir->defMask = 0;
+        return;
+    }
+
+    flags = EncodingMap[lir->opcode].flags;
+
+    /* Set up the mask for resources that are updated */
+    if (flags & (IS_LOAD | IS_STORE)) {
+        /* Default to heap - will catch specialized classes later */
+        setMemRefType(lir, flags & IS_LOAD, kHeapRef);
+    }
+
+    /*
+     * Conservatively assume the branch here will call out a function that in
+     * turn will trash everything.
+     */
+    if (flags & IS_BRANCH) {
+        lir->defMask = lir->useMask = ENCODE_ALL;
+        return;
+    }
+
+    if (flags & REG_DEF0) {
+        setupRegMask(&lir->defMask, lir->operands[0]);
+    }
+
+    if (flags & REG_DEF1) {
+        setupRegMask(&lir->defMask, lir->operands[1]);
+    }
+
+    if (flags & REG_DEF_SP) {
+        lir->defMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & REG_DEF_LIST0) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_DEF_LIST1) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & SETS_CCODES) {
+        lir->defMask |= ENCODE_CCODE;
+    }
+
+    /* Conservatively treat the IT block */
+    if (flags & IS_IT) {
+        lir->defMask = ENCODE_ALL;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & REG_USE_PC) {
+        lir->useMask |= ENCODE_REG_PC;
+    }
+
+    if (flags & REG_USE_SP) {
+        lir->useMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_USE_LIST0) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_USE_LIST1) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+
+    /* Fixup for kThumbPush/lr and kThumbPop/pc */
+    if (opcode == kThumbPush || opcode == kThumbPop) {
+        u8 r8Mask = getRegMaskCommon(r8);
+        if ((opcode == kThumbPush) && (lir->useMask & r8Mask)) {
+            lir->useMask &= ~r8Mask;
+            lir->useMask |= ENCODE_REG_LR;
+        } else if ((opcode == kThumbPop) && (lir->defMask & r8Mask)) {
+            lir->defMask &= ~r8Mask;
+            lir->defMask |= ENCODE_REG_PC;
+        }
+    }
+}
+
+/*
+ * Set up the accurate resource mask for branch instructions
+ */
+static void relaxBranchMasks(ArmLIR *lir)
+{
+    int flags = EncodingMap[lir->opcode].flags;
+
+    /* Make sure only branch instructions are passed here */
+    assert(flags & IS_BRANCH);
+
+    lir->useMask = lir->defMask = ENCODE_REG_PC;
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+}
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 4
+ * operands.
+ */
+static ArmLIR *newLIR0(CompilationUnit *cUnit, ArmOpcode opcode)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND));
+    insn->opcode = opcode;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static ArmLIR *newLIR1(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static ArmLIR *newLIR2(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest, int src1)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) ||
+           (EncodingMap[opcode].flags & IS_BINARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static ArmLIR *newLIR3(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest, int src1, int src2)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    if (!(EncodingMap[opcode].flags & IS_TERTIARY_OP)) {
+        ALOGE("Bad LIR3: %s[%d]",EncodingMap[opcode].name,opcode);
+    }
+    assert(isPseudoOpcode(opcode) ||
+           (EncodingMap[opcode].flags & IS_TERTIARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+#if defined(_ARMV7_A) || defined(_ARMV7_A_NEON)
+static ArmLIR *newLIR4(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest, int src1, int src2, int info)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) ||
+           (EncodingMap[opcode].flags & IS_QUAD_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    insn->operands[3] = info;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+#endif
+
+/*
+ * If the next instruction is a move-result or move-result-long,
+ * return the target Dalvik sReg[s] and convert the next to a
+ * nop.  Otherwise, return INVALID_SREG.  Used to optimize method inlining.
+ */
+static RegLocation inlinedTarget(CompilationUnit *cUnit, MIR *mir,
+                                  bool fpHint)
+{
+    if (mir->next &&
+        ((mir->next->dalvikInsn.opcode == OP_MOVE_RESULT) ||
+         (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_OBJECT))) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDest(cUnit, mir->next, 0);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+/*
+ * Search the existing constants in the literal pool for an exact or close match
+ * within specified delta (greater or equal to 0).
+ */
+static ArmLIR *scanLiteralPool(LIR *dataTarget, int value, unsigned int delta)
+{
+    while (dataTarget) {
+        if (((unsigned) (value - ((ArmLIR *) dataTarget)->operands[0])) <=
+            delta)
+            return (ArmLIR *) dataTarget;
+        dataTarget = dataTarget->next;
+    }
+    return NULL;
+}
+
+/* Search the existing constants in the literal pool for an exact wide match */
+ArmLIR* scanLiteralPoolWide(LIR* dataTarget, int valLo, int valHi)
+{
+  bool lowMatch = false;
+  ArmLIR* lowTarget = NULL;
+  while (dataTarget) {
+    if (lowMatch && (((ArmLIR *)dataTarget)->operands[0] == valHi)) {
+      return lowTarget;
+    }
+    lowMatch = false;
+    if (((ArmLIR *) dataTarget)->operands[0] == valLo) {
+      lowMatch = true;
+      lowTarget = (ArmLIR *) dataTarget;
+    }
+    dataTarget = dataTarget->next;
+  }
+  return NULL;
+}
+
+/*
+ * The following are building blocks to insert constants into the pool or
+ * instruction streams.
+ */
+
+/* Add a 32-bit constant either in the constant pool or mixed with code */
+static ArmLIR *addWordData(CompilationUnit *cUnit, LIR **constantListP,
+                           int value)
+{
+    /* Add the constant to the literal pool */
+    if (constantListP) {
+        ArmLIR *newValue = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+        newValue->operands[0] = value;
+        newValue->generic.next = *constantListP;
+        *constantListP = (LIR *) newValue;
+        return newValue;
+    } else {
+        /* Add the constant in the middle of code stream */
+        newLIR1(cUnit, kArm16BitData, (value & 0xffff));
+        newLIR1(cUnit, kArm16BitData, (value >> 16));
+    }
+    return NULL;
+}
+
+/* Add a 64-bit constant to the literal pool or mixed with code */
+ArmLIR* addWideData(CompilationUnit* cUnit, LIR** constantListP,
+                 int valLo, int valHi)
+{
+    addWordData(cUnit, constantListP, valHi);
+    return addWordData(cUnit, constantListP, valLo);
+}
+
+static RegLocation inlinedTargetWide(CompilationUnit *cUnit, MIR *mir,
+                                      bool fpHint)
+{
+    if (mir->next &&
+        (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_WIDE)) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDestWide(cUnit, mir->next, 0, 1);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL_WIDE;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+
+/*
+ * Generate an kArmPseudoBarrier marker to indicate the boundary of special
+ * blocks.
+ */
+static void genBarrier(CompilationUnit *cUnit)
+{
+    ArmLIR *barrier = newLIR0(cUnit, kArmPseudoBarrier);
+    /* Mark all resources as being clobbered */
+    barrier->defMask = -1;
+}
+
+/* Create the PC reconstruction slot if not already done */
+static ArmLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset,
+                              ArmLIR *branch,
+                              ArmLIR *pcrLabel)
+{
+    /* Forget all def info (because we might rollback here.  Bug #2367397 */
+    dvmCompilerResetDefTracking(cUnit);
+
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + dOffset);
+        pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+        pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = dOffset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+
+    /* Clear the conservative flags for branches that punt to the interpreter */
+    relaxBranchMasks(branch);
+
+    return pcrLabel;
+}
diff --git a/vm/compiler/codegen/arm/CodegenDriver.cpp b/vm/compiler/codegen/arm/CodegenDriver.cpp
new file mode 100644
index 0000000..7346c83
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenDriver.cpp
@@ -0,0 +1,4757 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * ARM variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+static void markCard(CompilationUnit *cUnit, int valReg, int tgtAddrReg)
+{
+    int regCardBase = dvmCompilerAllocTemp(cUnit);
+    int regCardNo = dvmCompilerAllocTemp(cUnit);
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread, cardTable),
+                 regCardBase);
+    opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
+    storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
+                     kUnsignedByte);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *)target;
+    dvmCompilerFreeTemp(cUnit, regCardBase);
+    dvmCompilerFreeTemp(cUnit, regCardNo);
+}
+
+static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct,
+                                     int srcSize, int tgtSize)
+{
+    /*
+     * Don't optimize the register usage since it calls out to template
+     * functions
+     */
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    if (srcSize == 1) {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+        loadValueDirectFixed(cUnit, rlSrc, r0);
+    } else {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
+    }
+    LOAD_FUNC_ADDR(cUnit, r2, (int)funct);
+    opReg(cUnit, kOpBlx, r2);
+    dvmCompilerClobberCallRegs(cUnit);
+    if (tgtSize == 1) {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerGetReturn(cUnit);
+        storeValue(cUnit, rlDest, rlResult);
+    } else {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerGetReturnWide(cUnit);
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+    return false;
+}
+
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            funct = (void*) __aeabi_fadd;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            funct = (void*) __aeabi_fsub;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            funct = (void*) __aeabi_fdiv;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            funct = (void*) __aeabi_fmul;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+            funct = (void*) fmodf;
+            break;
+        case OP_NEG_FLOAT: {
+            genNegFloat(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    loadValueDirectFixed(cUnit, rlSrc1, r0);
+    loadValueDirectFixed(cUnit, rlSrc2, r1);
+    LOAD_FUNC_ADDR(cUnit, r2, (int)funct);
+    opReg(cUnit, kOpBlx, r2);
+    dvmCompilerClobberCallRegs(cUnit);
+    rlResult = dvmCompilerGetReturn(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            funct = (void*) __aeabi_dadd;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            funct = (void*) __aeabi_dsub;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            funct = (void*) __aeabi_ddiv;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            funct = (void*) __aeabi_dmul;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+            funct = (void*) (double (*)(double, double)) fmod;
+            break;
+        case OP_NEG_DOUBLE: {
+            genNegDouble(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    LOAD_FUNC_ADDR(cUnit, r14lr, (int)funct);
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+    opReg(cUnit, kOpBlx, r14lr);
+    dvmCompilerClobberCallRegs(cUnit);
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+    cUnit->usesLinkRegister = true;
+#endif
+    return false;
+}
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_i2f, 1, 1);
+        case OP_FLOAT_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_f2iz, 1, 1);
+        case OP_DOUBLE_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_d2f, 2, 1);
+        case OP_FLOAT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_f2d, 1, 2);
+        case OP_INT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_i2d, 1, 2);
+        case OP_DOUBLE_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_d2iz, 2, 1);
+        case OP_FLOAT_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)dvmJitf2l, 1, 2);
+        case OP_LONG_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_l2f, 2, 1);
+        case OP_DOUBLE_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)dvmJitd2l, 2, 2);
+        case OP_LONG_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_l2d, 2, 2);
+        default:
+            return true;
+    }
+    return false;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void selfVerificationBranchInsert(LIR *currentLIR, ArmOpcode opcode,
+                          int dest, int src1)
+{
+     ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+     insn->opcode = opcode;
+     insn->operands[0] = dest;
+     insn->operands[1] = src1;
+     setupResourceMasks(insn);
+     dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn);
+}
+
+/*
+ * Example where r14 (LR) is preserved around a heap access under
+ * self-verification mode in Thumb2:
+ *
+ * D/dalvikvm( 1538): 0x59414c5e (0026): ldr     r14, [r15pc, #220] <-hoisted
+ * D/dalvikvm( 1538): 0x59414c62 (002a): mla     r4, r0, r8, r4
+ * D/dalvikvm( 1538): 0x59414c66 (002e): adds    r3, r4, r3
+ * D/dalvikvm( 1538): 0x59414c6a (0032): push    <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c6c (0034): blx_1   0x5940f494      |
+ * D/dalvikvm( 1538): 0x59414c6e (0036): blx_2   see above       <-MEM_OP_DECODE
+ * D/dalvikvm( 1538): 0x59414c70 (0038): ldr     r10, [r9, #0]   |
+ * D/dalvikvm( 1538): 0x59414c74 (003c): pop     <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c78 (0040): mov     r11, r10
+ * D/dalvikvm( 1538): 0x59414c7a (0042): asr     r12, r11, #31
+ * D/dalvikvm( 1538): 0x59414c7e (0046): movs    r0, r2
+ * D/dalvikvm( 1538): 0x59414c80 (0048): movs    r1, r3
+ * D/dalvikvm( 1538): 0x59414c82 (004a): str     r2, [r5, #16]
+ * D/dalvikvm( 1538): 0x59414c84 (004c): mov     r2, r11
+ * D/dalvikvm( 1538): 0x59414c86 (004e): str     r3, [r5, #20]
+ * D/dalvikvm( 1538): 0x59414c88 (0050): mov     r3, r12
+ * D/dalvikvm( 1538): 0x59414c8a (0052): str     r11, [r5, #24]
+ * D/dalvikvm( 1538): 0x59414c8e (0056): str     r12, [r5, #28]
+ * D/dalvikvm( 1538): 0x59414c92 (005a): blx     r14             <-use of LR
+ *
+ */
+static void selfVerificationBranchInsertPass(CompilationUnit *cUnit)
+{
+    ArmLIR *thisLIR;
+    TemplateOpcode opcode = TEMPLATE_MEM_OP_DECODE;
+
+    for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn;
+         thisLIR != (ArmLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+        if (!thisLIR->flags.isNop && thisLIR->flags.insertWrapper) {
+            /*
+             * Push r5(FP) and r14(LR) onto stack. We need to make sure that
+             * SP is 8-byte aligned, and we use r5 as a temp to restore LR
+             * for Thumb-only target since LR cannot be directly accessed in
+             * Thumb mode. Another reason to choose r5 here is it is the Dalvik
+             * frame pointer and cannot be the target of the emulated heap
+             * load.
+             */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPreBranch(cUnit, thisLIR);
+            }
+
+            /* Branch to mem op decode template */
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx1,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx2,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+
+            /* Restore LR */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPostBranch(cUnit, thisLIR);
+            }
+        }
+    }
+}
+#endif
+
+/* Generate conditional branch instructions */
+static ArmLIR *genConditionalBranch(CompilationUnit *cUnit,
+                                    ArmConditionCode cond,
+                                    ArmLIR *target)
+{
+    ArmLIR *branch = opCondBranch(cUnit, cond);
+    branch->generic.target = (LIR *) target;
+    return branch;
+}
+
+/* Generate a unconditional branch to go to the interpreter */
+static inline ArmLIR *genTrap(CompilationUnit *cUnit, int dOffset,
+                                  ArmLIR *pcrLabel)
+{
+    ArmLIR *branch = opNone(cUnit, kOpUncondBr);
+    return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/* Load a wide field from an object instance */
+static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+
+    assert(rlDest.wide);
+
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    HEAP_ACCESS_SHADOW(true);
+    loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/* Store a wide field to an object instance */
+static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 2);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr;
+    rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    regPtr = dvmCompilerAllocTemp(cUnit);
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+
+    HEAP_ACCESS_SHADOW(true);
+    storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+}
+
+/*
+ * Load a field from an object instance
+ *
+ */
+static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                    int fieldOffset, bool isVolatile)
+{
+    RegLocation rlResult;
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+
+    HEAP_ACCESS_SHADOW(true);
+    loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
+                 size, rlObj.sRegLow);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kSY);
+    }
+
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+/*
+ * Store a field to an object instance
+ *
+ */
+static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                    int fieldOffset, bool isObject, bool isVolatile)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 1);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlSrc = loadValue(cUnit, rlSrc, regClass);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kST);
+    }
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kSY);
+    }
+    if (isObject) {
+        /* NOTE: marking card based on object head */
+        markCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
+    }
+}
+
+
+/*
+ * Generate array load
+ */
+static void genArrayGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlDest, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlResult;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+    int regPtr;
+
+    /* null object? */
+    ArmLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow,
+                                rlArray.lowReg, mir->offset, NULL);
+    }
+
+    regPtr = dvmCompilerAllocTemp(cUnit);
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    } else {
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
+    }
+    if ((size == kLong) || (size == kDouble)) {
+        if (scale) {
+            int rNewIndex = dvmCompilerAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
+            opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
+            dvmCompilerFreeTemp(cUnit, rNewIndex);
+        } else {
+            opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
+        }
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+
+        HEAP_ACCESS_SHADOW(true);
+        loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+        HEAP_ACCESS_SHADOW(false);
+
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+
+        HEAP_ACCESS_SHADOW(true);
+        loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
+                        scale, size);
+        HEAP_ACCESS_SHADOW(false);
+
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+}
+
+/*
+ * Generate array store
+ *
+ */
+static void genArrayPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlSrc, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    int regPtr;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+
+    if (dvmCompilerIsTemp(cUnit, rlArray.lowReg)) {
+        dvmCompilerClobber(cUnit, rlArray.lowReg);
+        regPtr = rlArray.lowReg;
+    } else {
+        regPtr = dvmCompilerAllocTemp(cUnit);
+        genRegCopy(cUnit, regPtr, rlArray.lowReg);
+    }
+
+    /* null object? */
+    ArmLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg,
+                                mir->offset, NULL);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        //NOTE: max live temps(4) here.
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    } else {
+        /* regPtr -> array data */
+        opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
+    }
+    /* at this point, regPtr points to array, 2 live temps */
+    if ((size == kLong) || (size == kDouble)) {
+        //TODO: need specific wide routine that can handle fp regs
+        if (scale) {
+            int rNewIndex = dvmCompilerAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
+            opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
+            dvmCompilerFreeTemp(cUnit, rNewIndex);
+        } else {
+            opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
+        }
+        rlSrc = loadValueWide(cUnit, rlSrc, regClass);
+
+        HEAP_ACCESS_SHADOW(true);
+        storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+        HEAP_ACCESS_SHADOW(false);
+
+        dvmCompilerFreeTemp(cUnit, regPtr);
+    } else {
+        rlSrc = loadValue(cUnit, rlSrc, regClass);
+
+        HEAP_ACCESS_SHADOW(true);
+        storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
+                         scale, size);
+        HEAP_ACCESS_SHADOW(false);
+    }
+}
+
+/*
+ * Generate array object store
+ * Must use explicit register allocation here because of
+ * call-out to dvmCanPutArrayElement
+ */
+static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir,
+                              RegLocation rlArray, RegLocation rlIndex,
+                              RegLocation rlSrc, int scale)
+{
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    dvmCompilerFlushAllRegs(cUnit);
+
+    int regLen = r0;
+    int regPtr = r4PC;  /* Preserved across call */
+    int regArray = r1;
+    int regIndex = r7;  /* Preserved across call */
+
+    loadValueDirectFixed(cUnit, rlArray, regArray);
+    loadValueDirectFixed(cUnit, rlIndex, regIndex);
+
+    /* null object? */
+    ArmLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, regArray,
+                                mir->offset, NULL);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        /* Get len */
+        loadWordDisp(cUnit, regArray, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+        genBoundsCheck(cUnit, regIndex, regLen, mir->offset,
+                       pcrLabel);
+    } else {
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+    }
+
+    /* Get object to store */
+    loadValueDirectFixed(cUnit, rlSrc, r0);
+    LOAD_FUNC_ADDR(cUnit, r2, (int)dvmCanPutArrayElement);
+
+    /* Are we storing null?  If so, avoid check */
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
+
+    /* Make sure the types are compatible */
+    loadWordDisp(cUnit, regArray, offsetof(Object, clazz), r1);
+    loadWordDisp(cUnit, r0, offsetof(Object, clazz), r0);
+    opReg(cUnit, kOpBlx, r2);
+    dvmCompilerClobberCallRegs(cUnit);
+
+    /*
+     * Using fixed registers here, and counting on r4 and r7 being
+     * preserved across the above call.  Tell the register allocation
+     * utilities about the regs we are using directly
+     */
+    dvmCompilerLockTemp(cUnit, regPtr);   // r4PC
+    dvmCompilerLockTemp(cUnit, regIndex); // r7
+    dvmCompilerLockTemp(cUnit, r0);
+    dvmCompilerLockTemp(cUnit, r1);
+
+    /* Bad? - roll back and re-execute if so */
+    genRegImmCheck(cUnit, kArmCondEq, r0, 0, mir->offset, pcrLabel);
+
+    /* Resume here - must reload element & array, regPtr & index preserved */
+    loadValueDirectFixed(cUnit, rlSrc, r0);
+    loadValueDirectFixed(cUnit, rlArray, r1);
+
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseIndexed(cUnit, regPtr, regIndex, r0,
+                     scale, kWord);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    dvmCompilerFreeTemp(cUnit, regIndex);
+
+    /* NOTE: marking card here based on object head */
+    markCard(cUnit, r0, r1);
+}
+
+static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlShift)
+{
+    /*
+     * Don't mess with the regsiters here as there is a particular calling
+     * convention to the out-of-line handler.
+     */
+    RegLocation rlResult;
+
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirect(cUnit, rlShift, r2);
+    switch( mir->dalvikInsn.opcode) {
+        case OP_SHL_LONG:
+        case OP_SHL_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG);
+            break;
+        case OP_SHR_LONG:
+        case OP_SHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG);
+            break;
+        case OP_USHR_LONG:
+        case OP_USHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG);
+            break;
+        default:
+            return true;
+    }
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    OpKind firstOp = kOpBkpt;
+    OpKind secondOp = kOpBkpt;
+    bool callOut = false;
+    bool checkZero = false;
+    void *callTgt;
+    int retReg = r0;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_NOT_LONG:
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
+            opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        case OP_ADD_LONG:
+        case OP_ADD_LONG_2ADDR:
+            firstOp = kOpAdd;
+            secondOp = kOpAdc;
+            break;
+        case OP_SUB_LONG:
+        case OP_SUB_LONG_2ADDR:
+            firstOp = kOpSub;
+            secondOp = kOpSbc;
+            break;
+        case OP_MUL_LONG:
+        case OP_MUL_LONG_2ADDR:
+            genMulLong(cUnit, rlDest, rlSrc1, rlSrc2);
+            return false;
+        case OP_DIV_LONG:
+        case OP_DIV_LONG_2ADDR:
+            callOut = true;
+            retReg = r0;
+            checkZero = true;
+            callTgt = (void*)__aeabi_ldivmod;
+            break;
+        /* NOTE - result is in r2/r3 instead of r0/r1 */
+        case OP_REM_LONG:
+        case OP_REM_LONG_2ADDR:
+            callOut = true;
+            callTgt = (void*)__aeabi_ldivmod;
+            retReg = r2;
+            checkZero = true;
+            break;
+        case OP_AND_LONG_2ADDR:
+        case OP_AND_LONG:
+            firstOp = kOpAnd;
+            secondOp = kOpAnd;
+            break;
+        case OP_OR_LONG:
+        case OP_OR_LONG_2ADDR:
+            firstOp = kOpOr;
+            secondOp = kOpOr;
+            break;
+        case OP_XOR_LONG:
+        case OP_XOR_LONG_2ADDR:
+            firstOp = kOpXor;
+            secondOp = kOpXor;
+            break;
+        case OP_NEG_LONG: {
+            //TUNING: can improve this using Thumb2 code
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, tReg, 0);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
+                        tReg, rlSrc2.lowReg);
+            opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg);
+            genRegCopy(cUnit, rlResult.highReg, tReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+        }
+        default:
+            ALOGE("Invalid long arith op");
+            dvmCompilerAbort(cUnit);
+    }
+    if (!callOut) {
+        genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
+    } else {
+        // Adjust return regs in to handle case of rem returning r2/r3
+        dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+        loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+        loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+        LOAD_FUNC_ADDR(cUnit, r14lr, (int) callTgt);
+        if (checkZero) {
+            int tReg = r12; // Using fixed registers during call sequence
+            opRegRegReg(cUnit, kOpOr, tReg, r2, r3);
+            genRegImmCheck(cUnit, kArmCondEq, tReg, 0, mir->offset, NULL);
+        }
+        opReg(cUnit, kOpBlx, r14lr);
+        dvmCompilerClobberCallRegs(cUnit);
+        if (retReg == r0)
+            rlResult = dvmCompilerGetReturnWide(cUnit);
+        else
+            rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+        storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+        cUnit->usesLinkRegister = true;
+#endif
+    }
+    return false;
+}
+
+static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir,
+                          RegLocation rlDest, RegLocation rlSrc1,
+                          RegLocation rlSrc2)
+{
+    OpKind op = kOpBkpt;
+    bool callOut = false;
+    bool checkZero = false;
+    bool unary = false;
+    int retReg = r0;
+    int (*callTgt)(int, int);
+    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:
+            ALOGE("Invalid word arith op: %#x(%d)",
+                 mir->dalvikInsn.opcode, mir->dalvikInsn.opcode);
+            dvmCompilerAbort(cUnit);
+    }
+    if (!callOut) {
+        rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+        if (unary) {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, op, rlResult.lowReg,
+                     rlSrc1.lowReg);
+        } else {
+            rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+            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, gDvmJit.methodTraceSupport ?
+                         TEMPLATE_RETURN_PROF : TEMPLATE_RETURN);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.returnOp++;
+#endif
+    int dPC = (int) (cUnit->method->insns + mir->offset);
+    /* Insert branch, but defer setting of target */
+    ArmLIR *branch = genUnconditionalBranch(cUnit, NULL);
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+    pcrLabel->operands[0] = dPC;
+    pcrLabel->operands[1] = mir->offset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+}
+
+static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir,
+                                  DecodedInstruction *dInsn,
+                                  ArmLIR **pcrLabel)
+{
+    unsigned int i;
+    unsigned int regMask = 0;
+    RegLocation rlArg;
+    int numDone = 0;
+
+    /*
+     * Load arguments to r0..r4.  Note that these registers may contain
+     * live values, so we clobber them immediately after loading to prevent
+     * them from being used as sources for subsequent loads.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    for (i = 0; i < dInsn->vA; i++) {
+        regMask |= 1 << i;
+        rlArg = dvmCompilerGetSrc(cUnit, mir, numDone++);
+        loadValueDirectFixed(cUnit, rlArg, i);
+    }
+    if (regMask) {
+        /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+        opRegRegImm(cUnit, kOpSub, r7, r5FP,
+                    sizeof(StackSaveArea) + (dInsn->vA << 2));
+        /* generate null check */
+        if (pcrLabel) {
+            *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0,
+                                     mir->offset, NULL);
+        }
+        storeMultiple(cUnit, r7, regMask);
+    }
+}
+
+static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir,
+                                DecodedInstruction *dInsn,
+                                ArmLIR **pcrLabel)
+{
+    int srcOffset = dInsn->vC << 2;
+    int numArgs = dInsn->vA;
+    int regMask;
+
+    /*
+     * Note: here, all promoted registers will have been flushed
+     * back to the Dalvik base locations, so register usage restrictins
+     * are lifted.  All parms loaded from original Dalvik register
+     * region - even though some might conceivably have valid copies
+     * cached in a preserved register.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * r4PC     : &r5FP[vC]
+     * r7: &newFP[0]
+     */
+    opRegRegImm(cUnit, kOpAdd, r4PC, r5FP, srcOffset);
+    /* load [r0 .. min(numArgs,4)] */
+    regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1;
+    /*
+     * Protect the loadMultiple instruction from being reordered with other
+     * Dalvik stack accesses.
+     */
+    if (numArgs != 0) loadMultiple(cUnit, r4PC, regMask);
+
+    opRegRegImm(cUnit, kOpSub, r7, r5FP,
+                sizeof(StackSaveArea) + (numArgs << 2));
+    /* generate null check */
+    if (pcrLabel) {
+        *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0,
+                                 mir->offset, NULL);
+    }
+
+    /*
+     * Handle remaining 4n arguments:
+     * store previously loaded 4 values and load the next 4 values
+     */
+    if (numArgs >= 8) {
+        ArmLIR *loopLabel = NULL;
+        /*
+         * r0 contains "this" and it will be used later, so push it to the stack
+         * first. Pushing r5FP is just for stack alignment purposes.
+         */
+        opImm(cUnit, kOpPush, (1 << r0 | 1 << r5FP));
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2);
+            loopLabel = newLIR0(cUnit, kArmPseudoTargetLabel);
+            loopLabel->defMask = ENCODE_ALL;
+        }
+        storeMultiple(cUnit, r7, regMask);
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            opRegImm(cUnit, kOpSub, r5FP, 4);
+            genConditionalBranch(cUnit, kArmCondNe, loopLabel);
+        }
+    }
+
+    /* Save the last batch of loaded values */
+    if (numArgs != 0) storeMultiple(cUnit, r7, regMask);
+
+    /* Generate the loop epilogue - don't use r0 */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        regMask = ((1 << (numArgs & 0x3)) - 1) << 1;
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+    }
+    if (numArgs >= 8)
+        opImm(cUnit, kOpPop, (1 << r0 | 1 << r5FP));
+
+    /* Save the modulo 4 arguments */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        storeMultiple(cUnit, r7, regMask);
+    }
+}
+
+/*
+ * Generate code to setup the call stack then jump to the chaining cell if it
+ * is not a native method.
+ */
+static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir,
+                                     BasicBlock *bb, ArmLIR *labelList,
+                                     ArmLIR *pcrLabel,
+                                     const Method *calleeMethod)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  All temp & preserved registers may be used.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    ArmLIR *retChainingCell = &labelList[bb->fallThrough->id];
+
+    /* r1 = &retChainingCell */
+    ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r7 = calleeMethod->registersSize */
+    loadConstant(cUnit, r7, calleeMethod->registersSize);
+    /*
+     * r0 = calleeMethod (loaded upon calling genInvokeSingletonCommon)
+     * r1 = &ChainingCell
+     * r2 = calleeMethod->outsSize (to be loaded later for Java callees)
+     * r4PC = callsiteDPC
+     * r7 = calleeMethod->registersSize
+     */
+    if (dvmIsNativeMethod(calleeMethod)) {
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_NATIVE_PROF :
+            TEMPLATE_INVOKE_METHOD_NATIVE);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeNative++;
+#endif
+    } else {
+        /* For Java callees, set up r2 to be calleeMethod->outsSize */
+        loadConstant(cUnit, r2, calleeMethod->outsSize);
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_CHAIN_PROF :
+            TEMPLATE_INVOKE_METHOD_CHAIN);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonomorphic++;
+#endif
+        /* Branch to the chaining cell */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/*
+ * Generate code to check the validity of a predicted chain and take actions
+ * based on the result.
+ *
+ * 0x426a99aa : ldr     r4, [pc, #72] --> r4 <- dalvikPC of this invoke
+ * 0x426a99ac : add     r1, pc, #32   --> r1 <- &retChainingCell
+ * 0x426a99ae : add     r2, pc, #40   --> r2 <- &predictedChainingCell
+ * 0x426a99b0 : blx_1   0x426a918c    --+ TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+ * 0x426a99b2 : blx_2   see above     --+
+ * 0x426a99b4 : b       0x426a99d8    --> off to the predicted chain
+ * 0x426a99b6 : b       0x426a99c8    --> punt to the interpreter
+ * 0x426a99b8 : ldr     r0, [r7, #44] --> r0 <- this->class->vtable[methodIdx]
+ * 0x426a99ba : cmp     r1, #0        --> compare r1 (rechain count) against 0
+ * 0x426a99bc : bgt     0x426a99c2    --> >=0? don't rechain
+ * 0x426a99be : ldr     r7, [pc, #off]--+ dvmJitToPatchPredictedChain
+ * 0x426a99c0 : blx     r7            --+
+ * 0x426a99c2 : add     r1, pc, #12   --> r1 <- &retChainingCell
+ * 0x426a99c4 : blx_1   0x426a9098    --+ TEMPLATE_INVOKE_METHOD_NO_OPT
+ * 0x426a99c6 : blx_2   see above     --+
+ */
+static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir,
+                                   int methodIndex,
+                                   ArmLIR *retChainingCell,
+                                   ArmLIR *predChainingCell,
+                                   ArmLIR *pcrLabel)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  Lock temps to prevent them from being
+     * allocated by utility routines.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * For verbose printing, store the method pointer in operands[1] first as
+     * operands[0] will be clobbered in dvmCompilerMIR2LIR.
+     */
+    predChainingCell->operands[1] = (int) mir->meta.callsiteInfo->method;
+
+    /* "this" is already left in r0 by genProcessArgs* */
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+
+    /* r1 = &retChainingCell */
+    ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r2 = &predictedChainingCell */
+    ArmLIR *predictedChainingCell = opRegRegImm(cUnit, kOpAdd, r2, r15pc, 0);
+    predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+    /* return through lr - jump to the chaining cell */
+    genUnconditionalBranch(cUnit, predChainingCell);
+
+    /*
+     * null-check on "this" may have been eliminated, but we still need a PC-
+     * reconstruction label for stack overflow bailout.
+     */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + mir->offset);
+        pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+        pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = mir->offset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+
+    /* return through lr+2 - punt to the interpreter */
+    genUnconditionalBranch(cUnit, pcrLabel);
+
+    /*
+     * return through lr+4 - fully resolve the callee method.
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+
+    /* r0 <- calleeMethod */
+    loadWordDisp(cUnit, r7, methodIndex * 4, r0);
+
+    /* Check if rechain limit is reached */
+    ArmLIR *bypassRechaining = genCmpImmBranch(cUnit, kArmCondGt, r1, 0);
+
+    LOAD_FUNC_ADDR(cUnit, r7, (int) dvmJitToPatchPredictedChain);
+
+    genRegCopy(cUnit, r1, r6SELF);
+
+    /*
+     * r0 = calleeMethod
+     * r2 = &predictedChainingCell
+     * r3 = class
+     *
+     * &returnChainingCell has been loaded into r1 but is not needed
+     * when patching the chaining cell and will be clobbered upon
+     * returning so it will be reconstructed again.
+     */
+    opReg(cUnit, kOpBlx, r7);
+
+    /* r1 = &retChainingCell */
+    addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    bypassRechaining->generic.target = (LIR *) addrRetChain;
+    /*
+     * r0 = calleeMethod,
+     * r1 = &ChainingCell,
+     * r4PC = callsiteDPC,
+     */
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+        TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.invokePolymorphic++;
+#endif
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/* "this" pointer is already in r0 */
+static void genInvokeVirtualWholeMethod(CompilationUnit *cUnit,
+                                        MIR *mir,
+                                        void *calleeAddr,
+                                        ArmLIR *retChainingCell)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    dvmCompilerLockAllTemps(cUnit);
+
+    loadClassPointer(cUnit, r1, (int) callsiteInfo);
+
+    loadWordDisp(cUnit, r0, offsetof(Object, clazz), r2);
+    /* Branch to the slow path if classes are not equal */
+    opRegReg(cUnit, kOpCmp, r1, r2);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    ArmLIR *classCheck = opCondBranch(cUnit, kArmCondNe);
+
+    /* r0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR2(cUnit, kThumbBl1, (int) calleeAddr, (int) calleeAddr);
+    newLIR2(cUnit, kThumbBl2, (int) calleeAddr, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    /* Target of slow path */
+    ArmLIR *slowPathLabel = newLIR0(cUnit, kArmPseudoTargetLabel);
+
+    slowPathLabel->defMask = ENCODE_ALL;
+    classCheck->generic.target = (LIR *) slowPathLabel;
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+static void genInvokeSingletonWholeMethod(CompilationUnit *cUnit,
+                                          MIR *mir,
+                                          void *calleeAddr,
+                                          ArmLIR *retChainingCell)
+{
+    /* r0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR2(cUnit, kThumbBl1, (int) calleeAddr, (int) calleeAddr);
+    newLIR2(cUnit, kThumbBl2, (int) calleeAddr, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+/* Geneate a branch to go back to the interpreter */
+static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset)
+{
+    /* r0 = dalvik pc */
+    dvmCompilerFlushAllRegs(cUnit);
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset));
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread,
+                 jitToInterpEntries.dvmJitToInterpPunt), r1);
+    opReg(cUnit, kOpBlx, r1);
+}
+
+/*
+ * Attempt to single step one instruction using the interpreter and return
+ * to the compiled code for the next Dalvik instruction
+ */
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir)
+{
+    int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode);
+    int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn;
+
+    // Single stepping is considered loop mode breaker
+    if (cUnit->jitMode == kJitLoop) {
+        cUnit->quitLoopMode = true;
+        return;
+    }
+
+    //If already optimized out, just ignore
+    if (mir->dalvikInsn.opcode == OP_NOP)
+        return;
+
+    //Ugly, but necessary.  Flush all Dalvik regs so Interp can find them
+    dvmCompilerFlushAllRegs(cUnit);
+
+    if ((mir->next == NULL) || (flags & flagsToCheck)) {
+       genPuntToInterp(cUnit, mir->offset);
+       return;
+    }
+    int entryAddr = offsetof(Thread,
+                             jitToInterpEntries.dvmJitToInterpSingleStep);
+    loadWordDisp(cUnit, r6SELF, entryAddr, r2);
+    /* r0 = dalvik pc */
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+    /* r1 = dalvik pc of following instruction */
+    loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset));
+    opReg(cUnit, kOpBlx, r2);
+}
+
+#if defined(_ARMV5TE) || defined(_ARMV5TE_VFP)
+/*
+ * To prevent a thread in a monitor wait from blocking the Jit from
+ * resetting the code cache, heavyweight monitor lock will not
+ * be allowed to return to an existing translation.  Instead, we will
+ * handle them by branching to a handler, which will in turn call the
+ * runtime lock routine and then branch directly back to the
+ * interpreter main loop.  Given the high cost of the heavyweight
+ * lock operation, this additional cost should be slight (especially when
+ * considering that we expect the vast majority of lock operations to
+ * use the fast-path thin lock bypass).
+ */
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    bool isEnter = (mir->dalvikInsn.opcode == OP_MONITOR_ENTER);
+    genExportPC(cUnit, mir);
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    loadValueDirectFixed(cUnit, rlSrc, r1);
+    genRegCopy(cUnit, r0, r6SELF);
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    if (isEnter) {
+        /* Get dPC of next insn */
+        loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_ENTER)));
+        genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+    } else {
+        LOAD_FUNC_ADDR(cUnit, r2, (int)dvmUnlockObject);
+        /* Do the call */
+        opReg(cUnit, kOpBlx, r2);
+        /* Did we throw? */
+        ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+        loadConstant(cUnit, r0,
+                     (int) (cUnit->method->insns + mir->offset +
+                     dexGetWidthFromOpcode(OP_MONITOR_EXIT)));
+        genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+        ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+        target->defMask = ENCODE_ALL;
+        branchOver->generic.target = (LIR *) target;
+        dvmCompilerClobberCallRegs(cUnit);
+    }
+}
+#endif
+
+/*
+ * Fetch *self->info.breakFlags. If the breakFlags are non-zero,
+ * punt to the interpreter.
+ */
+static void genSuspendPoll(CompilationUnit *cUnit, MIR *mir)
+{
+    int rTemp = dvmCompilerAllocTemp(cUnit);
+    ArmLIR *ld;
+    ld = loadBaseDisp(cUnit, NULL, r6SELF,
+                      offsetof(Thread, interpBreak.ctl.breakFlags),
+                      rTemp, kUnsignedByte, INVALID_SREG);
+    setMemRefType(ld, true /* isLoad */, kMustNotAlias);
+    genRegImmCheck(cUnit, kArmCondNe, rTemp, 0, mir->offset, NULL);
+}
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ */
+
+static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+                                       BasicBlock *bb, ArmLIR *labelList)
+{
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    int numPredecessors = dvmCountSetBits(bb->taken->predecessors);
+    /*
+     * Things could be hoisted out of the taken block into the predecessor, so
+     * make sure it is dominated by the predecessor.
+     */
+    if (numPredecessors == 1 && bb->taken->visited == false &&
+        bb->taken->blockType == kDalvikByteCode) {
+        cUnit->nextCodegenBlock = bb->taken;
+    } else {
+        /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    return false;
+}
+
+static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    if ((dalvikOpcode >= OP_UNUSED_3E) && (dalvikOpcode <= OP_UNUSED_43)) {
+        ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+        return true;
+    }
+    switch (dalvikOpcode) {
+        case OP_RETURN_VOID_BARRIER:
+            dvmCompilerGenMemBarrier(cUnit, kST);
+            // Intentional fallthrough
+        case OP_RETURN_VOID:
+            genReturnCommon(cUnit,mir);
+            break;
+        case OP_UNUSED_73:
+        case OP_UNUSED_79:
+        case OP_UNUSED_7A:
+        case OP_UNUSED_FF:
+            ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+            return true;
+        case OP_NOP:
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST:
+        case OP_CONST_4: {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_32: {
+            //TUNING: single routine to load constant pair for support doubles
+            //TUNING: load 0/-1 separately to avoid load dependency
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_HIGH16: {
+            loadConstantNoClobber(cUnit, rlResult.lowReg,
+                                  mir->dalvikInsn.vB << 16);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_HIGH16: {
+            loadConstantValueWide(cUnit, rlResult.lowReg, rlResult.highReg,
+                                  0, mir->dalvikInsn.vB << 16);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+    /* For OP_THROW_VERIFICATION_ERROR */
+    genInterpSingleStep(cUnit, mir);
+    return false;
+}
+
+static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlResult;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_STRING_JUMBO:
+        case OP_CONST_STRING: {
+            void *strPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]);
+
+            if (strPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null string");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_CLASS: {
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET:
+        case OP_SGET_VOLATILE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_CHAR:
+        case OP_SGET_BYTE:
+        case OP_SGET_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            bool isVolatile;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            /*
+             * On SMP systems, Dalvik opcodes found to be referencing
+             * volatile fields are rewritten to their _VOLATILE variant.
+             * However, this does not happen on non-SMP systems. The JIT
+             * still needs to know about volatility to avoid unsafe
+             * optimizations so we determine volatility based on either
+             * the opcode or the field access flags.
+             */
+#if ANDROID_SMP != 0
+            Opcode opcode = mir->dalvikInsn.opcode;
+            isVolatile = (opcode == OP_SGET_VOLATILE) ||
+                         (opcode == OP_SGET_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kSY);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            loadWordDisp(cUnit, tReg, 0, rlResult.lowReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET_WIDE: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            loadPair(cUnit, tReg, rlResult.lowReg, rlResult.highReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SPUT:
+        case OP_SPUT_VOLATILE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int objHead;
+            bool isVolatile;
+            bool isSputObject;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+            Opcode opcode = mir->dalvikInsn.opcode;
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+#if ANDROID_SMP != 0
+            isVolatile = (opcode == OP_SPUT_VOLATILE) ||
+                         (opcode == OP_SPUT_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            isSputObject = (opcode == OP_SPUT_OBJECT) ||
+                           (opcode == OP_SPUT_OBJECT_VOLATILE);
+
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr);
+            if (isSputObject) {
+                objHead = dvmCompilerAllocTemp(cUnit);
+                loadWordDisp(cUnit, tReg, OFFSETOF_MEMBER(Field, clazz), objHead);
+            }
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kST);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            HEAP_ACCESS_SHADOW(false);
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kSY);
+            }
+            if (isSputObject) {
+                /* NOTE: marking card based sfield->clazz */
+                markCard(cUnit, rlSrc.lowReg, objHead);
+                dvmCompilerFreeTemp(cUnit, objHead);
+            }
+
+            break;
+        }
+        case OP_SPUT_WIDE: {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            storePair(cUnit, tReg, rlSrc.lowReg, rlSrc.highReg);
+            HEAP_ACCESS_SHADOW(false);
+            break;
+        }
+        case OP_NEW_INSTANCE: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr = (ClassObject *)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            /*
+             * If it is going to throw, it should not make to the trace to begin
+             * with.  However, Alloc might throw, so we need to genExportPC()
+             */
+            assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            LOAD_FUNC_ADDR(cUnit, r2, (int)dvmAllocObject);
+            loadConstant(cUnit, r0, (int) classPtr);
+            loadConstant(cUnit, r1, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r2);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CHECK_CAST: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                LOGVV("null clazz in OP_CHECK_CAST, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                return false;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadConstant(cUnit, r1, (int) classPtr );
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            /* Null? */
+            ArmLIR *branch1 = genCmpImmBranch(cUnit, kArmCondEq,
+                                              rlSrc.lowReg, 0);
+            /*
+             *  rlSrc.lowReg now contains object->clazz.  Note that
+             *  it could have been allocated r0, but we're okay so long
+             *  as we don't do anything desctructive until r0 is loaded
+             *  with clazz.
+             */
+            /* r0 now contains object->clazz */
+            loadWordDisp(cUnit, rlSrc.lowReg, offsetof(Object, clazz), r0);
+            LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInstanceofNonTrivial);
+            opRegReg(cUnit, kOpCmp, r0, r1);
+            ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq);
+            opReg(cUnit, kOpBlx, r2);
+            dvmCompilerClobberCallRegs(cUnit);
+            /*
+             * If null, check cast failed - punt to the interpreter.  Because
+             * interpreter will be the one throwing, we don't need to
+             * genExportPC() here.
+             */
+            genZeroCheck(cUnit, r0, mir->offset, NULL);
+            /* check cast passed - branch target here */
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_SGET_WIDE_VOLATILE:
+        case OP_SPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * A typical example of inlined getter/setter from a monomorphic callsite:
+ *
+ * D/dalvikvm(  289): -------- dalvik offset: 0x0000 @ invoke-static (I)
+ * D/dalvikvm(  289): -------- dalvik offset: 0x0000 @ sget-object (C) v0, ...
+ * D/dalvikvm(  289): 0x4427fc22 (0002): ldr     r0, [pc, #56]
+ * D/dalvikvm(  289): 0x4427fc24 (0004): ldr     r1, [r0, #0]
+ * D/dalvikvm(  289): 0x4427fc26 (0006): str     r1, [r5, #0]
+ * D/dalvikvm(  289): 0x4427fc28 (0008): .align4
+ * D/dalvikvm(  289): L0x0003:
+ * D/dalvikvm(  289): -------- dalvik offset: 0x0003 @ move-result-object (I) v0
+ *
+ * Note the invoke-static and move-result-object with the (I) notation are
+ * turned into no-op.
+ */
+static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlResult;
+    switch (dalvikOpcode) {
+        case OP_MOVE_EXCEPTION: {
+            int exOffset = offsetof(Thread, exception);
+            int resetReg = dvmCompilerAllocTemp(cUnit);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, r6SELF, exOffset, rlResult.lowReg);
+            loadConstant(cUnit, resetReg, 0);
+            storeWordDisp(cUnit, r6SELF, exOffset, resetReg);
+            storeValue(cUnit, rlDest, rlResult);
+           break;
+        }
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_OBJECT: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL;
+            rlSrc.fp = rlDest.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_MOVE_RESULT_WIDE: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlSrc.fp = rlDest.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_RETURN_WIDE: {
+            RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlDest.fp = rlSrc.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit,mir);
+            break;
+        }
+        case OP_RETURN:
+        case OP_RETURN_OBJECT: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL;
+            rlDest.fp = rlSrc.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit, mir);
+            break;
+        }
+        case OP_MONITOR_EXIT:
+        case OP_MONITOR_ENTER:
+            genMonitor(cUnit, mir);
+            break;
+        case OP_THROW:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+    RegLocation rlResult;
+
+    if ( (opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    if (mir->ssaRep->numUses == 2)
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    else
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    if (mir->ssaRep->numDefs == 2)
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    else
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+
+    switch (opcode) {
+        case OP_DOUBLE_TO_INT:
+        case OP_INT_TO_FLOAT:
+        case OP_FLOAT_TO_INT:
+        case OP_DOUBLE_TO_FLOAT:
+        case OP_FLOAT_TO_DOUBLE:
+        case OP_INT_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+        case OP_LONG_TO_DOUBLE:
+            return genConversion(cUnit, mir);
+        case OP_NEG_INT:
+        case OP_NOT_INT:
+            return genArithOpInt(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_LONG:
+        case OP_NOT_LONG:
+            return genArithOpLong(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_FLOAT:
+            return genArithOpFloat(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_DOUBLE:
+            return genArithOpDouble(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_MOVE_WIDE:
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_LONG:
+            rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            //TUNING: shouldn't loadValueDirect already check for phys reg?
+            if (rlSrc.location == kLocPhysReg) {
+                genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+            } else {
+                loadValueDirect(cUnit, rlSrc, rlResult.lowReg);
+            }
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        case OP_LONG_TO_INT:
+            rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+            rlSrc = dvmCompilerWideToNarrow(cUnit, rlSrc);
+            // Intentional fallthrough
+        case OP_MOVE:
+        case OP_MOVE_OBJECT:
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_BYTE:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Byte, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_SHORT:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Short, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_CHAR:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Char, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_ARRAY_LENGTH: {
+            int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            genNullCheck(cUnit, rlSrc.sRegLow, rlSrc.lowReg,
+                         mir->offset, NULL);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, rlSrc.lowReg, lenOffset,
+                         rlResult.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlResult;
+    int BBBB = mir->dalvikInsn.vB;
+    if (dalvikOpcode == OP_CONST_WIDE_16) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        //TUNING: do high separately to avoid load dependency
+        opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (dalvikOpcode == OP_CONST_16) {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        storeValue(cUnit, rlDest, rlResult);
+    } else
+        return true;
+    return false;
+}
+
+/* Compare agaist zero */
+static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         ArmLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    ArmConditionCode cond;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+
+    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 = (ArmConditionCode)0;
+            ALOGE("Unexpected opcode (%d) for Fmt21t", 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 = (OpKind)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_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+#if ANDROID_SMP != 0
+            isVolatile = true;
+        // NOTE: intentional fallthrough
+#endif
+        case OP_IGET:
+        case OP_IGET_WIDE:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+        case OP_IPUT:
+        case OP_IPUT_WIDE:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT: {
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            Field *fieldPtr =
+                method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null instance field");
+                dvmAbort();
+            }
+
+#if ANDROID_SMP != 0
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+            fieldOffset = ((InstField *)fieldPtr)->byteOffset;
+            break;
+        }
+        default:
+            break;
+    }
+
+    switch (dalvikOpcode) {
+        case OP_NEW_ARRAY: {
+            // Generates a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r1);   /* Len */
+            loadConstant(cUnit, r0, (int) classPtr );
+            LOAD_FUNC_ADDR(cUnit, r3, (int)dvmAllocArrayByClass);
+            /*
+             * "len < 0": bail to the interpreter to re-execute the
+             * instruction
+             */
+            genRegImmCheck(cUnit, kArmCondMi, r1, 0, mir->offset, NULL);
+            loadConstant(cUnit, r2, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r3);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_INSTANCE_OF: {
+            // May generate a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGD("null clazz in OP_INSTANCE_OF, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                break;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r0);  /* Ref */
+            loadConstant(cUnit, r2, (int) classPtr );
+            /* When taken r0 has NULL which can be used for store directly */
+            ArmLIR *branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
+            /* r1 now contains object->clazz */
+            loadWordDisp(cUnit, r0, offsetof(Object, clazz), r1);
+            /* r1 now contains object->clazz */
+            LOAD_FUNC_ADDR(cUnit, r3, (int)dvmInstanceofNonTrivial);
+            loadConstant(cUnit, r0, 1);                /* Assume true */
+            opRegReg(cUnit, kOpCmp, r1, r2);
+            ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq);
+            genRegCopy(cUnit, r0, r1);
+            genRegCopy(cUnit, r1, r2);
+            opReg(cUnit, kOpBlx, r3);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* branch target here */
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_IGET_WIDE:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IGET:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            genIGet(cUnit, mir, kWord, fieldOffset, isVolatile);
+            break;
+        case OP_IPUT_WIDE:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile);
+            break;
+        case OP_IPUT_OBJECT_VOLATILE:
+        case OP_IPUT_OBJECT:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile);
+            break;
+        case OP_IGET_WIDE_VOLATILE:
+        case OP_IPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    int fieldOffset =  mir->dalvikInsn.vC;
+    switch (dalvikOpcode) {
+        case OP_IGET_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+            genIGet(cUnit, mir, kWord, fieldOffset, false);
+            break;
+        case OP_IPUT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, false);
+            break;
+        case OP_IPUT_OBJECT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, false);
+            break;
+        case OP_IGET_WIDE_QUICK:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_WIDE_QUICK:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        default:
+            return true;
+    }
+    return false;
+
+}
+
+/* Compare agaist zero */
+static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         ArmLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    ArmConditionCode cond;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+
+    opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
+
+    switch (dalvikOpcode) {
+        case OP_IF_EQ:
+            cond = kArmCondEq;
+            break;
+        case OP_IF_NE:
+            cond = kArmCondNe;
+            break;
+        case OP_IF_LT:
+            cond = kArmCondLt;
+            break;
+        case OP_IF_GE:
+            cond = kArmCondGe;
+            break;
+        case OP_IF_GT:
+            cond = kArmCondGt;
+            break;
+        case OP_IF_LE:
+            cond = kArmCondLe;
+            break;
+        default:
+            cond = (ArmConditionCode)0;
+            ALOGE("Unexpected opcode (%d) for Fmt22t", dalvikOpcode);
+            dvmCompilerAbort(cUnit);
+    }
+    genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
+    /* This mostly likely will be optimized away in a later phase */
+    genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+    return false;
+}
+
+static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_MOVE_16:
+        case OP_MOVE_OBJECT_16:
+        case OP_MOVE_FROM16:
+        case OP_MOVE_OBJECT_FROM16: {
+            storeValue(cUnit, dvmCompilerGetDest(cUnit, mir, 0),
+                       dvmCompilerGetSrc(cUnit, mir, 0));
+            break;
+        }
+        case OP_MOVE_WIDE_16:
+        case OP_MOVE_WIDE_FROM16: {
+            storeValueWide(cUnit, dvmCompilerGetDestWide(cUnit, mir, 0, 1),
+                           dvmCompilerGetSrcWide(cUnit, mir, 0, 1));
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlSrc1;
+    RegLocation rlSrc2;
+    RegLocation rlDest;
+
+    if ( (opcode >= OP_ADD_INT) && (opcode <= OP_REM_DOUBLE)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    /* APUTs have 3 sources and no targets */
+    if (mir->ssaRep->numDefs == 0) {
+        if (mir->ssaRep->numUses == 3) {
+            rlDest = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 1);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+        } else {
+            assert(mir->ssaRep->numUses == 4);
+            rlDest = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 2);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 3);
+        }
+    } else {
+        /* Two sources and 1 dest.  Deduce the operand sizes */
+        if (mir->ssaRep->numUses == 4) {
+            rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+        } else {
+            assert(mir->ssaRep->numUses == 2);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+        }
+        if (mir->ssaRep->numDefs == 2) {
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        } else {
+            assert(mir->ssaRep->numDefs == 1);
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        }
+    }
+
+
+    switch (opcode) {
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+            return genCmpFP(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        case OP_CMP_LONG:
+            genCmpLong(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+            break;
+        case OP_AGET_WIDE:
+            genArrayGet(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_AGET:
+        case OP_AGET_OBJECT:
+            genArrayGet(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_AGET_BOOLEAN:
+            genArrayGet(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_BYTE:
+            genArrayGet(cUnit, mir, kSignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_CHAR:
+            genArrayGet(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_AGET_SHORT:
+            genArrayGet(cUnit, mir, kSignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_WIDE:
+            genArrayPut(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_APUT:
+            genArrayPut(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_OBJECT:
+            genArrayObjectPut(cUnit, mir, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_SHORT:
+        case OP_APUT_CHAR:
+            genArrayPut(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_BYTE:
+        case OP_APUT_BOOLEAN:
+            genArrayPut(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * Find the matching case.
+ *
+ * return values:
+ * r0 (low 32-bit): pc of the chaining cell corresponding to the resolved case,
+ *    including default which is placed at MIN(size, MAX_CHAINED_SWITCH_CASES).
+ * r1 (high 32-bit): the branch offset of the matching case (only for indexes
+ *    above MAX_CHAINED_SWITCH_CASES).
+ *
+ * Instructions around the call are:
+ *
+ * mov r2, pc
+ * blx &findPackedSwitchIndex
+ * mov pc, r0
+ * .align4
+ * chaining cell for case 0 [12 bytes]
+ * chaining cell for case 1 [12 bytes]
+ *               :
+ * chaining cell for case MIN(size, MAX_CHAINED_SWITCH_CASES)-1 [12 bytes]
+ * chaining cell for case default [8 bytes]
+ * noChain exit
+ */
+static u8 findPackedSwitchIndex(const u2* switchData, int testVal, uintptr_t pc)
+{
+    int size;
+    int firstKey;
+    const int *entries;
+    int index;
+    int jumpIndex;
+    uintptr_t caseDPCOffset = 0;
+    /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */
+    uintptr_t chainingPC = (pc + 4) & ~3;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    size = switchData[1];
+    assert(size > 0);
+
+    firstKey = switchData[2];
+    firstKey |= switchData[3] << 16;
+
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = (const int*) &switchData[4];
+    assert(((u4)entries & 0x3) == 0);
+
+    index = testVal - firstKey;
+
+    /* Jump to the default cell */
+    if (index < 0 || index >= size) {
+        jumpIndex = MIN(size, MAX_CHAINED_SWITCH_CASES);
+    /* Jump to the non-chaining exit point */
+    } else if (index >= MAX_CHAINED_SWITCH_CASES) {
+        jumpIndex = MAX_CHAINED_SWITCH_CASES + 1;
+        caseDPCOffset = entries[index];
+    /* Jump to the inline chaining cell */
+    } else {
+        jumpIndex = index;
+    }
+
+    chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE;
+    return (((u8) caseDPCOffset) << 32) | (u8) chainingPC;
+}
+
+/* See comments for findPackedSwitchIndex */
+static u8 findSparseSwitchIndex(const u2* switchData, int testVal, uintptr_t pc)
+{
+    int size;
+    const int *keys;
+    const int *entries;
+    uintptr_t chainingPC = (pc + 4) & ~3;
+    int i;
+
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+
+    size = switchData[1];
+    assert(size > 0);
+
+    /* The keys are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    keys = (const int*) &switchData[2];
+    assert(((u4)keys & 0x3) == 0);
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = keys + size;
+    assert(((u4)entries & 0x3) == 0);
+
+    /*
+     * Run through the list of keys, which are guaranteed to
+     * be sorted low-to-high.
+     *
+     * Most tables have 3-4 entries.  Few have more than 10.  A binary
+     * search here is probably not useful.
+     */
+    for (i = 0; i < size; i++) {
+        int k = keys[i];
+        if (k == testVal) {
+            /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */
+            int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ?
+                           i : MAX_CHAINED_SWITCH_CASES + 1;
+            chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE;
+            return (((u8) entries[i]) << 32) | (u8) chainingPC;
+        } else if (k > testVal) {
+            break;
+        }
+    }
+    return chainingPC + MIN(size, MAX_CHAINED_SWITCH_CASES) *
+           CHAIN_CELL_NORMAL_SIZE;
+}
+
+static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_FILL_ARRAY_DATA: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            // Making a call - use explicit registers
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r0);
+            LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInterpHandleFillArrayData);
+            loadConstant(cUnit, r1,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            opReg(cUnit, kOpBlx, r2);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if successful */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            break;
+        }
+        /*
+         * Compute the goto target of up to
+         * MIN(switchSize, MAX_CHAINED_SWITCH_CASES) + 1 chaining cells.
+         * See the comment before findPackedSwitchIndex for the code layout.
+         */
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r1);
+            dvmCompilerLockAllTemps(cUnit);
+            if (dalvikOpcode == OP_PACKED_SWITCH) {
+                LOAD_FUNC_ADDR(cUnit, r4PC, (int)findPackedSwitchIndex);
+            } else {
+                LOAD_FUNC_ADDR(cUnit, r4PC, (int)findSparseSwitchIndex);
+            }
+            /* r0 <- Addr of the switch data */
+            loadConstant(cUnit, r0,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            /* r2 <- pc of the instruction following the blx */
+            opRegReg(cUnit, kOpMov, r2, r15pc);
+            opReg(cUnit, kOpBlx, r4PC);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* pc <- computed goto target */
+            opRegReg(cUnit, kOpMov, r15pc, r0);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * See the example of predicted inlining listed before the
+ * genValidationForPredictedInline function. The function here takes care the
+ * branch over at 0x4858de78 and the misprediction target at 0x4858de7a.
+ */
+static void genLandingPadForMispredictedCallee(CompilationUnit *cUnit, MIR *mir,
+                                               BasicBlock *bb,
+                                               ArmLIR *labelList)
+{
+    BasicBlock *fallThrough = bb->fallThrough;
+
+    /* Bypass the move-result block if there is one */
+    if (fallThrough->firstMIRInsn) {
+        assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
+        fallThrough = fallThrough->fallThrough;
+    }
+    /* Generate a branch over if the predicted inlining is correct */
+    genUnconditionalBranch(cUnit, &labelList[fallThrough->id]);
+
+    /* Reset the register state */
+    dvmCompilerResetRegPool(cUnit);
+    dvmCompilerClobberAllRegs(cUnit);
+    dvmCompilerResetNullCheck(cUnit);
+
+    /* Target for the slow invoke path */
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    /* Hook up the target to the verification branch */
+    mir->meta.callsiteInfo->misPredBranchOver->target = (LIR *) target;
+}
+
+static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir,
+                             BasicBlock *bb, ArmLIR *labelList)
+{
+    ArmLIR *retChainingCell = NULL;
+    ArmLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    if (bb->fallThrough != NULL)
+        retChainingCell = &labelList[bb->fallThrough->id];
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /*
+         * calleeMethod = this->clazz->vtable[
+         *     method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex
+         * ]
+         */
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_VIRTUAL_RANGE: {
+            ArmLIR *predChainingCell = &labelList[bb->taken->id];
+            int methodIndex =
+                cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]->
+                methodIndex;
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /*
+         * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex
+         *                ->pResMethods[BBBB]->methodIndex]
+         */
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod == cUnit->method->clazz->super->vtable[
+                                     cUnit->method->clazz->pDvmDex->
+                                       pResMethods[dInsn->vB]->methodIndex]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r0 = calleeMethod */
+                loadConstant(cUnit, r0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r0 = calleeMethod */
+            loadConstant(cUnit, r0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_STATIC)
+                genProcessArgsNoRange(cUnit, mir, dInsn,
+                                      NULL /* no null check */);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn,
+                                    NULL /* no null check */);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r0 = calleeMethod */
+                loadConstant(cUnit, r0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+        /*
+         * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz,
+         *                    BBBB, method, method->clazz->pDvmDex)
+         *
+         * The following is an example of generated code for
+         *      "invoke-interface v0"
+         *
+         * -------- dalvik offset: 0x0008 @ invoke-interface v0
+         * 0x47357e36 : ldr     r0, [r5, #0]   --+
+         * 0x47357e38 : sub     r7,r5,#24        |
+         * 0x47357e3c : cmp     r0, #0           | genProcessArgsNoRange
+         * 0x47357e3e : beq     0x47357e82       |
+         * 0x47357e40 : stmia   r7, <r0>       --+
+         * 0x47357e42 : ldr     r4, [pc, #120] --> r4 <- dalvikPC of this invoke
+         * 0x47357e44 : add     r1, pc, #64    --> r1 <- &retChainingCell
+         * 0x47357e46 : add     r2, pc, #72    --> r2 <- &predictedChainingCell
+         * 0x47357e48 : blx_1   0x47348190     --+ TEMPLATE_INVOKE_METHOD_
+         * 0x47357e4a : blx_2   see above      --+     PREDICTED_CHAIN
+         * 0x47357e4c : b       0x47357e90     --> off to the predicted chain
+         * 0x47357e4e : b       0x47357e82     --> punt to the interpreter
+         * 0x47357e50 : mov     r8, r1         --+
+         * 0x47357e52 : mov     r9, r2           |
+         * 0x47357e54 : ldr     r2, [pc, #96]    |
+         * 0x47357e56 : mov     r10, r3          |
+         * 0x47357e58 : movs    r0, r3           | dvmFindInterfaceMethodInCache
+         * 0x47357e5a : ldr     r3, [pc, #88]    |
+         * 0x47357e5c : ldr     r7, [pc, #80]    |
+         * 0x47357e5e : mov     r1, #1452        |
+         * 0x47357e62 : blx     r7             --+
+         * 0x47357e64 : cmp     r0, #0         --> calleeMethod == NULL?
+         * 0x47357e66 : bne     0x47357e6e     --> branch over the throw if !r0
+         * 0x47357e68 : ldr     r0, [pc, #80]  --> load Dalvik PC of the invoke
+         * 0x47357e6a : blx_1   0x47348494     --+ TEMPLATE_THROW_EXCEPTION_
+         * 0x47357e6c : blx_2   see above      --+     COMMON
+         * 0x47357e6e : mov     r1, r8         --> r1 <- &retChainingCell
+         * 0x47357e70 : cmp     r1, #0         --> compare against 0
+         * 0x47357e72 : bgt     0x47357e7c     --> >=0? don't rechain
+         * 0x47357e74 : ldr     r7, [pc, #off] --+
+         * 0x47357e76 : mov     r2, r9           | dvmJitToPatchPredictedChain
+         * 0x47357e78 : mov     r3, r10          |
+         * 0x47357e7a : blx     r7             --+
+         * 0x47357e7c : add     r1, pc, #8     --> r1 <- &retChainingCell
+         * 0x47357e7e : blx_1   0x4734809c     --+ TEMPLATE_INVOKE_METHOD_NO_OPT
+         * 0x47357e80 : blx_2   see above      --+
+         * -------- reconstruct dalvik PC : 0x425719dc @ +0x0008
+         * 0x47357e82 : ldr     r0, [pc, #56]
+         * Exception_Handling:
+         * 0x47357e84 : ldr     r1, [r6, #92]
+         * 0x47357e86 : blx     r1
+         * 0x47357e88 : .align4
+         * -------- chaining cell (hot): 0x000b
+         * 0x47357e88 : ldr     r0, [r6, #104]
+         * 0x47357e8a : blx     r0
+         * 0x47357e8c : data    0x19e2(6626)
+         * 0x47357e8e : data    0x4257(16983)
+         * 0x47357e90 : .align4
+         * -------- chaining cell (predicted)
+         * 0x47357e90 : data    0xe7fe(59390)  --> will be patched into bx
+         * 0x47357e92 : data    0x0000(0)
+         * 0x47357e94 : data    0x0000(0)      --> class
+         * 0x47357e96 : data    0x0000(0)
+         * 0x47357e98 : data    0x0000(0)      --> method
+         * 0x47357e9a : data    0x0000(0)
+         * 0x47357e9c : data    0x0000(0)      --> rechain count
+         * 0x47357e9e : data    0x0000(0)
+         * -------- end of chaining cells (0x006c)
+         * 0x47357eb0 : .word (0xad03e369)
+         * 0x47357eb4 : .word (0x28a90)
+         * 0x47357eb8 : .word (0x41a63394)
+         * 0x47357ebc : .word (0x425719dc)
+         */
+        case OP_INVOKE_INTERFACE:
+        case OP_INVOKE_INTERFACE_RANGE: {
+            ArmLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* "this" is already left in r0 by genProcessArgs* */
+
+            /* r4PC = dalvikCallsite */
+            loadConstant(cUnit, r4PC,
+                         (int) (cUnit->method->insns + mir->offset));
+
+            /* r1 = &retChainingCell */
+            ArmLIR *addrRetChain =
+                opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+            /* r2 = &predictedChainingCell */
+            ArmLIR *predictedChainingCell =
+                opRegRegImm(cUnit, kOpAdd, r2, r15pc, 0);
+            predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+            /* return through lr - jump to the chaining cell */
+            genUnconditionalBranch(cUnit, predChainingCell);
+
+            /*
+             * null-check on "this" may have been eliminated, but we still need
+             * a PC-reconstruction label for stack overflow bailout.
+             */
+            if (pcrLabel == NULL) {
+                int dPC = (int) (cUnit->method->insns + mir->offset);
+                pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+                pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+                pcrLabel->operands[0] = dPC;
+                pcrLabel->operands[1] = mir->offset;
+                /* Insert the place holder to the growable list */
+                dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                                      (intptr_t) pcrLabel);
+            }
+
+            /* return through lr+2 - punt to the interpreter */
+            genUnconditionalBranch(cUnit, pcrLabel);
+
+            /*
+             * return through lr+4 - fully resolve the callee method.
+             * r1 <- count
+             * r2 <- &predictedChainCell
+             * r3 <- this->class
+             * r4 <- dPC
+             * r7 <- this->class->vtable
+             */
+
+            /* Save count, &predictedChainCell, and class to high regs first */
+            genRegCopy(cUnit, r8, r1);
+            genRegCopy(cUnit, r9, r2);
+            genRegCopy(cUnit, r10, r3);
+
+            /* r0 now contains this->clazz */
+            genRegCopy(cUnit, r0, r3);
+
+            /* r1 = BBBB */
+            loadConstant(cUnit, r1, dInsn->vB);
+
+            /* r2 = method (caller) */
+            loadConstant(cUnit, r2, (int) cUnit->method);
+
+            /* r3 = pDvmDex */
+            loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex);
+
+            LOAD_FUNC_ADDR(cUnit, r7,
+                           (intptr_t) dvmFindInterfaceMethodInCache);
+            opReg(cUnit, kOpBlx, r7);
+            /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */
+
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if the interface method is resolved */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            /*
+             * calleeMethod == NULL -> throw
+             */
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+
+            genRegCopy(cUnit, r1, r8);
+
+            /* Check if rechain limit is reached */
+            ArmLIR *bypassRechaining = genCmpImmBranch(cUnit, kArmCondGt,
+                                                       r1, 0);
+
+            LOAD_FUNC_ADDR(cUnit, r7, (int) dvmJitToPatchPredictedChain);
+
+            genRegCopy(cUnit, r1, r6SELF);
+            genRegCopy(cUnit, r2, r9);
+            genRegCopy(cUnit, r3, r10);
+
+            /*
+             * r0 = calleeMethod
+             * r2 = &predictedChainingCell
+             * r3 = class
+             *
+             * &returnChainingCell has been loaded into r1 but is not needed
+             * when patching the chaining cell and will be clobbered upon
+             * returning so it will be reconstructed again.
+             */
+            opReg(cUnit, kOpBlx, r7);
+
+            /* r1 = &retChainingCell */
+            addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+            bypassRechaining->generic.target = (LIR *) addrRetChain;
+
+            /*
+             * r0 = this, r1 = calleeMethod,
+             * r1 = &ChainingCell,
+             * r4PC = callsiteDPC,
+             */
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+                TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.invokePolymorphic++;
+#endif
+            /* Handle exceptions using the interpreter */
+            genTrap(cUnit, mir->offset, pcrLabel);
+            break;
+        }
+        case OP_INVOKE_OBJECT_INIT_RANGE:
+        case OP_FILLED_NEW_ARRAY:
+        case OP_FILLED_NEW_ARRAY_RANGE: {
+            /* Just let the interpreter deal with these */
+            genInterpSingleStep(cUnit, mir);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+                               BasicBlock *bb, ArmLIR *labelList)
+{
+    ArmLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /* calleeMethod = this->clazz->vtable[BBBB] */
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_VIRTUAL_QUICK: {
+            int methodIndex = dInsn->vB;
+            ArmLIR *retChainingCell = &labelList[bb->fallThrough->id];
+            ArmLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeVirtualWholeMethod(cUnit, mir, calleeAddr,
+                                            retChainingCell);
+            }
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /* calleeMethod = method->clazz->super->vtable[BBBB] */
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->super->vtable[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r0 = calleeMethod */
+            loadConstant(cUnit, r0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * This operation is complex enough that we'll do it partly inline
+ * and partly with a handler.  NOTE: the handler uses hardcoded
+ * values for string object offsets and must be revisitied if the
+ * layout changes.
+ */
+static bool genInlinedCompareTo(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    ArmLIR *rollback;
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlComp = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r0);
+    loadValueDirectFixed(cUnit, rlComp, r1);
+    /* Test objects for NULL */
+    rollback = genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL);
+    genNullCheck(cUnit, rlComp.sRegLow, r1, mir->offset, rollback);
+    /*
+     * TUNING: we could check for object pointer equality before invoking
+     * handler. Unclear whether the gain would be worth the added code size
+     * expansion.
+     */
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_COMPARETO);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlChar = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r0);
+    loadValueDirectFixed(cUnit, rlChar, r1);
+    RegLocation rlStart = dvmCompilerGetSrc(cUnit, mir, 2);
+    loadValueDirectFixed(cUnit, rlStart, r2);
+    /* Test objects for NULL */
+    genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL);
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_INDEXOF);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+// Generates an inlined String.isEmpty or String.length.
+static bool genInlinedStringIsEmptyOrLength(CompilationUnit *cUnit, MIR *mir,
+                                            bool isEmpty)
+{
+    // dst = src.length();
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count,
+                 rlResult.lowReg);
+    if (isEmpty) {
+        // dst = (dst == 0);
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegReg(cUnit, kOpNeg, tReg, rlResult.lowReg);
+        opRegRegReg(cUnit, kOpAdc, rlResult.lowReg, rlResult.lowReg, tReg);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, false);
+}
+
+static bool genInlinedStringIsEmpty(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, true);
+}
+
+static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
+{
+    int contents = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlIdx = dvmCompilerGetSrc(cUnit, mir, 1);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+    int regMax = dvmCompilerAllocTemp(cUnit);
+    int regOff = dvmCompilerAllocTemp(cUnit);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+    ArmLIR *pcrLabel = genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg,
+                                    mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, regMax);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_offset, regOff);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_value, regPtr);
+    genBoundsCheck(cUnit, rlIdx.lowReg, regMax, mir->offset, pcrLabel);
+    dvmCompilerFreeTemp(cUnit, regMax);
+    opRegImm(cUnit, kOpAdd, regPtr, contents);
+    opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2's IT block also yields 3 instructions, but imposes
+     * scheduling constraints.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2 IT block allows slightly shorter sequence,
+     * but introduces a scheduling barrier.  Stick with this
+     * mechanism for now.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    opRegRegReg(cUnit, kOpAdc, rlResult.highReg, rlSrc.highReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.highReg, signReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedIntFloatConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    storeValue(cUnit, rlDest, rlSrc);
+    return false;
+}
+
+static bool genInlinedLongDoubleConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    storeValueWide(cUnit, rlDest, rlSrc);
+    return false;
+}
+
+/*
+ * JITs a call to a C function.
+ * TODO: use this for faster native method invocation for simple native
+ * methods (http://b/3069458).
+ */
+static bool handleExecuteInlineC(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int operation = dInsn->vB;
+    unsigned int i;
+    const InlineOperation* inLineTable = dvmGetInlineOpsTable();
+    uintptr_t fn = (int) inLineTable[operation].func;
+    if (fn == 0) {
+        dvmCompilerAbort(cUnit);
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r4PC);
+    dvmCompilerClobber(cUnit, r7);
+    int offset = offsetof(Thread, interpSave.retval);
+    opRegRegImm(cUnit, kOpAdd, r4PC, r6SELF, offset);
+    opImm(cUnit, kOpPush, (1<<r4PC) | (1<<r7));
+    LOAD_FUNC_ADDR(cUnit, r4PC, fn);
+    genExportPC(cUnit, mir);
+    for (i=0; i < dInsn->vA; i++) {
+        loadValueDirect(cUnit, dvmCompilerGetSrc(cUnit, mir, i), i);
+    }
+    opReg(cUnit, kOpBlx, r4PC);
+    opRegImm(cUnit, kOpAdd, r13sp, 8);
+    /* NULL? */
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+    genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+    return false;
+}
+
+/*
+ * NOTE: Handles both range and non-range versions (arguments
+ * have already been normalized by this point).
+ */
+static bool handleExecuteInline(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    assert(dInsn->opcode == OP_EXECUTE_INLINE_RANGE ||
+           dInsn->opcode == OP_EXECUTE_INLINE);
+    switch (dInsn->vB) {
+        case INLINE_EMPTYINLINEMETHOD:
+            return false;  /* Nop */
+
+        /* These ones we potentially JIT inline. */
+
+        case INLINE_STRING_CHARAT:
+            return genInlinedStringCharAt(cUnit, mir);
+        case INLINE_STRING_LENGTH:
+            return genInlinedStringLength(cUnit, mir);
+        case INLINE_STRING_IS_EMPTY:
+            return genInlinedStringIsEmpty(cUnit, mir);
+        case INLINE_STRING_COMPARETO:
+            return genInlinedCompareTo(cUnit, mir);
+        case INLINE_STRING_FASTINDEXOF_II:
+            return genInlinedFastIndexOf(cUnit, mir);
+
+        case INLINE_MATH_ABS_INT:
+        case INLINE_STRICT_MATH_ABS_INT:
+            return genInlinedAbsInt(cUnit, mir);
+        case INLINE_MATH_ABS_LONG:
+        case INLINE_STRICT_MATH_ABS_LONG:
+            return genInlinedAbsLong(cUnit, mir);
+        case INLINE_MATH_MIN_INT:
+        case INLINE_STRICT_MATH_MIN_INT:
+            return genInlinedMinMaxInt(cUnit, mir, true);
+        case INLINE_MATH_MAX_INT:
+        case INLINE_STRICT_MATH_MAX_INT:
+            return genInlinedMinMaxInt(cUnit, mir, false);
+        case INLINE_MATH_SQRT:
+        case INLINE_STRICT_MATH_SQRT:
+            return genInlineSqrt(cUnit, mir);
+        case INLINE_MATH_ABS_FLOAT:
+        case INLINE_STRICT_MATH_ABS_FLOAT:
+            return genInlinedAbsFloat(cUnit, mir);
+        case INLINE_MATH_ABS_DOUBLE:
+        case INLINE_STRICT_MATH_ABS_DOUBLE:
+            return genInlinedAbsDouble(cUnit, mir);
+
+        case INLINE_FLOAT_TO_RAW_INT_BITS:
+        case INLINE_INT_BITS_TO_FLOAT:
+            return genInlinedIntFloatConversion(cUnit, mir);
+        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+        case INLINE_LONG_BITS_TO_DOUBLE:
+            return genInlinedLongDoubleConversion(cUnit, mir);
+
+        /*
+         * These ones we just JIT a call to a C function for.
+         * TODO: special-case these in the other "invoke" call paths.
+         */
+        case INLINE_STRING_EQUALS:
+        case INLINE_MATH_COS:
+        case INLINE_MATH_SIN:
+        case INLINE_FLOAT_TO_INT_BITS:
+        case INLINE_DOUBLE_TO_LONG_BITS:
+            return handleExecuteInlineC(cUnit, mir);
+    }
+    dvmCompilerAbort(cUnit);
+    return false; // Not reachable; keeps compiler happy.
+}
+
+static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+    //TUNING: We're using core regs here - not optimal when target is a double
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadConstantNoClobber(cUnit, rlResult.lowReg,
+                          mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL);
+    loadConstantNoClobber(cUnit, rlResult.highReg,
+                          (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * The following are special processing routines that handle transfer of
+ * controls between compiled code and the interpreter. Certain VM states like
+ * Dalvik PC and special-purpose registers are reconstructed here.
+ */
+
+/*
+ * Insert a
+ *    b   .+4
+ *    nop
+ * pair at the beginning of a chaining cell.  This serves as the
+ * switch branch that selects between reverting to the interpreter or
+ * not.  Once the cell is chained to a translation, the cell will
+ * contain a 32-bit branch.  Subsequent chain/unchain operations will
+ * then only alter that first 16-bits - the "b .+4" for unchaining,
+ * and the restoration of the first half of the 32-bit branch for
+ * rechaining.
+ */
+static void insertChainingSwitch(CompilationUnit *cUnit)
+{
+    ArmLIR *branch = newLIR0(cUnit, kThumbBUncond);
+    newLIR2(cUnit, kThumbOrr, r0, r0);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *) target;
+}
+
+/* Chaining cell for code that may need warmup. */
+static void handleNormalChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+            offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/*
+ * Chaining cell for instructions that immediately following already translated
+ * code.
+ */
+static void handleHotChainingCell(CompilationUnit *cUnit,
+                                  unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+            offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for branches that branch back into the same basic block */
+static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
+                                             unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+#if defined(WITH_SELF_VERIFICATION)
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+        offsetof(Thread,
+                 jitToInterpEntries.dvmJitToInterpBackwardBranch) >> 2);
+#else
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+#endif
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
+                                              const Method *callee)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+            offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (callee->insns));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit)
+{
+
+    /* Should not be executed in the initial state */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_BX_PAIR_INIT);
+    /* To be filled: class */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_CLAZZ_INIT);
+    /* To be filled: method */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_METHOD_INIT);
+    /*
+     * Rechain count. The initial value of 0 here will trigger chaining upon
+     * the first invocation of this callsite.
+     */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_COUNTER_INIT);
+}
+
+/* Load the Dalvik PC into r0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+                                   ArmLIR *targetLabel)
+{
+    ArmLIR **pcrLabel =
+        (ArmLIR **) cUnit->pcReconstructionList.elemList;
+    int numElems = cUnit->pcReconstructionList.numUsed;
+    int i;
+
+    /*
+     * We should never reach here through fall-through code, so insert
+     * a bomb to signal troubles immediately.
+     */
+    if (numElems) {
+        newLIR0(cUnit, kThumbUndefined);
+    }
+
+    for (i = 0; i < numElems; i++) {
+        dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+        /* r0 = dalvik PC */
+        loadConstant(cUnit, r0, pcrLabel[i]->operands[0]);
+        genUnconditionalBranch(cUnit, targetLabel);
+    }
+}
+
+static const char *extendedMIROpNames[kMirOpLast - kMirOpFirst] = {
+    "kMirOpPhi",
+    "kMirOpNullNRangeUpCheck",
+    "kMirOpNullNRangeDownCheck",
+    "kMirOpLowerBound",
+    "kMirOpPunt",
+    "kMirOpCheckInlinePrediction",
+};
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    /*
+     * NOTE: these synthesized blocks don't have ssa names assigned
+     * for Dalvik registers.  However, because they dominate the following
+     * blocks we can simply use the Dalvik name w/ subscript 0 as the
+     * ssa name.
+     */
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int maxC = dInsn->arg[0];
+    int regLength;
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxEnd = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxEnd = loadValue(cUnit, rlIdxEnd, kCoreReg);
+    genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    regLength = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    int delta = maxC;
+    /*
+     * If the loop end condition is ">=" instead of ">", then the largest value
+     * of the index is "endCondition - 1".
+     */
+    if (dInsn->arg[2] == OP_IF_GE) {
+        delta--;
+    }
+
+    if (delta) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxEnd.lowReg, delta);
+        rlIdxEnd.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+    /* Punt if "regIdxEnd < len(Array)" is false */
+    genRegRegCheck(cUnit, kArmCondGe, rlIdxEnd.lowReg, regLength, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int regLength = dvmCompilerAllocTemp(cUnit);
+    const int maxC = dInsn->arg[0];
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxInit = cUnit->regLocation[mir->dalvikInsn.vB];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxInit = loadValue(cUnit, rlIdxInit, kCoreReg);
+    genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    if (maxC) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxInit.lowReg, maxC);
+        rlIdxInit.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+
+    /* Punt if "regIdxInit < len(Array)" is false */
+    genRegRegCheck(cUnit, kArmCondGe, rlIdxInit.lowReg, regLength, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = idxReg;
+ * vB = minC;
+ */
+static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int minC = dInsn->vB;
+    RegLocation rlIdx = cUnit->regLocation[mir->dalvikInsn.vA];
+
+    /* regIdx <- initial index value */
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+
+    /* Punt if "regIdxInit + minC >= 0" is false */
+    genRegImmCheck(cUnit, kArmCondLt, rlIdx.lowReg, -minC, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vC = this
+ *
+ * A predicted inlining target looks like the following, where instructions
+ * between 0x4858de66 and 0x4858de72 are checking if the predicted class
+ * matches "this", and the verificaion code is generated by this routine.
+ *
+ * (C) means the instruction is inlined from the callee, and (PI) means the
+ * instruction is the predicted inlined invoke, whose corresponding
+ * instructions are still generated to handle the mispredicted case.
+ *
+ * D/dalvikvm(   86): -------- kMirOpCheckInlinePrediction
+ * D/dalvikvm(   86): 0x4858de66 (0002): ldr     r0, [r5, #68]
+ * D/dalvikvm(   86): 0x4858de68 (0004): ldr     r1, [pc, #140]
+ * D/dalvikvm(   86): 0x4858de6a (0006): cmp     r0, #0
+ * D/dalvikvm(   86): 0x4858de6c (0008): beq     0x4858deb2
+ * D/dalvikvm(   86): 0x4858de6e (000a): ldr     r2, [r0, #0]
+ * D/dalvikvm(   86): 0x4858de70 (000c): cmp     r1, r2
+ * D/dalvikvm(   86): 0x4858de72 (000e): bne     0x4858de7a
+ * D/dalvikvm(   86): -------- dalvik offset: 0x004c @ +iget-object-quick (C)
+ * v4, v17, (#8)
+ * D/dalvikvm(   86): 0x4858de74 (0010): ldr     r3, [r0, #8]
+ * D/dalvikvm(   86): 0x4858de76 (0012): str     r3, [r5, #16]
+ * D/dalvikvm(   86): -------- dalvik offset: 0x004c @
+ * +invoke-virtual-quick/range (PI) v17..v17
+ * D/dalvikvm(   86): 0x4858de78 (0014): b       0x4858debc
+ * D/dalvikvm(   86): 0x4858de7a (0016): add     r4,r5,#68
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de7e (001a): ldmia   r4, <r0>
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de80 (001c): sub     r7,r5,#24
+ * D/dalvikvm(   86): 0x4858de84 (0020): cmp     r0, #0
+ * D/dalvikvm(   86): 0x4858de86 (0022): beq     0x4858deb6
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de88 (0024): stmia   r7, <r0>
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de8a (0026): ldr     r4, [pc, #104]
+ * D/dalvikvm(   86): 0x4858de8c (0028): add     r1, pc, #28
+ * D/dalvikvm(   86): 0x4858de8e (002a): add     r2, pc, #56
+ * D/dalvikvm(   86): 0x4858de90 (002c): blx_1   0x48589198
+ * D/dalvikvm(   86): 0x4858de92 (002e): blx_2   see above
+ * D/dalvikvm(   86): 0x4858de94 (0030): b       0x4858dec8
+ * D/dalvikvm(   86): 0x4858de96 (0032): b       0x4858deb6
+ * D/dalvikvm(   86): 0x4858de98 (0034): ldr     r0, [r7, #72]
+ * D/dalvikvm(   86): 0x4858de9a (0036): cmp     r1, #0
+ * D/dalvikvm(   86): 0x4858de9c (0038): bgt     0x4858dea4
+ * D/dalvikvm(   86): 0x4858de9e (003a): ldr     r7, [r6, #116]
+ * D/dalvikvm(   86): 0x4858dea0 (003c): movs    r1, r6
+ * D/dalvikvm(   86): 0x4858dea2 (003e): blx     r7
+ * D/dalvikvm(   86): 0x4858dea4 (0040): add     r1, pc, #4
+ * D/dalvikvm(   86): 0x4858dea6 (0042): blx_1   0x485890a0
+ * D/dalvikvm(   86): 0x4858dea8 (0044): blx_2   see above
+ * D/dalvikvm(   86): 0x4858deaa (0046): b       0x4858deb6
+ * D/dalvikvm(   86): 0x4858deac (0048): .align4
+ * D/dalvikvm(   86): L0x004f:
+ * D/dalvikvm(   86): -------- dalvik offset: 0x004f @ move-result-object (PI)
+ * v4, (#0), (#0)
+ * D/dalvikvm(   86): 0x4858deac (0048): ldr     r4, [r6, #8]
+ * D/dalvikvm(   86): 0x4858deae (004a): str     r4, [r5, #16]
+ * D/dalvikvm(   86): 0x4858deb0 (004c): b       0x4858debc
+ * D/dalvikvm(   86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c
+ * D/dalvikvm(   86): 0x4858deb2 (004e): ldr     r0, [pc, #64]
+ * D/dalvikvm(   86): 0x4858deb4 (0050): b       0x4858deb8
+ * D/dalvikvm(   86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c
+ * D/dalvikvm(   86): 0x4858deb6 (0052): ldr     r0, [pc, #60]
+ * D/dalvikvm(   86): Exception_Handling:
+ * D/dalvikvm(   86): 0x4858deb8 (0054): ldr     r1, [r6, #100]
+ * D/dalvikvm(   86): 0x4858deba (0056): blx     r1
+ * D/dalvikvm(   86): 0x4858debc (0058): .align4
+ * D/dalvikvm(   86): -------- chaining cell (hot): 0x0050
+ * D/dalvikvm(   86): 0x4858debc (0058): b       0x4858dec0
+ * D/dalvikvm(   86): 0x4858debe (005a): orrs    r0, r0
+ * D/dalvikvm(   86): 0x4858dec0 (005c): ldr     r0, [r6, #112]
+ * D/dalvikvm(   86): 0x4858dec2 (005e): blx     r0
+ * D/dalvikvm(   86): 0x4858dec4 (0060): data    0xefd4(61396)
+ * D/dalvikvm(   86): 0x4858dec6 (0062): data    0x42be(17086)
+ * D/dalvikvm(   86): 0x4858dec8 (0064): .align4
+ * D/dalvikvm(   86): -------- chaining cell (predicted)
+ * D/dalvikvm(   86): 0x4858dec8 (0064): data    0xe7fe(59390)
+ * D/dalvikvm(   86): 0x4858deca (0066): data    0x0000(0)
+ * D/dalvikvm(   86): 0x4858decc (0068): data    0x0000(0)
+ * D/dalvikvm(   86): 0x4858dece (006a): data    0x0000(0)
+ * :
+ */
+static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    RegLocation rlThis = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    rlThis = loadValue(cUnit, rlThis, kCoreReg);
+    int regPredictedClass = dvmCompilerAllocTemp(cUnit);
+    loadClassPointer(cUnit, regPredictedClass, (int) callsiteInfo);
+    genNullCheck(cUnit, rlThis.sRegLow, rlThis.lowReg, mir->offset,
+                 NULL);/* null object? */
+    int regActualClass = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlThis.lowReg, offsetof(Object, clazz), regActualClass);
+    opRegReg(cUnit, kOpCmp, regPredictedClass, regActualClass);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    callsiteInfo->misPredBranchOver = (LIR *) opCondBranch(cUnit, kArmCondNe);
+}
+
+/* Extended MIR instructions like PHI */
+static void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
+{
+    int opOffset = mir->dalvikInsn.opcode - kMirOpFirst;
+    char *msg = (char *)dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
+                                        false);
+    strcpy(msg, extendedMIROpNames[opOffset]);
+    newLIR1(cUnit, kArmPseudoExtended, (int) msg);
+
+    switch ((ExtendedMIROpcode)mir->dalvikInsn.opcode) {
+        case kMirOpPhi: {
+            char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+            newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+            break;
+        }
+        case kMirOpNullNRangeUpCheck: {
+            genHoistedChecksForCountUpLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpNullNRangeDownCheck: {
+            genHoistedChecksForCountDownLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpLowerBound: {
+            genHoistedLowerBoundCheck(cUnit, mir);
+            break;
+        }
+        case kMirOpPunt: {
+            genUnconditionalBranch(cUnit,
+                                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+            break;
+        }
+        case kMirOpCheckInlinePrediction: {
+            genValidationForPredictedInline(cUnit, mir);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*
+ * Create a PC-reconstruction cell for the starting offset of this trace.
+ * Since the PCR cell is placed near the end of the compiled code which is
+ * usually out of range for a conditional branch, we put two branches (one
+ * branch over to the loop body and one layover branch to the actual PCR) at the
+ * end of the entry block.
+ */
+static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry,
+                                ArmLIR *bodyLabel)
+{
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+    pcrLabel->operands[0] =
+        (int) (cUnit->method->insns + entry->startOffset);
+    pcrLabel->operands[1] = entry->startOffset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+
+    /*
+     * Next, create two branches - one branch over to the loop body and the
+     * other branch to the PCR cell to punt.
+     */
+    ArmLIR *branchToBody = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    branchToBody->opcode = kThumbBUncond;
+    branchToBody->generic.target = (LIR *) bodyLabel;
+    setupResourceMasks(branchToBody);
+    cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody;
+
+    ArmLIR *branchToPCR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    branchToPCR->opcode = kThumbBUncond;
+    branchToPCR->generic.target = (LIR *) pcrLabel;
+    setupResourceMasks(branchToPCR);
+    cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static bool selfVerificationPuntOps(MIR *mir)
+{
+    DecodedInstruction *decInsn = &mir->dalvikInsn;
+
+    /*
+     * All opcodes that can throw exceptions and use the
+     * TEMPLATE_THROW_EXCEPTION_COMMON template should be excluded in the trace
+     * under self-verification mode.
+     */
+    switch (decInsn->opcode) {
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_NEW_INSTANCE:
+        case OP_NEW_ARRAY:
+        case OP_CHECK_CAST:
+        case OP_MOVE_EXCEPTION:
+        case OP_FILL_ARRAY_DATA:
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+            return true;
+        default:
+            return false;
+    }
+}
+#endif
+
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit)
+{
+    /* Used to hold the labels of each block */
+    ArmLIR *labelList =
+        (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
+    ArmLIR *headLIR = NULL;
+    GrowableList chainingListByType[kChainingCellGap];
+    int i;
+
+    /*
+     * Initialize various types chaining lists.
+     */
+    for (i = 0; i < kChainingCellGap; i++) {
+        dvmInitGrowableList(&chainingListByType[i], 2);
+    }
+
+    /* Clear the visited flag for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes, false /* isIterative */);
+
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    /* Traces start with a profiling entry point.  Generate it here */
+    cUnit->profileCodeSize = genTraceProfileEntry(cUnit);
+
+    /* Handle the content in each basic block */
+    for (i = 0; ; i++) {
+        MIR *mir;
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->visited == true) continue;
+
+        labelList[i].operands[0] = bb->startOffset;
+
+        if (bb->blockType >= kChainingCellGap) {
+            if (bb->isFallThroughFromInvoke == true) {
+                /* Align this block first since it is a return chaining cell */
+                newLIR0(cUnit, kArmPseudoPseudoAlign4);
+            }
+            /*
+             * Append the label pseudo LIR first. Chaining cells will be handled
+             * separately afterwards.
+             */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            labelList[i].opcode = kArmPseudoEntryBlock;
+            if (bb->firstMIRInsn == NULL) {
+                continue;
+            } else {
+              setupLoopEntryBlock(cUnit, bb,
+                                  &labelList[bb->fallThrough->id]);
+            }
+        } else if (bb->blockType == kExitBlock) {
+            labelList[i].opcode = kArmPseudoExitBlock;
+            goto gen_fallthrough;
+        } else if (bb->blockType == kDalvikByteCode) {
+            if (bb->hidden == true) continue;
+            labelList[i].opcode = kArmPseudoNormalBlockLabel;
+            /* Reset the register state */
+            dvmCompilerResetRegPool(cUnit);
+            dvmCompilerClobberAllRegs(cUnit);
+            dvmCompilerResetNullCheck(cUnit);
+        } else {
+            switch (bb->blockType) {
+                case kChainingCellNormal:
+                    labelList[i].opcode = kArmPseudoChainingCellNormal;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellNormal], i);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellInvokeSingleton;
+                    labelList[i].operands[0] =
+                        (int) bb->containingMethod;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokeSingleton], i);
+                    break;
+                case kChainingCellInvokePredicted:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellInvokePredicted;
+                    /*
+                     * Move the cached method pointer from operand 1 to 0.
+                     * Operand 0 was clobbered earlier in this routine to store
+                     * the block starting offset, which is not applicable to
+                     * predicted chaining cell.
+                     */
+                    labelList[i].operands[0] = labelList[i].operands[1];
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokePredicted], i);
+                    break;
+                case kChainingCellHot:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellHot;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellHot], i);
+                    break;
+                case kPCReconstruction:
+                    /* Make sure exception handling block is next */
+                    labelList[i].opcode =
+                        kArmPseudoPCReconstructionBlockLabel;
+                    handlePCReconstruction(cUnit,
+                                           &labelList[cUnit->puntBlock->id]);
+                    break;
+                case kExceptionHandling:
+                    labelList[i].opcode = kArmPseudoEHBlockLabel;
+                    if (cUnit->pcReconstructionList.numUsed) {
+                        loadWordDisp(cUnit, r6SELF, offsetof(Thread,
+                                     jitToInterpEntries.dvmJitToInterpPunt),
+                                     r1);
+                        opReg(cUnit, kOpBlx, r1);
+                    }
+                    break;
+                case kChainingCellBackwardBranch:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellBackwardBranch;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellBackwardBranch],
+                        i);
+                    break;
+                default:
+                    break;
+            }
+            continue;
+        }
+
+        /*
+         * Try to build a longer optimization unit. Currently if the previous
+         * block ends with a goto, we continue adding instructions and don't
+         * reset the register allocation pool.
+         */
+        for (BasicBlock *nextBB = bb; nextBB != NULL; nextBB = cUnit->nextCodegenBlock) {
+            bb = nextBB;
+            bb->visited = true;
+            cUnit->nextCodegenBlock = NULL;
+
+            for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+
+                dvmCompilerResetRegPool(cUnit);
+                if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+                    dvmCompilerClobberAllRegs(cUnit);
+                }
+
+                if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+                    dvmCompilerResetDefTracking(cUnit);
+                }
+
+                if ((int)mir->dalvikInsn.opcode >= (int)kMirOpFirst) {
+                    handleExtendedMIR(cUnit, mir);
+                    continue;
+                }
+
+                Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+                InstructionFormat dalvikFormat =
+                    dexGetFormatFromOpcode(dalvikOpcode);
+                const char *note;
+                if (mir->OptimizationFlags & MIR_INLINED) {
+                    note = " (I)";
+                } else if (mir->OptimizationFlags & MIR_INLINED_PRED) {
+                    note = " (PI)";
+                } else if (mir->OptimizationFlags & MIR_CALLEE) {
+                    note = " (C)";
+                } else {
+                    note = NULL;
+                }
+
+                ArmLIR *boundaryLIR;
+
+                /*
+                 * Don't generate the boundary LIR unless we are debugging this
+                 * trace or we need a scheduling barrier.
+                 */
+                if (headLIR == NULL || cUnit->printMe == true) {
+                    boundaryLIR =
+                        newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary,
+                                mir->offset,
+                                (int) dvmCompilerGetDalvikDisassembly(
+                                    &mir->dalvikInsn, note));
+                    /* Remember the first LIR for this block */
+                    if (headLIR == NULL) {
+                        headLIR = boundaryLIR;
+                        /* Set the first boundaryLIR as a scheduling barrier */
+                        headLIR->defMask = ENCODE_ALL;
+                    }
+                }
+
+                /*
+                 * Don't generate the SSA annotation unless verbose mode is on
+                 */
+                if (cUnit->printMe && mir->ssaRep) {
+                    char *ssaString = dvmCompilerGetSSAString(cUnit,
+                                                              mir->ssaRep);
+                    newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+                }
+
+                bool notHandled;
+                /*
+                 * Debugging: screen the opcode first to see if it is in the
+                 * do[-not]-compile list
+                 */
+                bool singleStepMe = SINGLE_STEP_OP(dalvikOpcode);
+#if defined(WITH_SELF_VERIFICATION)
+              if (singleStepMe == false) {
+                  singleStepMe = selfVerificationPuntOps(mir);
+              }
+#endif
+                if (singleStepMe || cUnit->allSingleStep) {
+                    notHandled = false;
+                    genInterpSingleStep(cUnit, mir);
+                } else {
+                    opcodeCoverage[dalvikOpcode]++;
+                    switch (dalvikFormat) {
+                        case kFmt10t:
+                        case kFmt20t:
+                        case kFmt30t:
+                            notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit,
+                                      mir, bb, labelList);
+                            break;
+                        case kFmt10x:
+                            notHandled = handleFmt10x(cUnit, mir);
+                            break;
+                        case kFmt11n:
+                        case kFmt31i:
+                            notHandled = handleFmt11n_Fmt31i(cUnit, mir);
+                            break;
+                        case kFmt11x:
+                            notHandled = handleFmt11x(cUnit, mir);
+                            break;
+                        case kFmt12x:
+                            notHandled = handleFmt12x(cUnit, mir);
+                            break;
+                        case kFmt20bc:
+                            notHandled = handleFmt20bc(cUnit, mir);
+                            break;
+                        case kFmt21c:
+                        case kFmt31c:
+                            notHandled = handleFmt21c_Fmt31c(cUnit, mir);
+                            break;
+                        case kFmt21h:
+                            notHandled = handleFmt21h(cUnit, mir);
+                            break;
+                        case kFmt21s:
+                            notHandled = handleFmt21s(cUnit, mir);
+                            break;
+                        case kFmt21t:
+                            notHandled = handleFmt21t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22b:
+                        case kFmt22s:
+                            notHandled = handleFmt22b_Fmt22s(cUnit, mir);
+                            break;
+                        case kFmt22c:
+                            notHandled = handleFmt22c(cUnit, mir);
+                            break;
+                        case kFmt22cs:
+                            notHandled = handleFmt22cs(cUnit, mir);
+                            break;
+                        case kFmt22t:
+                            notHandled = handleFmt22t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22x:
+                        case kFmt32x:
+                            notHandled = handleFmt22x_Fmt32x(cUnit, mir);
+                            break;
+                        case kFmt23x:
+                            notHandled = handleFmt23x(cUnit, mir);
+                            break;
+                        case kFmt31t:
+                            notHandled = handleFmt31t(cUnit, mir);
+                            break;
+                        case kFmt3rc:
+                        case kFmt35c:
+                            notHandled = handleFmt35c_3rc(cUnit, mir, bb,
+                                                          labelList);
+                            break;
+                        case kFmt3rms:
+                        case kFmt35ms:
+                            notHandled = handleFmt35ms_3rms(cUnit, mir, bb,
+                                                            labelList);
+                            break;
+                        case kFmt35mi:
+                        case kFmt3rmi:
+                            notHandled = handleExecuteInline(cUnit, mir);
+                            break;
+                        case kFmt51l:
+                            notHandled = handleFmt51l(cUnit, mir);
+                            break;
+                        default:
+                            notHandled = true;
+                            break;
+                    }
+                }
+                if (notHandled) {
+                    ALOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled",
+                         mir->offset,
+                         dalvikOpcode, dexGetOpcodeName(dalvikOpcode),
+                         dalvikFormat);
+                    dvmCompilerAbort(cUnit);
+                    break;
+                }
+            }
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToBody);
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToPCR);
+        }
+
+        if (headLIR) {
+            /*
+             * Eliminate redundant loads/stores and delay stores into later
+             * slots
+             */
+            dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                               cUnit->lastLIRInsn);
+            /* Reset headLIR which is also the optimization boundary */
+            headLIR = NULL;
+        }
+
+gen_fallthrough:
+        /*
+         * Check if the block is terminated due to trace length constraint -
+         * insert an unconditional branch to the chaining cell.
+         */
+        if (bb->needFallThroughBranch) {
+            genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+        }
+    }
+
+    /* Handle the chaining cells in predefined order */
+    for (i = 0; i < kChainingCellGap; i++) {
+        size_t j;
+        int *blockIdList = (int *) chainingListByType[i].elemList;
+
+        cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+        /* No chaining cells of this type */
+        if (cUnit->numChainingCells[i] == 0)
+            continue;
+
+        /* Record the first LIR for a new type of chaining cell */
+        cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+
+        for (j = 0; j < chainingListByType[i].numUsed; j++) {
+            int blockId = blockIdList[j];
+            BasicBlock *chainingBlock =
+                (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                         blockId);
+
+            /* Align this chaining cell first */
+            newLIR0(cUnit, kArmPseudoPseudoAlign4);
+
+            /* Insert the pseudo chaining instruction */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+            switch (chainingBlock->blockType) {
+                case kChainingCellNormal:
+                    handleNormalChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    handleInvokeSingletonChainingCell(cUnit,
+                        chainingBlock->containingMethod);
+                    break;
+                case kChainingCellInvokePredicted:
+                    handleInvokePredictedChainingCell(cUnit);
+                    break;
+                case kChainingCellHot:
+                    handleHotChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellBackwardBranch:
+                    handleBackwardBranchChainingCell(cUnit,
+                        chainingBlock->startOffset);
+                    break;
+                default:
+                    ALOGE("Bad blocktype %d", chainingBlock->blockType);
+                    dvmCompilerAbort(cUnit);
+            }
+        }
+    }
+
+    /* Mark the bottom of chaining cells */
+    cUnit->chainingCellBottom = (LIR *) newLIR0(cUnit, kArmChainingCellBottom);
+
+    /*
+     * Generate the branch to the dvmJitToInterpNoChain entry point at the end
+     * of all chaining cells for the overflow cases.
+     */
+    if (cUnit->switchOverflowPad) {
+        loadConstant(cUnit, r0, (int) cUnit->switchOverflowPad);
+        loadWordDisp(cUnit, r6SELF, offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpNoChain), r2);
+        opRegReg(cUnit, kOpAdd, r1, r1);
+        opRegRegReg(cUnit, kOpAdd, r4PC, r0, r1);
+#if defined(WITH_JIT_TUNING)
+        loadConstant(cUnit, r0, kSwitchOverflow);
+#endif
+        opReg(cUnit, kOpBlx, r2);
+    }
+
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+
+#if defined(WITH_SELF_VERIFICATION)
+    selfVerificationBranchInsertPass(cUnit);
+#endif
+}
+
+/*
+ * Accept the work and start compiling.  Returns true if compilation
+ * is attempted.
+ */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+    JitTraceDescription *desc;
+    bool isCompile;
+    bool success = true;
+
+    if (gDvmJit.codeCacheFull) {
+        return false;
+    }
+
+    switch (work->kind) {
+        case kWorkOrderTrace:
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            break;
+        case kWorkOrderTraceDebug: {
+            bool oldPrintMe = gDvmJit.printMe;
+            gDvmJit.printMe = true;
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            gDvmJit.printMe = oldPrintMe;
+            break;
+        }
+        case kWorkOrderProfileMode:
+            dvmJitChangeProfileMode((TraceProfilingModes)(int)work->info);
+            isCompile = false;
+            break;
+        default:
+            isCompile = false;
+            ALOGE("Jit: unknown work order type");
+            assert(0);  // Bail if debug build, discard otherwise
+    }
+    if (!success)
+        work->result.codeAddress = NULL;
+    return isCompile;
+}
+
+/* Architectural-specific debugging helpers go here */
+void dvmCompilerArchDump(void)
+{
+    /* Print compiled opcode in this VM instance */
+    int i, start, streak;
+    char buf[1024];
+
+    streak = i = 0;
+    buf[0] = 0;
+    while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
+        i++;
+    }
+    if (i == kNumPackedOpcodes) {
+        return;
+    }
+    for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
+        if (opcodeCoverage[i]) {
+            streak++;
+        } else {
+            if (streak == 1) {
+                sprintf(buf+strlen(buf), "%x,", start);
+            } else {
+                sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
+            }
+            streak = 0;
+            while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
+                i++;
+            }
+            if (i < kNumPackedOpcodes) {
+                streak = 1;
+                start = i;
+            }
+        }
+    }
+    if (streak) {
+        if (streak == 1) {
+            sprintf(buf+strlen(buf), "%x", start);
+        } else {
+            sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
+        }
+    }
+    if (strlen(buf)) {
+        ALOGD("dalvik.vm.jit.op = %s", buf);
+    }
+}
+
+/* Common initialization routine for an architecture family */
+bool dvmCompilerArchInit()
+{
+    int i;
+
+    for (i = 0; i < kArmLast; i++) {
+        if (EncodingMap[i].opcode != i) {
+            ALOGE("Encoding order for %s is wrong: expecting %d, seeing %d",
+                 EncodingMap[i].name, i, EncodingMap[i].opcode);
+            dvmAbort();  // OK to dvmAbort - build error
+        }
+    }
+
+    return dvmCompilerArchVariantInit();
+}
+
+void *dvmCompilerGetInterpretTemplate()
+{
+      return (void*) ((int)gDvmJit.codeCache +
+                      templateEntryOffsets[TEMPLATE_INTERPRET]);
+}
+
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet()
+{
+    return DALVIK_JIT_ARM;
+}
+
+/* Needed by the Assembler */
+void dvmCompilerSetupResourceMasks(ArmLIR *lir)
+{
+    setupResourceMasks(lir);
+}
+
+/* Needed by the ld/st optmizatons */
+ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopyNoInsert(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopy(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                            int srcLo, int srcHi)
+{
+    genRegCopyWide(cUnit, destLo, destHi, srcLo, srcHi);
+}
+
+void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    storeBaseDisp(cUnit, rBase, displacement, rSrc, size);
+}
+
+void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    storeBaseDispWide(cUnit, rBase, displacement, rSrcLo, rSrcHi);
+}
diff --git a/vm/compiler/codegen/arm/FP/Thumb2VFP.cpp b/vm/compiler/codegen/arm/FP/Thumb2VFP.cpp
new file mode 100644
index 0000000..abbf2c9
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/Thumb2VFP.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+    int op = kThumbBkpt;
+    RegLocation rlResult;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            op = kThumb2Vadds;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            op = kThumb2Vsubs;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            op = kThumb2Vdivs;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            op = kThumb2Vmuls;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1,
+                                              rlSrc2);
+        }
+        default:
+            return true;
+    }
+    rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR3(cUnit, (ArmOpcode)op, rlResult.lowReg, rlSrc1.lowReg,
+            rlSrc2.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+    int op = kThumbBkpt;
+    RegLocation rlResult;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            op = kThumb2Vaddd;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            op = kThumb2Vsubd;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            op = kThumb2Vdivd;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            op = kThumb2Vmuld;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+                                               rlSrc2);
+        }
+        default:
+            return true;
+    }
+
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+    assert(rlSrc1.wide);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+    assert(rlSrc2.wide);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    assert(rlDest.wide);
+    assert(rlResult.wide);
+    newLIR3(cUnit, (ArmOpcode)op, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc1.lowReg, rlSrc1.highReg),
+            S2D(rlSrc2.lowReg, rlSrc2.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool 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, (ArmOpcode)op, S2D(rlResult.lowReg, rlResult.highReg),
+                srcReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+        newLIR2(cUnit, (ArmOpcode)op, rlResult.lowReg, srcReg);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+    return false;
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    ArmLIR *branch;
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
+    rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vsqrtd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc.lowReg, rlSrc.highReg));
+    newLIR2(cUnit, kThumb2Vcmpd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlResult.lowReg, rlResult.highReg));
+    newLIR0(cUnit, kThumb2Fmstat);
+    branch = newLIR2(cUnit, kThumbBCond, 0, kArmCondEq);
+    dvmCompilerClobberCallRegs(cUnit);
+    LOAD_FUNC_ADDR(cUnit, r2, (int) (double (*)(double)) sqrt);
+    newLIR3(cUnit, kThumb2Fmrrd, r0, r1, S2D(rlSrc.lowReg, rlSrc.highReg));
+    newLIR1(cUnit, kThumbBlxR, r2);
+    newLIR3(cUnit, kThumb2Fmdrr, S2D(rlResult.lowReg, rlResult.highReg),
+            r0, r1);
+    ArmLIR *label = newLIR0(cUnit, kArmPseudoTargetLabel);
+    label->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *)label;
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    bool isDouble;
+    int defaultResult;
+    RegLocation rlResult;
+
+    switch(mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            isDouble = false;
+            defaultResult = -1;
+            break;
+        case OP_CMPG_FLOAT:
+            isDouble = false;
+            defaultResult = 1;
+            break;
+        case OP_CMPL_DOUBLE:
+            isDouble = true;
+            defaultResult = -1;
+            break;
+        case OP_CMPG_DOUBLE:
+            isDouble = true;
+            defaultResult = 1;
+            break;
+        default:
+            return true;
+    }
+    if (isDouble) {
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+        dvmCompilerClobberSReg(cUnit, rlDest.sRegLow);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstant(cUnit, rlResult.lowReg, defaultResult);
+        newLIR2(cUnit, kThumb2Vcmpd, S2D(rlSrc1.lowReg, r1Src2.highReg),
+                S2D(rlSrc2.lowReg, rlSrc2.highReg));
+    } else {
+        rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+        rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+        dvmCompilerClobberSReg(cUnit, rlDest.sRegLow);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstant(cUnit, rlResult.lowReg, defaultResult);
+        newLIR2(cUnit, kThumb2Vcmps, rlSrc1.lowReg, rlSrc2.lowReg);
+    }
+    assert(!FPREG(rlResult.lowReg));
+    newLIR0(cUnit, kThumb2Fmstat);
+
+    genIT(cUnit, (defaultResult == -1) ? kArmCondGt : kArmCondMi, "");
+    newLIR2(cUnit, kThumb2MovImmShift, rlResult.lowReg,
+            modifiedImmediate(-defaultResult)); // Must not alter ccodes
+    genBarrier(cUnit);
+
+    genIT(cUnit, kArmCondEq, "");
+    loadConstant(cUnit, rlResult.lowReg, 0);
+    genBarrier(cUnit);
+
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/FP/ThumbPortableFP.cpp b/vm/compiler/codegen/arm/FP/ThumbPortableFP.cpp
new file mode 100644
index 0000000..7aac8e6
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbPortableFP.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Forward-declare the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+static bool handleExecuteInlineC(CompilationUnit *cUnit, MIR *mir);
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    return genConversionPortable(cUnit, mir);
+}
+
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+    return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+    return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleExecuteInlineC(cUnit, mir);
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult = LOC_C_RETURN;
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            loadValueDirectFixed(cUnit, rlSrc1, r0);
+            loadValueDirectFixed(cUnit, rlSrc2, r1);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPL_FLOAT);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_CMPG_FLOAT:
+            loadValueDirectFixed(cUnit, rlSrc1, r0);
+            loadValueDirectFixed(cUnit, rlSrc2, r1);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPG_FLOAT);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_CMPL_DOUBLE:
+            loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+            loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPL_DOUBLE);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_CMPG_DOUBLE:
+            loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+            loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPG_DOUBLE);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/FP/ThumbVFP.cpp b/vm/compiler/codegen/arm/FP/ThumbVFP.cpp
new file mode 100644
index 0000000..f685f24
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbVFP.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Take the address of a Dalvik register and store it into rDest.
+ * Clobber any live values associated either with the Dalvik value
+ * or the target register and lock the target fixed register.
+ */
+static void loadValueAddressDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+                                   int rDest)
+{
+     rlSrc = rlSrc.wide ? dvmCompilerUpdateLocWide(cUnit, rlSrc) :
+                          dvmCompilerUpdateLoc(cUnit, rlSrc);
+     if (rlSrc.location == kLocPhysReg) {
+         if (rlSrc.wide) {
+             dvmCompilerFlushRegWide(cUnit, rlSrc.lowReg, rlSrc.highReg);
+         } else {
+             dvmCompilerFlushReg(cUnit, rlSrc.lowReg);
+         }
+     }
+     dvmCompilerClobber(cUnit, rDest);
+     dvmCompilerLockTemp(cUnit, rDest);
+     opRegRegImm(cUnit, kOpAdd, rDest, r5FP,
+                 dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlResult = LOC_C_RETURN_WIDE;
+    RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+    loadValueAddressDirect(cUnit, rlSrc, r2);
+    genDispatchToHandler(cUnit, TEMPLATE_SQRT_DOUBLE_VFP);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * TUNING: On some implementations, it is quicker to pass addresses
+ * to the handlers rather than load the operands into core registers
+ * and then move the values to FP regs in the handlers.  Other implementations
+ * may prefer passing data in registers (and the latter approach would
+ * yield cleaner register handling - avoiding the requirement that operands
+ * be flushed to memory prior to the call).
+ */
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+    TemplateOpcode opcode;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            opcode = TEMPLATE_ADD_FLOAT_VFP;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            opcode = TEMPLATE_SUB_FLOAT_VFP;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            opcode = TEMPLATE_DIV_FLOAT_VFP;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            opcode = TEMPLATE_MUL_FLOAT_VFP;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddressDirect(cUnit, rlDest, r0);
+    loadValueAddressDirect(cUnit, rlSrc1, r1);
+    loadValueAddressDirect(cUnit, rlSrc2, r2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    }
+    return false;
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+    TemplateOpcode opcode;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            opcode = TEMPLATE_ADD_DOUBLE_VFP;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            opcode = TEMPLATE_SUB_DOUBLE_VFP;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            opcode = TEMPLATE_DIV_DOUBLE_VFP;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            opcode = TEMPLATE_MUL_DOUBLE_VFP;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+                                               rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddressDirect(cUnit, rlDest, r0);
+    loadValueAddressDirect(cUnit, rlSrc1, r1);
+    loadValueAddressDirect(cUnit, rlSrc2, r2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    }
+    return false;
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    bool longSrc = false;
+    bool longDest = false;
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    TemplateOpcode templateOpcode;
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_INT_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_INT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_FLOAT_TO_INT_VFP;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_FLOAT_TO_DOUBLE_VFP;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_INT_TO_DOUBLE_VFP;
+            break;
+        case OP_DOUBLE_TO_INT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_INT_VFP;
+            break;
+        case OP_LONG_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+            return genConversionPortable(cUnit, mir);
+        default:
+            return true;
+    }
+
+    if (longSrc) {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    } else {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    }
+
+    if (longDest) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    loadValueAddressDirect(cUnit, rlDest, r0);
+    loadValueAddressDirect(cUnit, rlSrc, r1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    if (rlDest.wide) {
+        rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    }
+    dvmCompilerClobber(cUnit, rlDest.lowReg);
+    return false;
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    TemplateOpcode templateOpcode;
+    RegLocation rlResult = dvmCompilerGetReturn(cUnit);
+    bool wide = true;
+
+    switch(mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            templateOpcode = TEMPLATE_CMPL_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPG_FLOAT:
+            templateOpcode = TEMPLATE_CMPG_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPL_DOUBLE:
+            templateOpcode = TEMPLATE_CMPL_DOUBLE_VFP;
+            break;
+        case OP_CMPG_DOUBLE:
+            templateOpcode = TEMPLATE_CMPG_DOUBLE_VFP;
+            break;
+        default:
+            return true;
+    }
+    loadValueAddressDirect(cUnit, rlSrc1, r0);
+    loadValueAddressDirect(cUnit, rlSrc2, r1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/GlobalOptimizations.cpp b/vm/compiler/codegen/arm/GlobalOptimizations.cpp
new file mode 100644
index 0000000..e52bd8a
--- /dev/null
+++ b/vm/compiler/codegen/arm/GlobalOptimizations.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "ArmLIR.h"
+
+/*
+ * Identify unconditional branches that jump to the immediate successor of the
+ * branch itself.
+ */
+static void applyRedundantBranchElimination(CompilationUnit *cUnit)
+{
+    ArmLIR *thisLIR;
+
+    for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn;
+         thisLIR != (ArmLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Branch to the next instruction */
+        if (thisLIR->opcode == kThumbBUncond) {
+            ArmLIR *nextLIR = thisLIR;
+
+            while (true) {
+                nextLIR = NEXT_LIR(nextLIR);
+
+                /*
+                 * Is the branch target the next instruction?
+                 */
+                if (nextLIR == (ArmLIR *) thisLIR->generic.target) {
+                    thisLIR->flags.isNop = true;
+                    break;
+                }
+
+                /*
+                 * Found real useful stuff between the branch and the target.
+                 * Need to explicitly check the lastLIRInsn here since with
+                 * method-based JIT the branch might be the last real
+                 * instruction.
+                 */
+                if (!isPseudoOpcode(nextLIR->opcode) ||
+                    (nextLIR = (ArmLIR *) cUnit->lastLIRInsn))
+                    break;
+            }
+        }
+    }
+}
+
+void dvmCompilerApplyGlobalOptimizations(CompilationUnit *cUnit)
+{
+    applyRedundantBranchElimination(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/LocalOptimizations.cpp b/vm/compiler/codegen/arm/LocalOptimizations.cpp
new file mode 100644
index 0000000..8013d00
--- /dev/null
+++ b/vm/compiler/codegen/arm/LocalOptimizations.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+
+#define DEBUG_OPT(X)
+
+/* Check RAW, WAR, and WAR dependency on the register operands */
+#define CHECK_REG_DEP(use, def, check) ((def & check->useMask) || \
+                                        ((use | def) & check->defMask))
+
+/* Scheduler heuristics */
+#define MAX_HOIST_DISTANCE 20
+#define LDLD_DISTANCE 4
+#define LD_LATENCY 2
+
+static inline bool isDalvikRegisterClobbered(ArmLIR *lir1, ArmLIR *lir2)
+{
+    int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->aliasInfo);
+    int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->aliasInfo);
+    int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->aliasInfo);
+    int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->aliasInfo);
+
+    return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
+}
+
+#if 0
+/* Debugging utility routine */
+static void dumpDependentInsnPair(ArmLIR *thisLIR, ArmLIR *checkLIR,
+                                  const char *optimization)
+{
+    ALOGD("************ %s ************", optimization);
+    dvmDumpLIRInsn((LIR *) thisLIR, 0);
+    dvmDumpLIRInsn((LIR *) checkLIR, 0);
+}
+#endif
+
+/* Convert a more expensive instruction (ie load) into a move */
+static void convertMemOpIntoMove(CompilationUnit *cUnit, ArmLIR *origLIR,
+                                 int dest, int src)
+{
+    /* Insert a move to replace the load */
+    ArmLIR *moveLIR;
+    moveLIR = dvmCompilerRegCopyNoInsert( cUnit, dest, src);
+    /*
+     * Insert the converted instruction after the original since the
+     * optimization is scannng in the top-down order and the new instruction
+     * will need to be re-checked (eg the new dest clobbers the src used in
+     * thisLIR).
+     */
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) moveLIR);
+}
+
+/*
+ * Perform a pass of top-down walk, from the second-last instruction in the
+ * superblock, to eliminate redundant loads and stores.
+ *
+ * An earlier load can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * An earlier store can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * A later store can be eliminated by an earlier store iff
+ *   1) They are must-aliases
+ *   2) The memory location is not written to in between
+ */
+static void applyLoadStoreElimination(CompilationUnit *cUnit,
+                                      ArmLIR *headLIR,
+                                      ArmLIR *tailLIR)
+{
+    ArmLIR *thisLIR;
+
+    if (headLIR == tailLIR) return;
+
+    for (thisLIR = PREV_LIR(tailLIR);
+         thisLIR != headLIR;
+         thisLIR = PREV_LIR(thisLIR)) {
+        int sinkDistance = 0;
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpcode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & (IS_LOAD | IS_STORE))) {
+            continue;
+        }
+
+        int nativeRegId = thisLIR->operands[0];
+        bool isThisLIRLoad = EncodingMap[thisLIR->opcode].flags & IS_LOAD;
+        ArmLIR *checkLIR;
+        /* Use the mem mask to determine the rough memory location */
+        u8 thisMemMask = (thisLIR->useMask | thisLIR->defMask) & ENCODE_MEM;
+
+        /*
+         * Currently only eliminate redundant ld/st for constant and Dalvik
+         * register accesses.
+         */
+        if (!(thisMemMask & (ENCODE_LITERAL | ENCODE_DALVIK_REG))) continue;
+
+        /*
+         * Add r15 (pc) to the resource mask to prevent this instruction
+         * from sinking past branch instructions. Also take out the memory
+         * region bits since stopMask is used to check data/control
+         * dependencies.
+         */
+        u8 stopUseRegMask = (ENCODE_REG_PC | thisLIR->useMask) &
+                            ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        for (checkLIR = NEXT_LIR(thisLIR);
+             checkLIR != tailLIR;
+             checkLIR = NEXT_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = (checkLIR->useMask | checkLIR->defMask) &
+                              ENCODE_MEM;
+            u8 aliasCondition = thisMemMask & checkMemMask;
+            bool stopHere = false;
+
+            /*
+             * Potential aliases seen - check the alias relations
+             */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                bool isCheckLIRLoad = EncodingMap[checkLIR->opcode].flags &
+                                      IS_LOAD;
+                if  (aliasCondition == ENCODE_LITERAL) {
+                    /*
+                     * Should only see literal loads in the instruction
+                     * stream.
+                     */
+                    assert(!(EncodingMap[checkLIR->opcode].flags &
+                             IS_STORE));
+                    /* Same value && same register type */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo &&
+                        REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId)){
+                        /*
+                         * Different destination register - insert
+                         * a move
+                         */
+                        if (checkLIR->operands[0] != nativeRegId) {
+                            convertMemOpIntoMove(cUnit, checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                        }
+                        checkLIR->flags.isNop = true;
+                    }
+                } else if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo) {
+                        /* Only optimize compatible registers */
+                        bool regCompatible =
+                            REGTYPE(checkLIR->operands[0]) ==
+                            REGTYPE(nativeRegId);
+                        if ((isThisLIRLoad && isCheckLIRLoad) ||
+                            (!isThisLIRLoad && isCheckLIRLoad)) {
+                            /* RAR or RAW */
+                            if (regCompatible) {
+                                /*
+                                 * Different destination register -
+                                 * insert a move
+                                 */
+                                if (checkLIR->operands[0] !=
+                                    nativeRegId) {
+                                    convertMemOpIntoMove(cUnit,
+                                                 checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                                }
+                                checkLIR->flags.isNop = true;
+                            } else {
+                                /*
+                                 * Destinaions are of different types -
+                                 * something complicated going on so
+                                 * stop looking now.
+                                 */
+                                stopHere = true;
+                            }
+                        } else if (isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAR - register value is killed */
+                            stopHere = true;
+                        } else if (!isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAW - nuke the earlier store */
+                            thisLIR->flags.isNop = true;
+                            stopHere = true;
+                        }
+                    /* Partial overlap */
+                    } else if (isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        /*
+                         * It is actually ok to continue if checkLIR
+                         * is a read. But it is hard to make a test
+                         * case for this so we just stop here to be
+                         * conservative.
+                         */
+                        stopHere = true;
+                    }
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    break;
+                /* The checkLIR has been transformed - check the next one */
+                } else if (checkLIR->flags.isNop) {
+                    continue;
+                }
+            }
+
+
+            /*
+             * this and check LIRs have no memory dependency. Now check if
+             * their register operands have any RAW, WAR, and WAW
+             * dependencies. If so, stop looking.
+             */
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+                                                "REG CLOBBERED"));
+                /* Only sink store instructions */
+                if (sinkDistance && !isThisLIRLoad) {
+                    ArmLIR *newStoreLIR =
+                        (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+                    *newStoreLIR = *thisLIR;
+                    /*
+                     * Stop point found - insert *before* the checkLIR
+                     * since the instruction list is scanned in the
+                     * top-down order.
+                     */
+                    dvmCompilerInsertLIRBefore((LIR *) checkLIR,
+                                               (LIR *) newStoreLIR);
+                    thisLIR->flags.isNop = true;
+                }
+                break;
+            } else if (!checkLIR->flags.isNop) {
+                sinkDistance++;
+            }
+        }
+    }
+}
+
+/*
+ * Perform a pass of bottom-up walk, from the second instruction in the
+ * superblock, to try to hoist loads to earlier slots.
+ */
+static void applyLoadHoisting(CompilationUnit *cUnit,
+                              ArmLIR *headLIR,
+                              ArmLIR *tailLIR)
+{
+    ArmLIR *thisLIR, *checkLIR;
+    /*
+     * Store the list of independent instructions that can be hoisted past.
+     * Will decide the best place to insert later.
+     */
+    ArmLIR *prevInstList[MAX_HOIST_DISTANCE];
+
+    /* Empty block */
+    if (headLIR == tailLIR) return;
+
+    /* Start from the second instruction */
+    for (thisLIR = NEXT_LIR(headLIR);
+         thisLIR != tailLIR;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpcode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & IS_LOAD)) {
+            continue;
+        }
+
+        u8 stopUseAllMask = thisLIR->useMask;
+
+        /*
+         * Branches for null/range checks are marked with the true resource
+         * bits, and loads to Dalvik registers, constant pools, and non-alias
+         * locations are safe to be hoisted. So only mark the heap references
+         * conservatively here.
+         */
+        if (stopUseAllMask & ENCODE_HEAP_REF) {
+            stopUseAllMask |= ENCODE_REG_PC;
+        }
+
+        /* Similar as above, but just check for pure register dependency */
+        u8 stopUseRegMask = stopUseAllMask & ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        int nextSlot = 0;
+        bool stopHere = false;
+
+        /* Try to hoist the load to a good spot */
+        for (checkLIR = PREV_LIR(thisLIR);
+             checkLIR != headLIR;
+             checkLIR = PREV_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = checkLIR->defMask & ENCODE_MEM;
+            u8 aliasCondition = stopUseAllMask & checkMemMask;
+            stopHere = false;
+
+            /* Potential WAR alias seen - check the exact relation */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                /* We can fully disambiguate Dalvik references */
+                if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias or partually overlap */
+                    if ((checkLIR->aliasInfo == thisLIR->aliasInfo) ||
+                        isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        stopHere = true;
+                    }
+                /* Conservatively treat all heap refs as may-alias */
+                } else {
+                    assert(aliasCondition == ENCODE_HEAP_REF);
+                    stopHere = true;
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    prevInstList[nextSlot++] = checkLIR;
+                    break;
+                }
+            }
+
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            /*
+             * Store the dependent or non-pseudo/indepedent instruction to the
+             * list.
+             */
+            if (stopHere || !isPseudoOpcode(checkLIR->opcode)) {
+                prevInstList[nextSlot++] = checkLIR;
+                if (nextSlot == MAX_HOIST_DISTANCE) break;
+            }
+
+            /* Found a new place to put the load - move it here */
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(checkLIR, thisLIR
+                                                "HOIST STOP"));
+                break;
+            }
+        }
+
+        /*
+         * Reached the top - use headLIR as the dependent marker as all labels
+         * are barriers.
+         */
+        if (stopHere == false && nextSlot < MAX_HOIST_DISTANCE) {
+            prevInstList[nextSlot++] = headLIR;
+        }
+
+        /*
+         * At least one independent instruction is found. Scan in the reversed
+         * direction to find a beneficial slot.
+         */
+        if (nextSlot >= 2) {
+            int firstSlot = nextSlot - 2;
+            int slot;
+            ArmLIR *depLIR = prevInstList[nextSlot-1];
+            /* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */
+            if (!isPseudoOpcode(depLIR->opcode) &&
+                (EncodingMap[depLIR->opcode].flags & IS_LOAD)) {
+                firstSlot -= LDLD_DISTANCE;
+            }
+            /*
+             * Make sure we check slot >= 0 since firstSlot may be negative
+             * when the loop is first entered.
+             */
+            for (slot = firstSlot; slot >= 0; slot--) {
+                ArmLIR *curLIR = prevInstList[slot];
+                ArmLIR *prevLIR = prevInstList[slot+1];
+
+                /*
+                 * Check the highest instruction.
+                 * ENCODE_ALL represents a scheduling barrier.
+                 */
+                if (prevLIR->defMask == ENCODE_ALL) {
+                    /*
+                     * If the first instruction is a load, don't hoist anything
+                     * above it since it is unlikely to be beneficial.
+                     */
+                    if (EncodingMap[curLIR->opcode].flags & IS_LOAD) continue;
+                    /*
+                     * Need to unconditionally break here even if the hoisted
+                     * distance is greater than LD_LATENCY (ie more than enough
+                     * cycles are inserted to hide the load latency) since theu
+                     * subsequent code doesn't expect to compare against a
+                     * pseudo opcode (whose opcode value is negative).
+                     */
+                    break;
+                }
+
+                /*
+                 * NOTE: now prevLIR is guaranteed to be a non-pseudo
+                 * instruction (ie accessing EncodingMap[prevLIR->opcode] is
+                 * safe).
+                 *
+                 * Try to find two instructions with load/use dependency until
+                 * the remaining instructions are less than LD_LATENCY.
+                 */
+                if (((curLIR->useMask & prevLIR->defMask) &&
+                     (EncodingMap[prevLIR->opcode].flags & IS_LOAD)) ||
+                    (slot < LD_LATENCY)) {
+                    break;
+                }
+            }
+
+            /* Found a slot to hoist to */
+            if (slot >= 0) {
+                ArmLIR *curLIR = prevInstList[slot];
+                ArmLIR *newLoadLIR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR),
+                                                               true);
+                *newLoadLIR = *thisLIR;
+                /*
+                 * Insertion is guaranteed to succeed since checkLIR
+                 * is never the first LIR on the list
+                 */
+                dvmCompilerInsertLIRBefore((LIR *) curLIR,
+                                           (LIR *) newLoadLIR);
+                thisLIR->flags.isNop = true;
+            }
+        }
+    }
+}
+
+void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR,
+                                        LIR *tailLIR)
+{
+    if (!(gDvmJit.disableOpt & (1 << kLoadStoreElimination))) {
+        applyLoadStoreElimination(cUnit, (ArmLIR *) headLIR,
+                                  (ArmLIR *) tailLIR);
+    }
+    if (!(gDvmJit.disableOpt & (1 << kLoadHoisting))) {
+        applyLoadHoisting(cUnit, (ArmLIR *) headLIR, (ArmLIR *) tailLIR);
+    }
+}
diff --git a/vm/compiler/codegen/arm/README.txt b/vm/compiler/codegen/arm/README.txt
new file mode 100644
index 0000000..1bb4603
--- /dev/null
+++ b/vm/compiler/codegen/arm/README.txt
@@ -0,0 +1,48 @@
+The codegen file for the ARM-based JIT is composed by files broken by
+functionality hierarchies. The goal is to separate architectural dependent
+and independent components to facilitate maintenance and future extension.
+
+For example, the codegen file for armv7-a is assembled by the following
+components:
+
+--
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.cpp"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.cpp"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.cpp"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.cpp"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
+
+--
+
+For the Thumb/Thumb2 directories, each contain the followin three files:
+
+- Factory.c (low-level routines for instruction selections)
+- Gen.c     (invoke the ISA-specific instruction selection routines)
+- Ralloc.c  (arch-dependent register pools)
+
+The FP directory contains FP-specific codegen routines depending on
+Thumb/Thumb2/VFP/PortableFP:
+
+- Thumb2VFP.c
+- ThumbVFP.c
+- ThumbPortableFP.c
+
+In this way the dependency between generic and specific code tied to
+particular architectures can be explicitly represented.
diff --git a/vm/compiler/codegen/arm/Thumb/Factory.cpp b/vm/compiler/codegen/arm/Thumb/Factory.cpp
new file mode 100644
index 0000000..1b65a03
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Factory.cpp
@@ -0,0 +1,944 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7};
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg,
+                      int highReg);
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
+static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+                            int rDest);
+static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc);
+static ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
+                              ArmConditionCode cond,
+                              int reg1, int reg2, int dOffset,
+                              ArmLIR *pcrLabel);
+
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.  If target is
+ * a high register, build constant into a low register and copy.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static ArmLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+                                     int value)
+{
+    ArmLIR *res;
+    int tDest = LOWREG(rDest) ? rDest : dvmCompilerAllocTemp(cUnit);
+    /* See if the value can be constructed cheaply */
+    if ((value >= 0) && (value <= 255)) {
+        res = newLIR2(cUnit, kThumbMovImm, tDest, value);
+        if (rDest != tDest) {
+           opRegReg(cUnit, kOpMov, rDest, tDest);
+           dvmCompilerFreeTemp(cUnit, tDest);
+        }
+        return res;
+    } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
+        res = newLIR2(cUnit, kThumbMovImm, tDest, ~value);
+        newLIR2(cUnit, kThumbMvn, tDest, tDest);
+        if (rDest != tDest) {
+           opRegReg(cUnit, kOpMov, rDest, tDest);
+           dvmCompilerFreeTemp(cUnit, tDest);
+        }
+        return res;
+    }
+    /* No shortcut - go ahead and use literal pool */
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->literalList, value, 255);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->literalList, value);
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumbLdrPcRel;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = tDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+
+    /*
+     * To save space in the constant pool, we use the ADD_RRI8 instruction to
+     * add up to 255 to an existing constant value.
+     */
+    if (dataTarget->operands[0] != value) {
+        newLIR2(cUnit, kThumbAddRI8, tDest, value - dataTarget->operands[0]);
+    }
+    if (rDest != tDest) {
+       opRegReg(cUnit, kOpMov, rDest, tDest);
+       dvmCompilerFreeTemp(cUnit, tDest);
+    }
+    return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+/*
+ * Load a class pointer value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
+{
+    ArmLIR *res;
+    cUnit->hasClassLiterals = true;
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->classPointerList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->classPointerList, value);
+        /* Counts the number of class pointers in this translation */
+        cUnit->numClassPointers++;
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumbLdrPcRel;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+    return res;
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpUncondBr:
+            opcode = kThumbBUncond;
+            break;
+        default:
+            ALOGE("Jit: bad case in opNone");
+            dvmCompilerAbort(cUnit);
+    }
+    return newLIR0(cUnit, opcode);
+}
+
+static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
+{
+    return newLIR2(cUnit, kThumbBCond, 0 /* offset to be patched */, cc);
+}
+
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpPush:
+            opcode = kThumbPush;
+            break;
+        case kOpPop:
+            opcode = kThumbPop;
+            break;
+        default:
+            ALOGE("Jit: bad case in opCondBranch");
+            dvmCompilerAbort(cUnit);
+    }
+    return newLIR1(cUnit, opcode, value);
+}
+
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpBlx:
+            opcode = kThumbBlxR;
+            break;
+        default:
+            ALOGE("Jit: bad case in opReg");
+            dvmCompilerAbort(cUnit);
+    }
+    return newLIR1(cUnit, opcode, rDestSrc);
+}
+
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value)
+{
+    ArmLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    bool shortForm = (absValue & 0xff) == absValue;
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdd:
+            if ( !neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+            } else
+                opcode = kThumbAddRRR;
+            break;
+        case kOpSub:
+            if (!neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+            } else
+                opcode = kThumbSubRRR;
+            break;
+        case kOpCmp:
+            if (neg)
+               shortForm = false;
+            if (LOWREG(rDestSrc1) && shortForm) {
+                opcode = kThumbCmpRI8;
+            } else if (LOWREG(rDestSrc1)) {
+                opcode = kThumbCmpRR;
+            } else {
+                shortForm = false;
+                opcode = kThumbCmpHL;
+            }
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    if (shortForm)
+        res = newLIR2(cUnit, opcode, rDestSrc1, absValue);
+    else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, rScratch, value);
+        if (op == kOpCmp)
+            newLIR2(cUnit, opcode, rDestSrc1, rScratch);
+        else
+            newLIR3(cUnit, opcode, rDestSrc1, rDestSrc1, rScratch);
+    }
+    return res;
+}
+
+static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdd:
+            opcode = kThumbAddRRR;
+            break;
+        case kOpSub:
+            opcode = kThumbSubRRR;
+            break;
+        default:
+            if (rDest == rSrc1) {
+                return opRegReg(cUnit, op, rDest, rSrc2);
+            } else if (rDest == rSrc2) {
+                assert(dvmCompilerIsTemp(cUnit, rSrc1));
+                dvmCompilerClobber(cUnit, rSrc1);
+                opRegReg(cUnit, op, rSrc1, rSrc2);
+                return opRegReg(cUnit, kOpMov, rDest, rSrc1);
+            } else {
+                opRegReg(cUnit, kOpMov, rDest, rSrc1);
+                return opRegReg(cUnit, op, rDest, rSrc2);
+            }
+            break;
+    }
+    return newLIR3(cUnit, opcode, rDest, rSrc1, rSrc2);
+}
+
+static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value)
+{
+    ArmLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = (absValue & 0x7) == absValue;
+    switch(op) {
+        case kOpAdd:
+            if (rDest == rSrc1)
+                return opRegImm(cUnit, op, rDest, value);
+            if ((rSrc1 == r13sp) && (value <= 1020)) { /* sp */
+                assert((value & 0x3) == 0);
+                shortForm = true;
+                opcode = kThumbAddSpRel;
+                value >>= 2;
+            } else if ((rSrc1 == r15pc) && (value <= 1020)) { /* pc */
+                assert((value & 0x3) == 0);
+                shortForm = true;
+                opcode = kThumbAddPcRel;
+                value >>= 2;
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+            } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+                /* Two shots - 1st handle the 7 */
+                opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+                res = newLIR3(cUnit, opcode, rDest, rSrc1, 7);
+                opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+                newLIR2(cUnit, opcode, rDest, absValue - 7);
+                return res;
+            } else
+                opcode = kThumbAddRRR;
+            break;
+
+        case kOpSub:
+            if (rDest == rSrc1)
+                return opRegImm(cUnit, op, rDest, value);
+            if (shortForm) {
+                opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+            } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+                /* Two shots - 1st handle the 7 */
+                opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+                res = newLIR3(cUnit, opcode, rDest, rSrc1, 7);
+                opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+                newLIR2(cUnit, opcode, rDest, absValue - 7);
+                return res;
+            } else
+                opcode = kThumbSubRRR;
+            break;
+        case kOpLsl:
+                shortForm = (!neg && value <= 31);
+                opcode = kThumbLslRRI5;
+                break;
+        case kOpLsr:
+                shortForm = (!neg && value <= 31);
+                opcode = kThumbLsrRRI5;
+                break;
+        case kOpAsr:
+                shortForm = (!neg && value <= 31);
+                opcode = kThumbAsrRRI5;
+                break;
+        case kOpMul:
+        case kOpAnd:
+        case kOpOr:
+        case kOpXor:
+                if (rDest == rSrc1) {
+                    int rScratch = dvmCompilerAllocTemp(cUnit);
+                    res = loadConstant(cUnit, rScratch, value);
+                    opRegReg(cUnit, op, rDest, rScratch);
+                } else {
+                    res = loadConstant(cUnit, rDest, value);
+                    opRegReg(cUnit, op, rDest, rSrc1);
+                }
+                return res;
+        default:
+            ALOGE("Jit: bad case in opRegRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    if (shortForm)
+        res = newLIR3(cUnit, opcode, rDest, rSrc1, absValue);
+    else {
+        if (rDest != rSrc1) {
+            res = loadConstant(cUnit, rDest, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rDest);
+        } else {
+            int rScratch = dvmCompilerAllocTemp(cUnit);
+            res = loadConstant(cUnit, rScratch, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rScratch);
+        }
+    }
+    return res;
+}
+
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2)
+{
+    ArmLIR *res;
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdc:
+            opcode = kThumbAdcRR;
+            break;
+        case kOpAnd:
+            opcode = kThumbAndRR;
+            break;
+        case kOpBic:
+            opcode = kThumbBicRR;
+            break;
+        case kOpCmn:
+            opcode = kThumbCmnRR;
+            break;
+        case kOpCmp:
+            opcode = kThumbCmpRR;
+            break;
+        case kOpXor:
+            opcode = kThumbEorRR;
+            break;
+        case kOpMov:
+            if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
+                opcode = kThumbMovRR;
+            else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+                opcode = kThumbMovRR_H2H;
+            else if (LOWREG(rDestSrc1))
+                opcode = kThumbMovRR_H2L;
+            else
+                opcode = kThumbMovRR_L2H;
+            break;
+        case kOpMul:
+            opcode = kThumbMul;
+            break;
+        case kOpMvn:
+            opcode = kThumbMvn;
+            break;
+        case kOpNeg:
+            opcode = kThumbNeg;
+            break;
+        case kOpOr:
+            opcode = kThumbOrr;
+            break;
+        case kOpSbc:
+            opcode = kThumbSbc;
+            break;
+        case kOpTst:
+            opcode = kThumbTst;
+            break;
+        case kOpLsl:
+            opcode = kThumbLslRR;
+            break;
+        case kOpLsr:
+            opcode = kThumbLsrRR;
+            break;
+        case kOpAsr:
+            opcode = kThumbAsrRR;
+            break;
+        case kOpRor:
+            opcode = kThumbRorRR;
+        case kOpAdd:
+        case kOpSub:
+            return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
+        case kOp2Byte:
+             res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
+             opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
+             return res;
+        case kOp2Short:
+             res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+             opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
+             return res;
+        case kOp2Char:
+             res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+             opRegRegImm(cUnit, kOpLsr, rDestSrc1, rDestSrc1, 16);
+             return res;
+        default:
+            ALOGE("Jit: bad case in opRegReg");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
+}
+
+static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+                                     int rDestHi, int valLo, int valHi)
+{
+    ArmLIR *res;
+    res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+    loadConstantNoClobber(cUnit, rDestHi, valHi);
+    return res;
+}
+
+/* Load value from base + scaled index. */
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size)
+{
+    ArmLIR *first = NULL;
+    ArmLIR *res;
+    ArmOpcode opcode = kThumbBkpt;
+    int rNewIndex = rIndex;
+    if (scale) {
+        // Scale the index, but can't trash the original.
+        rNewIndex = dvmCompilerAllocTemp(cUnit);
+        first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
+    }
+    switch (size) {
+        case kWord:
+            opcode = kThumbLdrRRR;
+            break;
+        case kUnsignedHalf:
+            opcode = kThumbLdrhRRR;
+            break;
+        case kSignedHalf:
+            opcode = kThumbLdrshRRR;
+            break;
+        case kUnsignedByte:
+            opcode = kThumbLdrbRRR;
+            break;
+        case kSignedByte:
+            opcode = kThumbLdrsbRRR;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opcode, rDest, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    if (scale)
+        dvmCompilerFreeTemp(cUnit, rNewIndex);
+    return (first) ? first : res;
+}
+
+/* store value base base + scaled index. */
+static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+                                int rIndex, int rSrc, int scale, OpSize size)
+{
+    ArmLIR *first = NULL;
+    ArmLIR *res;
+    ArmOpcode opcode = kThumbBkpt;
+    int rNewIndex = rIndex;
+    if (scale) {
+        rNewIndex = dvmCompilerAllocTemp(cUnit);
+        first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
+    }
+    switch (size) {
+        case kWord:
+            opcode = kThumbStrRRR;
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = kThumbStrhRRR;
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = kThumbStrbRRR;
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opcode, rSrc, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    if (scale)
+        dvmCompilerFreeTemp(cUnit, rNewIndex);
+    return (first) ? first : res;
+}
+
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDest, int rDestHi,
+                                OpSize size, int sReg)
+/*
+ * Load value from base + displacement.  Optionally perform null check
+ * on base (which must have an associated sReg and MIR).  If not
+ * performing null check, incoming MIR can be null. IMPORTANT: this
+ * code must not allocate any new temps.  If a new register is needed
+ * and base and dest are the same, spill some other register to
+ * rlp and then restore.
+ */
+{
+    ArmLIR *res;
+    ArmLIR *load = NULL;
+    ArmLIR *load2 = NULL;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    int encodedDisp = displacement;
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            if ((displacement < 124) && (displacement >= 0)) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrRRI5;
+            } else {
+                opcode = kThumbLdrRRR;
+            }
+            break;
+        case kWord:
+            if (LOWREG(rDest) && (rBase == r15pc) &&
+                (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrPcRel;
+            } else if (LOWREG(rDest) && (rBase == r13sp) &&
+                      (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrSpRel;
+            } else if (displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrRRI5;
+            } else {
+                opcode = kThumbLdrRRR;
+            }
+            break;
+        case kUnsignedHalf:
+            if (displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbLdrhRRI5;
+            } else {
+                opcode = kThumbLdrhRRR;
+            }
+            break;
+        case kSignedHalf:
+            opcode = kThumbLdrshRRR;
+            break;
+        case kUnsignedByte:
+            if (displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbLdrbRRI5;
+            } else {
+                opcode = kThumbLdrbRRR;
+            }
+            break;
+        case kSignedByte:
+            opcode = kThumbLdrsbRRR;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+    if (shortForm) {
+        load = res = newLIR3(cUnit, opcode, rDest, rBase, encodedDisp);
+        if (pair) {
+            load2 = newLIR3(cUnit, opcode, rDestHi, rBase, encodedDisp+1);
+        }
+    } else {
+        if (pair) {
+            int rTmp = dvmCompilerAllocFreeTemp(cUnit);
+            res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
+            load = newLIR3(cUnit, kThumbLdrRRI5, rDest, rTmp, 0);
+            load2 = newLIR3(cUnit, kThumbLdrRRI5, rDestHi, rTmp, 1);
+            dvmCompilerFreeTemp(cUnit, rTmp);
+        } else {
+            int rTmp = (rBase == rDest) ? dvmCompilerAllocFreeTemp(cUnit)
+                                        : rDest;
+            res = loadConstant(cUnit, rTmp, displacement);
+            load = newLIR3(cUnit, opcode, rDest, rBase, rTmp);
+            if (rBase == r5FP)
+                annotateDalvikRegAccess(load, displacement >> 2,
+                                        true /* isLoad */);
+            if (rTmp != rDest)
+                dvmCompilerFreeTemp(cUnit, rTmp);
+        }
+    }
+    if (rBase == r5FP) {
+        if (load != NULL)
+            annotateDalvikRegAccess(load, displacement >> 2,
+                                    true /* isLoad */);
+        if (load2 != NULL)
+            annotateDalvikRegAccess(load2, (displacement >> 2) + 1,
+                                    true /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (load != NULL && cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+    if (load2 != NULL && cUnit->heapMemOp)
+        load2->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+                            size, sReg);
+}
+
+static ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDestLo, int rDestHi,
+                                int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+                            kLong, sReg);
+}
+
+static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrc, int rSrcHi,
+                                 OpSize size)
+{
+    ArmLIR *res;
+    ArmLIR *store = NULL;
+    ArmLIR *store2 = NULL;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    int encodedDisp = displacement;
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            if ((displacement < 124) && (displacement >= 0)) {
+                assert((displacement & 0x3) == 0);
+                pair = true;
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbStrRRI5;
+            } else {
+                opcode = kThumbStrRRR;
+            }
+            break;
+        case kWord:
+            if (displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbStrRRI5;
+            } else {
+                opcode = kThumbStrRRR;
+            }
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            if (displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbStrhRRI5;
+            } else {
+                opcode = kThumbStrhRRR;
+            }
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            if (displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbStrbRRI5;
+            } else {
+                opcode = kThumbStrbRRR;
+            }
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+    if (shortForm) {
+        store = res = newLIR3(cUnit, opcode, rSrc, rBase, encodedDisp);
+        if (pair) {
+            store2 = newLIR3(cUnit, opcode, rSrcHi, rBase, encodedDisp + 1);
+        }
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        if (pair) {
+            res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
+            store =  newLIR3(cUnit, kThumbStrRRI5, rSrc, rScratch, 0);
+            store2 = newLIR3(cUnit, kThumbStrRRI5, rSrcHi, rScratch, 1);
+        } else {
+            res = loadConstant(cUnit, rScratch, displacement);
+            store = newLIR3(cUnit, opcode, rSrc, rBase, rScratch);
+        }
+        dvmCompilerFreeTemp(cUnit, rScratch);
+    }
+    if (rBase == r5FP) {
+        if (store != NULL)
+            annotateDalvikRegAccess(store, displacement >> 2,
+                                    false /* isLoad */);
+        if (store2 != NULL)
+            annotateDalvikRegAccess(store2, (displacement >> 2) + 1,
+                                    false /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (store != NULL && cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+    if (store2 != NULL && cUnit->heapMemOp)
+        store2->flags.insertWrapper = true;
+#endif
+    return res;
+}
+
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    if (lowReg < highReg) {
+        storeMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
+    } else {
+        storeWordDisp(cUnit, base, 0, lowReg);
+        storeWordDisp(cUnit, base, 4, highReg);
+    }
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    if (lowReg < highReg) {
+        loadMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
+    } else {
+        loadWordDisp(cUnit, base, 0 , lowReg);
+        loadWordDisp(cUnit, base, 4 , highReg);
+    }
+}
+
+static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR* res;
+    ArmOpcode opcode;
+    res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    if (LOWREG(rDest) && LOWREG(rSrc))
+        opcode = kThumbMovRR;
+    else if (!LOWREG(rDest) && !LOWREG(rSrc))
+         opcode = kThumbMovRR_H2H;
+    else if (LOWREG(rDest))
+         opcode = kThumbMovRR_H2L;
+    else
+         opcode = kThumbMovRR_L2H;
+
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    res->opcode = opcode;
+    setupResourceMasks(res);
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    }
+    return res;
+}
+
+static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+    dvmCompilerAppendLIR(cUnit, (LIR*)res);
+    return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                           int srcLo, int srcHi)
+{
+    // Handle overlap
+    if (srcHi == destLo) {
+        genRegCopy(cUnit, destHi, srcHi);
+        genRegCopy(cUnit, destLo, srcLo);
+    } else {
+        genRegCopy(cUnit, destLo, srcLo);
+        genRegCopy(cUnit, destHi, srcHi);
+    }
+}
+
+static ArmLIR *genCmpImmBranch(CompilationUnit *cUnit,
+                                     ArmConditionCode cond, int reg,
+                                     int checkValue)
+{
+    if ((checkValue & 0xff) != checkValue) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, tReg, checkValue);
+        newLIR2(cUnit, kThumbCmpRR, reg, tReg);
+        dvmCompilerFreeTemp(cUnit, tReg);
+    } else {
+        newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
+    }
+    ArmLIR *branch = newLIR2(cUnit, kThumbBCond, 0, cond);
+    return branch;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    /*
+     * We need two separate pushes, since we want r5 to be pushed first.
+     * Store multiple will push LR first.
+     */
+    ArmLIR *pushFP = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pushFP->opcode = kThumbPush;
+    pushFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(pushFP);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushFP);
+
+    ArmLIR *pushLR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pushLR->opcode = kThumbPush;
+    /* Thumb push can handle LR, but is encoded differently at bit 8 */
+    pushLR->operands[0] = 1 << 8;
+    setupResourceMasks(pushLR);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushLR);
+}
+
+static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    /*
+     * Since Thumb cannot pop memory content into LR, we have to pop LR
+     * to a temp first (r5 in this case). Then we move r5 to LR, then pop the
+     * original r5 from stack.
+     */
+    /* Pop memory content(LR) into r5 first */
+    ArmLIR *popForLR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    popForLR->opcode = kThumbPop;
+    popForLR->operands[0] = 1 << r5FP;
+    setupResourceMasks(popForLR);
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) popForLR);
+
+    ArmLIR *copy = genRegCopyNoInsert(cUnit, r14lr, r5FP);
+    dvmCompilerInsertLIRAfter((LIR *) popForLR, (LIR *) copy);
+
+    /* Now restore the original r5 */
+    ArmLIR *popFP = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    popFP->opcode = kThumbPop;
+    popFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(popFP);
+    dvmCompilerInsertLIRAfter((LIR *) copy, (LIR *) popFP);
+}
+#endif
diff --git a/vm/compiler/codegen/arm/Thumb/Gen.cpp b/vm/compiler/codegen/arm/Thumb/Gen.cpp
new file mode 100644
index 0000000..abc4420
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Gen.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Reserve 6 bytes at the beginning of the trace
+ *        +----------------------------+
+ *        | prof count addr (4 bytes)  |
+ *        +----------------------------+
+ *        | chain cell offset (2 bytes)|
+ *        +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (12 bytes):
+ *
+ *       mov   r0, pc       @ move adr of "mov r0,pc" + 4 to r0
+ *       sub   r0, #10      @ back up to addr prof count pointer
+ *       ldr   r0, [r0]     @ get address of counter
+ *       ldr   r1, [r0]
+ *       add   r1, #1
+ *       str   r1, [r0]
+ *
+ * For periodic profiling (4 bytes):
+ *       call  TEMPLATE_PERIODIC_PROFILING
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+    assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+    newLIR1(cUnit, kArm16BitData, addr & 0xffff);
+    newLIR1(cUnit, kArm16BitData, (addr >> 16) & 0xffff);
+    cUnit->chainCellOffsetLIR =
+        (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+    cUnit->headerSize = 6;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        /* Thumb instruction used directly here to ensure correct size */
+        newLIR2(cUnit, kThumbMovRR_H2L, r0, r15pc);
+        newLIR2(cUnit, kThumbSubRI8, r0, 10);
+        newLIR3(cUnit, kThumbLdrRRI5, r0, r0, 0);
+        newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+        newLIR2(cUnit, kThumbAddRI8, r1, 1);
+        newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+        return 12;
+    } else {
+        int opcode = TEMPLATE_PERIODIC_PROFILING;
+        newLIR2(cUnit, kThumbBlx1,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        newLIR2(cUnit, kThumbBlx2,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        return 4;
+    }
+}
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+                        RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.lowReg,
+                rlSrc.lowReg, 0x80000000);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+                         RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg,
+                        0x80000000);
+    genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+    genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG);
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static bool partialOverlap(int sreg1, int sreg2)
+{
+    return abs(sreg1 - sreg2) == 1;
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+                         OpKind secondOp, RegLocation rlDest,
+                         RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    if (partialOverlap(rlSrc1.sRegLow,rlSrc2.sRegLow) ||
+        partialOverlap(rlSrc1.sRegLow,rlDest.sRegLow) ||
+        partialOverlap(rlSrc2.sRegLow,rlDest.sRegLow)) {
+        // Rare case - not enough registers to properly handle
+        genInterpSingleStep(cUnit, mir);
+    } else if (rlDest.sRegLow == rlSrc1.sRegLow) {
+        // Already 2-operand
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        opRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc2.lowReg);
+        opRegReg(cUnit, secondOp, rlResult.highReg, rlSrc2.highReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (rlDest.sRegLow == rlSrc2.sRegLow) {
+        // Bad case - must use/clobber Src1 and reassign Dest
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        opRegReg(cUnit, firstOp, rlSrc1.lowReg, rlResult.lowReg);
+        opRegReg(cUnit, secondOp, rlSrc1.highReg, rlResult.highReg);
+        // Old reg assignments are now invalid
+        dvmCompilerClobber(cUnit, rlResult.lowReg);
+        dvmCompilerClobber(cUnit, rlResult.highReg);
+        dvmCompilerClobber(cUnit, rlSrc1.lowReg);
+        dvmCompilerClobber(cUnit, rlSrc1.highReg);
+        rlDest.location = kLocDalvikFrame;
+        assert(rlSrc1.location == kLocPhysReg);
+        // Reassign registers - rlDest will now get rlSrc1's old regs
+        storeValueWide(cUnit, rlDest, rlSrc1);
+    } else {
+        // Copy Src1 to Dest
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, false);
+        loadValueDirectWide(cUnit, rlSrc1, rlResult.lowReg,
+                            rlResult.highReg);
+        rlResult.location = kLocPhysReg;
+        opRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc2.lowReg);
+        opRegReg(cUnit, secondOp, rlResult.highReg, rlSrc2.highReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+    int numTemps = sizeof(coreTemps)/sizeof(int);
+    RegisterPool *pool = (RegisterPool *) dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps = (RegisterInfo *)
+            dvmCompilerNew(numTemps * sizeof(*pool->coreTemps), true);
+    pool->numFPTemps = 0;
+    pool->FPTemps = NULL;
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+    dvmCompilerInitPool(pool->FPTemps, NULL, 0);
+    pool->nullCheckedRegs =
+        dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+    ArmLIR *res;
+    int rDPC = dvmCompilerAllocTemp(cUnit);
+    int rAddr = dvmCompilerAllocTemp(cUnit);
+    int offset = offsetof(StackSaveArea, xtra.currentPc);
+    res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+    newLIR2(cUnit, kThumbMovRR, rAddr, r5FP);
+    newLIR2(cUnit, kThumbSubRI8, rAddr, sizeof(StackSaveArea) - offset);
+    storeWordDisp( cUnit, rAddr, 0, rDPC);
+    return res;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+    genMonitorPortable(cUnit, mir);
+}
+
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+    genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
+    rlResult = dvmCompilerGetReturn(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    int reg0 = loadValue(cUnit, rlSrc, kCoreReg).lowReg;
+    int signMask = dvmCompilerAllocTemp(cUnit);
+    loadConstant(cUnit, signMask, 0x7fffffff);
+    newLIR2(cUnit, kThumbAndRR, reg0, signMask);
+    dvmCompilerFreeTemp(cUnit, signMask);
+    storeWordDisp(cUnit, r6SELF, offset, reg0);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reg0);
+    return false;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation regSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    int reglo = regSrc.lowReg;
+    int reghi = regSrc.highReg;
+    int signMask = dvmCompilerAllocTemp(cUnit);
+    loadConstant(cUnit, signMask, 0x7fffffff);
+    storeWordDisp(cUnit, r6SELF, offset, reglo);
+    newLIR2(cUnit, kThumbAndRR, reghi, signMask);
+    dvmCompilerFreeTemp(cUnit, signMask);
+    storeWordDisp(cUnit, r6SELF, offset + 4, reghi);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reghi);
+    return false;
+}
+
+/* No select in thumb, so we need to branch.  Thumb2 will do better */
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    int reg0 = loadValue(cUnit, rlSrc1, kCoreReg).lowReg;
+    int reg1 = loadValue(cUnit, rlSrc2, kCoreReg).lowReg;
+    newLIR2(cUnit, kThumbCmpRR, reg0, reg1);
+    ArmLIR *branch1 = newLIR2(cUnit, kThumbBCond, 2,
+           isMin ? kArmCondLt : kArmCondGt);
+    newLIR2(cUnit, kThumbMovRR, reg0, reg1);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    newLIR3(cUnit, kThumbStrRRI5, reg0, r6SELF, offset >> 2);
+    branch1->generic.target = (LIR *)target;
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit,reg0);
+    return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit,
+        int firstBit, int secondBit)
+{
+    // We can't implement "add src, src, src, lsl#shift" on Thumb, so we have
+    // to do a regular multiply.
+    opRegRegImm(cUnit, kOpMul, rlResult.lowReg, rlSrc.lowReg, lit);
+}
diff --git a/vm/compiler/codegen/arm/Thumb/Ralloc.cpp b/vm/compiler/codegen/arm/Thumb/Ralloc.cpp
new file mode 100644
index 0000000..6769972
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Ralloc.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Alloc a pair of core registers, or a double.  Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit, bool fpHint,
+                                  int regClass)
+{
+    int highReg;
+    int lowReg;
+    int res = 0;
+    lowReg = dvmCompilerAllocTemp(cUnit);
+    highReg = dvmCompilerAllocTemp(cUnit);
+    res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+    return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint, int regClass)
+{
+    return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Factory.cpp b/vm/compiler/codegen/arm/Thumb2/Factory.cpp
new file mode 100644
index 0000000..c3c3712
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Factory.cpp
@@ -0,0 +1,1299 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7, r8, r9, r10, r11, r12};
+static int fpTemps[] = {fr16, fr17, fr18, fr19, fr20, fr21, fr22, fr23,
+                        fr24, fr25, fr26, fr27, fr28, fr29, fr30, fr31};
+
+static int encodeImmSingle(int value)
+{
+    int res;
+    int bitA =    (value & 0x80000000) >> 31;
+    int notBitB = (value & 0x40000000) >> 30;
+    int bitB =    (value & 0x20000000) >> 29;
+    int bSmear =  (value & 0x3e000000) >> 25;
+    int slice =   (value & 0x01f80000) >> 19;
+    int zeroes =  (value & 0x0007ffff);
+    if (zeroes != 0)
+        return -1;
+    if (bitB) {
+        if ((notBitB != 0) || (bSmear != 0x1f))
+            return -1;
+    } else {
+        if ((notBitB != 1) || (bSmear != 0x0))
+            return -1;
+    }
+    res = (bitA << 7) | (bitB << 6) | slice;
+    return res;
+}
+
+static ArmLIR *loadFPConstantValue(CompilationUnit *cUnit, int rDest,
+                                   int value)
+{
+    int encodedImm = encodeImmSingle(value);
+    assert(SINGLEREG(rDest));
+    if (value == 0) {
+      // TODO: we need better info about the target CPU.  a vector exclusive or
+      //       would probably be better here if we could rely on its existance.
+      // Load an immediate +2.0 (which encodes to 0)
+      newLIR2(cUnit, kThumb2Vmovs_IMM8, rDest, 0);
+      // +0.0 = +2.0 - +2.0
+      return newLIR3(cUnit, kThumb2Vsubs, rDest, rDest, rDest);
+    } else if (encodedImm >= 0) {
+        return newLIR2(cUnit, kThumb2Vmovs_IMM8, rDest, encodedImm);
+    }
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->literalList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->literalList, value);
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumb2Vldrs;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    loadPcRel->operands[1] = r15pc;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+    return loadPcRel;
+}
+
+static int leadingZeros(u4 val)
+{
+    u4 alt;
+    int n;
+    int count;
+
+    count = 16;
+    n = 32;
+    do {
+        alt = val >> count;
+        if (alt != 0) {
+            n = n - count;
+            val = alt;
+        }
+        count >>= 1;
+    } while (count);
+    return n - val;
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 modified
+ * immediate.  If not, return -1.  If so, return i:imm3:a:bcdefgh form.
+ */
+static int modifiedImmediate(u4 value)
+{
+   int zLeading;
+   int zTrailing;
+   u4 b0 = value & 0xff;
+
+   /* Note: case of value==0 must use 0:000:0:0000000 encoding */
+   if (value <= 0xFF)
+       return b0;  // 0:000:a:bcdefgh
+   if (value == ((b0 << 16) | b0))
+       return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
+   if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
+       return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
+   b0 = (value >> 8) & 0xff;
+   if (value == ((b0 << 24) | (b0 << 8)))
+       return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
+   /* Can we do it with rotation? */
+   zLeading = leadingZeros(value);
+   zTrailing = 32 - leadingZeros(~value & (value - 1));
+   /* A run of eight or fewer active bits? */
+   if ((zLeading + zTrailing) < 24)
+       return -1;  /* No - bail */
+   /* left-justify the constant, discarding msb (known to be 1) */
+   value <<= zLeading + 1;
+   /* Create bcdefgh */
+   value >>= 25;
+   /* Put it all together */
+   return value | ((0x8 + zLeading) << 7); /* [01000..11111]:bcdefgh */
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static ArmLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+                                     int value)
+{
+    ArmLIR *res;
+    int modImm;
+
+    if (FPREG(rDest)) {
+        return loadFPConstantValue(cUnit, rDest, value);
+    }
+
+    /* See if the value can be constructed cheaply */
+    if (LOWREG(rDest) && (value >= 0) && (value <= 255)) {
+        return newLIR2(cUnit, kThumbMovImm, rDest, value);
+    }
+    /* Check Modified immediate special cases */
+    modImm = modifiedImmediate(value);
+    if (modImm >= 0) {
+        res = newLIR2(cUnit, kThumb2MovImmShift, rDest, modImm);
+        return res;
+    }
+    modImm = modifiedImmediate(~value);
+    if (modImm >= 0) {
+        res = newLIR2(cUnit, kThumb2MvnImmShift, rDest, modImm);
+        return res;
+    }
+    /* 16-bit immediate? */
+    if ((value & 0xffff) == value) {
+        res = newLIR2(cUnit, kThumb2MovImm16, rDest, value);
+        return res;
+    }
+    /* No shortcut - go ahead and use literal pool */
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->literalList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->literalList, value);
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumb2LdrPcRel12;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+
+    /*
+     * To save space in the constant pool, we use the ADD_RRI8 instruction to
+     * add up to 255 to an existing constant value.
+     */
+    if (dataTarget->operands[0] != value) {
+        opRegImm(cUnit, kOpAdd, rDest, value - dataTarget->operands[0]);
+    }
+    return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+/*
+ * Load a class pointer value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
+{
+    ArmLIR *res;
+    cUnit->hasClassLiterals = true;
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->classPointerList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->classPointerList, value);
+        /* Counts the number of class pointers in this translation */
+        cUnit->numClassPointers++;
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumb2LdrPcRel12;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+    return res;
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpUncondBr:
+            opcode = kThumbBUncond;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR0(cUnit, opcode);
+}
+
+static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
+{
+    return newLIR2(cUnit, kThumb2BCond, 0 /* offset to be patched */, cc);
+}
+
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpPush: {
+            if ((value & 0xff00) == 0) {
+                opcode = kThumbPush;
+            } else if ((value & 0xff00) == (1 << r14lr)) {
+                /* Thumb push can handle lr, which is encoded by bit 8 */
+                opcode = kThumbPush;
+                value = (value & 0xff) | (1<<8);
+            } else {
+                opcode = kThumb2Push;
+            }
+            break;
+        }
+        case kOpPop: {
+            if ((value & 0xff00) == 0) {
+                opcode = kThumbPop;
+            } else if ((value & 0xff00) == (1 << r15pc)) {
+                /* Thumb pop can handle pc, which is encoded by bit 8 */
+                opcode = kThumbPop;
+                value = (value & 0xff) | (1<<8);
+            } else {
+                opcode = kThumb2Pop;
+            }
+            break;
+        }
+        default:
+            assert(0);
+    }
+    return newLIR1(cUnit, opcode, value);
+}
+
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpBlx:
+            opcode = kThumbBlxR;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR1(cUnit, opcode, rDestSrc);
+}
+
+static ArmLIR *opRegRegShift(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2, int shift)
+{
+    bool thumbForm = ((shift == 0) && LOWREG(rDestSrc1) && LOWREG(rSrc2));
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdc:
+            opcode = (thumbForm) ? kThumbAdcRR : kThumb2AdcRRR;
+            break;
+        case kOpAnd:
+            opcode = (thumbForm) ? kThumbAndRR : kThumb2AndRRR;
+            break;
+        case kOpBic:
+            opcode = (thumbForm) ? kThumbBicRR : kThumb2BicRRR;
+            break;
+        case kOpCmn:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbCmnRR : kThumb2CmnRR;
+            break;
+        case kOpCmp:
+            if (thumbForm)
+                opcode = kThumbCmpRR;
+            else if ((shift == 0) && !LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+                opcode = kThumbCmpHH;
+            else if ((shift == 0) && LOWREG(rDestSrc1))
+                opcode = kThumbCmpLH;
+            else if (shift == 0)
+                opcode = kThumbCmpHL;
+            else
+                opcode = kThumb2CmpRR;
+            break;
+        case kOpXor:
+            opcode = (thumbForm) ? kThumbEorRR : kThumb2EorRRR;
+            break;
+        case kOpMov:
+            assert(shift == 0);
+            if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
+                opcode = kThumbMovRR;
+            else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+                opcode = kThumbMovRR_H2H;
+            else if (LOWREG(rDestSrc1))
+                opcode = kThumbMovRR_H2L;
+            else
+                opcode = kThumbMovRR_L2H;
+            break;
+        case kOpMul:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbMul : kThumb2MulRRR;
+            break;
+        case 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 == r13sp) &&
+                (value <= 1020) && ((value & 0x3)==0)) {
+                return newLIR3(cUnit, kThumbAddSpRel, rDest, rSrc1,
+                               value >> 2);
+            } else if (LOWREG(rDest) && (rSrc1 == r15pc) &&
+                       (value <= 1020) && ((value & 0x3)==0)) {
+                return newLIR3(cUnit, kThumbAddPcRel, rDest, rSrc1,
+                               value >> 2);
+            }
+            opcode = kThumb2AddRRI8;
+            altOpcode = kThumb2AddRRR;
+            // Note: intentional fallthrough
+        case kOpSub:
+            if (allLowRegs && ((absValue & 0x7) == absValue)) {
+                if (op == kOpAdd)
+                    opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+                else
+                    opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+                return newLIR3(cUnit, opcode, rDest, rSrc1, absValue);
+            } else if ((absValue & 0xff) == absValue) {
+                if (op == kOpAdd)
+                    opcode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12;
+                else
+                    opcode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12;
+                return newLIR3(cUnit, opcode, rDest, rSrc1, absValue);
+            }
+            if (modImmNeg >= 0) {
+                op = (op == kOpAdd) ? kOpSub : kOpAdd;
+                modImm = modImmNeg;
+            }
+            if (op == kOpSub) {
+                opcode = kThumb2SubRRI8;
+                altOpcode = kThumb2SubRRR;
+            }
+            break;
+        case kOpAdc:
+            opcode = kThumb2AdcRRI8;
+            altOpcode = kThumb2AdcRRR;
+            break;
+        case kOpSbc:
+            opcode = kThumb2SbcRRI8;
+            altOpcode = kThumb2SbcRRR;
+            break;
+        case kOpOr:
+            opcode = kThumb2OrrRRI8;
+            altOpcode = kThumb2OrrRRR;
+            break;
+        case kOpAnd:
+            opcode = kThumb2AndRRI8;
+            altOpcode = kThumb2AndRRR;
+            break;
+        case kOpXor:
+            opcode = kThumb2EorRRI8;
+            altOpcode = kThumb2EorRRR;
+            break;
+        case kOpMul:
+            //TUNING: power of 2, shift & add
+            modImm = -1;
+            altOpcode = kThumb2MulRRR;
+            break;
+        case kOpCmp: {
+            int modImm = modifiedImmediate(value);
+            ArmLIR *res;
+            if (modImm >= 0) {
+                res = newLIR2(cUnit, kThumb2CmpRI8, rSrc1, modImm);
+            } else {
+                int rTmp = dvmCompilerAllocTemp(cUnit);
+                res = loadConstant(cUnit, rTmp, value);
+                opRegReg(cUnit, kOpCmp, rSrc1, rTmp);
+                dvmCompilerFreeTemp(cUnit, rTmp);
+            }
+            return res;
+        }
+        default:
+            assert(0);
+    }
+
+    if (modImm >= 0) {
+        return newLIR3(cUnit, opcode, rDest, rSrc1, modImm);
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, rScratch, value);
+        if (EncodingMap[altOpcode].flags & IS_QUAD_OP)
+            res = newLIR4(cUnit, altOpcode, rDest, rSrc1, rScratch, 0);
+        else
+            res = newLIR3(cUnit, altOpcode, rDest, rSrc1, rScratch);
+        dvmCompilerFreeTemp(cUnit, rScratch);
+        return res;
+    }
+}
+
+/* Handle Thumb-only variants here - otherwise punt to opRegRegImm */
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value)
+{
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    bool shortForm = (((absValue & 0xff) == absValue) && LOWREG(rDestSrc1));
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdd:
+            if ( !neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+            }
+            break;
+        case kOpSub:
+            if (!neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+            }
+            break;
+        case kOpCmp:
+            if (LOWREG(rDestSrc1) && shortForm)
+                opcode = (shortForm) ?  kThumbCmpRI8 : kThumbCmpRR;
+            else if (LOWREG(rDestSrc1))
+                opcode = kThumbCmpRR;
+            else {
+                shortForm = false;
+                opcode = kThumbCmpHL;
+            }
+            break;
+        default:
+            /* Punt to opRegRegImm - if bad case catch it there */
+            shortForm = false;
+            break;
+    }
+    if (shortForm)
+        return newLIR2(cUnit, opcode, rDestSrc1, absValue);
+    else {
+        return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
+    }
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 floating point
+ * immediate.  If not, return -1.  If so return encoded 8-bit value.
+ */
+static int encodeImmDoubleHigh(int value)
+{
+    int res;
+    int bitA =    (value & 0x80000000) >> 31;
+    int notBitB = (value & 0x40000000) >> 30;
+    int bitB =    (value & 0x20000000) >> 29;
+    int bSmear =  (value & 0x3fc00000) >> 22;
+    int slice =   (value & 0x003f0000) >> 16;
+    int zeroes =  (value & 0x0000ffff);
+    if (zeroes != 0)
+        return -1;
+    if (bitB) {
+        if ((notBitB != 0) || (bSmear != 0x1f))
+            return -1;
+    } else {
+        if ((notBitB != 1) || (bSmear != 0x0))
+            return -1;
+    }
+    res = (bitA << 7) | (bitB << 6) | slice;
+    return res;
+}
+
+static int encodeImmDouble(int valLo, int valHi)
+{
+    int res = -1;
+    if (valLo == 0)
+        res = encodeImmDoubleHigh(valHi);
+    return res;
+}
+
+static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+                                     int rDestHi, int valLo, int valHi)
+{
+    int encodedImm = encodeImmDouble(valLo, valHi);
+    ArmLIR *res;
+    int targetReg = S2D(rDestLo, rDestHi);
+    if (FPREG(rDestLo)) {
+        if ((valLo == 0) && (valHi == 0)) {
+          // TODO: we need better info about the target CPU.  a vector
+          // exclusive or would probably be better here if we could rely on
+          // its existance.
+          // Load an immediate +2.0 (which encodes to 0)
+          newLIR2(cUnit, kThumb2Vmovd_IMM8, targetReg, 0);
+          // +0.0 = +2.0 - +2.0
+          res = newLIR3(cUnit, kThumb2Vsubd, targetReg, targetReg, targetReg);
+        } else if (encodedImm >= 0) {
+            res = newLIR2(cUnit, kThumb2Vmovd_IMM8, targetReg, encodedImm);
+        } else {
+            ArmLIR* dataTarget = scanLiteralPoolWide(cUnit->literalList, valLo, valHi);
+            if (dataTarget == NULL) {
+                dataTarget = addWideData(cUnit, &cUnit->literalList, valLo, valHi);
+            }
+            ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+            loadPcRel->opcode = kThumb2Vldrd;
+            loadPcRel->generic.target = (LIR *) dataTarget;
+            loadPcRel->operands[0] = targetReg;
+            loadPcRel->operands[1] = r15pc;
+            setupResourceMasks(loadPcRel);
+            setMemRefType(loadPcRel, true, kLiteral);
+            loadPcRel->aliasInfo = dataTarget->operands[0];
+            dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+            res =  loadPcRel;
+        }
+    } else {
+        res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+        loadConstantNoClobber(cUnit, rDestHi, valHi);
+    }
+    return res;
+}
+
+static int encodeShift(int code, int amount) {
+    return ((amount & 0x1f) << 2) | code;
+}
+
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size)
+{
+    bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rDest);
+    ArmLIR *load;
+    ArmOpcode opcode = kThumbBkpt;
+    bool thumbForm = (allLowRegs && (scale == 0));
+    int regPtr;
+
+    if (FPREG(rDest)) {
+        assert(SINGLEREG(rDest));
+        assert((size == kWord) || (size == kSingle));
+        opcode = kThumb2Vldrs;
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+
+    switch (size) {
+        case kSingle:
+            regPtr = dvmCompilerAllocTemp(cUnit);
+            if (scale) {
+                newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
+                        encodeShift(kArmLsl, scale));
+            } else {
+                opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
+            }
+            load = newLIR3(cUnit, opcode, rDest, regPtr, 0);
+#if defined(WITH_SELF_VERIFICATION)
+            if (cUnit->heapMemOp)
+                load->flags.insertWrapper = true;
+#endif
+            return load;
+        case kWord:
+            opcode = (thumbForm) ? kThumbLdrRRR : kThumb2LdrRRR;
+            break;
+        case kUnsignedHalf:
+            opcode = (thumbForm) ? kThumbLdrhRRR : kThumb2LdrhRRR;
+            break;
+        case kSignedHalf:
+            opcode = (thumbForm) ? kThumbLdrshRRR : kThumb2LdrshRRR;
+            break;
+        case kUnsignedByte:
+            opcode = (thumbForm) ? kThumbLdrbRRR : kThumb2LdrbRRR;
+            break;
+        case kSignedByte:
+            opcode = (thumbForm) ? kThumbLdrsbRRR : kThumb2LdrsbRRR;
+            break;
+        default:
+            assert(0);
+    }
+    if (thumbForm)
+        load = newLIR3(cUnit, opcode, rDest, rBase, rIndex);
+    else
+        load = newLIR4(cUnit, opcode, rDest, rBase, rIndex, scale);
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+                                int rIndex, int rSrc, int scale, OpSize size)
+{
+    bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rSrc);
+    ArmLIR *store;
+    ArmOpcode opcode = kThumbBkpt;
+    bool thumbForm = (allLowRegs && (scale == 0));
+    int regPtr;
+
+    if (FPREG(rSrc)) {
+        assert(SINGLEREG(rSrc));
+        assert((size == kWord) || (size == kSingle));
+        opcode = kThumb2Vstrs;
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+
+    switch (size) {
+        case kSingle:
+            regPtr = dvmCompilerAllocTemp(cUnit);
+            if (scale) {
+                newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
+                        encodeShift(kArmLsl, scale));
+            } else {
+                opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
+            }
+            store = newLIR3(cUnit, opcode, rSrc, regPtr, 0);
+#if defined(WITH_SELF_VERIFICATION)
+            if (cUnit->heapMemOp)
+                store->flags.insertWrapper = true;
+#endif
+            return store;
+        case kWord:
+            opcode = (thumbForm) ? kThumbStrRRR : kThumb2StrRRR;
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = (thumbForm) ? kThumbStrhRRR : kThumb2StrhRRR;
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = (thumbForm) ? kThumbStrbRRR : kThumb2StrbRRR;
+            break;
+        default:
+            assert(0);
+    }
+    if (thumbForm)
+        store = newLIR3(cUnit, opcode, rSrc, rBase, rIndex);
+    else
+        store = newLIR4(cUnit, opcode, rSrc, rBase, rIndex, scale);
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+#endif
+    return store;
+}
+
+/*
+ * Load value from base + displacement.  Optionally perform null check
+ * on base (which must have an associated sReg and MIR).  If not
+ * performing null check, incoming MIR can be null.
+ */
+static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDest, int rDestHi,
+                                OpSize size, int sReg)
+{
+    ArmLIR *res, *load;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    bool thumb2Form = (displacement < 4092 && displacement >= 0);
+    bool allLowRegs = (LOWREG(rBase) && LOWREG(rDest));
+    int encodedDisp = displacement;
+
+    switch (size) {
+        case kDouble:
+        case kLong:
+            if (FPREG(rDest)) {
+                if (SINGLEREG(rDest)) {
+                    assert(FPREG(rDestHi));
+                    rDest = S2D(rDest, rDestHi);
+                }
+                opcode = kThumb2Vldrd;
+                if (displacement <= 1020) {
+                    shortForm = true;
+                    encodedDisp >>= 2;
+                }
+                break;
+            } else {
+                res = loadBaseDispBody(cUnit, mir, rBase, displacement, rDest,
+                                       -1, kWord, sReg);
+                loadBaseDispBody(cUnit, NULL, rBase, displacement + 4, rDestHi,
+                                 -1, kWord, INVALID_SREG);
+                return res;
+            }
+        case kSingle:
+        case kWord:
+            if (FPREG(rDest)) {
+                opcode = kThumb2Vldrs;
+                if (displacement <= 1020) {
+                    shortForm = true;
+                    encodedDisp >>= 2;
+                }
+                break;
+            }
+            if (LOWREG(rDest) && (rBase == r15pc) &&
+                (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrPcRel;
+            } else if (LOWREG(rDest) && (rBase == r13sp) &&
+                      (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrSpRel;
+            } else if (allLowRegs && displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrRRI12;
+            }
+            break;
+        case kUnsignedHalf:
+            if (allLowRegs && displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbLdrhRRI5;
+            } else if (displacement < 4092 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumb2LdrhRRI12;
+            }
+            break;
+        case kSignedHalf:
+            if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrshRRI12;
+            }
+            break;
+        case kUnsignedByte:
+            if (allLowRegs && displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbLdrbRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrbRRI12;
+            }
+            break;
+        case kSignedByte:
+            if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrsbRRI12;
+            }
+            break;
+        default:
+            assert(0);
+    }
+
+    if (shortForm) {
+        load = res = newLIR3(cUnit, opcode, rDest, rBase, encodedDisp);
+    } else {
+        int regOffset = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, regOffset, encodedDisp);
+        load = loadBaseIndexed(cUnit, rBase, regOffset, rDest, 0, size);
+        dvmCompilerFreeTemp(cUnit, regOffset);
+    }
+
+    if (rBase == r5FP) {
+        annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+                            size, sReg);
+}
+
+static  ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                 int displacement, int rDestLo, int rDestHi,
+                                 int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+                            kLong, sReg);
+}
+
+
+static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrc, int rSrcHi,
+                                 OpSize size)
+{
+    ArmLIR *res, *store;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    bool thumb2Form = (displacement < 4092 && displacement >= 0);
+    bool allLowRegs = (LOWREG(rBase) && LOWREG(rSrc));
+    int encodedDisp = displacement;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            if (!FPREG(rSrc)) {
+                res = storeBaseDispBody(cUnit, rBase, displacement, rSrc,
+                                        -1, kWord);
+                storeBaseDispBody(cUnit, rBase, displacement + 4, rSrcHi,
+                                  -1, kWord);
+                return res;
+            }
+            if (SINGLEREG(rSrc)) {
+                assert(FPREG(rSrcHi));
+                rSrc = S2D(rSrc, rSrcHi);
+            }
+            opcode = kThumb2Vstrd;
+            if (displacement <= 1020) {
+                shortForm = true;
+                encodedDisp >>= 2;
+            }
+            break;
+        case kSingle:
+        case kWord:
+            if (FPREG(rSrc)) {
+                assert(SINGLEREG(rSrc));
+                opcode = kThumb2Vstrs;
+                if (displacement <= 1020) {
+                    shortForm = true;
+                    encodedDisp >>= 2;
+                }
+            break;
+            }
+            if (allLowRegs && displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbStrRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2StrRRI12;
+            }
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            if (allLowRegs && displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbStrhRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2StrhRRI12;
+            }
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            if (allLowRegs && displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbStrbRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2StrbRRI12;
+            }
+            break;
+        default:
+            assert(0);
+    }
+    if (shortForm) {
+        store = res = newLIR3(cUnit, opcode, rSrc, rBase, encodedDisp);
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, rScratch, encodedDisp);
+        store = storeBaseIndexed(cUnit, rBase, rScratch, rSrc, 0, size);
+        dvmCompilerFreeTemp(cUnit, rScratch);
+    }
+
+    if (rBase == r5FP) {
+        annotateDalvikRegAccess(store, displacement >> 2, false /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+#endif
+    return res;
+}
+
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
+        res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
+    } else {
+        res = newLIR2(cUnit, kThumb2Ldmia, rBase, rMask);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
+        res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
+    } else {
+        res = newLIR2(cUnit, kThumb2Stmia, rBase, rMask);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    storeBaseDispWide(cUnit, base, 0, lowReg, highReg);
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    loadBaseDispWide(cUnit, NULL, base, 0, lowReg, highReg, INVALID_SREG);
+}
+
+/*
+ * Generate a register comparison to an immediate and branch.  Caller
+ * is responsible for setting branch target field.
+ */
+static ArmLIR *genCmpImmBranch(CompilationUnit *cUnit,
+                              ArmConditionCode cond, int reg,
+                              int checkValue)
+{
+    ArmLIR *branch;
+    int modImm;
+    if ((LOWREG(reg)) && (checkValue == 0) &&
+       ((cond == kArmCondEq) || (cond == kArmCondNe))) {
+        branch = newLIR2(cUnit,
+                         (cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
+                         reg, 0);
+    } else {
+        modImm = modifiedImmediate(checkValue);
+        if (LOWREG(reg) && ((checkValue & 0xff) == checkValue)) {
+            newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
+        } else if (modImm >= 0) {
+            newLIR2(cUnit, kThumb2CmpRI8, reg, modImm);
+        } else {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            loadConstant(cUnit, tReg, checkValue);
+            opRegReg(cUnit, kOpCmp, reg, tReg);
+        }
+        branch = newLIR2(cUnit, kThumbBCond, 0, cond);
+    }
+    return branch;
+}
+
+static ArmLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR* res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    } else {
+        assert(DOUBLEREG(rDest) == DOUBLEREG(rSrc));
+        if (DOUBLEREG(rDest)) {
+            res->opcode = kThumb2Vmovd;
+        } else {
+            if (SINGLEREG(rDest)) {
+                res->opcode = SINGLEREG(rSrc) ? kThumb2Vmovs : kThumb2Fmsr;
+            } else {
+                assert(SINGLEREG(rSrc));
+                res->opcode = kThumb2Fmrs;
+            }
+        }
+        res->operands[0] = rDest;
+        res->operands[1] = rSrc;
+    }
+    setupResourceMasks(res);
+    return res;
+}
+
+static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR* res;
+    ArmOpcode opcode;
+    if (FPREG(rDest) || FPREG(rSrc))
+        return fpRegCopy(cUnit, rDest, rSrc);
+    res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    if (LOWREG(rDest) && LOWREG(rSrc))
+        opcode = kThumbMovRR;
+    else if (!LOWREG(rDest) && !LOWREG(rSrc))
+         opcode = kThumbMovRR_H2H;
+    else if (LOWREG(rDest))
+         opcode = kThumbMovRR_H2L;
+    else
+         opcode = kThumbMovRR_L2H;
+
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    res->opcode = opcode;
+    setupResourceMasks(res);
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    }
+    return res;
+}
+
+static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+    dvmCompilerAppendLIR(cUnit, (LIR*)res);
+    return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                           int srcLo, int srcHi)
+{
+    bool destFP = FPREG(destLo) && FPREG(destHi);
+    bool srcFP = FPREG(srcLo) && FPREG(srcHi);
+    assert(FPREG(srcLo) == FPREG(srcHi));
+    assert(FPREG(destLo) == FPREG(destHi));
+    if (destFP) {
+        if (srcFP) {
+            genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
+        } else {
+            newLIR3(cUnit, kThumb2Fmdrr, S2D(destLo, destHi), srcLo, srcHi);
+        }
+    } else {
+        if (srcFP) {
+            newLIR3(cUnit, kThumb2Fmrrd, destLo, destHi, S2D(srcLo, srcHi));
+        } else {
+            // Handle overlap
+            if (srcHi == destLo) {
+                genRegCopy(cUnit, destHi, srcHi);
+                genRegCopy(cUnit, destLo, srcLo);
+            } else {
+                genRegCopy(cUnit, destLo, srcLo);
+                genRegCopy(cUnit, destHi, srcHi);
+            }
+        }
+    }
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    ArmLIR *push = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    push->opcode = kThumbPush;
+    /* Thumb push can handle LR (encoded at bit 8) */
+    push->operands[0] = (1 << r5FP | 1 << 8);
+    setupResourceMasks(push);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) push);
+}
+
+static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    ArmLIR *pop = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    /* Thumb pop cannot store into LR - use Thumb2 here */
+    pop->opcode = kThumb2Pop;
+    pop->operands[0] = (1 << r5FP | 1 << r14lr);
+    setupResourceMasks(pop);
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) pop);
+}
+#endif
diff --git a/vm/compiler/codegen/arm/Thumb2/Gen.cpp b/vm/compiler/codegen/arm/Thumb2/Gen.cpp
new file mode 100644
index 0000000..ea64547
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Gen.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb2 ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Reserve 6 bytes at the beginning of the trace
+ *        +----------------------------+
+ *        | prof count addr (4 bytes)  |
+ *        +----------------------------+
+ *        | chain cell offset (2 bytes)|
+ *        +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (10 bytes)
+ *       ldr   r0, [pc-8]   @ get prof count addr    [4 bytes]
+ *       ldr   r1, [r0]     @ load counter           [2 bytes]
+ *       add   r1, #1       @ increment              [2 bytes]
+ *       str   r1, [r0]     @ store                  [2 bytes]
+ *
+ * For periodic profiling (4 bytes)
+ *       call  TEMPLATE_PERIODIC_PROFILING
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+    assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+    newLIR1(cUnit, kArm16BitData, addr & 0xffff);
+    newLIR1(cUnit, kArm16BitData, (addr >> 16) & 0xffff);
+    cUnit->chainCellOffsetLIR =
+        (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+    cUnit->headerSize = 6;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        /* Thumb[2] instruction used directly here to ensure correct size */
+        newLIR2(cUnit, kThumb2LdrPcReln12, r0, 8);
+        newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+        newLIR2(cUnit, kThumbAddRI8, r1, 1);
+        newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+        return 10;
+    } else {
+        int opcode = TEMPLATE_PERIODIC_PROFILING;
+        newLIR2(cUnit, kThumbBlx1,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        newLIR2(cUnit, kThumbBlx2,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        return 4;
+    }
+}
+
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+                        RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+                         RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc.lowReg, rlSrc.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/*
+ * To avoid possible conflicts, we use a lot of temps here.  Note that
+ * our usage of Thumb2 instruction forms avoids the problems with register
+ * reuse for multiply instructions prior to arm6.
+ */
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    int resLo = dvmCompilerAllocTemp(cUnit);
+    int resHi = dvmCompilerAllocTemp(cUnit);
+    int tmp1 = dvmCompilerAllocTemp(cUnit);
+
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+
+    newLIR3(cUnit, kThumb2MulRRR, tmp1, rlSrc2.lowReg, rlSrc1.highReg);
+    newLIR4(cUnit, kThumb2Umull, resLo, resHi, rlSrc2.lowReg, rlSrc1.lowReg);
+    newLIR4(cUnit, kThumb2Mla, tmp1, rlSrc1.lowReg, rlSrc2.highReg, tmp1);
+    newLIR4(cUnit, kThumb2AddRRR, resHi, tmp1, resHi, 0);
+    dvmCompilerFreeTemp(cUnit, tmp1);
+
+    rlResult = dvmCompilerGetReturnWide(cUnit);  // Just as a template, will patch
+    rlResult.lowReg = resLo;
+    rlResult.highReg = resHi;
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+                         OpKind secondOp, RegLocation rlDest,
+                         RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
+                rlSrc2.highReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+    int numTemps = sizeof(coreTemps)/sizeof(int);
+    int numFPTemps = sizeof(fpTemps)/sizeof(int);
+    RegisterPool *pool = (RegisterPool *)dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps = (RegisterInfo *)
+            dvmCompilerNew(numTemps * sizeof(*cUnit->regPool->coreTemps), true);
+    pool->numFPTemps = numFPTemps;
+    pool->FPTemps = (RegisterInfo *)
+            dvmCompilerNew(numFPTemps * sizeof(*cUnit->regPool->FPTemps), true);
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+    dvmCompilerInitPool(pool->FPTemps, fpTemps, pool->numFPTemps);
+    pool->nullCheckedRegs =
+        dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/*
+ * Generate a Thumb2 IT instruction, which can nullify up to
+ * four subsequent instructions based on a condition and its
+ * inverse.  The condition applies to the first instruction, which
+ * is executed if the condition is met.  The string "guide" consists
+ * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
+ * A "T" means the instruction is executed if the condition is
+ * met, and an "E" means the instruction is executed if the condition
+ * is not met.
+ */
+static ArmLIR *genIT(CompilationUnit *cUnit, ArmConditionCode code,
+                     const char *guide)
+{
+    int mask;
+    int condBit = code & 1;
+    int altBit = condBit ^ 1;
+    int mask3 = 0;
+    int mask2 = 0;
+    int mask1 = 0;
+
+    //Note: case fallthroughs intentional
+    switch(strlen(guide)) {
+        case 3:
+            mask1 = (guide[2] == 'T') ? condBit : altBit;
+        case 2:
+            mask2 = (guide[1] == 'T') ? condBit : altBit;
+        case 1:
+            mask3 = (guide[0] == 'T') ? condBit : altBit;
+            break;
+        case 0:
+            break;
+        default:
+            ALOGE("Jit: bad case in genIT");
+            dvmCompilerAbort(cUnit);
+    }
+    mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
+           (1 << (3 - strlen(guide)));
+    return newLIR2(cUnit, kThumb2It, code, mask);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+    ArmLIR *res;
+    int offset = offsetof(StackSaveArea, xtra.currentPc);
+    int rDPC = dvmCompilerAllocTemp(cUnit);
+    res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+    newLIR3(cUnit, kThumb2StrRRI8Predec, rDPC, r5FP,
+            sizeof(StackSaveArea) - offset);
+    dvmCompilerFreeTemp(cUnit, rDPC);
+    return res;
+}
+
+/*
+ * Handle simple case (thin lock) inline.  If it's complicated, bail
+ * out to the heavyweight lock/unlock routines.  We'll use dedicated
+ * registers here in order to be in the right position in case we
+ * to bail to dvm[Lock/Unlock]Object(self, object)
+ *
+ * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
+ * r1 -> object [arg1 for dvm[Lock/Unlock]Object
+ * r2 -> intial contents of object->lock, later result of strex
+ * r3 -> self->threadId
+ * r7 -> temp to hold new lock value [unlock only]
+ * r4 -> allow to be used by utilities as general temp
+ *
+ * The result of the strex is 0 if we acquire the lock.
+ *
+ * See comments in Sync.c for the layout of the lock word.
+ * Of particular interest to this code is the test for the
+ * simple case - which we handle inline.  For monitor enter, the
+ * simple case is thin lock, held by no-one.  For monitor exit,
+ * the simple case is thin lock, held by the unlocking thread with
+ * a recurse count of 0.
+ *
+ * A minor complication is that there is a field in the lock word
+ * unrelated to locking: the hash state.  This field must be ignored, but
+ * preserved.
+ *
+ */
+static void genMonitorEnter(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    ArmLIR *target;
+    ArmLIR *hopTarget;
+    ArmLIR *branch;
+    ArmLIR *hopBranch;
+
+    assert(LW_SHAPE_THIN == 0);
+    loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
+    dvmCompilerLockAllTemps(cUnit);  // Prepare for explicit register usage
+    dvmCompilerFreeTemp(cUnit, r4PC);  // Free up r4 for general use
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread, threadId), r3); // Get threadId
+    newLIR3(cUnit, kThumb2Ldrex, r2, r1,
+            offsetof(Object, lock) >> 2); // Get object->lock
+    opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner
+    // Is lock unheld on lock or held by us (==threadId) on unlock?
+    newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1);
+    newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT,
+            LW_LOCK_OWNER_SHIFT - 1);
+    hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
+    newLIR4(cUnit, kThumb2Strex, r2, r3, r1, offsetof(Object, lock) >> 2);
+    dvmCompilerGenMemBarrier(cUnit, kSY);
+    branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
+
+    hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+    hopTarget->defMask = ENCODE_ALL;
+    hopBranch->generic.target = (LIR *)hopTarget;
+
+    // Export PC (part 1)
+    loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset));
+
+    /* Get dPC of next insn */
+    loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_ENTER)));
+    // Export PC (part 2)
+    newLIR3(cUnit, kThumb2StrRRI8Predec, r3, r5FP,
+            sizeof(StackSaveArea) -
+            offsetof(StackSaveArea, xtra.currentPc));
+    /* Call template, and don't return */
+    genRegCopy(cUnit, r0, r6SELF);
+    genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+    // Resume here
+    target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *)target;
+}
+
+/*
+ * For monitor unlock, we don't have to use ldrex/strex.  Once
+ * we've determined that the lock is thin and that we own it with
+ * a zero recursion count, it's safe to punch it back to the
+ * initial, unlock thin state with a store word.
+ */
+static void genMonitorExit(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    ArmLIR *target;
+    ArmLIR *branch;
+    ArmLIR *hopTarget;
+    ArmLIR *hopBranch;
+
+    assert(LW_SHAPE_THIN == 0);
+    loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
+    dvmCompilerLockAllTemps(cUnit);  // Prepare for explicit register usage
+    dvmCompilerFreeTemp(cUnit, r4PC);  // Free up r4 for general use
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    loadWordDisp(cUnit, r1, offsetof(Object, lock), r2); // Get object->lock
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread, threadId), r3); // Get threadId
+    // Is lock unheld on lock or held by us (==threadId) on unlock?
+    opRegRegImm(cUnit, kOpAnd, r7, r2,
+                (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+    opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner
+    newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT,
+            LW_LOCK_OWNER_SHIFT - 1);
+    opRegReg(cUnit, kOpSub, r2, r3);
+    hopBranch = opCondBranch(cUnit, kArmCondNe);
+    dvmCompilerGenMemBarrier(cUnit, kSY);
+    storeWordDisp(cUnit, r1, offsetof(Object, lock), r7);
+    branch = opNone(cUnit, kOpUncondBr);
+
+    hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+    hopTarget->defMask = ENCODE_ALL;
+    hopBranch->generic.target = (LIR *)hopTarget;
+
+    // Export PC (part 1)
+    loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset));
+
+    LOAD_FUNC_ADDR(cUnit, r7, (int)dvmUnlockObject);
+    genRegCopy(cUnit, r0, r6SELF);
+    // Export PC (part 2)
+    newLIR3(cUnit, kThumb2StrRRI8Predec, r3, r5FP,
+            sizeof(StackSaveArea) -
+            offsetof(StackSaveArea, xtra.currentPc));
+    opReg(cUnit, kOpBlx, r7);
+    /* Did we throw? */
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+    loadConstant(cUnit, r0,
+                 (int) (cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_EXIT)));
+    genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+
+    // Resume here
+    target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *)target;
+    branchOver->generic.target = (LIR *) target;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+    if (mir->dalvikInsn.opcode == OP_MONITOR_ENTER)
+        genMonitorEnter(cUnit, mir);
+    else
+        genMonitorExit(cUnit, mir);
+}
+
+/*
+ * 64-bit 3way compare function.
+ *     mov   r7, #-1
+ *     cmp   op1hi, op2hi
+ *     blt   done
+ *     bgt   flip
+ *     sub   r7, op1lo, op2lo (treat as unsigned)
+ *     beq   done
+ *     ite   hi
+ *     mov(hi)   r7, #-1
+ *     mov(!hi)  r7, #1
+ * flip:
+ *     neg   r7
+ * done:
+ */
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
+                       RegLocation rlDest, RegLocation rlSrc1,
+                       RegLocation rlSrc2)
+{
+    RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
+    ArmLIR *target1;
+    ArmLIR *target2;
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+    rlTemp.lowReg = dvmCompilerAllocTemp(cUnit);
+    loadConstant(cUnit, rlTemp.lowReg, -1);
+    opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
+    ArmLIR *branch1 = opCondBranch(cUnit, kArmCondLt);
+    ArmLIR *branch2 = opCondBranch(cUnit, kArmCondGt);
+    opRegRegReg(cUnit, kOpSub, rlTemp.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    ArmLIR *branch3 = opCondBranch(cUnit, kArmCondEq);
+
+    genIT(cUnit, kArmCondHi, "E");
+    newLIR2(cUnit, kThumb2MovImmShift, rlTemp.lowReg, modifiedImmediate(-1));
+    loadConstant(cUnit, rlTemp.lowReg, 1);
+    genBarrier(cUnit);
+
+    target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target2->defMask = -1;
+    opRegReg(cUnit, kOpNeg, rlTemp.lowReg, rlTemp.lowReg);
+
+    target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target1->defMask = -1;
+
+    storeValue(cUnit, rlDest, rlTemp);
+
+    branch1->generic.target = (LIR *)target1;
+    branch2->generic.target = (LIR *)target2;
+    branch3->generic.target = branch1->generic.target;
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, true);
+    rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vabss, rlResult.lowReg, rlSrc.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
+    rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vabsd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc.lowReg, rlSrc.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
+    genIT(cUnit, (isMin) ? kArmCondGt : kArmCondLt, "E");
+    opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc2.lowReg);
+    opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc1.lowReg);
+    genBarrier(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit,
+        int firstBit, int secondBit)
+{
+    opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
+                     encodeShift(kArmLsl, secondBit - firstBit));
+    if (firstBit != 0) {
+        opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
+    }
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Ralloc.cpp b/vm/compiler/codegen/arm/Thumb2/Ralloc.cpp
new file mode 100644
index 0000000..6adfd62
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Ralloc.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/* Stress mode for testing: if defined will reverse corereg/floatreg hint */
+//#define REGCLASS_STRESS_MODE
+
+/*
+ * Alloc a pair of core registers, or a double.  Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+                                         bool fpHint, int regClass)
+{
+    int highReg;
+    int lowReg;
+    int res = 0;
+
+#if defined(REGCLASS_STRESS_MODE)
+    fpHint = !fpHint;
+#endif
+
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg)) {
+        lowReg = dvmCompilerAllocTempDouble(cUnit);
+        highReg = lowReg + 1;
+    } else {
+        lowReg = dvmCompilerAllocTemp(cUnit);
+        highReg = dvmCompilerAllocTemp(cUnit);
+    }
+    res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+    return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+                                     int regClass)
+{
+#if defined(REGCLASS_STRESS_MODE)
+    fpHint = !fpHint;
+#endif
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg))
+        return dvmCompilerAllocTempFloat(cUnit);
+    return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp
new file mode 100644
index 0000000..bc827be
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 9; // 512
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold != 0) {
+        gDvmJit.threshold = 200;
+    }
+    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_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* No method JIT for Thumb backend */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h
new file mode 100644
index 0000000..727c521
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode {
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S b/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+    vstmia r0, {d8-d15}
+    bx     lr
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+    vldmia r0, {d8-d15}
+    bx     lr
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/Codegen.cpp b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.cpp
new file mode 100644
index 0000000..55321bb
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV5TE_VFP
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb-specific factory utilities */
+#include "../Thumb/Factory.cpp"
+/* Target independent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.cpp"
+/* Thumb+VFP codegen routines */
+#include "../FP/ThumbVFP.cpp"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Dummy driver for method-based JIT */
+#include "../armv5te/MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp b/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp
new file mode 100644
index 0000000..50ba537
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * This file is included by Codegen-armv5te.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 9; // 512
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold != 0) {
+        gDvmJit.threshold = 200;
+    }
+    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_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* No method JIT for Thumb backend */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.h b/vm/compiler/codegen/arm/armv5te/ArchVariant.h
new file mode 100644
index 0000000..39a9548
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode {
+#include "../../../template/armv5te/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/arm/armv5te/CallingConvention.S b/vm/compiler/codegen/arm/armv5te/CallingConvention.S
new file mode 100644
index 0000000..0cbc64f
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/CallingConvention.S
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+    bx     lr
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+    bx     lr
diff --git a/vm/compiler/codegen/arm/armv5te/Codegen.cpp b/vm/compiler/codegen/arm/armv5te/Codegen.cpp
new file mode 100644
index 0000000..a379c2a
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV5TE
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb-specific building blocks */
+#include "../Thumb/Factory.cpp"
+/* Target independent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.cpp"
+/* Thumb+Portable FP codegen routines */
+#include "../FP/ThumbPortableFP.cpp"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Dummy driver for method-based JIT */
+#include "MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/arm/armv5te/MethodCodegenDriver.cpp b/vm/compiler/codegen/arm/armv5te/MethodCodegenDriver.cpp
new file mode 100644
index 0000000..a0dba0a
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/MethodCodegenDriver.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    ALOGE("Method-based JIT not supported for the v5te target");
+    dvmAbort();
+}
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
new file mode 100644
index 0000000..857960f
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB2;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 12; // 4096
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold != 0) {
+        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_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* FIXME - comment out the following to enable method-based JIT */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 7;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, barrierKind);
+    dmb->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h
new file mode 100644
index 0000000..33e262c
--- /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.cpp b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.cpp
new file mode 100644
index 0000000..8f15174
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV7_A_NEON
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.cpp"
+/* Target indepedent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.cpp"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.cpp"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Driver for method-based JIT */
+#include "MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.cpp b/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.cpp
new file mode 100644
index 0000000..84744b5
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.cpp
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if 0
+
+/*
+ * Rebuild the interpreter frame then punt to the interpreter to execute
+ * instruction at specified PC.
+ *
+ * Currently parameters are passed to the current frame, so we just need to
+ * grow the stack save area above it, fill certain fields in StackSaveArea and
+ * Thread that are skipped during whole-method invocation (specified below),
+ * then return to the interpreter.
+ *
+ * StackSaveArea:
+ *  - prevSave
+ *  - prevFrame
+ *  - savedPc
+ *  - returnAddr
+ *  - method
+ *
+ * Thread:
+ *  - method
+ *  - methodClassDex
+ *  - curFrame
+ */
+static void genMethodInflateAndPunt(CompilationUnit *cUnit, MIR *mir,
+                                    BasicBlock *bb)
+{
+    int oldStackSave = r0;
+    int newStackSave = r1;
+    int oldFP = r2;
+    int savedPC = r3;
+    int currentPC = r4PC;
+    int returnAddr = r7;
+    int method = r8;
+    int pDvmDex = r9;
+
+    /*
+     * TODO: check whether to raise the stack overflow exception when growing
+     * the stack save area.
+     */
+
+    /* Send everything to home location */
+    dvmCompilerFlushAllRegs(cUnit);
+
+    /* oldStackSave = r5FP + sizeof(current frame) */
+    opRegRegImm(cUnit, kOpAdd, oldStackSave, r5FP,
+                cUnit->method->registersSize * 4);
+    /* oldFP = oldStackSave + sizeof(stackSaveArea) */
+    opRegRegImm(cUnit, kOpAdd, oldFP, oldStackSave, sizeof(StackSaveArea));
+    /* newStackSave = r5FP - sizeof(StackSaveArea) */
+    opRegRegImm(cUnit, kOpSub, newStackSave, r5FP, sizeof(StackSaveArea));
+
+    loadWordDisp(cUnit, r13sp, 0, savedPC);
+    loadConstant(cUnit, currentPC, (int) (cUnit->method->insns + mir->offset));
+    loadConstant(cUnit, method, (int) cUnit->method);
+    loadConstant(cUnit, pDvmDex, (int) cUnit->method->clazz->pDvmDex);
+#ifdef EASY_GDB
+    /* newStackSave->prevSave = oldStackSave */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, prevSave),
+                  oldStackSave);
+#endif
+    /* newStackSave->prevSave = oldStackSave */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, prevFrame),
+                  oldFP);
+    /* newStackSave->savedPc = savedPC */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, savedPc),
+                  savedPC);
+    /* return address */
+    loadConstant(cUnit, returnAddr, 0);
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, returnAddr),
+                  returnAddr);
+    /* newStackSave->method = method */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, method), method);
+    /* thread->method = method */
+    storeWordDisp(cUnit, r6SELF, offsetof(InterpSaveState, method), method);
+    /* thread->interpSave.curFrame = current FP */
+    storeWordDisp(cUnit, r6SELF, offsetof(Thread, interpSave.curFrame), r5FP);
+    /* thread->methodClassDex = pDvmDex */
+    storeWordDisp(cUnit, r6SELF, offsetof(InterpSaveState, methodClassDex),
+                  pDvmDex);
+    /* Restore the stack pointer */
+    opRegImm(cUnit, kOpAdd, r13sp, 16);
+    genPuntToInterp(cUnit, mir->offset);
+}
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ *
+ * TODO - most them are just pass-through to the trace-based versions for now
+ */
+static bool handleMethodFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+                                             BasicBlock *bb, ArmLIR *labelList)
+{
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch && gDvmJit.genSuspendPoll) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+    genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    return false;
+}
+
+static bool handleMethodFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_RETURN_VOID:
+            return false;
+        default:
+            return handleFmt10x(cUnit, mir);
+    }
+}
+
+static bool handleMethodFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt11n_Fmt31i(cUnit, mir);
+}
+
+static bool handleMethodFmt11x(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                               ArmLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_THROW:
+            genMethodInflateAndPunt(cUnit, mir, bb);
+            return false;
+        default:
+            return handleFmt11x(cUnit, mir);
+    }
+}
+
+static bool handleMethodFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt12x(cUnit, mir);
+}
+
+static bool handleMethodFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt20bc(cUnit, mir);
+}
+
+static bool handleMethodFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt21c_Fmt31c(cUnit, mir);
+}
+
+static bool handleMethodFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt21h(cUnit, mir);
+}
+
+static bool handleMethodFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt21s(cUnit, mir);
+}
+
+static bool handleMethodFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                               ArmLIR *labelList)
+{
+    return handleFmt21t(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22b_Fmt22s(cUnit, mir);
+}
+
+static bool handleMethodFmt22c(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22c(cUnit, mir);
+}
+
+static bool handleMethodFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22cs(cUnit, mir);
+}
+
+static bool handleMethodFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                               ArmLIR *labelList)
+{
+    return handleFmt22t(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22x_Fmt32x(cUnit, mir);
+}
+
+static bool handleMethodFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt23x(cUnit, mir);
+}
+
+static bool handleMethodFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt31t(cUnit, mir);
+}
+
+static bool handleMethodFmt35c_3rc(CompilationUnit *cUnit, MIR *mir,
+                                       BasicBlock *bb, ArmLIR *labelList)
+{
+    return handleFmt35c_3rc(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+                                     BasicBlock *bb, ArmLIR *labelList)
+{
+    return handleFmt35ms_3rms(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodExecuteInline(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleExecuteInline(cUnit, mir);
+}
+
+static bool handleMethodFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt51l(cUnit, mir);
+}
+
+/* Handle the content in each basic block */
+static bool methodBlockCodeGen(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    ArmLIR *labelList = (ArmLIR *) cUnit->blockLabelList;
+    int blockId = bb->id;
+
+    cUnit->curBlock = bb;
+    labelList[blockId].operands[0] = bb->startOffset;
+
+    /* Insert the block label */
+    labelList[blockId].opcode = kArmPseudoNormalBlockLabel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+    dvmCompilerClobberAllRegs(cUnit);
+    dvmCompilerResetNullCheck(cUnit);
+
+    ArmLIR *headLIR = NULL;
+
+    if (bb->blockType == kEntryBlock) {
+        /* r0 = callsitePC */
+        opImm(cUnit, kOpPush, (1 << r0 | 1 << r1 | 1 << r5FP | 1 << r14lr));
+        opRegImm(cUnit, kOpSub, r5FP,
+                 sizeof(StackSaveArea) + cUnit->method->registersSize * 4);
+
+    } else if (bb->blockType == kExitBlock) {
+        /* No need to pop r0 and r1 */
+        opRegImm(cUnit, kOpAdd, r13sp, 8);
+        opImm(cUnit, kOpPop, (1 << r5FP | 1 << r15pc));
+    }
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+
+        dvmCompilerResetRegPool(cUnit);
+        if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+            dvmCompilerClobberAllRegs(cUnit);
+        }
+
+        if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+            dvmCompilerResetDefTracking(cUnit);
+        }
+
+        Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+        InstructionFormat dalvikFormat =
+            dexGetFormatFromOpcode(dalvikOpcode);
+
+        ArmLIR *boundaryLIR;
+
+        /*
+         * Don't generate the boundary LIR unless we are debugging this
+         * trace or we need a scheduling barrier.
+         */
+        if (headLIR == NULL || cUnit->printMe == true) {
+            boundaryLIR =
+                newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary,
+                        mir->offset,
+                        (int) dvmCompilerGetDalvikDisassembly(
+                            &mir->dalvikInsn, ""));
+            /* Remember the first LIR for this block */
+            if (headLIR == NULL) {
+                headLIR = boundaryLIR;
+                /* Set the first boundaryLIR as a scheduling barrier */
+                headLIR->defMask = ENCODE_ALL;
+            }
+        }
+
+        /* Don't generate the SSA annotation unless verbose mode is on */
+        if (cUnit->printMe && mir->ssaRep) {
+            char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+            newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+        }
+
+        bool notHandled;
+        switch (dalvikFormat) {
+            case kFmt10t:
+            case kFmt20t:
+            case kFmt30t:
+                notHandled = handleMethodFmt10t_Fmt20t_Fmt30t(cUnit, mir, bb,
+                                                              labelList);
+                break;
+            case kFmt10x:
+                notHandled = handleMethodFmt10x(cUnit, mir);
+                break;
+            case kFmt11n:
+            case kFmt31i:
+                notHandled = handleMethodFmt11n_Fmt31i(cUnit, mir);
+                break;
+            case kFmt11x:
+                notHandled = handleMethodFmt11x(cUnit, mir, bb, labelList);
+                break;
+            case kFmt12x:
+                notHandled = handleMethodFmt12x(cUnit, mir);
+                break;
+            case kFmt20bc:
+                notHandled = handleMethodFmt20bc(cUnit, mir);
+                break;
+            case kFmt21c:
+            case kFmt31c:
+                notHandled = handleMethodFmt21c_Fmt31c(cUnit, mir);
+                break;
+            case kFmt21h:
+                notHandled = handleMethodFmt21h(cUnit, mir);
+                break;
+            case kFmt21s:
+                notHandled = handleMethodFmt21s(cUnit, mir);
+                break;
+            case kFmt21t:
+                notHandled = handleMethodFmt21t(cUnit, mir, bb, labelList);
+                break;
+            case kFmt22b:
+            case kFmt22s:
+                notHandled = handleMethodFmt22b_Fmt22s(cUnit, mir);
+                break;
+            case kFmt22c:
+                notHandled = handleMethodFmt22c(cUnit, mir);
+                break;
+            case kFmt22cs:
+                notHandled = handleMethodFmt22cs(cUnit, mir);
+                break;
+            case kFmt22t:
+                notHandled = handleMethodFmt22t(cUnit, mir, bb, labelList);
+                break;
+            case kFmt22x:
+            case kFmt32x:
+                notHandled = handleMethodFmt22x_Fmt32x(cUnit, mir);
+                break;
+            case kFmt23x:
+                notHandled = handleMethodFmt23x(cUnit, mir);
+                break;
+            case kFmt31t:
+                notHandled = handleMethodFmt31t(cUnit, mir);
+                break;
+            case kFmt3rc:
+            case kFmt35c:
+                notHandled = handleMethodFmt35c_3rc(cUnit, mir, bb, labelList);
+                break;
+            case kFmt3rms:
+            case kFmt35ms:
+                notHandled = handleMethodFmt35ms_3rms(cUnit, mir, bb,
+                                                      labelList);
+                break;
+            case kFmt35mi:
+            case kFmt3rmi:
+                notHandled = handleMethodExecuteInline(cUnit, mir);
+                break;
+            case kFmt51l:
+                notHandled = handleMethodFmt51l(cUnit, mir);
+                break;
+            default:
+                notHandled = true;
+                break;
+        }
+
+        /* FIXME - to be implemented */
+        if (notHandled == true && dalvikOpcode >= kNumPackedOpcodes) {
+            notHandled = false;
+        }
+
+        if (notHandled) {
+            ALOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled",
+                 mir->offset,
+                 dalvikOpcode, dexGetOpcodeName(dalvikOpcode),
+                 dalvikFormat);
+            dvmCompilerAbort(cUnit);
+            break;
+        }
+    }
+
+    if (headLIR) {
+        /*
+         * Eliminate redundant loads/stores and delay stores into later
+         * slots
+         */
+        dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                           cUnit->lastLIRInsn);
+
+        /*
+         * Generate an unconditional branch to the fallthrough block.
+         */
+        if (bb->fallThrough) {
+            genUnconditionalBranch(cUnit,
+                                   &labelList[bb->fallThrough->id]);
+        }
+    }
+    return false;
+}
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    // FIXME - enable method compilation for selected routines here
+    if (strcmp(cUnit->method->name, "add")) return;
+
+    /* Used to hold the labels of each block */
+    cUnit->blockLabelList =
+        (void *) dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, methodBlockCodeGen,
+                                          kPreOrderDFSTraversal,
+                                          false /* isIterative */);
+
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+
+    // FIXME - temporarily enable verbose printing for all methods
+    cUnit->printMe = true;
+
+#if defined(WITH_SELF_VERIFICATION)
+    selfVerificationBranchInsertPass(cUnit);
+#endif
+}
+
+#else
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit) {
+    // Method-based JIT not supported for ARM.
+}
+
+#endif
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp b/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp
new file mode 100644
index 0000000..857960f
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB2;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 12; // 4096
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold != 0) {
+        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_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* FIXME - comment out the following to enable method-based JIT */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 7;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, barrierKind);
+    dmb->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.h b/vm/compiler/codegen/arm/armv7-a/ArchVariant.h
new file mode 100644
index 0000000..b4f4eb7
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV7_A_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV7_A_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode {
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV7_A_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/arm/armv7-a/CallingConvention.S b/vm/compiler/codegen/arm/armv7-a/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+    vstmia r0, {d8-d15}
+    bx     lr
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+    vldmia r0, {d8-d15}
+    bx     lr
diff --git a/vm/compiler/codegen/arm/armv7-a/Codegen.cpp b/vm/compiler/codegen/arm/armv7-a/Codegen.cpp
new file mode 100644
index 0000000..e1b0ee9
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV7_A
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.cpp"
+/* Target independent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.cpp"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.cpp"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Driver for method-based JIT */
+#include "../armv7-a-neon/MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/mips/ArchUtility.cpp b/vm/compiler/codegen/mips/ArchUtility.cpp
new file mode 100644
index 0000000..1f6d593
--- /dev/null
+++ b/vm/compiler/codegen/mips/ArchUtility.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../../CompilerInternals.h"
+#include "libdex/DexOpcodes.h"
+#include "MipsLIR.h"
+
+/* For dumping instructions */
+#define MIPS_REG_COUNT 32
+static const char *mipsRegName[MIPS_REG_COUNT] = {
+    "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+    "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+    "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+    "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
+};
+
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+static void buildInsnString(const char *fmt, MipsLIR *lir, char* buf,
+                            unsigned char *baseAddr, int size)
+{
+    int i;
+    char *bufEnd = &buf[size-1];
+    const char *fmtEnd = &fmt[strlen(fmt)];
+    char tbuf[256];
+    char nc;
+    while (fmt < fmtEnd) {
+        int operand;
+        if (*fmt == '!') {
+            fmt++;
+            assert(fmt < fmtEnd);
+            nc = *fmt++;
+            if (nc=='!') {
+                strcpy(tbuf, "!");
+            } else {
+               assert(fmt < fmtEnd);
+               assert((unsigned)(nc-'0') < 4);
+               operand = lir->operands[nc-'0'];
+               switch(*fmt++) {
+                   case 'b':
+                       strcpy(tbuf,"0000");
+                       for (i=3; i>= 0; i--) {
+                           tbuf[i] += operand & 1;
+                           operand >>= 1;
+                       }
+                       break;
+                   case 's':
+                       sprintf(tbuf,"$f%d",operand & FP_REG_MASK);
+                       break;
+                   case 'S':
+		       assert(((operand & FP_REG_MASK) & 1) == 0);
+                       sprintf(tbuf,"$f%d",operand & FP_REG_MASK);
+                       break;
+                   case 'h':
+                       sprintf(tbuf,"%04x", operand);
+                       break;
+                   case 'M':
+                   case 'd':
+                       sprintf(tbuf,"%d", operand);
+                       break;
+                   case 'D':
+                       sprintf(tbuf,"%d", operand+1);
+                       break;
+                   case 'E':
+                       sprintf(tbuf,"%d", operand*4);
+                       break;
+                   case 'F':
+                       sprintf(tbuf,"%d", operand*2);
+                       break;
+                   case 'c':
+                       switch (operand) {
+                           case kMipsCondEq:
+                               strcpy(tbuf, "eq");
+                               break;
+                           case kMipsCondNe:
+                               strcpy(tbuf, "ne");
+                               break;
+                           case kMipsCondLt:
+                               strcpy(tbuf, "lt");
+                               break;
+                           case kMipsCondGe:
+                               strcpy(tbuf, "ge");
+                               break;
+                           case kMipsCondGt:
+                               strcpy(tbuf, "gt");
+                               break;
+                           case kMipsCondLe:
+                               strcpy(tbuf, "le");
+                               break;
+                           case kMipsCondCs:
+                               strcpy(tbuf, "cs");
+                               break;
+                           case kMipsCondMi:
+                               strcpy(tbuf, "mi");
+                               break;
+                           default:
+                               strcpy(tbuf, "");
+                               break;
+                       }
+                       break;
+                   case 't':
+                       sprintf(tbuf,"0x%08x (L%p)",
+                               (int) baseAddr + lir->generic.offset + 4 +
+                               (operand << 2),
+                               lir->generic.target);
+                       break;
+                   case 'T':
+                       sprintf(tbuf,"0x%08x",
+                               (int) (operand << 2));
+                       break;
+                   case 'u': {
+                       int offset_1 = lir->operands[0];
+                       int offset_2 = NEXT_LIR(lir)->operands[0];
+                       intptr_t target =
+                           ((((intptr_t) baseAddr + lir->generic.offset + 4) &
+                            ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
+                           0xfffffffc;
+                       sprintf(tbuf, "%p", (void *) target);
+                       break;
+                    }
+
+                   /* Nothing to print for BLX_2 */
+                   case 'v':
+                       strcpy(tbuf, "see above");
+                       break;
+                   case 'r':
+                       assert(operand >= 0 && operand < MIPS_REG_COUNT);
+                       strcpy(tbuf, mipsRegName[operand]);
+                       break;
+                   default:
+                       strcpy(tbuf,"DecodeError");
+                       break;
+               }
+               if (buf+strlen(tbuf) <= bufEnd) {
+                   strcpy(buf, tbuf);
+                   buf += strlen(tbuf);
+               } else {
+                   break;
+               }
+            }
+        } else {
+           *buf++ = *fmt++;
+        }
+        if (buf == bufEnd)
+            break;
+    }
+    *buf = 0;
+}
+
+void dvmDumpResourceMask(LIR *lir, u8 mask, const char *prefix)
+{
+    char buf[256];
+    buf[0] = 0;
+    MipsLIR *mipsLIR = (MipsLIR *) lir;
+
+    if (mask == ENCODE_ALL) {
+        strcpy(buf, "all");
+    } else {
+        char num[8];
+        int i;
+
+        for (i = 0; i < kRegEnd; i++) {
+            if (mask & (1ULL << i)) {
+                sprintf(num, "%d ", i);
+                strcat(buf, num);
+            }
+        }
+
+        if (mask & ENCODE_CCODE) {
+            strcat(buf, "cc ");
+        }
+        if (mask & ENCODE_FP_STATUS) {
+            strcat(buf, "fpcc ");
+        }
+        /* Memory bits */
+        if (mipsLIR && (mask & ENCODE_DALVIK_REG)) {
+            sprintf(buf + strlen(buf), "dr%d%s", mipsLIR->aliasInfo & 0xffff,
+                    (mipsLIR->aliasInfo & 0x80000000) ? "(+1)" : "");
+        }
+        if (mask & ENCODE_LITERAL) {
+            strcat(buf, "lit ");
+        }
+
+        if (mask & ENCODE_HEAP_REF) {
+            strcat(buf, "heap ");
+        }
+        if (mask & ENCODE_MUST_NOT_ALIAS) {
+            strcat(buf, "noalias ");
+        }
+    }
+    if (buf[0]) {
+        ALOGD("%s: %s", prefix, buf);
+    }
+}
+
+/*
+ * Debugging macros
+ */
+#define DUMP_RESOURCE_MASK(X)
+#define DUMP_SSA_REP(X)
+
+/* Pretty-print a LIR instruction */
+void dvmDumpLIRInsn(LIR *arg, unsigned char *baseAddr)
+{
+    MipsLIR *lir = (MipsLIR *) arg;
+    char buf[256];
+    char opName[256];
+    int offset = lir->generic.offset;
+    int dest = lir->operands[0];
+    const bool dumpNop = false;
+
+    /* Handle pseudo-ops individually, and all regular insns as a group */
+    switch(lir->opcode) {
+        case kMipsChainingCellBottom:
+            ALOGD("-------- end of chaining cells (0x%04x)", offset);
+            break;
+        case kMipsPseudoBarrier:
+            ALOGD("-------- BARRIER");
+            break;
+        case kMipsPseudoExtended:
+            /* intentional fallthrough */
+        case kMipsPseudoSSARep:
+            DUMP_SSA_REP(ALOGD("-------- %s", (char *) dest));
+            break;
+        case kMipsPseudoChainingCellBackwardBranch:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (backward branch): 0x%04x", dest);
+            break;
+        case kMipsPseudoChainingCellNormal:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (normal): 0x%04x", dest);
+            break;
+        case kMipsPseudoChainingCellHot:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (hot): 0x%04x", dest);
+            break;
+        case kMipsPseudoChainingCellInvokePredicted:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (predicted): %s%s",
+                 dest ? ((Method *) dest)->clazz->descriptor : "",
+                 dest ? ((Method *) dest)->name : "N/A");
+            break;
+        case kMipsPseudoChainingCellInvokeSingleton:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (invoke singleton): %s%s/%p",
+                 ((Method *)dest)->clazz->descriptor,
+                 ((Method *)dest)->name,
+                 ((Method *)dest)->insns);
+            break;
+        case kMipsPseudoEntryBlock:
+            ALOGD("-------- entry offset: 0x%04x", dest);
+            break;
+        case kMipsPseudoDalvikByteCodeBoundary:
+            ALOGD("-------- dalvik offset: 0x%04x @ %s", dest,
+                 (char *) lir->operands[1]);
+            break;
+        case kMipsPseudoExitBlock:
+            ALOGD("-------- exit offset: 0x%04x", dest);
+            break;
+        case kMipsPseudoPseudoAlign4:
+            ALOGD("%p (%04x): .align4", baseAddr + offset, offset);
+            break;
+        case kMipsPseudoPCReconstructionCell:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x", dest,
+                 lir->operands[1]);
+            break;
+        case kMipsPseudoPCReconstructionBlockLabel:
+            /* Do nothing */
+            break;
+        case kMipsPseudoEHBlockLabel:
+            ALOGD("Exception_Handling:");
+            break;
+        case kMipsPseudoTargetLabel:
+        case kMipsPseudoNormalBlockLabel:
+            ALOGD("L%p:", lir);
+            break;
+        default:
+            if (lir->flags.isNop && !dumpNop) {
+                break;
+            }
+            buildInsnString(EncodingMap[lir->opcode].name, lir, opName,
+                            baseAddr, 256);
+            buildInsnString(EncodingMap[lir->opcode].fmt, lir, buf, baseAddr,
+                            256);
+            ALOGD("%p (%04x): %08x %-9s%s%s",
+                 baseAddr + offset, offset, *(u4 *)(baseAddr + offset), opName, buf,
+                 lir->flags.isNop ? "(nop)" : "");
+            break;
+    }
+
+    if (lir->useMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->useMask, "use"));
+    }
+    if (lir->defMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->defMask, "def"));
+    }
+}
+
+/* Dump instructions and constant pool contents */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit)
+{
+    ALOGD("Dumping LIR insns");
+    LIR *lirInsn;
+    MipsLIR *mipsLIR;
+
+    ALOGD("installed code is at %p", cUnit->baseAddr);
+    ALOGD("total size is %d bytes", cUnit->totalSize);
+    for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
+        dvmDumpLIRInsn(lirInsn, (unsigned char *) cUnit->baseAddr);
+    }
+    for (lirInsn = cUnit->classPointerList; lirInsn; lirInsn = lirInsn->next) {
+        mipsLIR = (MipsLIR *) lirInsn;
+        ALOGD("%p (%04x): .class (%s)",
+             (char*)cUnit->baseAddr + mipsLIR->generic.offset,
+             mipsLIR->generic.offset,
+             ((CallsiteInfo *) mipsLIR->operands[0])->classDescriptor);
+    }
+    for (lirInsn = cUnit->literalList; lirInsn; lirInsn = lirInsn->next) {
+        mipsLIR = (MipsLIR *) lirInsn;
+        ALOGD("%p (%04x): .word (%#x)",
+             (char*)cUnit->baseAddr + mipsLIR->generic.offset,
+             mipsLIR->generic.offset,
+             mipsLIR->operands[0]);
+    }
+}
+
+/* Target-specific cache flushing */
+void dvmCompilerCacheFlush(long start, long end, long flags)
+{
+    cacheflush(start, end, flags);
+}
+
+/* Target-specific cache clearing */
+void dvmCompilerCacheClear(char *start, size_t size)
+{
+    /* 0x66 is an invalid opcode for mips. */
+    memset(start, 0x66, size);
+}
diff --git a/vm/compiler/codegen/mips/Assemble.cpp b/vm/compiler/codegen/mips/Assemble.cpp
new file mode 100644
index 0000000..713bced
--- /dev/null
+++ b/vm/compiler/codegen/mips/Assemble.cpp
@@ -0,0 +1,2324 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+
+#include "../../CompilerInternals.h"
+#include "MipsLIR.h"
+#include "Codegen.h"
+#include <unistd.h>             /* for cacheflush */
+#include <sys/mman.h>           /* for protection change */
+
+#define MAX_ASSEMBLER_RETRIES 10
+
+/*
+ * opcode: MipsOpCode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+                     k3, k3s, k3e, flags, name, fmt, size) \
+        {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+                    {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+
+/* Instruction dump string format keys: !pf, where "!" is the start
+ * of the key, "p" is which numeric operand to use and "f" is the
+ * print format.
+ *
+ * [p]ositions:
+ *     0 -> operands[0] (dest)
+ *     1 -> operands[1] (src1)
+ *     2 -> operands[2] (src2)
+ *     3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ *     h -> 4-digit hex
+ *     d -> decimal
+ *     E -> decimal*4
+ *     F -> decimal*2
+ *     c -> branch condition (beq, bne, etc.)
+ *     t -> pc-relative target
+ *     T -> pc-region target
+ *     u -> 1st half of bl[x] target
+ *     v -> 2nd half ob bl[x] target
+ *     R -> register list
+ *     s -> single precision floating point register
+ *     S -> double precision floating point register
+ *     m -> Thumb2 modified immediate
+ *     n -> complimented Thumb2 modified immediate
+ *     M -> Thumb2 16-bit zero-extended immediate
+ *     b -> 4-digit binary
+ *
+ *  [!] escape.  To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum MipsOpcode from MipsLIR.h */
+MipsEncodingMap EncodingMap[kMipsLast] = {
+    ENCODING_MAP(kMips32BitData, 0x00000000,
+                 kFmtBitBlt, 31, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP,
+                 "data", "0x!0h(!0d)", 2),
+    ENCODING_MAP(kMipsAddiu, 0x24000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "addiu", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsAddu, 0x00000021,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "addu", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsAnd, 0x00000024,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "and", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsAndi, 0x30000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "andi", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsB, 0x10000000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
+                 "b", "!0t", 2),
+    ENCODING_MAP(kMipsBal, 0x04110000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH | REG_DEF_LR,
+                 "bal", "!0t", 2),
+    ENCODING_MAP(kMipsBeq, 0x10000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01,
+                 "beq", "!0r,!1r,!2t", 2),
+    ENCODING_MAP(kMipsBeqz, 0x10000000, /* same as beq above with t = $zero */
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "beqz", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBgez, 0x04010000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bgez", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBgtz, 0x1C000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bgtz", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBlez, 0x18000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "blez", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBltz, 0x04000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bltz", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBnez, 0x14000000, /* same as bne below with t = $zero */
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bnez", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBne, 0x14000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01,
+                 "bne", "!0r,!1r,!2t", 2),
+    ENCODING_MAP(kMipsDiv, 0x0000001a,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtBitBlt, 25, 21,
+                 kFmtBitBlt, 20, 16, IS_QUAD_OP | REG_DEF01 | REG_USE23,
+                 "div", "!2r,!3r", 2),
+#if __mips_isa_rev>=2
+    ENCODING_MAP(kMipsExt, 0x7c000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 10, 6,
+                 kFmtBitBlt, 15, 11, IS_QUAD_OP | REG_DEF0 | REG_USE1,
+                 "ext", "!0r,!1r,!2d,!3D", 2),
+#endif
+    ENCODING_MAP(kMipsJal, 0x0c000000,
+                 kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "jal", "!0T(!0E)", 2),
+    ENCODING_MAP(kMipsJalr, 0x00000009,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF0_USE1,
+                 "jalr", "!0r,!1r", 2),
+    ENCODING_MAP(kMipsJr, 0x00000008,
+                 kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "jr", "!0r", 2),
+    ENCODING_MAP(kMipsLahi, 0x3C000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "lahi/lui", "!0r,0x!1h(!1d)", 2),
+    ENCODING_MAP(kMipsLalo, 0x34000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "lalo/ori", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsLui, 0x3C000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "lui", "!0r,0x!1h(!1d)", 2),
+    ENCODING_MAP(kMipsLb, 0x80000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lb", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLbu, 0x90000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lbu", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLh, 0x84000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lh", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLhu, 0x94000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lhu", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLw, 0x8C000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lw", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsMfhi, 0x00000010,
+                 kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mfhi", "!0r", 2),
+    ENCODING_MAP(kMipsMflo, 0x00000012,
+                 kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mflo", "!0r", 2),
+    ENCODING_MAP(kMipsMove, 0x00000025, /* or using zero reg */
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "move", "!0r,!1r", 2),
+    ENCODING_MAP(kMipsMovz, 0x0000000a,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "movz", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsMul, 0x70000002,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsNop, 0x00000000,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND,
+                 "nop", "", 2),
+    ENCODING_MAP(kMipsNor, 0x00000027, /* used for "not" too */
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "nor", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsOr, 0x00000025,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "or", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsOri, 0x34000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "ori", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsPref, 0xCC000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE2,
+                 "pref", "!0d,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsSb, 0xA0000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sb", "!0r,!1d(!2r)", 2),
+#if __mips_isa_rev>=2
+    ENCODING_MAP(kMipsSeb, 0x7c000420,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "seb", "!0r,!1r", 2),
+    ENCODING_MAP(kMipsSeh, 0x7c000620,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "seh", "!0r,!1r", 2),
+#endif
+    ENCODING_MAP(kMipsSh, 0xA4000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sh", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsSll, 0x00000000,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "sll", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSllv, 0x00000004,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sllv", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSlt, 0x0000002a,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "slt", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSlti, 0x28000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "slti", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSltu, 0x0000002b,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sltu", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSra, 0x00000003,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "sra", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSrav, 0x00000007,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "srav", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSrl, 0x00000002,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "srl", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSrlv, 0x00000006,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "srlv", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSubu, 0x00000023, /* used for "neg" too */
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "subu", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSw, 0xAC000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sw", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsXor, 0x00000026,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "xor", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsXori, 0x38000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "xori", "!0r,!1r,0x!2h(!2d)", 2),
+#ifdef __mips_hard_float
+    ENCODING_MAP(kMipsFadds, 0x46000000,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "add.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFsubs, 0x46000001,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sub.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFmuls, 0x46000002,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFdivs, 0x46000003,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "div.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFaddd, 0x46200000,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "add.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFsubd, 0x46200001,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sub.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFmuld, 0x46200002,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFdivd, 0x46200003,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "div.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFcvtsd, 0x46200020,
+                 kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.s.d", "!0s,!1S", 2),
+    ENCODING_MAP(kMipsFcvtsw, 0x46800020,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.s.w", "!0s,!1s", 2),
+    ENCODING_MAP(kMipsFcvtds, 0x46000021,
+                 kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.d.s", "!0S,!1s", 2),
+    ENCODING_MAP(kMipsFcvtdw, 0x46800021,
+                 kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.d.w", "!0S,!1s", 2),
+    ENCODING_MAP(kMipsFcvtws, 0x46000024,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.w.s", "!0s,!1s", 2),
+    ENCODING_MAP(kMipsFcvtwd, 0x46200024,
+                 kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.w.d", "!0s,!1S", 2),
+    ENCODING_MAP(kMipsFmovs, 0x46000006,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov.s", "!0s,!1s", 2),
+    ENCODING_MAP(kMipsFmovd, 0x46200006,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov.d", "!0S,!1S", 2),
+    ENCODING_MAP(kMipsFlwc1, 0xC4000000,
+                 kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lwc1", "!0s,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsFldc1, 0xD4000000,
+                 kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "ldc1", "!0S,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsFswc1, 0xE4000000,
+                 kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "swc1", "!0s,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsFsdc1, 0xF4000000,
+                 kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sdc1", "!0S,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsMfc1, 0x44000000,
+                 kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mfc1", "!0r,!1s", 2),
+    ENCODING_MAP(kMipsMtc1, 0x44800000,
+                 kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1,
+                 "mtc1", "!0r,!1s", 2),
+#endif
+    ENCODING_MAP(kMipsUndefined, 0x64000000,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND,
+                 "undefined", "", 2),
+};
+
+/* Track the number of times that the code cache is patched */
+#if defined(WITH_JIT_TUNING)
+#define UPDATE_CODE_CACHE_PATCHES()    (gDvmJit.codeCachePatches++)
+#else
+#define UPDATE_CODE_CACHE_PATCHES()
+#endif
+
+/* Write the numbers in the constant and class pool to the output stream */
+static void installLiteralPools(CompilationUnit *cUnit)
+{
+    int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset);
+    /* Install number of class pointer literals */
+    *dataPtr++ = cUnit->numClassPointers;
+    MipsLIR *dataLIR = (MipsLIR *) cUnit->classPointerList;
+    while (dataLIR) {
+        /*
+         * Install the callsiteinfo pointers into the cells for now. They will
+         * be converted into real pointers in dvmJitInstallClassObjectPointers.
+         */
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+    dataLIR = (MipsLIR *) cUnit->literalList;
+    while (dataLIR) {
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+}
+
+/*
+ * Assemble the LIR into binary instruction format.  Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction.  In those cases we will try to substitute a new code
+ * sequence or request that the trace be shortened and retried.
+ */
+static AssemblerStatus assembleInstructions(CompilationUnit *cUnit,
+                                            intptr_t startAddr)
+{
+    int *bufferAddr = (int *) cUnit->codeBuffer;
+    MipsLIR *lir;
+
+    for (lir = (MipsLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
+        if (lir->opcode < 0) {
+            continue;
+        }
+
+
+        if (lir->flags.isNop) {
+            continue;
+        }
+
+        if (lir->opcode == kMipsB || lir->opcode == kMipsBal) {
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiple of 4: %d", delta);
+                dvmAbort();
+            }
+            if (delta > 131068 || delta < -131069) {
+                ALOGE("Unconditional branch distance out of range: %d", delta);
+                dvmAbort();
+            }
+            lir->operands[0] = delta >> 2;
+        } else if (lir->opcode >= kMipsBeqz && lir->opcode <= kMipsBnez) {
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiple of 4: %d", delta);
+                dvmAbort();
+            }
+            if (delta > 131068 || delta < -131069) {
+                ALOGE("Conditional branch distance out of range: %d", delta);
+                dvmAbort();
+            }
+            lir->operands[1] = delta >> 2;
+        } else if (lir->opcode == kMipsBeq || lir->opcode == kMipsBne) {
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiple of 4: %d", delta);
+                dvmAbort();
+            }
+            if (delta > 131068 || delta < -131069) {
+                ALOGE("Conditional branch distance out of range: %d", delta);
+                dvmAbort();
+            }
+            lir->operands[2] = delta >> 2;
+        } else if (lir->opcode == kMipsJal) {
+            intptr_t curPC = (startAddr + lir->generic.offset + 4) & ~3;
+            intptr_t target = lir->operands[0];
+            /* ensure PC-region branch can be used */
+            assert((curPC & 0xF0000000) == (target & 0xF0000000));
+            if (target & 0x3) {
+                ALOGE("Jump target is not multiple of 4: %d", target);
+                dvmAbort();
+            }
+            lir->operands[0] =  target >> 2;
+        } else if (lir->opcode == kMipsLahi) { /* load address hi (via lui) */
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t target = startAddr + targetLIR->generic.offset;
+            lir->operands[1] = target >> 16;
+        } else if (lir->opcode == kMipsLalo) { /* load address lo (via ori) */
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t target = startAddr + targetLIR->generic.offset;
+            lir->operands[2] = lir->operands[2] + target;
+        }
+
+
+        MipsEncodingMap *encoder = &EncodingMap[lir->opcode];
+        u4 bits = encoder->skeleton;
+        int i;
+        for (i = 0; i < 4; i++) {
+            u4 operand;
+            u4 value;
+            operand = lir->operands[i];
+            switch(encoder->fieldLoc[i].kind) {
+                case kFmtUnused:
+                    break;
+                case kFmtBitBlt:
+                    if (encoder->fieldLoc[i].start == 0 && encoder->fieldLoc[i].end == 31) {
+                        value = operand;
+                    } else {
+                        value = (operand << encoder->fieldLoc[i].start) &
+                                ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    }
+                    bits |= value;
+                    break;
+                case kFmtDfp: {
+                    assert(DOUBLEREG(operand));
+                    assert((operand & 0x1) == 0);
+                    value = ((operand & FP_REG_MASK) << encoder->fieldLoc[i].start) &
+                            ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    bits |= value;
+                    break;
+                }
+                case kFmtSfp:
+                    assert(SINGLEREG(operand));
+                    value = ((operand & FP_REG_MASK) << encoder->fieldLoc[i].start) &
+                            ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    bits |= value;
+                    break;
+                default:
+                    assert(0);
+            }
+        }
+        assert(encoder->size == 2);
+        *bufferAddr++ = bits;
+    }
+    return kSuccess;
+}
+
+static int assignLiteralOffsetCommon(LIR *lir, int offset)
+{
+    for (;lir != NULL; lir = lir->next) {
+        lir->offset = offset;
+        offset += 4;
+    }
+    return offset;
+}
+
+/* Determine the offset of each literal field */
+static int assignLiteralOffset(CompilationUnit *cUnit, int offset)
+{
+    /* Reserved for the size field of class pointer pool */
+    offset += 4;
+    offset = assignLiteralOffsetCommon(cUnit->classPointerList, offset);
+    offset = assignLiteralOffsetCommon(cUnit->literalList, offset);
+    return offset;
+}
+
+/*
+ * Translation layout in the code cache.  Note that the codeAddress pointer
+ * in JitTable will point directly to the code body (field codeAddress).  The
+ * chain cell offset codeAddress - 4, and the address of the trace profile
+ * counter is at codeAddress - 8.
+ *
+ *      +----------------------------+
+ *      | Trace Profile Counter addr |  -> 4 bytes (PROF_COUNTER_ADDR_SIZE)
+ *      +----------------------------+
+ *   +--| Offset to chain cell counts|  -> 4 bytes (CHAIN_CELL_OFFSET_SIZE)
+ *   |  +----------------------------+
+ *   |  | Trace profile code         |  <- entry point when profiling
+ *   |  .  -   -   -   -   -   -   - .
+ *   |  | Code body                  |  <- entry point when not profiling
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Chaining Cells             |  -> 16/20 bytes, 4 byte aligned
+ *   |  .                            .
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Gap for large switch stmt  |  -> # cases >= MAX_CHAINED_SWITCH_CASES
+ *   |  +----------------------------+
+ *   +->| Chaining cell counts       |  -> 8 bytes, chain cell counts by type
+ *      +----------------------------+
+ *      | Trace description          |  -> variable sized
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | # Class pointer pool size  |  -> 4 bytes
+ *      +----------------------------+
+ *      | Class pointer pool         |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | Literal pool               |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *
+ */
+
+#define PROF_COUNTER_ADDR_SIZE 4
+#define CHAIN_CELL_OFFSET_SIZE 4
+
+/*
+ * Utility functions to navigate various parts in a trace. If we change the
+ * layout/offset in the future, we just modify these functions and we don't need
+ * to propagate the changes to all the use cases.
+ */
+static inline char *getTraceBase(const JitEntry *p)
+{
+    return (char*)p->codeAddress -
+        (PROF_COUNTER_ADDR_SIZE + CHAIN_CELL_OFFSET_SIZE);
+}
+
+/* Handy function to retrieve the profile count */
+static inline JitTraceCounter_t getProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return 0;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    return **p;
+}
+
+/* Handy function to reset the profile count */
+static inline void resetProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    **p = 0;
+}
+
+/* Get the pointer of the chain cell count */
+static inline ChainCellCounts* getChainCellCountsPointer(const char *base)
+{
+    /* 4 is the size of the profile count */
+    u4 *chainCellOffsetP = (u4 *) (base + PROF_COUNTER_ADDR_SIZE);
+    u4 chainCellOffset = *chainCellOffsetP;
+    return (ChainCellCounts *) ((char *) chainCellOffsetP + chainCellOffset);
+}
+
+/* Get the size of all chaining cells */
+static inline u4 getChainCellSize(const ChainCellCounts* pChainCellCounts)
+{
+    int cellSize = 0;
+    int i;
+
+    /* Get total count of chain cells */
+    for (i = 0; i < kChainingCellGap; i++) {
+        if (i != kChainingCellInvokePredicted) {
+            cellSize += pChainCellCounts->u.count[i] *
+                        (CHAIN_CELL_NORMAL_SIZE >> 2);
+        } else {
+            cellSize += pChainCellCounts->u.count[i] *
+                (CHAIN_CELL_PREDICTED_SIZE >> 2);
+        }
+    }
+    return cellSize;
+}
+
+/* Get the starting pointer of the trace description section */
+static JitTraceDescription* getTraceDescriptionPointer(const char *base)
+{
+    ChainCellCounts* pCellCounts = getChainCellCountsPointer(base);
+    return (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+}
+
+/* Get the size of a trace description */
+static int getTraceDescriptionSize(const JitTraceDescription *desc)
+{
+    int runCount;
+    /* Trace end is always of non-meta type (ie isCode == true) */
+    for (runCount = 0; ; runCount++) {
+        if (desc->trace[runCount].isCode &&
+            desc->trace[runCount].info.frag.runEnd)
+           break;
+    }
+    return sizeof(JitTraceDescription) + ((runCount+1) * sizeof(JitTraceRun));
+}
+
+#if defined(SIGNATURE_BREAKPOINT)
+/* Inspect the assembled instruction stream to find potential matches */
+static void matchSignatureBreakpoint(const CompilationUnit *cUnit,
+                                     unsigned int size)
+{
+    unsigned int i, j;
+    u4 *ptr = (u4 *) cUnit->codeBuffer;
+
+    for (i = 0; i < size - gDvmJit.signatureBreakpointSize + 1; i++) {
+        if (ptr[i] == gDvmJit.signatureBreakpoint[0]) {
+            for (j = 1; j < gDvmJit.signatureBreakpointSize; j++) {
+                if (ptr[i+j] != gDvmJit.signatureBreakpoint[j]) {
+                    break;
+                }
+            }
+            if (j == gDvmJit.signatureBreakpointSize) {
+                ALOGD("Signature match starting from offset %#x (%d words)",
+                     i*4, gDvmJit.signatureBreakpointSize);
+                int descSize = getTraceDescriptionSize(cUnit->traceDesc);
+                JitTraceDescription *newCopy =
+                    (JitTraceDescription *) malloc(descSize);
+                memcpy(newCopy, cUnit->traceDesc, descSize);
+                dvmCompilerWorkEnqueue(NULL, kWorkOrderTraceDebug, newCopy);
+                break;
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    MipsLIR *mipsLIR;
+    int offset = 0;
+    int i;
+    ChainCellCounts chainCellCounts;
+    int descSize = (cUnit->jitMode == kJitMethod) ?
+        0 : getTraceDescriptionSize(cUnit->traceDesc);
+    int chainingCellGap = 0;
+
+    info->instructionSet = cUnit->instructionSet;
+
+    /* Beginning offset needs to allow space for chain cell offset */
+    for (mipsLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         mipsLIR;
+         mipsLIR = NEXT_LIR(mipsLIR)) {
+        mipsLIR->generic.offset = offset;
+        if (mipsLIR->opcode >= 0 && !mipsLIR->flags.isNop) {
+            mipsLIR->flags.size = EncodingMap[mipsLIR->opcode].size * 2;
+            offset += mipsLIR->flags.size;
+        }
+        /* Pseudo opcodes don't consume space */
+    }
+
+    /* Const values have to be word aligned */
+    offset = (offset + 3) & ~3;
+
+    u4 chainCellOffset = offset;
+    MipsLIR *chainCellOffsetLIR = NULL;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /*
+         * Get the gap (# of u4) between the offset of chaining cell count and
+         * the bottom of real chaining cells. If the translation has chaining
+         * cells, the gap is guaranteed to be multiples of 4.
+         */
+        chainingCellGap = (offset - cUnit->chainingCellBottom->offset) >> 2;
+
+        /* Add space for chain cell counts & trace description */
+        chainCellOffsetLIR = (MipsLIR *) cUnit->chainCellOffsetLIR;
+        assert(chainCellOffsetLIR);
+        assert(chainCellOffset < 0x10000);
+        assert(chainCellOffsetLIR->opcode == kMips32BitData &&
+               chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG);
+
+        /*
+         * Adjust the CHAIN_CELL_OFFSET_TAG LIR's offset to remove the
+         * space occupied by the pointer to the trace profiling counter.
+         */
+        chainCellOffsetLIR->operands[0] = chainCellOffset - 4;
+
+        offset += sizeof(chainCellCounts) + descSize;
+
+        assert((offset & 0x3) == 0);  /* Should still be word aligned */
+    }
+
+    /* Set up offsets for literals */
+    cUnit->dataOffset = offset;
+
+    /*
+     * Assign each class pointer/constant an offset from the beginning of the
+     * compilation unit.
+     */
+    offset = assignLiteralOffset(cUnit, offset);
+
+    cUnit->totalSize = offset;
+
+    if (gDvmJit.codeCacheByteUsed + cUnit->totalSize > gDvmJit.codeCacheSize) {
+        gDvmJit.codeCacheFull = true;
+        info->discardResult = true;
+        return;
+    }
+
+    /* Allocate enough space for the code block */
+    cUnit->codeBuffer = (unsigned char *)dvmCompilerNew(chainCellOffset, true);
+    if (cUnit->codeBuffer == NULL) {
+        ALOGE("Code buffer allocation failure");
+        info->discardResult = true;
+        return;
+    }
+
+    /*
+     * Attempt to assemble the trace.  Note that assembleInstructions
+     * may rewrite the code sequence and request a retry.
+     */
+    cUnit->assemblerStatus = assembleInstructions(cUnit,
+          (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed);
+
+    switch(cUnit->assemblerStatus) {
+        case kSuccess:
+            break;
+        case kRetryAll:
+            if (cUnit->assemblerRetries < MAX_ASSEMBLER_RETRIES) {
+                if (cUnit->jitMode != kJitMethod) {
+                    /* Restore pristine chain cell marker on retry */
+                    chainCellOffsetLIR->operands[0] = CHAIN_CELL_OFFSET_TAG;
+                }
+                return;
+            }
+            /* Too many retries - reset and try cutting the trace in half */
+            cUnit->assemblerRetries = 0;
+            cUnit->assemblerStatus = kRetryHalve;
+            return;
+        case kRetryHalve:
+            return;
+        default:
+             ALOGE("Unexpected assembler status: %d", cUnit->assemblerStatus);
+             dvmAbort();
+    }
+
+#if defined(SIGNATURE_BREAKPOINT)
+    if (info->discardResult == false && gDvmJit.signatureBreakpoint != NULL &&
+        chainCellOffset/4 >= gDvmJit.signatureBreakpointSize) {
+        matchSignatureBreakpoint(cUnit, chainCellOffset/4);
+    }
+#endif
+
+    /* Don't go all the way if the goal is just to get the verbose output */
+    if (info->discardResult) return;
+
+    /*
+     * The cache might disappear - acquire lock and check version
+     * Continue holding lock until translation cache update is complete.
+     * These actions are required here in the compiler thread because
+     * it is unaffected by suspend requests and doesn't know if a
+     * translation cache flush is in progress.
+     */
+    dvmLockMutex(&gDvmJit.compilerLock);
+    if (info->cacheVersion != gDvmJit.cacheVersion) {
+        /* Cache changed - discard current translation */
+        info->discardResult = true;
+        info->codeAddress = NULL;
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        return;
+    }
+
+    cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+    gDvmJit.codeCacheByteUsed += offset;
+
+    UNPROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Install the code block */
+    memcpy((char*)cUnit->baseAddr, cUnit->codeBuffer, chainCellOffset);
+    gDvmJit.numCompilations++;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /* Install the chaining cell counts */
+        for (i=0; i< kChainingCellGap; i++) {
+            chainCellCounts.u.count[i] = cUnit->numChainingCells[i];
+        }
+
+        /* Set the gap number in the chaining cell count structure */
+        chainCellCounts.u.count[kChainingCellGap] = chainingCellGap;
+
+        memcpy((char*)cUnit->baseAddr + chainCellOffset, &chainCellCounts,
+               sizeof(chainCellCounts));
+
+        /* Install the trace description */
+        memcpy((char*) cUnit->baseAddr + chainCellOffset +
+                       sizeof(chainCellCounts),
+               cUnit->traceDesc, descSize);
+    }
+
+    /* Write the literals directly into the code cache */
+    installLiteralPools(cUnit);
+
+    /* Flush dcache and invalidate the icache to maintain coherence */
+    dvmCompilerCacheFlush((long)cUnit->baseAddr,
+                          (long)((char *) cUnit->baseAddr + offset), 0);
+
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Translation cache update complete - release lock */
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /* Record code entry point and instruction set */
+    info->codeAddress = (char*)cUnit->baseAddr + cUnit->headerSize;
+    /* transfer the size of the profiling code */
+    info->profileCodeSize = cUnit->profileCodeSize;
+}
+
+/*
+ * Returns the skeleton bit pattern associated with an opcode.  All
+ * variable fields are zeroed.
+ */
+static u4 getSkeleton(MipsOpCode op)
+{
+    return EncodingMap[op].skeleton;
+}
+
+static u4 assembleChainingBranch(int branchOffset, bool thumbTarget)
+{
+    return getSkeleton(kMipsJal) | ((branchOffset & 0x0FFFFFFF) >> 2);
+}
+
+/*
+ * Perform translation chain operation.
+ * For MIPS, we'll use a JAL instruction to generate an
+ * unconditional chaining branch of up to 256M. The JAL
+ * instruction also has a restriction that the jump target
+ * must be in the same 256M page as the JAL instruction's
+ * delay slot address.
+ * If the target is out of JAL's range, don't chain.
+ * If one or more threads is suspended, don't chain.
+ */
+void* dvmJitChain(void* tgtAddr, u4* branchAddr)
+{
+    u4 newInst;
+
+    /*
+     * Only chain translations when there is no urge to ask all threads to
+     * suspend themselves via the interpreter.
+     */
+    if ((gDvmJit.pProfTable != NULL) && (gDvm.sumThreadSuspendCount == 0) &&
+        (gDvmJit.codeCacheFull == false) &&
+        ((((int) tgtAddr) & 0xF0000000) == (((int) branchAddr+4) & 0xF0000000))) {
+        gDvmJit.translationChains++;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: chaining 0x%x to 0x%x",
+                 (int) branchAddr, (int) tgtAddr & -2));
+
+        newInst = assembleChainingBranch((int) tgtAddr & -2, 0);
+
+        UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        *branchAddr = newInst;
+        dvmCompilerCacheFlush((long)branchAddr, (long)branchAddr + 4, 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,
+            (volatile int32_t *)(void*) &cellAddr->clazz);
+        dvmCompilerCacheFlush((long) cellAddr, (long) (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++;
+        const ClassObject *clazz = newContent->clazz;
+
+        gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+        gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+        gDvmJit.compilerICPatchQueue[index].classDescriptor = clazz->descriptor;
+        gDvmJit.compilerICPatchQueue[index].classLoader = clazz->classLoader;
+        /* For verification purpose only */
+        gDvmJit.compilerICPatchQueue[index].serialNumber = clazz->serialNumber;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchQueued++;
+#endif
+    } else {
+    /* Queue is full - just drop this patch request */
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchDropped++;
+#endif
+    }
+
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+#endif
+
+/*
+ * This method is called from the invoke templates for virtual and interface
+ * methods to speculatively setup a chain to the callee. The templates are
+ * written in assembly and have setup method, cell, and clazz at r0, r2, and
+ * r3 respectively, so there is a unused argument in the list. Upon return one
+ * of the following three results may happen:
+ *   1) Chain is not setup because the callee is native. Reset the rechain
+ *      count to a big number so that it will take a long time before the next
+ *      rechain attempt to happen.
+ *   2) Chain is not setup because the callee has not been created yet. Reset
+ *      the rechain count to a small number and retry in the near future.
+ *   3) Ask all other threads to stop before patching this chaining cell.
+ *      This is required because another thread may have passed the class check
+ *      but hasn't reached the chaining cell yet to follow the chain. If we
+ *      patch the content before halting the other thread, there could be a
+ *      small window for race conditions to happen that it may follow the new
+ *      but wrong chain to invoke a different method.
+ */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz)
+{
+    int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#if defined(WITH_SELF_VERIFICATION)
+    newRechainCount = PREDICTED_CHAIN_COUNTER_AVOID;
+    goto done;
+#else
+    PredictedChainingCell newCell;
+    int baseAddr, tgtAddr;
+    if (dvmIsNativeMethod(method)) {
+        UNPROTECT_CODE_CACHE(cell, sizeof(*cell));
+
+        /*
+         * Put a non-zero/bogus value in the clazz field so that it won't
+         * trigger immediate patching and will continue to fail to match with
+         * a real clazz pointer.
+         */
+        cell->clazz = (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cell, sizeof(*cell));
+        goto done;
+    }
+
+    tgtAddr = (int) dvmJitGetTraceAddr(method->insns);
+    baseAddr = (int) cell + 4;   // PC is cur_addr + 4
+
+    if ((baseAddr & 0xF0000000) != (tgtAddr & 0xF0000000)) {
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p to distant target %s ignored",
+                 cell, method->name));
+        goto done;
+    }
+
+    /*
+     * Compilation not made yet for the callee. Reset the counter to a small
+     * value and come back to check soon.
+     */
+    if ((tgtAddr == 0) ||
+        ((void*)tgtAddr == dvmCompilerGetInterpretTemplate())) {
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p to method %s%s delayed",
+                 cell, method->clazz->descriptor, method->name));
+        goto done;
+    }
+
+    if (cell->clazz == NULL) {
+        newRechainCount = self->icRechainCount;
+    }
+
+    newCell.branch = assembleChainingBranch(tgtAddr, true);
+    newCell.delay_slot = getSkeleton(kMipsNop);
+    newCell.clazz = clazz;
+    newCell.method = method;
+    newCell.stagedClazz = NULL;
+
+    /*
+     * Enter the work order to the queue and the chaining cell will be patched
+     * the next time a safe point is entered.
+     *
+     * If the enqueuing fails reset the rechain count to a normal value so that
+     * it won't get indefinitely delayed.
+     */
+    inlineCachePatchEnqueue(cell, &newCell);
+#endif
+done:
+    self->icRechainCount = newRechainCount;
+    return method;
+}
+
+/*
+ * Patch the inline cache content based on the content passed from the work
+ * order.
+ */
+void dvmCompilerPatchInlineCache(void)
+{
+    int i;
+    PredictedChainingCell *minAddr, *maxAddr;
+
+    /* Nothing to be done */
+    if (gDvmJit.compilerICPatchIndex == 0) return;
+
+    /*
+     * Since all threads are already stopped we don't really need to acquire
+     * the lock. But race condition can be easily introduced in the future w/o
+     * paying attention so we still acquire the lock here.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    //ALOGD("Number of IC patch work orders: %d", gDvmJit.compilerICPatchIndex);
+
+    /* Initialize the min/max address range */
+    minAddr = (PredictedChainingCell *)
+        ((char *) gDvmJit.codeCache + gDvmJit.codeCacheSize);
+    maxAddr = (PredictedChainingCell *) gDvmJit.codeCache;
+
+    for (i = 0; i < gDvmJit.compilerICPatchIndex; i++) {
+        ICPatchWorkOrder *workOrder = &gDvmJit.compilerICPatchQueue[i];
+        PredictedChainingCell *cellAddr = workOrder->cellAddr;
+        PredictedChainingCell *cellContent = &workOrder->cellContent;
+        ClassObject *clazz = dvmFindClassNoInit(workOrder->classDescriptor,
+                                                workOrder->classLoader);
+
+        assert(clazz->serialNumber == workOrder->serialNumber);
+
+        /* Use the newly resolved clazz pointer */
+        cellContent->clazz = clazz;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p from %s to %s (%s) "
+                 "patched",
+                 cellAddr,
+                 cellAddr->clazz->descriptor,
+                 cellContent->clazz->descriptor,
+                 cellContent->method->name));
+
+        /* Patch the chaining cell */
+        *cellAddr = *cellContent;
+        minAddr = (cellAddr < minAddr) ? cellAddr : minAddr;
+        maxAddr = (cellAddr > maxAddr) ? cellAddr : maxAddr;
+    }
+
+    /* Then synchronize the I/D cache */
+    dvmCompilerCacheFlush((long) minAddr, (long) (maxAddr+1), 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.
+ */
+static u4* unchainSingle(JitEntry *trace)
+{
+    const char *base = getTraceBase(trace);
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    u4* pChainCells;
+    int i,j;
+    PredictedChainingCell *predChainCell;
+
+    if (cellSize == 0)
+        return (u4 *) pChainCellCounts;
+
+    /* Locate the beginning of the chain cell region */
+    pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+                  pChainCellCounts->u.count[kChainingCellGap];
+
+    /* The cells are sorted in order - walk through them and reset */
+    for (i = 0; i < kChainingCellGap; i++) {
+        int elemSize = CHAIN_CELL_NORMAL_SIZE >> 2;  /* In 32-bit words */
+        if (i == kChainingCellInvokePredicted) {
+            elemSize = CHAIN_CELL_PREDICTED_SIZE >> 2;
+        }
+
+        for (j = 0; j < pChainCellCounts->u.count[i]; j++) {
+            int targetOffset;
+            switch(i) {
+                case kChainingCellNormal:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpNormal);
+                    break;
+                case kChainingCellHot:
+                case kChainingCellInvokeSingleton:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpTraceSelect);
+                    break;
+                case kChainingCellInvokePredicted:
+                    targetOffset = 0;
+                    predChainCell = (PredictedChainingCell *) pChainCells;
+                    /*
+                     * There could be a race on another mutator thread to use
+                     * this particular predicted cell and the check has passed
+                     * the clazz comparison. So we cannot safely wipe the
+                     * method and branch but it is safe to clear the clazz,
+                     * which serves as the key.
+                     */
+                    predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT;
+                    break;
+#if defined(WITH_SELF_VERIFICATION)
+                case kChainingCellBackwardBranch:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpBackwardBranch);
+                    break;
+#else
+                case kChainingCellBackwardBranch:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpNormal);
+                    break;
+#endif
+                default:
+                    targetOffset = 0; // make gcc happy
+                    ALOGE("Unexpected chaining type: %d", i);
+                    dvmAbort();  // dvmAbort OK here - can't safely recover
+            }
+            COMPILER_TRACE_CHAINING(
+                ALOGD("Jit Runtime: unchaining %#x", (int)pChainCells));
+            /*
+             * Code sequence for a chaining cell is:
+             *     lw   a0, offset(rSELF)
+             *     jalr ra, a0
+             */
+            if (i != kChainingCellInvokePredicted) {
+                *pChainCells = getSkeleton(kMipsLw) | (r_A0 << 16) |
+                               targetOffset | (rSELF << 21);
+                *(pChainCells+1) = getSkeleton(kMipsJalr) | (r_RA << 11) |
+                                   (r_A0 << 21);
+            }
+            pChainCells += elemSize;  /* Advance by a fixed number of words */
+        }
+    }
+    return pChainCells;
+}
+
+/* Unchain all translation in the cache. */
+void dvmJitUnchainAll()
+{
+    u4* lowAddress = NULL;
+    u4* highAddress = NULL;
+    unsigned int i;
+    if (gDvmJit.pJitEntryTable != NULL) {
+        COMPILER_TRACE_CHAINING(ALOGD("Jit Runtime: unchaining all"));
+        dvmLockMutex(&gDvmJit.tableLock);
+
+        UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        for (i = 0; i < gDvmJit.jitTableSize; i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC &&
+                !gDvmJit.pJitEntryTable[i].u.info.isMethodEntry &&
+                gDvmJit.pJitEntryTable[i].codeAddress &&
+                (gDvmJit.pJitEntryTable[i].codeAddress !=
+                 dvmCompilerGetInterpretTemplate())) {
+                u4* lastAddress;
+                lastAddress = unchainSingle(&gDvmJit.pJitEntryTable[i]);
+                if (lowAddress == NULL ||
+                      (u4*)gDvmJit.pJitEntryTable[i].codeAddress < lowAddress)
+                    lowAddress = (u4*)gDvmJit.pJitEntryTable[i].codeAddress;
+                if (lastAddress > highAddress)
+                    highAddress = lastAddress;
+            }
+        }
+
+        if (lowAddress && highAddress)
+            dvmCompilerCacheFlush((long)lowAddress, (long)highAddress, 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;
+}
+
+/* Dumps profile info for a single trace */
+static int dumpTraceProfile(JitEntry *p, bool silent, bool reset,
+                            unsigned long sum)
+{
+    int idx;
+
+    if (p->codeAddress == NULL) {
+        if (!silent)
+            ALOGD("TRACEPROFILE NULL");
+        return 0;
+    }
+    if (p->codeAddress == dvmCompilerGetInterpretTemplate()) {
+        if (!silent)
+            ALOGD("TRACEPROFILE INTERPRET_ONLY");
+        return 0;
+    }
+
+    JitTraceCounter_t count = getProfileCount(p);
+    if (reset) {
+        resetProfileCount(p);
+    }
+    if (silent) {
+        return count;
+    }
+    JitTraceDescription *desc = getTraceDescriptionPointer(getTraceBase(p));
+    const Method *method = desc->method;
+    char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+    jitProfileAddrToLine addrToLine = {0, desc->trace[0].info.frag.startOffset};
+
+    /*
+     * We may end up decoding the debug information for the same method
+     * multiple times, but the tradeoff is we don't need to allocate extra
+     * space to store the addr/line mapping. Since this is a debugging feature
+     * and done infrequently so the slower but simpler mechanism should work
+     * just fine.
+     */
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+                       dvmGetMethodCode(method),
+                       method->clazz->descriptor,
+                       method->prototype.protoIdx,
+                       method->accessFlags,
+                       addrToLineCb, NULL, &addrToLine);
+
+    ALOGD("TRACEPROFILE 0x%08x % 10d %5.2f%% [%#x(+%d), %d] %s%s;%s",
+         (int) getTraceBase(p),
+         count,
+         ((float ) count) / sum * 100.0,
+         desc->trace[0].info.frag.startOffset,
+         desc->trace[0].info.frag.numInsts,
+         addrToLine.lineNum,
+         method->clazz->descriptor, method->name, methodDesc);
+    free(methodDesc);
+
+    /* Find the last fragment (ie runEnd is set) */
+    for (idx = 0;
+         desc->trace[idx].isCode && !desc->trace[idx].info.frag.runEnd;
+         idx++) {
+    }
+
+    /*
+     * runEnd must comes with a JitCodeDesc frag. If isCode is false it must
+     * be a meta info field (only used by callsite info for now).
+     */
+    if (!desc->trace[idx].isCode) {
+        const Method *method = (const Method *)
+            desc->trace[idx+JIT_TRACE_CUR_METHOD-1].info.meta;
+        char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+        /* Print the callee info in the trace */
+        ALOGD("    -> %s%s;%s", method->clazz->descriptor, method->name,
+             methodDesc);
+    }
+
+    return count;
+}
+
+/* Create a copy of the trace descriptor of an existing compilation */
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const JitEntry *knownEntry)
+{
+    const JitEntry *jitEntry = knownEntry ? knownEntry
+                                          : dvmJitFindEntry(pc, false);
+    if ((jitEntry == NULL) || (jitEntry->codeAddress == 0))
+        return NULL;
+
+    JitTraceDescription *desc =
+        getTraceDescriptionPointer(getTraceBase(jitEntry));
+
+    /* Now make a copy and return */
+    int descSize = getTraceDescriptionSize(desc);
+    JitTraceDescription *newCopy = (JitTraceDescription *) malloc(descSize);
+    memcpy(newCopy, desc, descSize);
+    return newCopy;
+}
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+    const JitEntry *jitEntry1 = (const JitEntry *)entry1;
+    const JitEntry *jitEntry2 = (const JitEntry *)entry2;
+
+    JitTraceCounter_t count1 = getProfileCount(jitEntry1);
+    JitTraceCounter_t count2 = getProfileCount(jitEntry2);
+    return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles()
+{
+    JitEntry *sortedEntries;
+    int numTraces = 0;
+    unsigned long sum = 0;
+    unsigned int i;
+
+    /* Make sure that the table is not changing */
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Sort the entries by descending order */
+    sortedEntries = (JitEntry *)malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+    if (sortedEntries == NULL)
+        goto done;
+    memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+           sizeof(JitEntry) * gDvmJit.jitTableSize);
+    qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+          sortTraceProfileCount);
+
+    /* Analyze the sorted entries */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            sum += dumpTraceProfile(&sortedEntries[i],
+                                       true /* silent */,
+                                       false /* reset */,
+                                       0);
+            numTraces++;
+        }
+    }
+    if (numTraces == 0)
+        numTraces = 1;
+    if (sum == 0) {
+        sum = 1;
+    }
+
+    ALOGD("JIT: Average execution count -> %d",(int)(sum / numTraces));
+
+    /* Dump the sorted entries. The count of each trace will be reset to 0. */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            dumpTraceProfile(&sortedEntries[i],
+                             false /* silent */,
+                             true /* reset */,
+                             sum);
+        }
+    }
+
+    for (i=0; i < gDvmJit.jitTableSize && i < 10; i++) {
+        /* Stip interpreter stubs */
+        if (sortedEntries[i].codeAddress == dvmCompilerGetInterpretTemplate()) {
+            continue;
+        }
+        JitTraceDescription* desc =
+            dvmCopyTraceDescriptor(NULL, &sortedEntries[i]);
+        if (desc) {
+            dvmCompilerWorkEnqueue(sortedEntries[i].dPC,
+                                   kWorkOrderTraceDebug, desc);
+        }
+    }
+
+    free(sortedEntries);
+done:
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    return;
+}
+
+static void findClassPointersSingleTrace(char *base, void (*callback)(void *))
+{
+    unsigned int chainTypeIdx, chainIdx;
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    /* Scan the chaining cells */
+    if (cellSize) {
+        /* Locate the beginning of the chain cell region */
+        u4 *pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+            pChainCellCounts->u.count[kChainingCellGap];
+        /* The cells are sorted in order - walk through them */
+        for (chainTypeIdx = 0; chainTypeIdx < kChainingCellGap;
+             chainTypeIdx++) {
+            if (chainTypeIdx != kChainingCellInvokePredicted) {
+                /* In 32-bit words */
+                pChainCells += (CHAIN_CELL_NORMAL_SIZE >> 2) *
+                    pChainCellCounts->u.count[chainTypeIdx];
+                continue;
+            }
+            for (chainIdx = 0;
+                 chainIdx < pChainCellCounts->u.count[chainTypeIdx];
+                 chainIdx++) {
+                PredictedChainingCell *cell =
+                    (PredictedChainingCell *) pChainCells;
+                /*
+                 * Report the cell if it contains a sane class
+                 * pointer.
+                 */
+                if (cell->clazz != NULL &&
+                    cell->clazz !=
+                      (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ) {
+                    callback(&cell->clazz);
+                }
+                pChainCells += CHAIN_CELL_PREDICTED_SIZE >> 2;
+            }
+        }
+    }
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    int *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *classPointerP++;
+    for (; numClassPointers; numClassPointers--, classPointerP++) {
+        callback(classPointerP);
+    }
+}
+
+/*
+ * Scan class pointers in each translation and pass its address to the callback
+ * function. Currently such a pointers can be found in the pointer pool and the
+ * clazz field in the predicted chaining cells.
+ */
+void dvmJitScanAllClassPointers(void (*callback)(void *))
+{
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    /* Handle the inflight compilation first */
+    if (gDvmJit.inflightBaseAddr)
+        findClassPointersSingleTrace((char *) gDvmJit.inflightBaseAddr,
+                                     callback);
+
+    if (gDvmJit.pJitEntryTable != NULL) {
+        unsigned int traceIdx;
+        dvmLockMutex(&gDvmJit.tableLock);
+        for (traceIdx = 0; traceIdx < gDvmJit.jitTableSize; traceIdx++) {
+            const JitEntry *entry = &gDvmJit.pJitEntryTable[traceIdx];
+            if (entry->dPC &&
+                !entry->u.info.isMethodEntry &&
+                entry->codeAddress &&
+                (entry->codeAddress != dvmCompilerGetInterpretTemplate())) {
+                char *base = getTraceBase(entry);
+                findClassPointersSingleTrace(base, callback);
+            }
+        }
+        dvmUnlockMutex(&gDvmJit.tableLock);
+    }
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+}
+
+/*
+ * Provide the final touch on the class object pointer pool to install the
+ * actual pointers. The thread has to be in the running state.
+ */
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit, char *codeAddress)
+{
+    char *base = codeAddress - cUnit->headerSize;
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    intptr_t *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *(int *)classPointerP++;
+    intptr_t *startClassPointerP = classPointerP;
+
+    /*
+     * Change the thread state to VM_RUNNING so that GC won't be happening
+     * when the assembler looks up the class pointers. May suspend the current
+     * thread if there is a pending request before the state is actually
+     * changed to RUNNING.
+     */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_RUNNING);
+
+    /*
+     * Unprotecting the code cache will need to acquire the code cache
+     * protection lock first. Doing so after the state change may increase the
+     * time spent in the RUNNING state (which may delay the next GC request
+     * should there be contention on codeCacheProtectionLock). In practice
+     * this is probably not going to happen often since a GC is just served.
+     * More importantly, acquiring the lock before the state change will
+     * cause deadlock (b/4192964).
+     */
+    UNPROTECT_CODE_CACHE(startClassPointerP,
+                         numClassPointers * sizeof(intptr_t));
+#if defined(WITH_JIT_TUNING)
+    u8 startTime = dvmGetRelativeTimeUsec();
+#endif
+    for (;numClassPointers; numClassPointers--) {
+        CallsiteInfo *callsiteInfo = (CallsiteInfo *) *classPointerP;
+        ClassObject *clazz = dvmFindClassNoInit(
+            callsiteInfo->classDescriptor, callsiteInfo->classLoader);
+        assert(!strcmp(clazz->descriptor, callsiteInfo->classDescriptor));
+        *classPointerP++ = (intptr_t) clazz;
+    }
+
+    /*
+     * Register the base address so that if GC kicks in after the thread state
+     * has been changed to VMWAIT and before the compiled code is registered
+     * in the JIT table, its content can be patched if class objects are
+     * moved.
+     */
+    gDvmJit.inflightBaseAddr = base;
+
+#if defined(WITH_JIT_TUNING)
+    u8 blockTime = dvmGetRelativeTimeUsec() - startTime;
+    gDvmJit.compilerThreadBlockGCTime += blockTime;
+    if (blockTime > gDvmJit.maxCompilerThreadBlockGCTime)
+        gDvmJit.maxCompilerThreadBlockGCTime = blockTime;
+    gDvmJit.numCompilerThreadBlockGC++;
+#endif
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(startClassPointerP, numClassPointers * sizeof(intptr_t));
+
+    /* Change the thread state back to VMWAIT */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_VMWAIT);
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * The following are used to keep compiled loads and stores from modifying
+ * memory during self verification mode.
+ *
+ * Stores do not modify memory. Instead, the address and value pair are stored
+ * into heapSpace. Addresses within heapSpace are unique. For accesses smaller
+ * than a word, the word containing the address is loaded first before being
+ * updated.
+ *
+ * Loads check heapSpace first and return data from there if an entry exists.
+ * Otherwise, data is loaded from memory as usual.
+ */
+
+/* Used to specify sizes of memory operations */
+enum {
+    kSVByte,
+    kSVSignedByte,
+    kSVHalfword,
+    kSVSignedHalfword,
+    kSVWord,
+    kSVDoubleword,
+    kSVVariable,
+};
+
+/* Load the value of a decoded register from the stack */
+static int selfVerificationMemRegLoad(int* sp, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    return *(sp + reg);
+}
+
+/* Load the value of a decoded doubleword register from the stack */
+static s8 selfVerificationMemRegLoadDouble(int* sp, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    return *((s8*)(sp + reg));
+}
+
+/* Store the value of a decoded register out to the stack */
+static void selfVerificationMemRegStore(int* sp, int data, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    *(sp + reg) = data;
+}
+
+/* Store the value of a decoded doubleword register out to the stack */
+static void selfVerificationMemRegStoreDouble(int* sp, s8 data, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    *((s8*)(sp + reg)) = data;
+}
+
+/*
+ * Load the specified size of data from the specified address, checking
+ * heapSpace first if Self Verification mode wrote to it previously, and
+ * falling back to actual memory otherwise.
+ */
+static int selfVerificationLoad(int addr, int size)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int data;
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) {
+            addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+            break;
+        }
+    }
+
+    switch (size) {
+        case kSVByte:
+            data = *((u1*) addr);
+            break;
+        case kSVSignedByte:
+            data = *((s1*) addr);
+            break;
+        case kSVHalfword:
+            data = *((u2*) addr);
+            break;
+        case kSVSignedHalfword:
+            data = *((s2*) addr);
+            break;
+        case kSVWord:
+            data = *((u4*) addr);
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationLoad: %d", size);
+            data = 0;
+            dvmAbort();
+    }
+
+    //ALOGD("*** HEAP LOAD: Addr: %#x Data: %#x Size: %d", addr, data, size);
+    return data;
+}
+
+/* Like selfVerificationLoad, but specifically for doublewords */
+static s8 selfVerificationLoadDoubleword(int addr)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    ShadowHeap* heapSpacePtr;
+
+    int addr2 = addr+4;
+    unsigned int data = *((unsigned int*) addr);
+    unsigned int data2 = *((unsigned int*) addr2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            data = heapSpacePtr->data;
+        } else if (heapSpacePtr->addr == addr2) {
+            data2 = heapSpacePtr->data;
+        }
+    }
+
+    //ALOGD("*** HEAP LOAD DOUBLEWORD: Addr: %#x Data: %#x Data2: %#x",
+    //    addr, data, data2);
+    return (((s8) data2) << 32) | data;
+}
+
+/*
+ * Handles a store of a specified size of data to a specified address.
+ * This gets logged as an addr/data pair in heapSpace instead of modifying
+ * memory.  Addresses in heapSpace are unique, and accesses smaller than a
+ * word pull the entire word from memory first before updating.
+ */
+static void selfVerificationStore(int addr, int data, int size)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    //ALOGD("*** HEAP STORE: Addr: %#x Data: %#x Size: %d", addr, data, size);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) break;
+    }
+
+    if (heapSpacePtr == shadowSpace->heapSpaceTail) {
+        heapSpacePtr->addr = maskedAddr;
+        heapSpacePtr->data = *((unsigned int*) maskedAddr);
+        shadowSpace->heapSpaceTail++;
+    }
+
+    addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+    switch (size) {
+        case kSVByte:
+            *((u1*) addr) = data;
+            break;
+        case kSVSignedByte:
+            *((s1*) addr) = data;
+            break;
+        case kSVHalfword:
+            *((u2*) addr) = data;
+            break;
+        case kSVSignedHalfword:
+            *((s2*) addr) = data;
+            break;
+        case kSVWord:
+            *((u4*) addr) = data;
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationSave: %d", size);
+            dvmAbort();
+    }
+}
+
+/* Like selfVerificationStore, but specifically for doublewords */
+static void selfVerificationStoreDoubleword(int addr, s8 double_data)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int addr2 = addr+4;
+    int data = double_data;
+    int data2 = double_data >> 32;
+    bool store1 = false, store2 = false;
+
+    //ALOGD("*** HEAP STORE DOUBLEWORD: Addr: %#x Data: %#x, Data2: %#x",
+    //    addr, data, data2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            heapSpacePtr->data = data;
+            store1 = true;
+        } else if (heapSpacePtr->addr == addr2) {
+            heapSpacePtr->data = data2;
+            store2 = true;
+        }
+    }
+
+    if (!store1) {
+        shadowSpace->heapSpaceTail->addr = addr;
+        shadowSpace->heapSpaceTail->data = data;
+        shadowSpace->heapSpaceTail++;
+    }
+    if (!store2) {
+        shadowSpace->heapSpaceTail->addr = addr2;
+        shadowSpace->heapSpaceTail->data = data2;
+        shadowSpace->heapSpaceTail++;
+    }
+}
+
+/*
+ * Decodes the memory instruction at the address specified in the link
+ * register. All registers (r0-r12,lr) and fp registers (d0-d15) are stored
+ * consecutively on the stack beginning at the specified stack pointer.
+ * Calls the proper Self Verification handler for the memory instruction and
+ * updates the link register to point past the decoded memory instruction.
+ */
+void dvmSelfVerificationMemOpDecode(int lr, int* sp)
+{
+assert(0); /* MIPSTODO retarg func */
+    enum {
+        kMemOpLdrPcRel = 0x09, // ldr(3)  [01001] rd[10..8] imm_8[7..0]
+        kMemOpRRR      = 0x0A, // Full opcode is 7 bits
+        kMemOp2Single  = 0x0A, // Used for Vstrs and Vldrs
+        kMemOpRRR2     = 0x0B, // Full opcode is 7 bits
+        kMemOp2Double  = 0x0B, // Used for Vstrd and Vldrd
+        kMemOpStrRRI5  = 0x0C, // str(1)  [01100] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRI5  = 0x0D, // ldr(1)  [01101] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRI5 = 0x0E, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRI5 = 0x0F, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRI5 = 0x10, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRI5 = 0x11, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrSpRel = 0x13, // ldr(4)  [10011] rd[10..8] imm_8[7..0]
+        kMemOpStmia    = 0x18, // stmia   [11000] rn[10..8] reglist [7..0]
+        kMemOpLdmia    = 0x19, // ldmia   [11001] rn[10..8] reglist [7..0]
+        kMemOpStrRRR   = 0x28, // str(2)  [0101000] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRR  = 0x29, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRR  = 0x2A, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrsbRRR = 0x2B, // ldrsb   [0101011] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRR   = 0x2C, // ldr(2)  [0101100] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRR  = 0x2D, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRR  = 0x2E, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrshRRR = 0x2F, // ldrsh   [0101111] rm[8..6] rn[5..3] rd[2..0]
+        kMemOp2Stmia   = 0xE88, // stmia  [111010001000[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia   = 0xE89, // ldmia  [111010001001[ rn[19..16] mask[15..0]
+        kMemOp2Stmia2  = 0xE8A, // stmia  [111010001010[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia2  = 0xE8B, // ldmia  [111010001011[ rn[19..16] mask[15..0]
+        kMemOp2Vstr    = 0xED8, // Used for Vstrs and Vstrd
+        kMemOp2Vldr    = 0xED9, // Used for Vldrs and Vldrd
+        kMemOp2Vstr2   = 0xEDC, // Used for Vstrs and Vstrd
+        kMemOp2Vldr2   = 0xEDD, // Used for Vstrs and Vstrd
+        kMemOp2StrbRRR = 0xF80, /* str rt,[rn,rm,LSL #imm] [111110000000]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrbRRR = 0xF81, /* ldrb rt,[rn,rm,LSL #imm] [111110000001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrhRRR = 0xF82, /* str rt,[rn,rm,LSL #imm] [111110000010]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrhRRR = 0xF83, /* ldrh rt,[rn,rm,LSL #imm] [111110000011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrRRR  = 0xF84, /* str rt,[rn,rm,LSL #imm] [111110000100]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrRRR  = 0xF85, /* ldr rt,[rn,rm,LSL #imm] [111110000101]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrbRRI12 = 0xF88, /* strb rt,[rn,#imm12] [111110001000]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrbRRI12 = 0xF89, /* ldrb rt,[rn,#imm12] [111110001001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrhRRI12 = 0xF8A, /* strh rt,[rn,#imm12] [111110001010]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrhRRI12 = 0xF8B, /* ldrh rt,[rn,#imm12] [111110001011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrRRI12 = 0xF8C, /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrRRI12 = 0xF8D, /* ldr(Imm,T3) rd,[rn,#imm12] [111110001101]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrsbRRR = 0xF91, /* ldrsb rt,[rn,rm,LSL #imm] [111110010001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrshRRR = 0xF93, /* ldrsh rt,[rn,rm,LSL #imm] [111110010011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrsbRRI12 = 0xF99, /* ldrsb rt,[rn,#imm12] [111110011001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrshRRI12 = 0xF9B, /* ldrsh rt,[rn,#imm12] [111110011011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2        = 0xE000, // top 3 bits set indicates Thumb2
+    };
+
+    int addr, offset, data;
+    long long double_data;
+    int size = kSVWord;
+    bool store = false;
+    unsigned int *lr_masked = (unsigned int *) (lr & 0xFFFFFFFE);
+    unsigned int insn = *lr_masked;
+
+    int old_lr;
+    old_lr = selfVerificationMemRegLoad(sp, 13);
+
+    if ((insn & kMemOp2) == kMemOp2) {
+        insn = (insn << 16) | (insn >> 16);
+        //ALOGD("*** THUMB2 - Addr: %#x Insn: %#x", lr, insn);
+
+        int opcode12 = (insn >> 20) & 0xFFF;
+        int opcode6 = (insn >> 6) & 0x3F;
+        int opcode4 = (insn >> 8) & 0xF;
+        int imm2 = (insn >> 4) & 0x3;
+        int imm8 = insn & 0xFF;
+        int imm12 = insn & 0xFFF;
+        int rd = (insn >> 12) & 0xF;
+        int rm = insn & 0xF;
+        int rn = (insn >> 16) & 0xF;
+        int rt = (insn >> 12) & 0xF;
+        bool wBack = true;
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+4, 13);
+
+        // Determine whether the mem op is a store or load
+        switch (opcode12) {
+            case kMemOp2Stmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2StrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2StrRRI12:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode12) {
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+                size = kSVByte;
+                break;
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrsbRRI12:
+                size = kSVSignedByte;
+                break;
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+                size = kSVHalfword;
+                break;
+            case kMemOp2LdrshRRR:
+            case kMemOp2LdrshRRI12:
+                size = kSVSignedHalfword;
+                break;
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                if (opcode4 == kMemOp2Double) size = kSVDoubleword;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode12) {
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                offset = imm8 << 2;
+                if (opcode4 == kMemOp2Single) {
+                    rt = rd << 1;
+                    if (insn & 0x400000) rt |= 0x1;
+                } else if (opcode4 == kMemOp2Double) {
+                    if (insn & 0x400000) rt |= 0x10;
+                    rt = rt << 1;
+                } else {
+                    ALOGE("*** ERROR: UNRECOGNIZED VECTOR MEM OP: %x", opcode4);
+                    dvmAbort();
+                }
+                rt += 14;
+                break;
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2LdrRRR:
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrshRRR:
+                offset = selfVerificationMemRegLoad(sp, rm) << imm2;
+                break;
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+            case kMemOp2StrRRI12:
+            case kMemOp2LdrRRI12:
+            case kMemOp2LdrsbRRI12:
+            case kMemOp2LdrshRRI12:
+                offset = imm12;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+                wBack = false;
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB2 MEM OP: %x", opcode12);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 STMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationMemRegLoadDouble(sp, rt);
+                selfVerificationStoreDoubleword(addr+offset, double_data);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 LDMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationLoadDoubleword(addr+offset);
+                selfVerificationMemRegStoreDouble(sp, double_data, rt);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    } else {
+        //ALOGD("*** THUMB - Addr: %#x Insn: %#x", lr, insn);
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+2, 13);
+
+        int opcode5 = (insn >> 11) & 0x1F;
+        int opcode7 = (insn >> 9) & 0x7F;
+        int imm = (insn >> 6) & 0x1F;
+        int rd = (insn >> 8) & 0x7;
+        int rm = (insn >> 6) & 0x7;
+        int rn = (insn >> 3) & 0x7;
+        int rt = insn & 0x7;
+
+        // Determine whether the mem op is a store or load
+        switch (opcode5) {
+            case kMemOpRRR:
+                switch (opcode7) {
+                    case kMemOpStrRRR:
+                    case kMemOpStrhRRR:
+                    case kMemOpStrbRRR:
+                        store = true;
+                }
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpStrbRRI5:
+            case kMemOpStrhRRI5:
+            case kMemOpStmia:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode5) {
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                switch (opcode7) {
+                    case kMemOpStrbRRR:
+                    case kMemOpLdrbRRR:
+                        size = kSVByte;
+                        break;
+                    case kMemOpLdrsbRRR:
+                        size = kSVSignedByte;
+                        break;
+                    case kMemOpStrhRRR:
+                    case kMemOpLdrhRRR:
+                        size = kSVHalfword;
+                        break;
+                    case kMemOpLdrshRRR:
+                        size = kSVSignedHalfword;
+                        break;
+                }
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                size = kSVByte;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                size = kSVHalfword;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        if (opcode5 == kMemOpLdrPcRel)
+            addr = selfVerificationMemRegLoad(sp, 4);
+        else if (opcode5 == kMemOpStmia || opcode5 == kMemOpLdmia)
+            addr = selfVerificationMemRegLoad(sp, rd);
+        else
+            addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode5) {
+            case kMemOpLdrPcRel:
+                offset = (insn & 0xFF) << 2;
+                rt = rd;
+                break;
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                offset = selfVerificationMemRegLoad(sp, rm);
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpLdrRRI5:
+                offset = imm << 2;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                offset = imm << 1;
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                offset = imm;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB MEM OP: %x", opcode5);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                bool wBack = true;
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        if (i == rd) wBack = false;
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    }
+}
+#endif
diff --git a/vm/compiler/codegen/mips/CalloutHelper.h b/vm/compiler/codegen/mips/CalloutHelper.h
new file mode 100644
index 0000000..8534361
--- /dev/null
+++ b/vm/compiler/codegen/mips/CalloutHelper.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_MIPS_CALLOUT_HELPER_H_
+#define DALVIK_VM_COMPILER_CODEGEN_MIPS_CALLOUT_HELPER_H_
+
+#include "Dalvik.h"
+
+/*
+ * Declare/comment prototypes of all native callout functions invoked by the
+ * JIT'ed code here and use the LOAD_FUNC_ADDR macro to load the address into
+ * a register. In this way we have a centralized place to find out all native
+ * helper functions and we can grep for LOAD_FUNC_ADDR to find out all the
+ * callsites.
+ */
+
+/* Load a statically compiled function address as a constant */
+#define LOAD_FUNC_ADDR(cUnit, reg, addr) loadConstant(cUnit, reg, addr)
+
+/* Conversions */
+extern "C" float __floatsisf(int op1);             // OP_INT_TO_FLOAT
+extern "C" int __fixsfsi(float op1);               // OP_FLOAT_TO_INT
+extern "C" float __truncdfsf2(double op1);         // OP_DOUBLE_TO_FLOAT
+extern "C" double __extendsfdf2(float op1);        // OP_FLOAT_TO_DOUBLE
+extern "C" double __floatsidf(int op1);            // OP_INT_TO_DOUBLE
+extern "C" int __fixdfsi(double op1);              // OP_DOUBLE_TO_INT
+extern "C" float __floatdisf(long long op1);       // OP_LONG_TO_FLOAT
+extern "C" double __floatdidf(long long op1);      // OP_LONG_TO_DOUBLE
+extern "C" long long __fixsfdi(float op1);         // OP_FLOAT_TO_LONG
+extern "C" long long __fixdfdi(double op1);        // OP_DOUBLE_TO_LONG
+
+/* Single-precision FP arithmetics */
+extern "C" float __addsf3(float a, float b);   // OP_ADD_FLOAT[_2ADDR]
+extern "C" float __subsf3(float a, float b);   // OP_SUB_FLOAT[_2ADDR]
+extern "C" float __divsf3(float a, float b);   // OP_DIV_FLOAT[_2ADDR]
+extern "C" float __mulsf3(float a, float b);   // OP_MUL_FLOAT[_2ADDR]
+extern "C" float fmodf(float a, float b);          // OP_REM_FLOAT[_2ADDR]
+
+/* Double-precision FP arithmetics */
+extern "C" double __adddf3(double a, double b); // OP_ADD_DOUBLE[_2ADDR]
+extern "C" double __subdf3(double a, double b); // OP_SUB_DOUBLE[_2ADDR]
+extern "C" double __divdf3(double a, double b); // OP_DIV_DOUBLE[_2ADDR]
+extern "C" double __muldf3(double a, double b); // OP_MUL_DOUBLE[_2ADDR]
+extern "C" double fmod(double a, double b);         // OP_REM_DOUBLE[_2ADDR]
+
+/* Long long arithmetics - OP_REM_LONG[_2ADDR] & OP_DIV_LONG[_2ADDR] */
+extern "C" long long __divdi3(long long op1, long long op2);
+extern "C" long long __moddi3(long long op1, long long op2);
+
+/* Originally declared in Sync.h */
+bool dvmUnlockObject(struct Thread* self, struct Object* obj); //OP_MONITOR_EXIT
+
+/* Originally declared in oo/TypeCheck.h */
+bool dvmCanPutArrayElement(const ClassObject* elemClass,   // OP_APUT_OBJECT
+                           const ClassObject* arrayClass);
+int dvmInstanceofNonTrivial(const ClassObject* instance,   // OP_CHECK_CAST &&
+                            const ClassObject* clazz);     // OP_INSTANCE_OF
+
+/* Originally declared in oo/Array.h */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass, // OP_NEW_ARRAY
+                                  size_t length, int allocFlags);
+
+/* Originally declared in interp/InterpDefs.h */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,// OP_FILL_ARRAY_DATA
+                                  const u2* arrayData);
+
+/* Originally declared in compiler/codegen/mips/Assemble.c */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz);
+
+/*
+ * Resolve interface callsites - OP_INVOKE_INTERFACE & OP_INVOKE_INTERFACE_RANGE
+ *
+ * Originally declared in mterp/common/FindInterface.h and only comment it here
+ * due to the INLINE attribute.
+ *
+ * INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+ *  u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+ */
+
+/* Originally declared in alloc/Alloc.h */
+Object* dvmAllocObject(ClassObject* clazz, int flags);  // OP_NEW_INSTANCE
+
+/*
+ * Functions declared in gDvmInlineOpsTable[] are used for
+ * OP_EXECUTE_INLINE & OP_EXECUTE_INLINE_RANGE.
+ */
+extern "C" double sqrt(double x);  // INLINE_MATH_SQRT
+
+/*
+ * The following functions are invoked through the compiler templates (declared
+ * in compiler/template/armv5te/footer.S:
+ *
+ *      __aeabi_cdcmple         // CMPG_DOUBLE
+ *      __aeabi_cfcmple         // CMPG_FLOAT
+ *      dvmLockObject           // MONITOR_ENTER
+ */
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_MIPS_CALLOUT_HELPER_H_
diff --git a/vm/compiler/codegen/mips/Codegen.h b/vm/compiler/codegen/mips/Codegen.h
new file mode 100644
index 0000000..107fa86
--- /dev/null
+++ b/vm/compiler/codegen/mips/Codegen.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerIR.h"
+#include "CalloutHelper.h"
+
+#if defined(_CODEGEN_C)
+/*
+ * loadConstant() sometimes needs to add a small imm to a pre-existing constant
+ */
+static MipsLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value);
+static MipsLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2);
+
+/* Forward-declare the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir);
+
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir);
+
+
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Self Verification memory instruction decoder */
+extern "C" void dvmSelfVerificationMemOpDecode(int lr, int* sp);
+#endif
+
+/*
+ * Architecture-dependent register allocation routines implemented in
+ * Mips/Ralloc.c
+ */
+extern int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+                                         bool fpHint, int regClass);
+
+extern int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+                                     int regClass);
+
+extern MipsLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest,
+                                          int rSrc);
+
+extern MipsLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+extern void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo,
+                                   int destHi, int srcLo, int srcHi);
+
+extern void dvmCompilerSetupResourceMasks(MipsLIR *lir);
+
+extern void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                                    int displacement, int rSrc, OpSize size);
+
+extern void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                        int displacement, int rSrcLo,
+                                        int rSrcHi);
diff --git a/vm/compiler/codegen/mips/CodegenCommon.cpp b/vm/compiler/codegen/mips/CodegenCommon.cpp
new file mode 100644
index 0000000..0622fb8
--- /dev/null
+++ b/vm/compiler/codegen/mips/CodegenCommon.cpp
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * Mips variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+#include "compiler/Loop.h"
+
+/* Array holding the entry offset of each template relative to the first one */
+static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK];
+
+/* Track exercised opcodes */
+static int opcodeCoverage[256];
+
+static void setMemRefType(MipsLIR *lir, bool isLoad, int memType)
+{
+    /* MIPSTODO simplify setMemRefType() */
+    u8 *maskPtr;
+    u8 mask = ENCODE_MEM;;
+    assert(EncodingMap[lir->opcode].flags & (IS_LOAD | IS_STORE));
+
+    if (isLoad) {
+        maskPtr = &lir->useMask;
+    } else {
+        maskPtr = &lir->defMask;
+    }
+    /* Clear out the memref flags */
+    *maskPtr &= ~mask;
+    /* ..and then add back the one we need */
+    switch(memType) {
+        case kLiteral:
+            assert(isLoad);
+            *maskPtr |= ENCODE_LITERAL;
+            break;
+        case kDalvikReg:
+            *maskPtr |= ENCODE_DALVIK_REG;
+            break;
+        case kHeapRef:
+            *maskPtr |= ENCODE_HEAP_REF;
+            break;
+        case kMustNotAlias:
+            /* Currently only loads can be marked as kMustNotAlias */
+            assert(!(EncodingMap[lir->opcode].flags & IS_STORE));
+            *maskPtr |= ENCODE_MUST_NOT_ALIAS;
+            break;
+        default:
+            ALOGE("Jit: invalid memref kind - %d", memType);
+            assert(0);  // Bail if debug build, set worst-case in the field
+            *maskPtr |= ENCODE_ALL;
+    }
+}
+
+/*
+ * Mark load/store instructions that access Dalvik registers through rFP +
+ * offset.
+ */
+static void annotateDalvikRegAccess(MipsLIR *lir, int regId, bool isLoad)
+{
+    /* MIPSTODO simplify annotateDalvikRegAccess() */
+    setMemRefType(lir, isLoad, kDalvikReg);
+
+    /*
+     * Store the Dalvik register id in aliasInfo. Mark he MSB if it is a 64-bit
+     * access.
+     */
+    lir->aliasInfo = regId;
+    if (DOUBLEREG(lir->operands[0])) {
+        lir->aliasInfo |= 0x80000000;
+    }
+}
+
+/*
+ * Decode the register id
+ */
+static inline u8 getRegMaskCommon(int reg)
+{
+    u8 seed;
+    int shift;
+    int regId = reg & 0x1f;
+
+    /*
+     * Each double register is equal to a pair of single-precision FP registers
+     */
+    if (!DOUBLEREG(reg)) {
+        seed = 1;
+    } else {
+        assert((regId & 1) == 0); /* double registers must be even */
+        seed = 3;
+    }
+
+    if (FPREG(reg)) {
+       assert(regId < 16); /* only 16 fp regs */
+       shift = kFPReg0;
+    } else if (EXTRAREG(reg)) {
+       assert(regId < 3); /* only 3 extra regs */
+       shift = kFPRegEnd;
+    } else {
+       shift = 0;
+    }
+
+    /* Expand the double register id into single offset */
+    shift += regId;
+    return (seed << shift);
+}
+
+/* External version of getRegMaskCommon */
+u8 dvmGetRegResourceMask(int reg)
+{
+    return getRegMaskCommon(reg);
+}
+
+/*
+ * Mark the corresponding bit(s).
+ */
+static inline void setupRegMask(u8 *mask, int reg)
+{
+    *mask |= getRegMaskCommon(reg);
+}
+
+/*
+ * Set up the proper fields in the resource mask
+ */
+static void setupResourceMasks(MipsLIR *lir)
+{
+    /* MIPSTODO simplify setupResourceMasks() */
+    int opcode = lir->opcode;
+    int flags;
+
+    if (opcode <= 0) {
+        lir->useMask = lir->defMask = 0;
+        return;
+    }
+
+    flags = EncodingMap[lir->opcode].flags;
+
+    /* Set up the mask for resources that are updated */
+    if (flags & (IS_LOAD | IS_STORE)) {
+        /* Default to heap - will catch specialized classes later */
+        setMemRefType(lir, flags & IS_LOAD, kHeapRef);
+    }
+
+    /*
+     * Conservatively assume the branch here will call out a function that in
+     * turn will trash everything.
+     */
+    if (flags & IS_BRANCH) {
+        lir->defMask = lir->useMask = ENCODE_ALL;
+        return;
+    }
+
+    if (flags & REG_DEF0) {
+        setupRegMask(&lir->defMask, lir->operands[0]);
+    }
+
+    if (flags & REG_DEF1) {
+        setupRegMask(&lir->defMask, lir->operands[1]);
+    }
+
+    if (flags & REG_DEF_SP) {
+        lir->defMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & REG_DEF_LIST0) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_DEF_LIST1) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & SETS_CCODES) {
+        lir->defMask |= ENCODE_CCODE;
+    }
+
+    /* Conservatively treat the IT block */
+    if (flags & IS_IT) {
+        lir->defMask = ENCODE_ALL;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & REG_USE_PC) {
+        lir->useMask |= ENCODE_REG_PC;
+    }
+
+    if (flags & REG_USE_SP) {
+        lir->useMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_USE_LIST0) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_USE_LIST1) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+}
+
+/*
+ * Set up the accurate resource mask for branch instructions
+ */
+static void relaxBranchMasks(MipsLIR *lir)
+{
+    int flags = EncodingMap[lir->opcode].flags;
+
+    /* Make sure only branch instructions are passed here */
+    assert(flags & IS_BRANCH);
+
+    lir->defMask |= ENCODE_REG_PC;
+    lir->useMask |= ENCODE_REG_PC;
+
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+}
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 4
+ * operands.
+ */
+static MipsLIR *newLIR0(CompilationUnit *cUnit, MipsOpCode opcode)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND));
+    insn->opcode = opcode;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR1(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR2(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest, int src1)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) ||
+           (EncodingMap[opcode].flags & IS_BINARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR3(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest, int src1, int src2)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    if (!(EncodingMap[opcode].flags & IS_TERTIARY_OP)) {
+        ALOGE("Bad LIR3: %s[%d]",EncodingMap[opcode].name,opcode);
+    }
+    assert(isPseudoOpCode(opcode) ||
+           (EncodingMap[opcode].flags & IS_TERTIARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR4(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest, int src1, int src2, int info)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) ||
+           (EncodingMap[opcode].flags & IS_QUAD_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    insn->operands[3] = info;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+/*
+ * If the next instruction is a move-result or move-result-long,
+ * return the target Dalvik sReg[s] and convert the next to a
+ * nop.  Otherwise, return INVALID_SREG.  Used to optimize method inlining.
+ */
+static RegLocation inlinedTarget(CompilationUnit *cUnit, MIR *mir,
+                                  bool fpHint)
+{
+    if (mir->next &&
+        ((mir->next->dalvikInsn.opcode == OP_MOVE_RESULT) ||
+         (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_OBJECT))) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDest(cUnit, mir->next, 0);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+/*
+ * The following are building blocks to insert constants into the pool or
+ * instruction streams.
+ */
+
+/* Add a 32-bit constant either in the constant pool or mixed with code */
+static MipsLIR *addWordData(CompilationUnit *cUnit, LIR **constantListP,
+                           int value)
+{
+    /* Add the constant to the literal pool */
+    if (constantListP) {
+        MipsLIR *newValue = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        newValue->operands[0] = value;
+        newValue->generic.next = *constantListP;
+        *constantListP = (LIR *) newValue;
+        return newValue;
+    } else {
+        /* Add the constant in the middle of code stream */
+        newLIR1(cUnit, kMips32BitData, value);
+    }
+    return NULL;
+}
+
+static RegLocation inlinedTargetWide(CompilationUnit *cUnit, MIR *mir,
+                                      bool fpHint)
+{
+    if (mir->next &&
+        (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_WIDE)) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDestWide(cUnit, mir->next, 0, 1);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL_WIDE;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+
+/*
+ * Generate an kMipsPseudoBarrier marker to indicate the boundary of special
+ * blocks.
+ */
+static void genBarrier(CompilationUnit *cUnit)
+{
+    MipsLIR *barrier = newLIR0(cUnit, kMipsPseudoBarrier);
+    /* Mark all resources as being clobbered */
+    barrier->defMask = -1;
+}
+
+/* Create the PC reconstruction slot if not already done */
+extern MipsLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset,
+                              MipsLIR *branch,
+                              MipsLIR *pcrLabel)
+{
+    /* Forget all def info (because we might rollback here.  Bug #2367397 */
+    dvmCompilerResetDefTracking(cUnit);
+
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + dOffset);
+        pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = dOffset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+
+    /* Clear the conservative flags for branches that punt to the interpreter */
+    relaxBranchMasks(branch);
+
+    return pcrLabel;
+}
diff --git a/vm/compiler/codegen/mips/CodegenDriver.cpp b/vm/compiler/codegen/mips/CodegenDriver.cpp
new file mode 100644
index 0000000..62a9a4f
--- /dev/null
+++ b/vm/compiler/codegen/mips/CodegenDriver.cpp
@@ -0,0 +1,4868 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * Mips variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+static void markCard(CompilationUnit *cUnit, int valReg, int tgtAddrReg)
+{
+    int regCardBase = dvmCompilerAllocTemp(cUnit);
+    int regCardNo = dvmCompilerAllocTemp(cUnit);
+    MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBeq, valReg, r_ZERO);
+    loadWordDisp(cUnit, rSELF, offsetof(Thread, cardTable),
+                 regCardBase);
+    opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
+    storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
+                     kUnsignedByte);
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *)target;
+    dvmCompilerFreeTemp(cUnit, regCardBase);
+    dvmCompilerFreeTemp(cUnit, regCardNo);
+}
+
+static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct,
+                                     int srcSize, int tgtSize)
+{
+    /*
+     * Don't optimize the register usage since it calls out to template
+     * functions
+     */
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    int srcReg = 0;
+    int srcRegHi = 0;
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+
+    if (srcSize == kWord) {
+        srcReg = r_A0;
+    } else if (srcSize == kSingle) {
+#ifdef __mips_hard_float
+        srcReg = r_F12;
+#else
+        srcReg = r_A0;
+#endif
+    } else if (srcSize == kLong) {
+        srcReg = r_ARG0;
+        srcRegHi = r_ARG1;
+    } else if (srcSize == kDouble) {
+#ifdef __mips_hard_float
+        srcReg = r_FARG0;
+        srcRegHi = r_FARG1;
+#else
+        srcReg = r_ARG0;
+        srcRegHi = r_ARG1;
+#endif
+    }
+    else {
+        assert(0);
+    }
+
+    if (srcSize == kWord || srcSize == kSingle) {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+        loadValueDirectFixed(cUnit, rlSrc, srcReg);
+    } else {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        loadValueDirectWideFixed(cUnit, rlSrc, srcReg, srcRegHi);
+    }
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)funct);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+    if (tgtSize == kWord || tgtSize == kSingle) {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+#ifdef __mips_hard_float
+        if (tgtSize == kSingle)
+            rlResult = dvmCompilerGetReturnAlt(cUnit);
+        else
+            rlResult = dvmCompilerGetReturn(cUnit);
+#else
+        rlResult = dvmCompilerGetReturn(cUnit);
+#endif
+        storeValue(cUnit, rlDest, rlResult);
+    } else {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+#ifdef __mips_hard_float
+        if (tgtSize == kDouble)
+            rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+        else
+            rlResult = dvmCompilerGetReturnWide(cUnit);
+#else
+        rlResult = dvmCompilerGetReturnWide(cUnit);
+#endif
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+    return false;
+}
+
+
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            funct = (void*) __addsf3;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            funct = (void*) __subsf3;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            funct = (void*) __divsf3;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            funct = (void*) __mulsf3;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+            funct = (void*) fmodf;
+            break;
+        case OP_NEG_FLOAT: {
+            genNegFloat(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+#ifdef __mips_hard_float
+    loadValueDirectFixed(cUnit, rlSrc1, r_F12);
+    loadValueDirectFixed(cUnit, rlSrc2, r_F14);
+#else
+    loadValueDirectFixed(cUnit, rlSrc1, r_A0);
+    loadValueDirectFixed(cUnit, rlSrc2, r_A1);
+#endif
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)funct);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+#ifdef __mips_hard_float
+    rlResult = dvmCompilerGetReturnAlt(cUnit);
+#else
+    rlResult = dvmCompilerGetReturn(cUnit);
+#endif
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            funct = (void*) __adddf3;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            funct = (void*) __subdf3;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            funct = (void*) __divsf3;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            funct = (void*) __muldf3;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+            funct = (void*) (double (*)(double, double)) fmod;
+            break;
+        case OP_NEG_DOUBLE: {
+            genNegDouble(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)funct);
+#ifdef __mips_hard_float
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_F12, r_F13);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_F14, r_F15);
+#else
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+#endif
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+#ifdef __mips_hard_float
+    rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+#else
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+#endif
+    storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+    cUnit->usesLinkRegister = true;
+#endif
+    return false;
+}
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__floatsisf, kWord, kSingle);
+        case OP_FLOAT_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__fixsfsi, kSingle, kWord);
+        case OP_DOUBLE_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__truncdfsf2, kDouble, kSingle);
+        case OP_FLOAT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__extendsfdf2, kSingle, kDouble);
+        case OP_INT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__floatsidf, kWord, kDouble);
+        case OP_DOUBLE_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__fixdfsi, kDouble, kWord);
+        case OP_FLOAT_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)__fixsfdi, kSingle, kLong);
+        case OP_LONG_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__floatdisf, kLong, kSingle);
+        case OP_DOUBLE_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)__fixdfdi, kDouble, kLong);
+        case OP_LONG_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__floatdidf, kLong, kDouble);
+        default:
+            return true;
+    }
+    return false;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void selfVerificationBranchInsert(LIR *currentLIR, Mipsopcode opcode,
+                          int dest, int src1)
+{
+assert(0); /* MIPSTODO port selfVerificationBranchInsert() */
+     MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+     insn->opcode = opcode;
+     insn->operands[0] = dest;
+     insn->operands[1] = src1;
+     setupResourceMasks(insn);
+     dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn);
+}
+
+/*
+ * Example where r14 (LR) is preserved around a heap access under
+ * self-verification mode in Thumb2:
+ *
+ * D/dalvikvm( 1538): 0x59414c5e (0026): ldr     r14, [r15pc, #220] <-hoisted
+ * D/dalvikvm( 1538): 0x59414c62 (002a): mla     r4, r0, r8, r4
+ * D/dalvikvm( 1538): 0x59414c66 (002e): adds    r3, r4, r3
+ * D/dalvikvm( 1538): 0x59414c6a (0032): push    <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c6c (0034): blx_1   0x5940f494      |
+ * D/dalvikvm( 1538): 0x59414c6e (0036): blx_2   see above       <-MEM_OP_DECODE
+ * D/dalvikvm( 1538): 0x59414c70 (0038): ldr     r10, [r9, #0]   |
+ * D/dalvikvm( 1538): 0x59414c74 (003c): pop     <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c78 (0040): mov     r11, r10
+ * D/dalvikvm( 1538): 0x59414c7a (0042): asr     r12, r11, #31
+ * D/dalvikvm( 1538): 0x59414c7e (0046): movs    r0, r2
+ * D/dalvikvm( 1538): 0x59414c80 (0048): movs    r1, r3
+ * D/dalvikvm( 1538): 0x59414c82 (004a): str     r2, [r5, #16]
+ * D/dalvikvm( 1538): 0x59414c84 (004c): mov     r2, r11
+ * D/dalvikvm( 1538): 0x59414c86 (004e): str     r3, [r5, #20]
+ * D/dalvikvm( 1538): 0x59414c88 (0050): mov     r3, r12
+ * D/dalvikvm( 1538): 0x59414c8a (0052): str     r11, [r5, #24]
+ * D/dalvikvm( 1538): 0x59414c8e (0056): str     r12, [r5, #28]
+ * D/dalvikvm( 1538): 0x59414c92 (005a): blx     r14             <-use of LR
+ *
+ */
+static void selfVerificationBranchInsertPass(CompilationUnit *cUnit)
+{
+assert(0); /* MIPSTODO port selfVerificationBranchInsertPass() */
+    MipsLIR *thisLIR;
+    Templateopcode opcode = TEMPLATE_MEM_OP_DECODE;
+
+    for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+        if (!thisLIR->flags.isNop && thisLIR->flags.insertWrapper) {
+            /*
+             * Push r5(FP) and r14(LR) onto stack. We need to make sure that
+             * SP is 8-byte aligned, and we use r5 as a temp to restore LR
+             * for Thumb-only target since LR cannot be directly accessed in
+             * Thumb mode. Another reason to choose r5 here is it is the Dalvik
+             * frame pointer and cannot be the target of the emulated heap
+             * load.
+             */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPreBranch(cUnit, thisLIR);
+            }
+
+            /* Branch to mem op decode template */
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx1,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx2,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+
+            /* Restore LR */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPostBranch(cUnit, thisLIR);
+            }
+        }
+    }
+}
+#endif
+
+/* Generate conditional branch instructions */
+static MipsLIR *genConditionalBranchMips(CompilationUnit *cUnit,
+                                    MipsOpCode opc, int rs, int rt,
+                                    MipsLIR *target)
+{
+    MipsLIR *branch = opCompareBranch(cUnit, opc, rs, rt);
+    branch->generic.target = (LIR *) target;
+    return branch;
+}
+
+/* Generate a unconditional branch to go to the interpreter */
+static inline MipsLIR *genTrap(CompilationUnit *cUnit, int dOffset,
+                                  MipsLIR *pcrLabel)
+{
+    MipsLIR *branch = opNone(cUnit, kOpUncondBr);
+    return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/* Load a wide field from an object instance */
+static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+
+    assert(rlDest.wide);
+
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    HEAP_ACCESS_SHADOW(true);
+    loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/* Store a wide field to an object instance */
+static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 2);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr;
+    rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    regPtr = dvmCompilerAllocTemp(cUnit);
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+
+    HEAP_ACCESS_SHADOW(true);
+    storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+}
+
+/*
+ * Load a field from an object instance
+ *
+ */
+static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                    int fieldOffset, bool isVolatile)
+{
+    RegLocation rlResult;
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+
+    HEAP_ACCESS_SHADOW(true);
+    loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
+                 size, rlObj.sRegLow);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, 0);
+    }
+
+    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, 0);
+    }
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, 0);
+    }
+    if (isObject) {
+        /* NOTE: marking card based on object head */
+        markCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
+    }
+}
+
+
+/*
+ * Generate array load
+ */
+static void genArrayGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlDest, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlResult;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+    int regPtr;
+
+    /* null object? */
+    MipsLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow,
+                                rlArray.lowReg, mir->offset, NULL);
+    }
+
+    regPtr = dvmCompilerAllocTemp(cUnit);
+
+    assert(IS_SIMM16(dataOffset));
+    if (scale) {
+        opRegRegImm(cUnit, kOpLsl, regPtr, rlIndex.lowReg, scale);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    }
+
+    if (scale) {
+        opRegReg(cUnit, kOpAdd, regPtr, rlArray.lowReg);
+    } else {
+        opRegRegReg(cUnit, kOpAdd, regPtr, rlArray.lowReg, rlIndex.lowReg);
+    }
+
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+    if ((size == kLong) || (size == kDouble)) {
+        HEAP_ACCESS_SHADOW(true);
+        loadBaseDispWide(cUnit, mir, regPtr, dataOffset, rlResult.lowReg,
+                         rlResult.highReg, INVALID_SREG);
+        HEAP_ACCESS_SHADOW(false);
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        HEAP_ACCESS_SHADOW(true);
+        loadBaseDisp(cUnit, mir, regPtr, dataOffset, rlResult.lowReg,
+                     size, INVALID_SREG);
+        HEAP_ACCESS_SHADOW(false);
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+}
+
+/*
+ * Generate array store
+ *
+ */
+static void genArrayPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlSrc, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    int regPtr;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+
+    if (dvmCompilerIsTemp(cUnit, rlArray.lowReg)) {
+        dvmCompilerClobber(cUnit, rlArray.lowReg);
+        regPtr = rlArray.lowReg;
+    } else {
+        regPtr = dvmCompilerAllocTemp(cUnit);
+        genRegCopy(cUnit, regPtr, rlArray.lowReg);
+    }
+
+    /* null object? */
+    MipsLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg,
+                                mir->offset, NULL);
+    }
+
+    assert(IS_SIMM16(dataOffset));
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    if (scale) {
+        opRegRegImm(cUnit, kOpLsl, tReg, rlIndex.lowReg, scale);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        //NOTE: max live temps(4) here.
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    }
+
+    if (scale) {
+        opRegReg(cUnit, kOpAdd, tReg, rlArray.lowReg);
+    } else {
+        opRegRegReg(cUnit, kOpAdd, tReg, rlArray.lowReg, rlIndex.lowReg);
+    }
+
+    /* at this point, tReg points to array, 2 live temps */
+    if ((size == kLong) || (size == kDouble)) {
+        rlSrc = loadValueWide(cUnit, rlSrc, regClass);
+        HEAP_ACCESS_SHADOW(true);
+        storeBaseDispWide(cUnit, tReg, dataOffset, rlSrc.lowReg, rlSrc.highReg)
+        HEAP_ACCESS_SHADOW(false);
+        dvmCompilerFreeTemp(cUnit, tReg);
+        dvmCompilerFreeTemp(cUnit, regPtr);
+    } else {
+        rlSrc = loadValue(cUnit, rlSrc, regClass);
+        HEAP_ACCESS_SHADOW(true);
+        storeBaseDisp(cUnit, tReg, dataOffset, rlSrc.lowReg, size);
+        dvmCompilerFreeTemp(cUnit, tReg);
+        HEAP_ACCESS_SHADOW(false);
+    }
+}
+
+/*
+ * Generate array object store
+ * Must use explicit register allocation here because of
+ * call-out to dvmCanPutArrayElement
+ */
+static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir,
+                              RegLocation rlArray, RegLocation rlIndex,
+                              RegLocation rlSrc, int scale)
+{
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    int regLen = r_A0;
+    int regPtr = r_S0;  /* Preserved across call */
+    int regArray = r_A1;
+    int regIndex = r_S4;  /* Preserved across call */
+
+    dvmCompilerFlushAllRegs(cUnit);
+    // moved lock for r_S0 and r_S4 here from below since genBoundsCheck
+    // allocates a temporary that can result in clobbering either of them
+    dvmCompilerLockTemp(cUnit, regPtr);   // r_S0
+    dvmCompilerLockTemp(cUnit, regIndex); // r_S4
+
+    loadValueDirectFixed(cUnit, rlArray, regArray);
+    loadValueDirectFixed(cUnit, rlIndex, regIndex);
+
+    /* null object? */
+    MipsLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, regArray,
+                                mir->offset, NULL);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        /* Get len */
+        loadWordDisp(cUnit, regArray, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+        genBoundsCheck(cUnit, regIndex, regLen, mir->offset,
+                       pcrLabel);
+    } else {
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+    }
+
+    /* Get object to store */
+    loadValueDirectFixed(cUnit, rlSrc, r_A0);
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmCanPutArrayElement);
+
+    /* Are we storing null?  If so, avoid check */
+    MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBeqz, r_A0, -1);
+
+    /* Make sure the types are compatible */
+    loadWordDisp(cUnit, regArray, offsetof(Object, clazz), r_A1);
+    loadWordDisp(cUnit, r_A0, offsetof(Object, clazz), r_A0);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+
+    /*
+     * Using fixed registers here, and counting on r_S0 and r_S4 being
+     * preserved across the above call.  Tell the register allocation
+     * utilities about the regs we are using directly
+     */
+    dvmCompilerLockTemp(cUnit, r_A0);
+    dvmCompilerLockTemp(cUnit, r_A1);
+
+    /* Bad? - roll back and re-execute if so */
+    genRegImmCheck(cUnit, kMipsCondEq, r_V0, 0, mir->offset, pcrLabel);
+
+    /* Resume here - must reload element & array, regPtr & index preserved */
+    loadValueDirectFixed(cUnit, rlSrc, r_A0);
+    loadValueDirectFixed(cUnit, rlArray, r_A1);
+
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseIndexed(cUnit, regPtr, regIndex, r_A0,
+                     scale, kWord);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    dvmCompilerFreeTemp(cUnit, regIndex);
+
+    /* NOTE: marking card here based on object head */
+    markCard(cUnit, r_A0, r_A1);
+}
+
+static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlShift)
+{
+    /*
+     * Don't mess with the regsiters here as there is a particular calling
+     * convention to the out-of-line handler.
+     */
+    RegLocation rlResult;
+
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirect(cUnit, rlShift, r_A2);
+    switch( mir->dalvikInsn.opcode) {
+        case OP_SHL_LONG:
+        case OP_SHL_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG);
+            break;
+        case OP_SHR_LONG:
+        case OP_SHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG);
+            break;
+        case OP_USHR_LONG:
+        case OP_USHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG);
+            break;
+        default:
+            return true;
+    }
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    OpKind firstOp = kOpBkpt;
+    OpKind secondOp = kOpBkpt;
+    bool callOut = false;
+    bool checkZero = false;
+    void *callTgt;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_NOT_LONG:
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
+            opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        case OP_ADD_LONG:
+        case OP_ADD_LONG_2ADDR:
+            firstOp = kOpAdd;
+            secondOp = kOpAdc;
+            break;
+        case OP_SUB_LONG:
+        case OP_SUB_LONG_2ADDR:
+            firstOp = kOpSub;
+            secondOp = kOpSbc;
+            break;
+        case OP_MUL_LONG:
+        case OP_MUL_LONG_2ADDR:
+            genMulLong(cUnit, rlDest, rlSrc1, rlSrc2);
+            return false;
+        case OP_DIV_LONG:
+        case OP_DIV_LONG_2ADDR:
+            callOut = true;
+            checkZero = true;
+            callTgt = (void*)__divdi3;
+            break;
+        case OP_REM_LONG:
+        case OP_REM_LONG_2ADDR:
+            callOut = true;
+            callTgt = (void*)__moddi3;
+            checkZero = true;
+            break;
+        case OP_AND_LONG_2ADDR:
+        case OP_AND_LONG:
+            firstOp = kOpAnd;
+            secondOp = kOpAnd;
+            break;
+        case OP_OR_LONG:
+        case OP_OR_LONG_2ADDR:
+            firstOp = kOpOr;
+            secondOp = kOpOr;
+            break;
+        case OP_XOR_LONG:
+        case OP_XOR_LONG_2ADDR:
+            firstOp = kOpXor;
+            secondOp = kOpXor;
+            break;
+        case OP_NEG_LONG: {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            newLIR3(cUnit, kMipsSubu, rlResult.lowReg, r_ZERO, rlSrc2.lowReg);
+            newLIR3(cUnit, kMipsSubu, tReg, r_ZERO, rlSrc2.highReg);
+            newLIR3(cUnit, kMipsSltu, rlResult.highReg, r_ZERO, rlResult.lowReg);
+            newLIR3(cUnit, kMipsSubu, rlResult.highReg, tReg, rlResult.highReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        }
+        default:
+            ALOGE("Invalid long arith op");
+            dvmCompilerAbort(cUnit);
+    }
+    if (!callOut) {
+        genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
+    } else {
+        dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+        loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+        loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+        LOAD_FUNC_ADDR(cUnit, r_T9, (int) callTgt);
+        if (checkZero) {
+            int tReg = r_T1; // Using fixed registers during call sequence
+            opRegRegReg(cUnit, kOpOr, tReg, r_ARG2, r_ARG3);
+            genRegImmCheck(cUnit, kMipsCondEq, tReg, 0, mir->offset, NULL);
+        }
+        opReg(cUnit, kOpBlx, r_T9);
+        newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+        dvmCompilerClobberCallRegs(cUnit);
+        rlResult = dvmCompilerGetReturnWide(cUnit);
+        storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+        cUnit->usesLinkRegister = true;
+#endif
+    }
+    return false;
+}
+
+static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir,
+                          RegLocation rlDest, RegLocation rlSrc1,
+                          RegLocation rlSrc2)
+{
+    OpKind op = kOpBkpt;
+    bool checkZero = false;
+    bool unary = false;
+    RegLocation rlResult;
+    bool shiftOp = false;
+    int isDivRem = false;
+    MipsOpCode opc;
+    int divReg;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_NEG_INT:
+            op = kOpNeg;
+            unary = true;
+            break;
+        case OP_NOT_INT:
+            op = kOpMvn;
+            unary = true;
+            break;
+        case OP_ADD_INT:
+        case OP_ADD_INT_2ADDR:
+            op = kOpAdd;
+            break;
+        case OP_SUB_INT:
+        case OP_SUB_INT_2ADDR:
+            op = kOpSub;
+            break;
+        case OP_MUL_INT:
+        case OP_MUL_INT_2ADDR:
+            op = kOpMul;
+            break;
+        case OP_DIV_INT:
+        case OP_DIV_INT_2ADDR:
+            isDivRem = true;
+            checkZero = true;
+            opc = kMipsMflo;
+            divReg = r_LO;
+            break;
+        case OP_REM_INT:
+        case OP_REM_INT_2ADDR:
+            isDivRem = true;
+            checkZero = true;
+            opc = kMipsMfhi;
+            divReg = r_HI;
+            break;
+        case OP_AND_INT:
+        case OP_AND_INT_2ADDR:
+            op = kOpAnd;
+            break;
+        case OP_OR_INT:
+        case OP_OR_INT_2ADDR:
+            op = kOpOr;
+            break;
+        case OP_XOR_INT:
+        case OP_XOR_INT_2ADDR:
+            op = kOpXor;
+            break;
+        case OP_SHL_INT:
+        case OP_SHL_INT_2ADDR:
+            shiftOp = true;
+            op = kOpLsl;
+            break;
+        case OP_SHR_INT:
+        case OP_SHR_INT_2ADDR:
+            shiftOp = true;
+            op = kOpAsr;
+            break;
+        case OP_USHR_INT:
+        case OP_USHR_INT_2ADDR:
+            shiftOp = true;
+            op = kOpLsr;
+            break;
+        default:
+            ALOGE("Invalid word arith op: %#x(%d)",
+                 mir->dalvikInsn.opcode, mir->dalvikInsn.opcode);
+            dvmCompilerAbort(cUnit);
+    }
+
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    if (unary) {
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        opRegReg(cUnit, op, rlResult.lowReg,
+                 rlSrc1.lowReg);
+    } else if (isDivRem) {
+        rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+        if (checkZero) {
+            genNullCheck(cUnit, rlSrc2.sRegLow, rlSrc2.lowReg, mir->offset, NULL);
+        }
+        newLIR4(cUnit, kMipsDiv, r_HI, r_LO, rlSrc1.lowReg, rlSrc2.lowReg);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        newLIR2(cUnit, opc, rlResult.lowReg, divReg);
+    } else {
+        rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+        if (shiftOp) {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegRegReg(cUnit, op, rlResult.lowReg,
+                        rlSrc1.lowReg, tReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+        } else {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegRegReg(cUnit, op, rlResult.lowReg,
+                        rlSrc1.lowReg, rlSrc2.lowReg);
+        }
+    }
+    storeValue(cUnit, rlDest, rlResult);
+
+    return false;
+}
+
+static bool genArithOp(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlSrc1;
+    RegLocation rlSrc2;
+    /* Deduce sizes of operands */
+    if (mir->ssaRep->numUses == 2) {
+        rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+        rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    } else if (mir->ssaRep->numUses == 3) {
+        rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+    } else {
+        rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+        assert(mir->ssaRep->numUses == 4);
+    }
+    if (mir->ssaRep->numDefs == 1) {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    } else {
+        assert(mir->ssaRep->numDefs == 2);
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    }
+
+    if ((opcode >= OP_ADD_LONG_2ADDR) && (opcode <= OP_XOR_LONG_2ADDR)) {
+        return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_LONG) && (opcode <= OP_XOR_LONG)) {
+        return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_SHL_LONG_2ADDR) && (opcode <= OP_USHR_LONG_2ADDR)) {
+        return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_SHL_LONG) && (opcode <= OP_USHR_LONG)) {
+        return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_USHR_INT_2ADDR)) {
+        return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_INT) && (opcode <= OP_USHR_INT)) {
+        return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_FLOAT_2ADDR) && (opcode <= OP_REM_FLOAT_2ADDR)) {
+        return genArithOpFloat(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_FLOAT) && (opcode <= OP_REM_FLOAT)) {
+        return genArithOpFloat(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_DOUBLE_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) {
+        return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_DOUBLE) && (opcode <= OP_REM_DOUBLE)) {
+        return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    return true;
+}
+
+/* Generate unconditional branch instructions */
+static MipsLIR *genUnconditionalBranch(CompilationUnit *cUnit, MipsLIR *target)
+{
+    MipsLIR *branch = opNone(cUnit, kOpUncondBr);
+    branch->generic.target = (LIR *) target;
+    return branch;
+}
+
+/* Perform the actual operation for OP_RETURN_* */
+void genReturnCommon(CompilationUnit *cUnit, MIR *mir)
+{
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                         TEMPLATE_RETURN_PROF : TEMPLATE_RETURN);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.returnOp++;
+#endif
+    int dPC = (int) (cUnit->method->insns + mir->offset);
+    /* Insert branch, but defer setting of target */
+    MipsLIR *branch = genUnconditionalBranch(cUnit, NULL);
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    MipsLIR *pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+    pcrLabel->operands[0] = dPC;
+    pcrLabel->operands[1] = mir->offset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+}
+
+static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir,
+                                  DecodedInstruction *dInsn,
+                                  MipsLIR **pcrLabel)
+{
+    unsigned int i;
+    unsigned int regMask = 0;
+    RegLocation rlArg;
+    int numDone = 0;
+
+    /*
+     * Load arguments to r_A0..r_T0.  Note that these registers may contain
+     * live values, so we clobber them immediately after loading to prevent
+     * them from being used as sources for subsequent loads.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    for (i = 0; i < dInsn->vA; i++) {
+        regMask |= 1 << i;
+        rlArg = dvmCompilerGetSrc(cUnit, mir, numDone++);
+        loadValueDirectFixed(cUnit, rlArg, i+r_A0); /* r_A0 thru r_T0 */
+    }
+    if (regMask) {
+        /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+        opRegRegImm(cUnit, kOpSub, r_S4, rFP,
+                    sizeof(StackSaveArea) + (dInsn->vA << 2));
+        /* generate null check */
+        if (pcrLabel) {
+            *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r_A0,
+                                     mir->offset, NULL);
+        }
+        storeMultiple(cUnit, r_S4, regMask);
+    }
+}
+
+static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir,
+                                DecodedInstruction *dInsn,
+                                MipsLIR **pcrLabel)
+{
+    int srcOffset = dInsn->vC << 2;
+    int numArgs = dInsn->vA;
+    int regMask;
+
+    /*
+     * Note: here, all promoted registers will have been flushed
+     * back to the Dalvik base locations, so register usage restrictins
+     * are lifted.  All parms loaded from original Dalvik register
+     * region - even though some might conceivably have valid copies
+     * cached in a preserved register.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * r4PC     : &rFP[vC]
+     * r_S4: &newFP[0]
+     */
+    opRegRegImm(cUnit, kOpAdd, r4PC, rFP, srcOffset);
+    /* load [r_A0 up to r_A3)] */
+    regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1;
+    /*
+     * Protect the loadMultiple instruction from being reordered with other
+     * Dalvik stack accesses.
+     */
+    if (numArgs != 0) loadMultiple(cUnit, r4PC, regMask);
+
+    opRegRegImm(cUnit, kOpSub, r_S4, rFP,
+                sizeof(StackSaveArea) + (numArgs << 2));
+    /* generate null check */
+    if (pcrLabel) {
+        *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r_A0,
+                                 mir->offset, NULL);
+    }
+
+    /*
+     * Handle remaining 4n arguments:
+     * store previously loaded 4 values and load the next 4 values
+     */
+    if (numArgs >= 8) {
+        MipsLIR *loopLabel = NULL;
+        /*
+         * r_A0 contains "this" and it will be used later, so push it to the stack
+         * first. Pushing r_S1 (rFP) is just for stack alignment purposes.
+         */
+
+        newLIR2(cUnit, kMipsMove, r_T0, r_A0);
+        newLIR2(cUnit, kMipsMove, r_T1, r_S1);
+
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            loadConstant(cUnit, rFP, ((numArgs - 4) >> 2) << 2);
+            loopLabel = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            loopLabel->defMask = ENCODE_ALL;
+        }
+        storeMultiple(cUnit, r_S4, regMask);
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            opRegImm(cUnit, kOpSub, rFP, 4);
+            genConditionalBranchMips(cUnit, kMipsBne, rFP, r_ZERO, loopLabel);
+        }
+    }
+
+    /* Save the last batch of loaded values */
+    if (numArgs != 0) storeMultiple(cUnit, r_S4, regMask);
+
+    /* Generate the loop epilogue - don't use r_A0 */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        regMask = ((1 << (numArgs & 0x3)) - 1) << 1;
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+    }
+    if (numArgs >= 8) {
+        newLIR2(cUnit, kMipsMove, r_A0, r_T0);
+        newLIR2(cUnit, kMipsMove, r_S1, r_T1);
+    }
+
+    /* Save the modulo 4 arguments */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        storeMultiple(cUnit, r_S4, regMask);
+    }
+}
+
+/*
+ * Generate code to setup the call stack then jump to the chaining cell if it
+ * is not a native method.
+ */
+static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir,
+                                     BasicBlock *bb, MipsLIR *labelList,
+                                     MipsLIR *pcrLabel,
+                                     const Method *calleeMethod)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  All temp & preserved registers may be used.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    MipsLIR *retChainingCell = &labelList[bb->fallThrough->id];
+
+    /* r_A1 = &retChainingCell */
+    dvmCompilerLockTemp(cUnit, r_A1);
+    MipsLIR *addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+    addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+    /*
+     * r_A0 = calleeMethod (loaded upon calling genInvokeSingletonCommon)
+     * r_A1 = &ChainingCell
+     * r4PC = callsiteDPC
+     */
+    if (dvmIsNativeMethod(calleeMethod)) {
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_NATIVE_PROF :
+            TEMPLATE_INVOKE_METHOD_NATIVE);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeNative++;
+#endif
+    } else {
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_CHAIN_PROF :
+            TEMPLATE_INVOKE_METHOD_CHAIN);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonomorphic++;
+#endif
+        /* Branch to the chaining cell */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/*
+ * Generate code to check the validity of a predicted chain and take actions
+ * based on the result.
+ *
+ * 0x2f1304c4 :  lui      s0,0x2d22(11554)            # s0 <- dalvikPC
+ * 0x2f1304c8 :  ori      s0,s0,0x2d22848c(757236876)
+ * 0x2f1304cc :  lahi/lui a1,0x2f13(12051)            # a1 <- &retChainingCell
+ * 0x2f1304d0 :  lalo/ori a1,a1,0x2f13055c(789775708)
+ * 0x2f1304d4 :  lahi/lui a2,0x2f13(12051)            # a2 <- &predictedChainingCell
+ * 0x2f1304d8 :  lalo/ori a2,a2,0x2f13056c(789775724)
+ * 0x2f1304dc :  jal      0x2f12d1ec(789762540)       # call TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+ * 0x2f1304e0 :  nop
+ * 0x2f1304e4 :  b        0x2f13056c (L0x11ec10)      # off to the predicted chain
+ * 0x2f1304e8 :  nop
+ * 0x2f1304ec :  b        0x2f13054c (L0x11fc80)      # punt to the interpreter
+ * 0x2f1304f0 :  lui      a0,0x2d22(11554)
+ * 0x2f1304f4 :  lw       a0,156(s4)                  # a0 <- this->class->vtable[methodIdx]
+ * 0x2f1304f8 :  bgtz     a1,0x2f13051c (L0x11fa40)   # if >0 don't rechain
+ * 0x2f1304fc :  nop
+ * 0x2f130500 :  lui      t9,0x2aba(10938)
+ * 0x2f130504 :  ori      t9,t9,0x2abae3f8(716891128)
+ * 0x2f130508 :  move     a1,s2
+ * 0x2f13050c :  jalr     ra,t9                       # call dvmJitToPatchPredictedChain
+ * 0x2f130510 :  nop
+ * 0x2f130514 :  lw       gp,84(sp)
+ * 0x2f130518 :  move     a0,v0
+ * 0x2f13051c :  lahi/lui a1,0x2f13(12051)            # a1 <- &retChainingCell
+ * 0x2f130520 :  lalo/ori a1,a1,0x2f13055c(789775708)
+ * 0x2f130524 :  jal      0x2f12d0c4(789762244)       # call TEMPLATE_INVOKE_METHOD_NO_OPT
+ * 0x2f130528 :  nop
+ */
+static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir,
+                                   int methodIndex,
+                                   MipsLIR *retChainingCell,
+                                   MipsLIR *predChainingCell,
+                                   MipsLIR *pcrLabel)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  Lock temps to prevent them from being
+     * allocated by utility routines.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * For verbose printing, store the method pointer in operands[1] first as
+     * operands[0] will be clobbered in dvmCompilerMIR2LIR.
+     */
+    predChainingCell->operands[1] = (int) mir->meta.callsiteInfo->method;
+
+    /* "this" is already left in r_A0 by genProcessArgs* */
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+
+    /* r_A1 = &retChainingCell */
+    MipsLIR *addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+    addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r_A2 = &predictedChainingCell */
+    MipsLIR *predictedChainingCell = newLIR2(cUnit, kMipsLahi, r_A2, 0);
+    predictedChainingCell->generic.target = (LIR *) predChainingCell;
+    predictedChainingCell = newLIR3(cUnit, kMipsLalo, r_A2, r_A2, 0);
+    predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+    /* return through ra - jump to the chaining cell */
+    genUnconditionalBranch(cUnit, predChainingCell);
+
+    /*
+     * null-check on "this" may have been eliminated, but we still need a PC-
+     * reconstruction label for stack overflow bailout.
+     */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + mir->offset);
+        pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = mir->offset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+
+    /* return through ra+8 - punt to the interpreter */
+    genUnconditionalBranch(cUnit, pcrLabel);
+
+    /*
+     * return through ra+16 - fully resolve the callee method.
+     * r_A1 <- count
+     * r_A2 <- &predictedChainCell
+     * r_A3 <- this->class
+     * r4 <- dPC
+     * r_S4 <- this->class->vtable
+     */
+
+    /* r_A0 <- calleeMethod */
+    loadWordDisp(cUnit, r_S4, methodIndex * 4, r_A0);
+
+    /* Check if rechain limit is reached */
+    MipsLIR *bypassRechaining = opCompareBranch(cUnit, kMipsBgtz, r_A1, -1);
+
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int) dvmJitToPatchPredictedChain);
+
+    genRegCopy(cUnit, r_A1, rSELF);
+
+    /*
+     * r_A0 = calleeMethod
+     * r_A2 = &predictedChainingCell
+     * r_A3 = class
+     *
+     * &returnChainingCell has been loaded into r_A1 but is not needed
+     * when patching the chaining cell and will be clobbered upon
+     * returning so it will be reconstructed again.
+     */
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    newLIR2(cUnit, kMipsMove, r_A0, r_V0);
+
+    /* r_A1 = &retChainingCell */
+    addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+    bypassRechaining->generic.target = (LIR *) addrRetChain;
+    addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /*
+     * r_A0 = calleeMethod,
+     * r_A1 = &ChainingCell,
+     * r4PC = callsiteDPC,
+     */
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+        TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.invokePolymorphic++;
+#endif
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/* "this" pointer is already in r0 */
+static void genInvokeVirtualWholeMethod(CompilationUnit *cUnit,
+                                        MIR *mir,
+                                        void *calleeAddr,
+                                        MipsLIR *retChainingCell)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    dvmCompilerLockAllTemps(cUnit);
+
+    loadClassPointer(cUnit, r_A1, (int) callsiteInfo);
+
+    loadWordDisp(cUnit, r_A0, offsetof(Object, clazz), r_A2);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    /* Branch to the slow path if classes are not equal */
+    MipsLIR *classCheck = opCompareBranch(cUnit, kMipsBne, r_A1, r_A2);
+
+    /* a0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR1(cUnit, kMipsJal, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    /* Target of slow path */
+    MipsLIR *slowPathLabel = newLIR0(cUnit, kMipsPseudoTargetLabel);
+
+    slowPathLabel->defMask = ENCODE_ALL;
+    classCheck->generic.target = (LIR *) slowPathLabel;
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+static void genInvokeSingletonWholeMethod(CompilationUnit *cUnit,
+                                          MIR *mir,
+                                          void *calleeAddr,
+                                          MipsLIR *retChainingCell)
+{
+    /* a0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR1(cUnit, kMipsJal, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+/* Geneate a branch to go back to the interpreter */
+static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset)
+{
+    /* a0 = dalvik pc */
+    dvmCompilerFlushAllRegs(cUnit);
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + offset));
+#if 0 /* MIPSTODO tempoary workaround unaligned access on sigma hardware
+             this can removed when we're not punting to genInterpSingleStep
+             for opcodes that haven't been activated yet */
+    loadWordDisp(cUnit, r_A0, offsetof(Object, clazz), r_A3);
+#endif
+    loadWordDisp(cUnit, rSELF, offsetof(Thread,
+                 jitToInterpEntries.dvmJitToInterpPunt), r_A1);
+
+    opReg(cUnit, kOpBlx, r_A1);
+}
+
+/*
+ * Attempt to single step one instruction using the interpreter and return
+ * to the compiled code for the next Dalvik instruction
+ */
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir)
+{
+    int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode);
+    int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn;
+
+    // Single stepping is considered loop mode breaker
+    if (cUnit->jitMode == kJitLoop) {
+        cUnit->quitLoopMode = true;
+        return;
+    }
+
+    //If already optimized out, just ignore
+    if (mir->dalvikInsn.opcode == OP_NOP)
+        return;
+
+    //Ugly, but necessary.  Flush all Dalvik regs so Interp can find them
+    dvmCompilerFlushAllRegs(cUnit);
+
+    if ((mir->next == NULL) || (flags & flagsToCheck)) {
+       genPuntToInterp(cUnit, mir->offset);
+       return;
+    }
+    int entryAddr = offsetof(Thread,
+                             jitToInterpEntries.dvmJitToInterpSingleStep);
+    loadWordDisp(cUnit, rSELF, entryAddr, r_A2);
+    /* a0 = dalvik pc */
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+    /* a1 = dalvik pc of following instruction */
+    loadConstant(cUnit, r_A1, (int) (cUnit->method->insns + mir->next->offset));
+    opReg(cUnit, kOpBlx, r_A2);
+}
+
+/*
+ * To prevent a thread in a monitor wait from blocking the Jit from
+ * resetting the code cache, heavyweight monitor lock will not
+ * be allowed to return to an existing translation.  Instead, we will
+ * handle them by branching to a handler, which will in turn call the
+ * runtime lock routine and then branch directly back to the
+ * interpreter main loop.  Given the high cost of the heavyweight
+ * lock operation, this additional cost should be slight (especially when
+ * considering that we expect the vast majority of lock operations to
+ * use the fast-path thin lock bypass).
+ */
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    bool isEnter = (mir->dalvikInsn.opcode == OP_MONITOR_ENTER);
+    genExportPC(cUnit, mir);
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    loadValueDirectFixed(cUnit, rlSrc, r_A1);
+    genRegCopy(cUnit, r_A0, rSELF);
+    genNullCheck(cUnit, rlSrc.sRegLow, r_A1, mir->offset, NULL);
+    if (isEnter) {
+        /* Get dPC of next insn */
+        loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_ENTER)));
+        genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+    } else {
+        LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmUnlockObject);
+        /* Do the call */
+        opReg(cUnit, kOpBlx, r_T9);
+        newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+        /* Did we throw? */
+        MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+        loadConstant(cUnit, r_A0,
+                     (int) (cUnit->method->insns + mir->offset +
+                     dexGetWidthFromOpcode(OP_MONITOR_EXIT)));
+        genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+        MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+        target->defMask = ENCODE_ALL;
+        branchOver->generic.target = (LIR *) target;
+        dvmCompilerClobberCallRegs(cUnit);
+    }
+}
+/*#endif*/
+
+/*
+ * Fetch *self->info.breakFlags. If the breakFlags are non-zero,
+ * punt to the interpreter.
+ */
+static void genSuspendPoll(CompilationUnit *cUnit, MIR *mir)
+{
+    int rTemp = dvmCompilerAllocTemp(cUnit);
+    MipsLIR *ld;
+    ld = loadBaseDisp(cUnit, NULL, rSELF,
+                      offsetof(Thread, interpBreak.ctl.breakFlags),
+                      rTemp, kUnsignedByte, INVALID_SREG);
+    setMemRefType(ld, true /* isLoad */, kMustNotAlias);
+    genRegImmCheck(cUnit, kMipsCondNe, rTemp, 0, mir->offset, NULL);
+}
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ */
+
+static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+                                       BasicBlock *bb, MipsLIR *labelList)
+{
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    int numPredecessors = dvmCountSetBits(bb->taken->predecessors);
+    /*
+     * Things could be hoisted out of the taken block into the predecessor, so
+     * make sure it is dominated by the predecessor.
+     */
+    if (numPredecessors == 1 && bb->taken->visited == false &&
+        bb->taken->blockType == kDalvikByteCode) {
+        cUnit->nextCodegenBlock = bb->taken;
+    } else {
+        /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    return false;
+}
+
+static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    if ((dalvikOpcode >= OP_UNUSED_3E) && (dalvikOpcode <= OP_UNUSED_43)) {
+        ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+        return true;
+    }
+    switch (dalvikOpcode) {
+        case OP_RETURN_VOID_BARRIER:
+            dvmCompilerGenMemBarrier(cUnit, 0);
+            // Intentional fallthrough
+        case OP_RETURN_VOID:
+            genReturnCommon(cUnit,mir);
+            break;
+        case OP_UNUSED_73:
+        case OP_UNUSED_79:
+        case OP_UNUSED_7A:
+        case OP_UNUSED_FF:
+            ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+            return true;
+        case OP_NOP:
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST:
+        case OP_CONST_4: {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_32: {
+            //TUNING: single routine to load constant pair for support doubles
+            //TUNING: load 0/-1 separately to avoid load dependency
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_HIGH16: {
+            loadConstantNoClobber(cUnit, rlResult.lowReg,
+                                  mir->dalvikInsn.vB << 16);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_HIGH16: {
+            loadConstantValueWide(cUnit, rlResult.lowReg, rlResult.highReg,
+                                  0, mir->dalvikInsn.vB << 16);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+    /* For OP_THROW_VERIFICATION_ERROR */
+    genInterpSingleStep(cUnit, mir);
+    return false;
+}
+
+static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlResult;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_STRING_JUMBO:
+        case OP_CONST_STRING: {
+            void *strPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]);
+
+            if (strPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null string");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_CLASS: {
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET:
+        case OP_SGET_VOLATILE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_CHAR:
+        case OP_SGET_BYTE:
+        case OP_SGET_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            bool isVolatile;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            /*
+             * On SMP systems, Dalvik opcodes found to be referencing
+             * volatile fields are rewritten to their _VOLATILE variant.
+             * However, this does not happen on non-SMP systems. The JIT
+             * still needs to know about volatility to avoid unsafe
+             * optimizations so we determine volatility based on either
+             * the opcode or the field access flags.
+             */
+#if ANDROID_SMP != 0
+            Opcode opcode = mir->dalvikInsn.opcode;
+            isVolatile = (opcode == OP_SGET_VOLATILE) ||
+                         (opcode == OP_SGET_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, 0);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            loadWordDisp(cUnit, tReg, 0, rlResult.lowReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET_WIDE: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            loadPair(cUnit, tReg, rlResult.lowReg, rlResult.highReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SPUT:
+        case OP_SPUT_VOLATILE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int objHead = 0;
+            bool isVolatile;
+            bool isSputObject;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+            Opcode opcode = mir->dalvikInsn.opcode;
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+#if ANDROID_SMP != 0
+            isVolatile = (opcode == OP_SPUT_VOLATILE) ||
+                         (opcode == OP_SPUT_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            isSputObject = (opcode == OP_SPUT_OBJECT) ||
+                           (opcode == OP_SPUT_OBJECT_VOLATILE);
+
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr);
+            if (isSputObject) {
+                objHead = dvmCompilerAllocTemp(cUnit);
+                loadWordDisp(cUnit, tReg, OFFSETOF_MEMBER(Field, clazz), objHead);
+            }
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, 0);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            HEAP_ACCESS_SHADOW(false);
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, 0);
+            }
+            if (isSputObject) {
+                /* NOTE: marking card based sfield->clazz */
+                markCard(cUnit, rlSrc.lowReg, objHead);
+                dvmCompilerFreeTemp(cUnit, objHead);
+            }
+
+            break;
+        }
+        case OP_SPUT_WIDE: {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            storePair(cUnit, tReg, rlSrc.lowReg, rlSrc.highReg);
+            HEAP_ACCESS_SHADOW(false);
+            break;
+        }
+        case OP_NEW_INSTANCE: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr = (ClassObject *)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            /*
+             * If it is going to throw, it should not make to the trace to begin
+             * with.  However, Alloc might throw, so we need to genExportPC()
+             */
+            assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmAllocObject);
+            loadConstant(cUnit, r_A0, (int) classPtr);
+            loadConstant(cUnit, r_A1, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CHECK_CAST: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                LOGVV("null clazz in OP_CHECK_CAST, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                return false;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadConstant(cUnit, r_A1, (int) classPtr );
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            MipsLIR *branch1 = opCompareBranch(cUnit, kMipsBeqz, rlSrc.lowReg, -1);
+            /*
+             *  rlSrc.lowReg now contains object->clazz.  Note that
+             *  it could have been allocated r_A0, but we're okay so long
+             *  as we don't do anything desctructive until r_A0 is loaded
+             *  with clazz.
+             */
+            /* r_A0 now contains object->clazz */
+            loadWordDisp(cUnit, rlSrc.lowReg, offsetof(Object, clazz), r_A0);
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmInstanceofNonTrivial);
+            MipsLIR *branch2 = opCompareBranch(cUnit, kMipsBeq, r_A0, r_A1);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /*
+             * If null, check cast failed - punt to the interpreter.  Because
+             * interpreter will be the one throwing, we don't need to
+             * genExportPC() here.
+             */
+            genRegCopy(cUnit, r_A0, r_V0);
+            genZeroCheck(cUnit, r_V0, mir->offset, NULL);
+            /* check cast passed - branch target here */
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_SGET_WIDE_VOLATILE:
+        case OP_SPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlResult;
+    switch (dalvikOpcode) {
+        case OP_MOVE_EXCEPTION: {
+            int exOffset = offsetof(Thread, exception);
+            int resetReg = dvmCompilerAllocTemp(cUnit);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, rSELF, exOffset, rlResult.lowReg);
+            loadConstant(cUnit, resetReg, 0);
+            storeWordDisp(cUnit, rSELF, exOffset, resetReg);
+            storeValue(cUnit, rlDest, rlResult);
+           break;
+        }
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_OBJECT: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL;
+            rlSrc.fp = rlDest.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_MOVE_RESULT_WIDE: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlSrc.fp = rlDest.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_RETURN_WIDE: {
+            RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlDest.fp = rlSrc.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit,mir);
+            break;
+        }
+        case OP_RETURN:
+        case OP_RETURN_OBJECT: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL;
+            rlDest.fp = rlSrc.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit, mir);
+            break;
+        }
+        case OP_MONITOR_EXIT:
+        case OP_MONITOR_ENTER:
+            genMonitor(cUnit, mir);
+            break;
+        case OP_THROW:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+    RegLocation rlResult;
+
+    if ( (opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    if (mir->ssaRep->numUses == 2)
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    else
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    if (mir->ssaRep->numDefs == 2)
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    else
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+
+    switch (opcode) {
+        case OP_DOUBLE_TO_INT:
+        case OP_INT_TO_FLOAT:
+        case OP_FLOAT_TO_INT:
+        case OP_DOUBLE_TO_FLOAT:
+        case OP_FLOAT_TO_DOUBLE:
+        case OP_INT_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+        case OP_LONG_TO_DOUBLE:
+            return genConversion(cUnit, mir);
+        case OP_NEG_INT:
+        case OP_NOT_INT:
+            return genArithOpInt(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_LONG:
+        case OP_NOT_LONG:
+            return genArithOpLong(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_FLOAT:
+            return genArithOpFloat(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_DOUBLE:
+            return genArithOpDouble(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_MOVE_WIDE:
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_LONG:
+            rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            //TUNING: shouldn't loadValueDirect already check for phys reg?
+            if (rlSrc.location == kLocPhysReg) {
+                genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+            } else {
+                loadValueDirect(cUnit, rlSrc, rlResult.lowReg);
+            }
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        case OP_LONG_TO_INT:
+            rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+            rlSrc = dvmCompilerWideToNarrow(cUnit, rlSrc);
+            // Intentional fallthrough
+        case OP_MOVE:
+        case OP_MOVE_OBJECT:
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_BYTE:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Byte, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_SHORT:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Short, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_CHAR:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Char, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_ARRAY_LENGTH: {
+            int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            genNullCheck(cUnit, rlSrc.sRegLow, rlSrc.lowReg,
+                         mir->offset, NULL);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, rlSrc.lowReg, lenOffset,
+                         rlResult.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlResult;
+    int BBBB = mir->dalvikInsn.vB;
+    if (dalvikOpcode == OP_CONST_WIDE_16) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        //TUNING: do high separately to avoid load dependency
+        opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (dalvikOpcode == OP_CONST_16) {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        storeValue(cUnit, rlDest, rlResult);
+    } else
+        return true;
+    return false;
+}
+
+/* Compare agaist zero */
+static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         MipsLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    MipsOpCode opc = kMipsNop;
+    int rt = -1;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+
+    switch (dalvikOpcode) {
+        case OP_IF_EQZ:
+            opc = kMipsBeqz;
+            break;
+        case OP_IF_NEZ:
+            opc = kMipsBne;
+            rt = r_ZERO;
+            break;
+        case OP_IF_LTZ:
+            opc = kMipsBltz;
+            break;
+        case OP_IF_GEZ:
+            opc = kMipsBgez;
+            break;
+        case OP_IF_GTZ:
+            opc = kMipsBgtz;
+            break;
+        case OP_IF_LEZ:
+            opc = kMipsBlez;
+            break;
+        default:
+            ALOGE("Unexpected opcode (%d) for Fmt21t", dalvikOpcode);
+            dvmCompilerAbort(cUnit);
+    }
+    genConditionalBranchMips(cUnit, opc, rlSrc.lowReg, rt, &labelList[bb->taken->id]);
+    /* This mostly likely will be optimized away in a later phase */
+    genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+    return false;
+}
+
+static bool isPowerOfTwo(int x)
+{
+    return (x & (x - 1)) == 0;
+}
+
+// Returns true if no more than two bits are set in 'x'.
+static bool isPopCountLE2(unsigned int x)
+{
+    x &= x - 1;
+    return (x & (x - 1)) == 0;
+}
+
+// Returns the index of the lowest set bit in 'x'.
+static int lowestSetBit(unsigned int x) {
+    int bit_posn = 0;
+    while ((x & 0xf) == 0) {
+        bit_posn += 4;
+        x >>= 4;
+    }
+    while ((x & 1) == 0) {
+        bit_posn++;
+        x >>= 1;
+    }
+    return bit_posn;
+}
+
+// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyDivide(CompilationUnit *cUnit, Opcode dalvikOpcode,
+                             RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+    if (lit < 2 || !isPowerOfTwo(lit)) {
+        return false;
+    }
+    int k = lowestSetBit(lit);
+    if (k >= 30) {
+        // Avoid special cases.
+        return false;
+    }
+    bool div = (dalvikOpcode == OP_DIV_INT_LIT8 || dalvikOpcode == OP_DIV_INT_LIT16);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    if (div) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        if (lit == 2) {
+            // Division by 2 is by far the most common division by constant.
+            opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+            opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+        } else {
+            opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
+            opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+            opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+        }
+    } else {
+        int cReg = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, cReg, lit - 1);
+        int tReg1 = dvmCompilerAllocTemp(cUnit);
+        int tReg2 = dvmCompilerAllocTemp(cUnit);
+        if (lit == 2) {
+            opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+            opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+        } else {
+            opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
+            opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+            opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+        }
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return true;
+}
+
+// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyMultiply(CompilationUnit *cUnit,
+                               RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+    // Can we simplify this multiplication?
+    bool powerOfTwo = false;
+    bool popCountLE2 = false;
+    bool powerOfTwoMinusOne = false;
+    if (lit < 2) {
+        // Avoid special cases.
+        return false;
+    } else if (isPowerOfTwo(lit)) {
+        powerOfTwo = true;
+    } else if (isPopCountLE2(lit)) {
+        popCountLE2 = true;
+    } else if (isPowerOfTwo(lit + 1)) {
+        powerOfTwoMinusOne = true;
+    } else {
+        return false;
+    }
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    if (powerOfTwo) {
+        // Shift.
+        opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
+                    lowestSetBit(lit));
+    } else if (popCountLE2) {
+        // Shift and add and shift.
+        int firstBit = lowestSetBit(lit);
+        int secondBit = lowestSetBit(lit ^ (1 << firstBit));
+        genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
+                                      firstBit, secondBit);
+    } else {
+        // Reverse subtract: (src << (shift + 1)) - src.
+        assert(powerOfTwoMinusOne);
+        // TODO: rsb dst, src, src lsl#lowestSetBit(lit + 1)
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
+        opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return true;
+}
+
+static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    RegLocation rlResult;
+    int lit = mir->dalvikInsn.vC;
+    OpKind op = (OpKind)0;      /* Make gcc happy */
+    int shiftOp = false;
+
+    switch (dalvikOpcode) {
+        case OP_RSUB_INT_LIT8:
+        case OP_RSUB_INT: {
+            int tReg;
+            //TUNING: add support for use of Arm rsub op
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            tReg = dvmCompilerAllocTemp(cUnit);
+            loadConstant(cUnit, tReg, lit);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
+                        tReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        }
+
+        case OP_ADD_INT_LIT8:
+        case OP_ADD_INT_LIT16:
+            op = kOpAdd;
+            break;
+        case OP_MUL_INT_LIT8:
+        case OP_MUL_INT_LIT16: {
+            if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
+                return false;
+            }
+            op = kOpMul;
+            break;
+        }
+        case OP_AND_INT_LIT8:
+        case OP_AND_INT_LIT16:
+            op = kOpAnd;
+            break;
+        case OP_OR_INT_LIT8:
+        case OP_OR_INT_LIT16:
+            op = kOpOr;
+            break;
+        case OP_XOR_INT_LIT8:
+        case OP_XOR_INT_LIT16:
+            op = kOpXor;
+            break;
+        case OP_SHL_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpLsl;
+            break;
+        case OP_SHR_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpAsr;
+            break;
+        case OP_USHR_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpLsr;
+            break;
+
+        case OP_DIV_INT_LIT8:
+        case OP_DIV_INT_LIT16:
+        case OP_REM_INT_LIT8:
+        case OP_REM_INT_LIT16: {
+            if (lit == 0) {
+                /* Let the interpreter deal with div by 0 */
+                genInterpSingleStep(cUnit, mir);
+                return false;
+            }
+            if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
+                return false;
+            }
+
+            MipsOpCode opc;
+            int divReg;
+
+            if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
+                (dalvikOpcode == OP_DIV_INT_LIT16)) {
+                opc = kMipsMflo;
+                divReg = r_LO;
+            } else {
+                opc = kMipsMfhi;
+                divReg = r_HI;
+            }
+
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            newLIR3(cUnit, kMipsAddiu, tReg, r_ZERO, lit);
+            newLIR4(cUnit, kMipsDiv, r_HI, r_LO, rlSrc.lowReg, tReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            newLIR2(cUnit, opc, rlResult.lowReg, divReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            storeValue(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        }
+        default:
+            return true;
+    }
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    // Avoid shifts by literal 0 - no support in Thumb.  Change to copy
+    if (shiftOp && (lit == 0)) {
+        genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+    } else {
+        opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    int fieldOffset = -1;
+    bool isVolatile = false;
+    switch (dalvikOpcode) {
+        /*
+         * Wide volatiles currently handled via single step.
+         * Add them here if generating in-line code.
+         *     case OP_IGET_WIDE_VOLATILE:
+         *     case OP_IPUT_WIDE_VOLATILE:
+         */
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+#if ANDROID_SMP != 0
+            isVolatile = true;
+        // NOTE: intentional fallthrough
+#endif
+        case OP_IGET:
+        case OP_IGET_WIDE:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+        case OP_IPUT:
+        case OP_IPUT_WIDE:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT: {
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            Field *fieldPtr =
+                method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null instance field");
+                dvmAbort();
+            }
+#if ANDROID_SMP != 0
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+            fieldOffset = ((InstField *)fieldPtr)->byteOffset;
+            break;
+        }
+        default:
+            break;
+    }
+
+    switch (dalvikOpcode) {
+        case OP_NEW_ARRAY: {
+#if 0 /* 080 triggers assert in Interp.c:1290 for out of memory exception.
+             i think the assert is in error and should be disabled. With
+             asserts disabled, 080 passes. */
+genInterpSingleStep(cUnit, mir);
+return false;
+#endif
+            // Generates a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r_A1);   /* Len */
+            loadConstant(cUnit, r_A0, (int) classPtr );
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmAllocArrayByClass);
+            /*
+             * "len < 0": bail to the interpreter to re-execute the
+             * instruction
+             */
+            genRegImmCheck(cUnit, kMipsCondMi, r_A1, 0, mir->offset, NULL);
+            loadConstant(cUnit, r_A2, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_INSTANCE_OF: {
+            // May generate a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGD("null clazz in OP_INSTANCE_OF, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                break;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r_V0);  /* Ref */
+            loadConstant(cUnit, r_A2, (int) classPtr );
+            /* When taken r_V0 has NULL which can be used for store directly */
+            MipsLIR *branch1 = opCompareBranch(cUnit, kMipsBeqz, r_V0, -1);
+            /* r_A1 now contains object->clazz */
+            loadWordDisp(cUnit, r_V0, offsetof(Object, clazz), r_A1);
+            /* r_A1 now contains object->clazz */
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmInstanceofNonTrivial);
+            loadConstant(cUnit, r_V0, 1);                /* Assume true */
+            MipsLIR *branch2 = opCompareBranch(cUnit, kMipsBeq, r_A1, r_A2);
+            genRegCopy(cUnit, r_A0, r_A1);
+            genRegCopy(cUnit, r_A1, r_A2);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* branch target here */
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_IGET_WIDE:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IGET:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            genIGet(cUnit, mir, kWord, fieldOffset, isVolatile);
+            break;
+        case OP_IPUT_WIDE:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile);
+            break;
+        case OP_IPUT_OBJECT_VOLATILE:
+        case OP_IPUT_OBJECT:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile);
+            break;
+        case OP_IGET_WIDE_VOLATILE:
+        case OP_IPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    int fieldOffset =  mir->dalvikInsn.vC;
+    switch (dalvikOpcode) {
+        case OP_IGET_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+            genIGet(cUnit, mir, kWord, fieldOffset, false);
+            break;
+        case OP_IPUT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, false);
+            break;
+        case OP_IPUT_OBJECT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, false);
+            break;
+        case OP_IGET_WIDE_QUICK:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_WIDE_QUICK:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        default:
+            return true;
+    }
+    return false;
+
+}
+
+/* Compare against zero */
+static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         MipsLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    MipsConditionCode cond;
+    MipsOpCode opc = kMipsNop;
+    MipsLIR * test = NULL;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+    int reg1 = rlSrc1.lowReg;
+    int reg2 = rlSrc2.lowReg;
+    int tReg;
+
+    switch (dalvikOpcode) {
+        case OP_IF_EQ:
+            opc = kMipsBeq;
+            break;
+        case OP_IF_NE:
+            opc = kMipsBne;
+            break;
+        case OP_IF_LT:
+            opc = kMipsBne;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
+            reg1 = tReg;
+            reg2 = r_ZERO;
+            break;
+        case OP_IF_LE:
+            opc = kMipsBeqz;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg2, reg1);
+            reg1 = tReg;
+            reg2 = -1;
+            break;
+        case OP_IF_GT:
+            opc = kMipsBne;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg2, reg1);
+            reg1 = tReg;
+            reg2 = r_ZERO;
+            break;
+        case OP_IF_GE:
+            opc = kMipsBeqz;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
+            reg1 = tReg;
+            reg2 = -1;
+            break;
+        default:
+            cond = (MipsConditionCode)0;
+            ALOGE("Unexpected opcode (%d) for Fmt22t", dalvikOpcode);
+            dvmCompilerAbort(cUnit);
+    }
+
+    genConditionalBranchMips(cUnit, opc, reg1, reg2, &labelList[bb->taken->id]);
+    /* This mostly likely will be optimized away in a later phase */
+    genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+    return false;
+}
+
+static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_MOVE_16:
+        case OP_MOVE_OBJECT_16:
+        case OP_MOVE_FROM16:
+        case OP_MOVE_OBJECT_FROM16: {
+            storeValue(cUnit, dvmCompilerGetDest(cUnit, mir, 0),
+                       dvmCompilerGetSrc(cUnit, mir, 0));
+            break;
+        }
+        case OP_MOVE_WIDE_16:
+        case OP_MOVE_WIDE_FROM16: {
+            storeValueWide(cUnit, dvmCompilerGetDestWide(cUnit, mir, 0, 1),
+                           dvmCompilerGetSrcWide(cUnit, mir, 0, 1));
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlSrc1;
+    RegLocation rlSrc2;
+    RegLocation rlDest;
+
+    if ((opcode >= OP_ADD_INT) && (opcode <= OP_REM_DOUBLE)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    /* APUTs have 3 sources and no targets */
+    if (mir->ssaRep->numDefs == 0) {
+        if (mir->ssaRep->numUses == 3) {
+            rlDest = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 1);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+        } else {
+            assert(mir->ssaRep->numUses == 4);
+            rlDest = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 2);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 3);
+        }
+    } else {
+        /* Two sources and 1 dest.  Deduce the operand sizes */
+        if (mir->ssaRep->numUses == 4) {
+            rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+        } else {
+            assert(mir->ssaRep->numUses == 2);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+        }
+        if (mir->ssaRep->numDefs == 2) {
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        } else {
+            assert(mir->ssaRep->numDefs == 1);
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        }
+    }
+
+    switch (opcode) {
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+            return genCmpFP(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        case OP_CMP_LONG:
+            genCmpLong(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+            break;
+        case OP_AGET_WIDE:
+            genArrayGet(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_AGET:
+        case OP_AGET_OBJECT:
+            genArrayGet(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_AGET_BOOLEAN:
+            genArrayGet(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_BYTE:
+            genArrayGet(cUnit, mir, kSignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_CHAR:
+            genArrayGet(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_AGET_SHORT:
+            genArrayGet(cUnit, mir, kSignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_WIDE:
+            genArrayPut(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_APUT:
+            genArrayPut(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_OBJECT:
+            genArrayObjectPut(cUnit, mir, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_SHORT:
+        case OP_APUT_CHAR:
+            genArrayPut(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_BYTE:
+        case OP_APUT_BOOLEAN:
+            genArrayPut(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * Find the matching case.
+ *
+ * return values:
+ * r_RESULT0 (low 32-bit): pc of the chaining cell corresponding to the resolved case,
+ *    including default which is placed at MIN(size, MAX_CHAINED_SWITCH_CASES).
+ * r_RESULT1 (high 32-bit): the branch offset of the matching case (only for indexes
+ *    above MAX_CHAINED_SWITCH_CASES).
+ *
+ * Instructions around the call are:
+ *
+ * jalr &findPackedSwitchIndex
+ * nop
+ * lw gp, 84(sp) |
+ * addu          | 20 bytes for these 5 instructions
+ * move          | (NOTE: if this sequence is shortened or lengthened, then
+ * jr            |  the 20 byte offset added below in 3 places must be changed
+ * nop           |  accordingly.)
+ * chaining cell for case 0 [16 bytes]
+ * chaining cell for case 1 [16 bytes]
+ *               :
+ * chaining cell for case MIN(size, MAX_CHAINED_SWITCH_CASES)-1 [16 bytes]
+ * chaining cell for case default [16 bytes]
+ * noChain exit
+ */
+static u8 findPackedSwitchIndex(const u2* switchData, int testVal)
+{
+    int size;
+    int firstKey;
+    const int *entries;
+    int index;
+    int jumpIndex;
+    uintptr_t caseDPCOffset = 0;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    size = switchData[1];
+    assert(size > 0);
+
+    firstKey = switchData[2];
+    firstKey |= switchData[3] << 16;
+
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = (const int*) &switchData[4];
+    assert(((u4)entries & 0x3) == 0);
+
+    index = testVal - firstKey;
+
+    /* Jump to the default cell */
+    if (index < 0 || index >= size) {
+        jumpIndex = MIN(size, MAX_CHAINED_SWITCH_CASES);
+    /* Jump to the non-chaining exit point */
+    } else if (index >= MAX_CHAINED_SWITCH_CASES) {
+        jumpIndex = MAX_CHAINED_SWITCH_CASES + 1;
+#ifdef HAVE_LITTLE_ENDIAN
+        caseDPCOffset = entries[index];
+#else
+        caseDPCOffset = (unsigned int)entries[index] >> 16 | entries[index] << 16;
+#endif
+    /* Jump to the inline chaining cell */
+    } else {
+        jumpIndex = index;
+    }
+
+    return (((u8) caseDPCOffset) << 32) | (u8) (jumpIndex * CHAIN_CELL_NORMAL_SIZE + 20);
+}
+
+/* See comments for findPackedSwitchIndex */
+static u8 findSparseSwitchIndex(const u2* switchData, int testVal)
+{
+    int size;
+    const int *keys;
+    const int *entries;
+    /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */
+    int i;
+
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+
+    size = switchData[1];
+    assert(size > 0);
+
+    /* The keys are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    keys = (const int*) &switchData[2];
+    assert(((u4)keys & 0x3) == 0);
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = keys + size;
+    assert(((u4)entries & 0x3) == 0);
+
+    /*
+     * Run through the list of keys, which are guaranteed to
+     * be sorted low-to-high.
+     *
+     * Most tables have 3-4 entries.  Few have more than 10.  A binary
+     * search here is probably not useful.
+     */
+    for (i = 0; i < size; i++) {
+#ifdef HAVE_LITTLE_ENDIAN
+        int k = keys[i];
+        if (k == testVal) {
+            /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */
+            int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ?
+                           i : MAX_CHAINED_SWITCH_CASES + 1;
+            return (((u8) entries[i]) << 32) | (u8) (jumpIndex * CHAIN_CELL_NORMAL_SIZE + 20);
+#else
+        int k = (unsigned int)keys[i] >> 16 | keys[i] << 16;
+        if (k == testVal) {
+            /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */
+            int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ?
+                           i : MAX_CHAINED_SWITCH_CASES + 1;
+            int temp = (unsigned int)entries[i] >> 16 | entries[i] << 16;
+            return (((u8) temp) << 32) | (u8) (jumpIndex * CHAIN_CELL_NORMAL_SIZE + 20);
+#endif
+        } else if (k > testVal) {
+            break;
+        }
+    }
+    return MIN(size, MAX_CHAINED_SWITCH_CASES) * CHAIN_CELL_NORMAL_SIZE + 20;
+}
+
+static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_FILL_ARRAY_DATA: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            // Making a call - use explicit registers
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r_A0);
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmInterpHandleFillArrayData);
+            loadConstant(cUnit, r_A1,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if successful */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            break;
+        }
+        /*
+         * Compute the goto target of up to
+         * MIN(switchSize, MAX_CHAINED_SWITCH_CASES) + 1 chaining cells.
+         * See the comment before findPackedSwitchIndex for the code layout.
+         */
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r_A1);
+            dvmCompilerLockAllTemps(cUnit);
+
+            if (dalvikOpcode == OP_PACKED_SWITCH) {
+                LOAD_FUNC_ADDR(cUnit, r_T9, (int)findPackedSwitchIndex);
+            } else {
+                LOAD_FUNC_ADDR(cUnit, r_T9, (int)findSparseSwitchIndex);
+            }
+            /* r_A0 <- Addr of the switch data */
+            loadConstant(cUnit, r_A0,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* pc <- computed goto target using value in RA */
+            newLIR3(cUnit, kMipsAddu, r_A0, r_RA, r_RESULT0);
+            newLIR2(cUnit, kMipsMove, r_A1, r_RESULT1);
+            newLIR1(cUnit, kMipsJr, r_A0);
+            newLIR0(cUnit, kMipsNop); /* for maintaining 20 byte offset */
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * See the example of predicted inlining listed before the
+ * genValidationForPredictedInline function. The function here takes care the
+ * branch over at 0x4858de78 and the misprediction target at 0x4858de7a.
+ */
+static void genLandingPadForMispredictedCallee(CompilationUnit *cUnit, MIR *mir,
+                                               BasicBlock *bb,
+                                               MipsLIR *labelList)
+{
+    BasicBlock *fallThrough = bb->fallThrough;
+
+    /* Bypass the move-result block if there is one */
+    if (fallThrough->firstMIRInsn) {
+        assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
+        fallThrough = fallThrough->fallThrough;
+    }
+    /* Generate a branch over if the predicted inlining is correct */
+    genUnconditionalBranch(cUnit, &labelList[fallThrough->id]);
+
+    /* Reset the register state */
+    dvmCompilerResetRegPool(cUnit);
+    dvmCompilerClobberAllRegs(cUnit);
+    dvmCompilerResetNullCheck(cUnit);
+
+    /* Target for the slow invoke path */
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    /* Hook up the target to the verification branch */
+    mir->meta.callsiteInfo->misPredBranchOver->target = (LIR *) target;
+}
+
+static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir,
+                             BasicBlock *bb, MipsLIR *labelList)
+{
+    MipsLIR *retChainingCell = NULL;
+    MipsLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    if (bb->fallThrough != NULL)
+        retChainingCell = &labelList[bb->fallThrough->id];
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /*
+         * calleeMethod = this->clazz->vtable[
+         *     method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex
+         * ]
+         */
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_VIRTUAL_RANGE: {
+            MipsLIR *predChainingCell = &labelList[bb->taken->id];
+            int methodIndex =
+                cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]->
+                methodIndex;
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /*
+         * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex
+         *                ->pResMethods[BBBB]->methodIndex]
+         */
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod == cUnit->method->clazz->super->vtable[
+                                     cUnit->method->clazz->pDvmDex->
+                                       pResMethods[dInsn->vB]->methodIndex]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r_A0 = calleeMethod */
+                loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r_A0 = calleeMethod */
+            loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_STATIC)
+                genProcessArgsNoRange(cUnit, mir, dInsn,
+                                      NULL /* no null check */);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn,
+                                    NULL /* no null check */);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r_A0 = calleeMethod */
+                loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+
+        /*
+         * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz,
+         *                    BBBB, method, method->clazz->pDvmDex)
+         *
+         * The following is an example of generated code for
+         *      "invoke-interface v0"
+         *
+         * -------- dalvik offset: 0x000f @ invoke-interface (PI) v2
+         * 0x2f140c54 : lw       a0,8(s1)                    # genProcessArgsNoRange
+         * 0x2f140c58 : addiu    s4,s1,0xffffffe8(-24)
+         * 0x2f140c5c : beqz     a0,0x2f140d5c (L0x11f864)
+         * 0x2f140c60 : pref     1,0(s4)
+         * -------- BARRIER
+         * 0x2f140c64 : sw       a0,0(s4)
+         * 0x2f140c68 : addiu    s4,s4,0x0004(4)
+         * -------- BARRIER
+         * 0x2f140c6c : lui      s0,0x2d23(11555)            # dalvikPC
+         * 0x2f140c70 : ori      s0,s0,0x2d2365a6(757294502)
+         * 0x2f140c74 : lahi/lui a1,0x2f14(12052)            # a1 <- &retChainingCell
+         * 0x2f140c78 : lalo/ori a1,a1,0x2f140d38(789843256)
+         * 0x2f140c7c : lahi/lui a2,0x2f14(12052)            # a2 <- &predictedChainingCell
+         * 0x2f140c80 : lalo/ori a2,a2,0x2f140d80(789843328)
+         * 0x2f140c84 : jal      0x2f1311ec(789778924)       # call TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+         * 0x2f140c88 : nop
+         * 0x2f140c8c : b        0x2f140d80 (L0x11efc0)      # off to the predicted chain
+         * 0x2f140c90 : nop
+         * 0x2f140c94 : b        0x2f140d60 (L0x12457c)      # punt to the interpreter
+         * 0x2f140c98 : lui      a0,0x2d23(11555)
+         * 0x2f140c9c : move     s5,a1                       # prepare for dvmFindInterfaceMethodInCache
+         * 0x2f140ca0 : move     s6,a2
+         * 0x2f140ca4 : move     s7,a3
+         * 0x2f140ca8 : move     a0,a3
+         * 0x2f140cac : ori      a1,zero,0x2b42(11074)
+         * 0x2f140cb0 : lui      a2,0x2c92(11410)
+         * 0x2f140cb4 : ori      a2,a2,0x2c92adf8(747810296)
+         * 0x2f140cb8 : lui      a3,0x0009(9)
+         * 0x2f140cbc : ori      a3,a3,0x924b8(599224)
+         * 0x2f140cc0 : lui      t9,0x2ab2(10930)
+         * 0x2f140cc4 : ori      t9,t9,0x2ab2a48c(716350604)
+         * 0x2f140cc8 : jalr     ra,t9                       # call dvmFindInterfaceMethodInCache
+         * 0x2f140ccc : nop
+         * 0x2f140cd0 : lw       gp,84(sp)
+         * 0x2f140cd4 : move     a0,v0
+         * 0x2f140cd8 : bne      v0,zero,0x2f140cf0 (L0x120064)
+         * 0x2f140cdc : nop
+         * 0x2f140ce0 : lui      a0,0x2d23(11555)            # a0 <- dalvikPC
+         * 0x2f140ce4 : ori      a0,a0,0x2d2365a6(757294502)
+         * 0x2f140ce8 : jal      0x2f131720(789780256)       # call TEMPLATE_THROW_EXCEPTION_COMMON
+         * 0x2f140cec : nop
+         * 0x2f140cf0 : move     a1,s5                       # a1 <- &retChainingCell
+         * 0x2f140cf4 : bgtz     s5,0x2f140d20 (L0x120324)   # >0? don't rechain
+         * 0x2f140cf8 : nop
+         * 0x2f140cfc : lui      t9,0x2aba(10938)            # prepare for dvmJitToPatchPredictedChain
+         * 0x2f140d00 : ori      t9,t9,0x2abae3c4(716891076)
+         * 0x2f140d04 : move     a1,s2
+         * 0x2f140d08 : move     a2,s6
+         * 0x2f140d0c : move     a3,s7
+         * 0x2f140d10 : jalr     ra,t9                       # call dvmJitToPatchPredictedChain
+         * 0x2f140d14 : nop
+         * 0x2f140d18 : lw       gp,84(sp)
+         * 0x2f140d1c : move     a0,v0
+         * 0x2f140d20 : lahi/lui a1,0x2f14(12052)
+         * 0x2f140d24 : lalo/ori a1,a1,0x2f140d38(789843256) # a1 <- &retChainingCell
+         * 0x2f140d28 : jal      0x2f1310c4(789778628)       # call TEMPLATE_INVOKE_METHOD_NO_OPT
+         * 0x2f140d2c : nop
+         * 0x2f140d30 : b        0x2f140d60 (L0x12457c)
+         * 0x2f140d34 : lui      a0,0x2d23(11555)
+         * 0x2f140d38 : .align4
+         * -------- dalvik offset: 0x0012 @ move-result (PI) v1, (#0), (#0)
+         * 0x2f140d38 : lw       a2,16(s2)
+         * 0x2f140d3c : sw       a2,4(s1)
+         * 0x2f140d40 : b        0x2f140d74 (L0x1246fc)
+         * 0x2f140d44 : lw       a0,116(s2)
+         * 0x2f140d48 : undefined
+         * -------- reconstruct dalvik PC : 0x2d2365a6 @ +0x000f
+         * 0x2f140d4c : lui      a0,0x2d23(11555)
+         * 0x2f140d50 : ori      a0,a0,0x2d2365a6(757294502)
+         * 0x2f140d54 : b        0x2f140d68 (L0x12463c)
+         * 0x2f140d58 : lw       a1,108(s2)
+         * -------- reconstruct dalvik PC : 0x2d2365a6 @ +0x000f
+         * 0x2f140d5c : lui      a0,0x2d23(11555)
+         * 0x2f140d60 : ori      a0,a0,0x2d2365a6(757294502)
+         * Exception_Handling:
+         * 0x2f140d64 : lw       a1,108(s2)
+         * 0x2f140d68 : jalr     ra,a1
+         * 0x2f140d6c : nop
+         * 0x2f140d70 : .align4
+         * -------- chaining cell (hot): 0x0013
+         * 0x2f140d70 : lw       a0,116(s2)
+         * 0x2f140d74 : jalr     ra,a0
+         * 0x2f140d78 : nop
+         * 0x2f140d7c : data     0x2d2365ae(757294510)
+         * 0x2f140d80 : .align4
+         * -------- chaining cell (predicted): N/A
+         * 0x2f140d80 : data     0xe7fe(59390)
+         * 0x2f140d84 : data     0x0000(0)
+         * 0x2f140d88 : data     0x0000(0)
+         * 0x2f140d8c : data     0x0000(0)
+         * 0x2f140d90 : data     0x0000(0)
+         * -------- end of chaining cells (0x0190)
+         */
+        case OP_INVOKE_INTERFACE:
+        case OP_INVOKE_INTERFACE_RANGE: {
+            MipsLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* "this" is already left in r_A0 by genProcessArgs* */
+
+            /* r4PC = dalvikCallsite */
+            loadConstant(cUnit, r4PC,
+                         (int) (cUnit->method->insns + mir->offset));
+
+            /* r_A1 = &retChainingCell */
+            MipsLIR *addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+            addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+
+            /* r_A2 = &predictedChainingCell */
+            MipsLIR *predictedChainingCell = newLIR2(cUnit, kMipsLahi, r_A2, 0);
+            predictedChainingCell->generic.target = (LIR *) predChainingCell;
+            predictedChainingCell = newLIR3(cUnit, kMipsLalo, r_A2, r_A2, 0);
+            predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+            /* return through ra - jump to the chaining cell */
+            genUnconditionalBranch(cUnit, predChainingCell);
+
+            /*
+             * null-check on "this" may have been eliminated, but we still need
+             * a PC-reconstruction label for stack overflow bailout.
+             */
+            if (pcrLabel == NULL) {
+                int dPC = (int) (cUnit->method->insns + mir->offset);
+                pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+                pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+                pcrLabel->operands[0] = dPC;
+                pcrLabel->operands[1] = mir->offset;
+                /* Insert the place holder to the growable list */
+                dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                                      (intptr_t) pcrLabel);
+            }
+
+            /* return through ra+8 - punt to the interpreter */
+            genUnconditionalBranch(cUnit, pcrLabel);
+
+            /*
+             * return through ra+16 - fully resolve the callee method.
+             * r_A1 <- count
+             * r_A2 <- &predictedChainCell
+             * r_A3 <- this->class
+             * r4 <- dPC
+             * r_S4 <- this->class->vtable
+             */
+
+            /* Save count, &predictedChainCell, and class to high regs first */
+            genRegCopy(cUnit, r_S5, r_A1);
+            genRegCopy(cUnit, r_S6, r_A2);
+            genRegCopy(cUnit, r_S7, r_A3);
+
+            /* r_A0 now contains this->clazz */
+            genRegCopy(cUnit, r_A0, r_A3);
+
+            /* r_A1 = BBBB */
+            loadConstant(cUnit, r_A1, dInsn->vB);
+
+            /* r_A2 = method (caller) */
+            loadConstant(cUnit, r_A2, (int) cUnit->method);
+
+            /* r_A3 = pDvmDex */
+            loadConstant(cUnit, r_A3, (int) cUnit->method->clazz->pDvmDex);
+
+            LOAD_FUNC_ADDR(cUnit, r_T9,
+                           (intptr_t) dvmFindInterfaceMethodInCache);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            /* r_V0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */
+            genRegCopy(cUnit, r_A0, r_V0);
+
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if the interface method is resolved */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+            /*
+             * calleeMethod == NULL -> throw
+             */
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+
+            genRegCopy(cUnit, r_A1, r_S5);
+
+            /* Check if rechain limit is reached */
+            MipsLIR *bypassRechaining = opCompareBranch(cUnit, kMipsBgtz, r_S5, -1);
+
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int) dvmJitToPatchPredictedChain);
+
+            genRegCopy(cUnit, r_A1, rSELF);
+            genRegCopy(cUnit, r_A2, r_S6);
+            genRegCopy(cUnit, r_A3, r_S7);
+
+            /*
+             * r_A0 = calleeMethod
+             * r_A2 = &predictedChainingCell
+             * r_A3 = class
+             *
+             * &returnChainingCell has been loaded into r_A1 but is not needed
+             * when patching the chaining cell and will be clobbered upon
+             * returning so it will be reconstructed again.
+             */
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            genRegCopy(cUnit, r_A0, r_V0);
+
+            /* r_A1 = &retChainingCell */
+            addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+            bypassRechaining->generic.target = (LIR *) addrRetChain;
+            addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+
+            /*
+             * r_A0 = this, r_A1 = calleeMethod,
+             * r_A1 = &ChainingCell,
+             * r4PC = callsiteDPC,
+             */
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+                TEMPLATE_INVOKE_METHOD_NO_OPT);
+
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.invokePolymorphic++;
+#endif
+            /* Handle exceptions using the interpreter */
+            genTrap(cUnit, mir->offset, pcrLabel);
+            break;
+        }
+        case OP_INVOKE_OBJECT_INIT_RANGE:
+        case OP_FILLED_NEW_ARRAY:
+        case OP_FILLED_NEW_ARRAY_RANGE: {
+            /* Just let the interpreter deal with these */
+            genInterpSingleStep(cUnit, mir);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+                               BasicBlock *bb, MipsLIR *labelList)
+{
+    MipsLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /* calleeMethod = this->clazz->vtable[BBBB] */
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_VIRTUAL_QUICK: {
+            int methodIndex = dInsn->vB;
+            MipsLIR *retChainingCell = &labelList[bb->fallThrough->id];
+            MipsLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeVirtualWholeMethod(cUnit, mir, calleeAddr,
+                                            retChainingCell);
+            }
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /* calleeMethod = method->clazz->super->vtable[BBBB] */
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->super->vtable[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r_A0 = calleeMethod */
+            loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * This operation is complex enough that we'll do it partly inline
+ * and partly with a handler.  NOTE: the handler uses hardcoded
+ * values for string object offsets and must be revisitied if the
+ * layout changes.
+ */
+static bool genInlinedCompareTo(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    MipsLIR *rollback;
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlComp = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r_A0);
+    loadValueDirectFixed(cUnit, rlComp, r_A1);
+    /* Test objects for NULL */
+    rollback = genNullCheck(cUnit, rlThis.sRegLow, r_A0, mir->offset, NULL);
+    genNullCheck(cUnit, rlComp.sRegLow, r_A1, mir->offset, rollback);
+    /*
+     * TUNING: we could check for object pointer equality before invoking
+     * handler. Unclear whether the gain would be worth the added code size
+     * expansion.
+     */
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_COMPARETO);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlChar = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r_A0);
+    loadValueDirectFixed(cUnit, rlChar, r_A1);
+
+    RegLocation rlStart = dvmCompilerGetSrc(cUnit, mir, 2);
+    loadValueDirectFixed(cUnit, rlStart, r_A2);
+
+    /* Test objects for NULL */
+    genNullCheck(cUnit, rlThis.sRegLow, r_A0, mir->offset, NULL);
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_INDEXOF);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+// Generates an inlined String.isEmpty or String.length.
+static bool genInlinedStringIsEmptyOrLength(CompilationUnit *cUnit, MIR *mir,
+                                            bool isEmpty)
+{
+    // dst = src.length();
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count,
+                 rlResult.lowReg);
+    if (isEmpty) {
+        // dst = (dst == 0);
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        newLIR3(cUnit, kMipsSltu, tReg, r_ZERO, rlResult.lowReg);
+        opRegRegImm(cUnit, kOpXor, rlResult.lowReg, tReg, 1);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, false);
+}
+
+static bool genInlinedStringIsEmpty(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, true);
+}
+
+static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
+{
+    int contents = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlIdx = dvmCompilerGetSrc(cUnit, mir, 1);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+    int regMax = dvmCompilerAllocTemp(cUnit);
+    int regOff = dvmCompilerAllocTemp(cUnit);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+    MipsLIR *pcrLabel = genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg,
+                                    mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, regMax);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_offset, regOff);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_value, regPtr);
+    genBoundsCheck(cUnit, rlIdx.lowReg, regMax, mir->offset, pcrLabel);
+    dvmCompilerFreeTemp(cUnit, regMax);
+    opRegImm(cUnit, kOpAdd, regPtr, contents);
+    opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2's IT block also yields 3 instructions, but imposes
+     * scheduling constraints.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2 IT block allows slightly shorter sequence,
+     * but introduces a scheduling barrier.  Stick with this
+     * mechanism for now.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    newLIR3(cUnit, kMipsSltu, tReg, rlResult.lowReg, signReg);
+    opRegRegReg(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg, signReg);
+    opRegRegReg(cUnit, kOpAdd, rlResult.highReg, rlResult.highReg, tReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.highReg, signReg);
+    dvmCompilerFreeTemp(cUnit, signReg);
+    dvmCompilerFreeTemp(cUnit, tReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedIntFloatConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    storeValue(cUnit, rlDest, rlSrc);
+    return false;
+}
+
+static bool genInlinedLongDoubleConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    storeValueWide(cUnit, rlDest, rlSrc);
+    return false;
+}
+/*
+ * JITs a call to a C function.
+ * TODO: use this for faster native method invocation for simple native
+ * methods (http://b/3069458).
+ */
+static bool handleExecuteInlineC(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int operation = dInsn->vB;
+    unsigned int i;
+    const InlineOperation* inLineTable = dvmGetInlineOpsTable();
+    uintptr_t fn = (int) inLineTable[operation].func;
+    if (fn == 0) {
+        dvmCompilerAbort(cUnit);
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r4PC);
+    dvmCompilerClobber(cUnit, rINST);
+    int offset = offsetof(Thread, interpSave.retval);
+    opRegRegImm(cUnit, kOpAdd, r4PC, rSELF, offset);
+    newLIR3(cUnit, kMipsSw, r4PC, 16, r_SP); /* sp has plenty of space */
+    genExportPC(cUnit, mir);
+    assert(dInsn->vA <= 4);
+    for (i=0; i < dInsn->vA; i++) {
+        loadValueDirect(cUnit, dvmCompilerGetSrc(cUnit, mir, i), i+r_A0);
+    }
+    LOAD_FUNC_ADDR(cUnit, r_T9, fn);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    /* NULL? */
+    MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+    genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+    return false;
+}
+
+/*
+ * NOTE: Handles both range and non-range versions (arguments
+ * have already been normalized by this point).
+ */
+static bool handleExecuteInline(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    assert(dInsn->opcode == OP_EXECUTE_INLINE_RANGE ||
+           dInsn->opcode == OP_EXECUTE_INLINE);
+    switch (dInsn->vB) {
+        case INLINE_EMPTYINLINEMETHOD:
+            return false;  /* Nop */
+
+        /* These ones we potentially JIT inline. */
+
+        case INLINE_STRING_CHARAT:
+            return genInlinedStringCharAt(cUnit, mir);
+        case INLINE_STRING_LENGTH:
+            return genInlinedStringLength(cUnit, mir);
+        case INLINE_STRING_IS_EMPTY:
+            return genInlinedStringIsEmpty(cUnit, mir);
+        case INLINE_STRING_COMPARETO:
+            return genInlinedCompareTo(cUnit, mir);
+        case INLINE_STRING_FASTINDEXOF_II:
+            return genInlinedFastIndexOf(cUnit, mir);
+
+        case INLINE_MATH_ABS_INT:
+        case INLINE_STRICT_MATH_ABS_INT:
+            return genInlinedAbsInt(cUnit, mir);
+        case INLINE_MATH_ABS_LONG:
+        case INLINE_STRICT_MATH_ABS_LONG:
+            return genInlinedAbsLong(cUnit, mir);
+        case INLINE_MATH_MIN_INT:
+        case INLINE_STRICT_MATH_MIN_INT:
+            return genInlinedMinMaxInt(cUnit, mir, true);
+        case INLINE_MATH_MAX_INT:
+        case INLINE_STRICT_MATH_MAX_INT:
+            return genInlinedMinMaxInt(cUnit, mir, false);
+        case INLINE_MATH_SQRT:
+        case INLINE_STRICT_MATH_SQRT:
+            return genInlineSqrt(cUnit, mir);
+        case INLINE_MATH_ABS_FLOAT:
+        case INLINE_STRICT_MATH_ABS_FLOAT:
+            return genInlinedAbsFloat(cUnit, mir);
+        case INLINE_MATH_ABS_DOUBLE:
+        case INLINE_STRICT_MATH_ABS_DOUBLE:
+            return genInlinedAbsDouble(cUnit, mir);
+
+        case INLINE_FLOAT_TO_RAW_INT_BITS:
+        case INLINE_INT_BITS_TO_FLOAT:
+            return genInlinedIntFloatConversion(cUnit, mir);
+        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+        case INLINE_LONG_BITS_TO_DOUBLE:
+            return genInlinedLongDoubleConversion(cUnit, mir);
+
+        /*
+         * These ones we just JIT a call to a C function for.
+         * TODO: special-case these in the other "invoke" call paths.
+         */
+        case INLINE_STRING_EQUALS:
+        case INLINE_MATH_COS:
+        case INLINE_MATH_SIN:
+        case INLINE_FLOAT_TO_INT_BITS:
+        case INLINE_DOUBLE_TO_LONG_BITS:
+            return handleExecuteInlineC(cUnit, mir);
+    }
+    dvmCompilerAbort(cUnit);
+    return false; // Not reachable; keeps compiler happy.
+}
+
+static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+    //TUNING: We're using core regs here - not optimal when target is a double
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadConstantNoClobber(cUnit, rlResult.lowReg,
+                          mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL);
+    loadConstantNoClobber(cUnit, rlResult.highReg,
+                          (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * The following are special processing routines that handle transfer of
+ * controls between compiled code and the interpreter. Certain VM states like
+ * Dalvik PC and special-purpose registers are reconstructed here.
+ */
+
+/* Chaining cell for code that may need warmup. */
+static void handleNormalChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset)
+{
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpNormal),
+        rSELF);
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/*
+ * Chaining cell for instructions that immediately following already translated
+ * code.
+ */
+static void handleHotChainingCell(CompilationUnit *cUnit,
+                                  unsigned int offset)
+{
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpTraceSelect),
+        rSELF);
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for branches that branch back into the same basic block */
+static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
+                                             unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+#if defined(WITH_SELF_VERIFICATION)
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpBackwardBranch),
+        rSELF);
+#else
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpNormal),
+        rSELF);
+#endif
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
+                                              const Method *callee)
+{
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpTraceSelect),
+        rSELF);
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (callee->insns));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit)
+{
+    /* Should not be executed in the initial state */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_BX_PAIR_INIT);
+    /* branch delay slot nop */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_DELAY_SLOT_INIT);
+    /* To be filled: class */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_CLAZZ_INIT);
+    /* To be filled: method */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_METHOD_INIT);
+    /*
+     * Rechain count. The initial value of 0 here will trigger chaining upon
+     * the first invocation of this callsite.
+     */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_COUNTER_INIT);
+}
+
+/* Load the Dalvik PC into a0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+                                   MipsLIR *targetLabel)
+{
+    MipsLIR **pcrLabel =
+        (MipsLIR **) cUnit->pcReconstructionList.elemList;
+    int numElems = cUnit->pcReconstructionList.numUsed;
+    int i;
+
+    /*
+     * We should never reach here through fall-through code, so insert
+     * a bomb to signal troubles immediately.
+     */
+    if (numElems) {
+        newLIR0(cUnit, kMipsUndefined);
+    }
+
+    for (i = 0; i < numElems; i++) {
+        dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+        /* a0 = dalvik PC */
+        loadConstant(cUnit, r_A0, pcrLabel[i]->operands[0]);
+        genUnconditionalBranch(cUnit, targetLabel);
+    }
+}
+
+static const char *extendedMIROpNames[kMirOpLast - kMirOpFirst] = {
+    "kMirOpPhi",
+    "kMirOpNullNRangeUpCheck",
+    "kMirOpNullNRangeDownCheck",
+    "kMirOpLowerBound",
+    "kMirOpPunt",
+    "kMirOpCheckInlinePrediction",
+};
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    /*
+     * NOTE: these synthesized blocks don't have ssa names assigned
+     * for Dalvik registers.  However, because they dominate the following
+     * blocks we can simply use the Dalvik name w/ subscript 0 as the
+     * ssa name.
+     */
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int maxC = dInsn->arg[0];
+    int regLength;
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxEnd = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxEnd = loadValue(cUnit, rlIdxEnd, kCoreReg);
+    genRegImmCheck(cUnit, kMipsCondEq, rlArray.lowReg, 0, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    regLength = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    int delta = maxC;
+    /*
+     * If the loop end condition is ">=" instead of ">", then the largest value
+     * of the index is "endCondition - 1".
+     */
+    if (dInsn->arg[2] == OP_IF_GE) {
+        delta--;
+    }
+
+    if (delta) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxEnd.lowReg, delta);
+        rlIdxEnd.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+    /* Punt if "regIdxEnd < len(Array)" is false */
+    genRegRegCheck(cUnit, kMipsCondGe, rlIdxEnd.lowReg, regLength, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int regLength = dvmCompilerAllocTemp(cUnit);
+    const int maxC = dInsn->arg[0];
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxInit = cUnit->regLocation[mir->dalvikInsn.vB];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxInit = loadValue(cUnit, rlIdxInit, kCoreReg);
+    genRegImmCheck(cUnit, kMipsCondEq, rlArray.lowReg, 0, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    if (maxC) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxInit.lowReg, maxC);
+        rlIdxInit.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+
+    /* Punt if "regIdxInit < len(Array)" is false */
+    genRegRegCheck(cUnit, kMipsCondGe, rlIdxInit.lowReg, regLength, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = idxReg;
+ * vB = minC;
+ */
+static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int minC = dInsn->vB;
+    RegLocation rlIdx = cUnit->regLocation[mir->dalvikInsn.vA];
+
+    /* regIdx <- initial index value */
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+
+    /* Punt if "regIdxInit + minC >= 0" is false */
+    genRegImmCheck(cUnit, kMipsCondLt, rlIdx.lowReg, -minC, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vC = this
+ *
+ * A predicted inlining target looks like the following, where instructions
+ * between 0x2f130d24 and 0x2f130d40 are checking if the predicted class
+ * matches "this", and the verificaion code is generated by this routine.
+ *
+ * (C) means the instruction is inlined from the callee, and (PI) means the
+ * instruction is the predicted inlined invoke, whose corresponding
+ * instructions are still generated to handle the mispredicted case.
+ *
+ * D/dalvikvm( 2377): -------- kMirOpCheckInlinePrediction
+ * D/dalvikvm( 2377): 0x2f130d24 (0020):  lw       v0,16(s1)
+ * D/dalvikvm( 2377): 0x2f130d28 (0024):  lui      v1,0x0011(17)
+ * D/dalvikvm( 2377): 0x2f130d2c (0028):  ori      v1,v1,0x11e418(1172504)
+ * D/dalvikvm( 2377): 0x2f130d30 (002c):  beqz     v0,0x2f130df0 (L0x11f1f0)
+ * D/dalvikvm( 2377): 0x2f130d34 (0030):  pref     0,0(v0)
+ * D/dalvikvm( 2377): 0x2f130d38 (0034):  lw       a0,0(v0)
+ * D/dalvikvm( 2377): 0x2f130d3c (0038):  bne      v1,a0,0x2f130d54 (L0x11f518)
+ * D/dalvikvm( 2377): 0x2f130d40 (003c):  pref     0,8(v0)
+ * D/dalvikvm( 2377): -------- dalvik offset: 0x000a @ +iget-object-quick (C) v3, v4, (#8)
+ * D/dalvikvm( 2377): 0x2f130d44 (0040):  lw       a1,8(v0)
+ * D/dalvikvm( 2377): -------- dalvik offset: 0x000a @ +invoke-virtual-quick (PI) v4
+ * D/dalvikvm( 2377): 0x2f130d48 (0044):  sw       a1,12(s1)
+ * D/dalvikvm( 2377): 0x2f130d4c (0048):  b        0x2f130e18 (L0x120150)
+ * D/dalvikvm( 2377): 0x2f130d50 (004c):  lw       a0,116(s2)
+ * D/dalvikvm( 2377): L0x11f518:
+ * D/dalvikvm( 2377): 0x2f130d54 (0050):  lw       a0,16(s1)
+ * D/dalvikvm( 2377): 0x2f130d58 (0054):  addiu    s4,s1,0xffffffe8(-24)
+ * D/dalvikvm( 2377): 0x2f130d5c (0058):  beqz     a0,0x2f130e00 (L0x11f618)
+ * D/dalvikvm( 2377): 0x2f130d60 (005c):  pref     1,0(s4)
+ * D/dalvikvm( 2377): -------- BARRIER
+ * D/dalvikvm( 2377): 0x2f130d64 (0060):  sw       a0,0(s4)
+ * D/dalvikvm( 2377): 0x2f130d68 (0064):  addiu    s4,s4,0x0004(4)
+ * D/dalvikvm( 2377): -------- BARRIER
+ * D/dalvikvm( 2377): 0x2f130d6c (0068):  lui      s0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130d70 (006c):  ori      s0,s0,0x2d228464(757236836)
+ * D/dalvikvm( 2377): 0x2f130d74 (0070):  lahi/lui a1,0x2f13(12051)
+ * D/dalvikvm( 2377): 0x2f130d78 (0074):  lalo/ori a1,a1,0x2f130ddc(789777884)
+ * D/dalvikvm( 2377): 0x2f130d7c (0078):  lahi/lui a2,0x2f13(12051)
+ * D/dalvikvm( 2377): 0x2f130d80 (007c):  lalo/ori a2,a2,0x2f130e24(789777956)
+ * D/dalvikvm( 2377): 0x2f130d84 (0080):  jal      0x2f12d1ec(789762540)
+ * D/dalvikvm( 2377): 0x2f130d88 (0084):  nop
+ * D/dalvikvm( 2377): 0x2f130d8c (0088):  b        0x2f130e24 (L0x11ed6c)
+ * D/dalvikvm( 2377): 0x2f130d90 (008c):  nop
+ * D/dalvikvm( 2377): 0x2f130d94 (0090):  b        0x2f130e04 (L0x11ffd0)
+ * D/dalvikvm( 2377): 0x2f130d98 (0094):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130d9c (0098):  lw       a0,44(s4)
+ * D/dalvikvm( 2377): 0x2f130da0 (009c):  bgtz     a1,0x2f130dc4 (L0x11fb98)
+ * D/dalvikvm( 2377): 0x2f130da4 (00a0):  nop
+ * D/dalvikvm( 2377): 0x2f130da8 (00a4):  lui      t9,0x2aba(10938)
+ * D/dalvikvm( 2377): 0x2f130dac (00a8):  ori      t9,t9,0x2abae3f8(716891128)
+ * D/dalvikvm( 2377): 0x2f130db0 (00ac):  move     a1,s2
+ * D/dalvikvm( 2377): 0x2f130db4 (00b0):  jalr     ra,t9
+ * D/dalvikvm( 2377): 0x2f130db8 (00b4):  nop
+ * D/dalvikvm( 2377): 0x2f130dbc (00b8):  lw       gp,84(sp)
+ * D/dalvikvm( 2377): 0x2f130dc0 (00bc):  move     a0,v0
+ * D/dalvikvm( 2377): 0x2f130dc4 (00c0):  lahi/lui a1,0x2f13(12051)
+ * D/dalvikvm( 2377): 0x2f130dc8 (00c4):  lalo/ori a1,a1,0x2f130ddc(789777884)
+ * D/dalvikvm( 2377): 0x2f130dcc (00c8):  jal      0x2f12d0c4(789762244)
+ * D/dalvikvm( 2377): 0x2f130dd0 (00cc):  nop
+ * D/dalvikvm( 2377): 0x2f130dd4 (00d0):  b        0x2f130e04 (L0x11ffd0)
+ * D/dalvikvm( 2377): 0x2f130dd8 (00d4):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130ddc (00d8): .align4
+ * D/dalvikvm( 2377): L0x11ed2c:
+ * D/dalvikvm( 2377): -------- dalvik offset: 0x000d @ move-result-object (PI) v3, (#0), (#0)
+ * D/dalvikvm( 2377): 0x2f130ddc (00d8):  lw       a2,16(s2)
+ * D/dalvikvm( 2377): 0x2f130de0 (00dc):  sw       a2,12(s1)
+ * D/dalvikvm( 2377): 0x2f130de4 (00e0):  b        0x2f130e18 (L0x120150)
+ * D/dalvikvm( 2377): 0x2f130de8 (00e4):  lw       a0,116(s2)
+ * D/dalvikvm( 2377): 0x2f130dec (00e8):  undefined
+ * D/dalvikvm( 2377): L0x11f1f0:
+ * D/dalvikvm( 2377): -------- reconstruct dalvik PC : 0x2d228464 @ +0x000a
+ * D/dalvikvm( 2377): 0x2f130df0 (00ec):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130df4 (00f0):  ori      a0,a0,0x2d228464(757236836)
+ * D/dalvikvm( 2377): 0x2f130df8 (00f4):  b        0x2f130e0c (L0x120090)
+ * D/dalvikvm( 2377): 0x2f130dfc (00f8):  lw       a1,108(s2)
+ * D/dalvikvm( 2377): L0x11f618:
+ * D/dalvikvm( 2377): -------- reconstruct dalvik PC : 0x2d228464 @ +0x000a
+ * D/dalvikvm( 2377): 0x2f130e00 (00fc):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130e04 (0100):  ori      a0,a0,0x2d228464(757236836)
+ * D/dalvikvm( 2377): Exception_Handling:
+ * D/dalvikvm( 2377): 0x2f130e08 (0104):  lw       a1,108(s2)
+ * D/dalvikvm( 2377): 0x2f130e0c (0108):  jalr     ra,a1
+ * D/dalvikvm( 2377): 0x2f130e10 (010c):  nop
+ * D/dalvikvm( 2377): 0x2f130e14 (0110): .align4
+ * D/dalvikvm( 2377): L0x11edac:
+ * D/dalvikvm( 2377): -------- chaining cell (hot): 0x000e
+ * D/dalvikvm( 2377): 0x2f130e14 (0110):  lw       a0,116(s2)
+ * D/dalvikvm( 2377): 0x2f130e18 (0114):  jalr     ra,a0
+ * D/dalvikvm( 2377): 0x2f130e1c (0118):  nop
+ * D/dalvikvm( 2377): 0x2f130e20 (011c):  data     0x2d22846c(757236844)
+ * D/dalvikvm( 2377): 0x2f130e24 (0120): .align4
+ * D/dalvikvm( 2377): L0x11ed6c:
+ * D/dalvikvm( 2377): -------- chaining cell (predicted)
+ * D/dalvikvm( 2377): 0x2f130e24 (0120):  data     0xe7fe(59390)
+ * D/dalvikvm( 2377): 0x2f130e28 (0124):  data     0x0000(0)
+ * D/dalvikvm( 2377): 0x2f130e2c (0128):  data     0x0000(0)
+ * D/dalvikvm( 2377): 0x2f130e30 (012c):  data     0x0000(0)
+ * D/dalvikvm( 2377): 0x2f130e34 (0130):  data     0x0000(0)
+ */
+static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    RegLocation rlThis = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    rlThis = loadValue(cUnit, rlThis, kCoreReg);
+    int regPredictedClass = dvmCompilerAllocTemp(cUnit);
+    loadClassPointer(cUnit, regPredictedClass, (int) callsiteInfo);
+    genNullCheck(cUnit, rlThis.sRegLow, rlThis.lowReg, mir->offset,
+                 NULL);/* null object? */
+    int regActualClass = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlThis.lowReg, offsetof(Object, clazz), regActualClass);
+//    opRegReg(cUnit, kOpCmp, regPredictedClass, regActualClass);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    callsiteInfo->misPredBranchOver = (LIR *) opCompareBranch(cUnit, kMipsBne, regPredictedClass, regActualClass);
+}
+
+/* Extended MIR instructions like PHI */
+static void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
+{
+    int opOffset = mir->dalvikInsn.opcode - kMirOpFirst;
+    char *msg = (char *)dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
+                                       false);
+    strcpy(msg, extendedMIROpNames[opOffset]);
+    newLIR1(cUnit, kMipsPseudoExtended, (int) msg);
+
+    switch ((ExtendedMIROpcode)mir->dalvikInsn.opcode) {
+        case kMirOpPhi: {
+            char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+            newLIR1(cUnit, kMipsPseudoSSARep, (int) ssaString);
+            break;
+        }
+        case kMirOpNullNRangeUpCheck: {
+            genHoistedChecksForCountUpLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpNullNRangeDownCheck: {
+            genHoistedChecksForCountDownLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpLowerBound: {
+            genHoistedLowerBoundCheck(cUnit, mir);
+            break;
+        }
+        case kMirOpPunt: {
+            genUnconditionalBranch(cUnit,
+                                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+            break;
+        }
+        case kMirOpCheckInlinePrediction: {
+            genValidationForPredictedInline(cUnit, mir);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*
+ * Create a PC-reconstruction cell for the starting offset of this trace.
+ * Since the PCR cell is placed near the end of the compiled code which is
+ * usually out of range for a conditional branch, we put two branches (one
+ * branch over to the loop body and one layover branch to the actual PCR) at the
+ * end of the entry block.
+ */
+static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry,
+                                MipsLIR *bodyLabel)
+{
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    MipsLIR *pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+    pcrLabel->operands[0] =
+        (int) (cUnit->method->insns + entry->startOffset);
+    pcrLabel->operands[1] = entry->startOffset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+
+    /*
+     * Next, create two branches - one branch over to the loop body and the
+     * other branch to the PCR cell to punt.
+     */
+    MipsLIR *branchToBody = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    branchToBody->opcode = kMipsB;
+    branchToBody->generic.target = (LIR *) bodyLabel;
+    setupResourceMasks(branchToBody);
+    cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody;
+
+    MipsLIR *branchToPCR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    branchToPCR->opcode = kMipsB;
+    branchToPCR->generic.target = (LIR *) pcrLabel;
+    setupResourceMasks(branchToPCR);
+    cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static bool selfVerificationPuntOps(MIR *mir)
+{
+assert(0); /* MIPSTODO port selfVerificationPuntOps() */
+    DecodedInstruction *decInsn = &mir->dalvikInsn;
+
+    /*
+     * All opcodes that can throw exceptions and use the
+     * TEMPLATE_THROW_EXCEPTION_COMMON template should be excluded in the trace
+     * under self-verification mode.
+     */
+    switch (decInsn->opcode) {
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_NEW_INSTANCE:
+        case OP_NEW_ARRAY:
+        case OP_CHECK_CAST:
+        case OP_MOVE_EXCEPTION:
+        case OP_FILL_ARRAY_DATA:
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+            return true;
+        default:
+            return false;
+    }
+}
+#endif
+
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit)
+{
+    /* Used to hold the labels of each block */
+    MipsLIR *labelList =
+        (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR) * cUnit->numBlocks, true);
+    MipsLIR *headLIR = NULL;
+    GrowableList chainingListByType[kChainingCellGap];
+    int i;
+
+    /*
+     * Initialize various types chaining lists.
+     */
+    for (i = 0; i < kChainingCellGap; i++) {
+        dvmInitGrowableList(&chainingListByType[i], 2);
+    }
+
+    /* Clear the visited flag for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes, false /* isIterative */);
+
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    /* Traces start with a profiling entry point.  Generate it here */
+    cUnit->profileCodeSize = genTraceProfileEntry(cUnit);
+
+    /* Handle the content in each basic block */
+    for (i = 0; ; i++) {
+        MIR *mir;
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->visited == true) continue;
+
+        labelList[i].operands[0] = bb->startOffset;
+
+        if (bb->blockType >= kChainingCellGap) {
+            if (bb->isFallThroughFromInvoke == true) {
+                /* Align this block first since it is a return chaining cell */
+                newLIR0(cUnit, kMipsPseudoPseudoAlign4);
+            }
+            /*
+             * Append the label pseudo LIR first. Chaining cells will be handled
+             * separately afterwards.
+             */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            labelList[i].opcode = kMipsPseudoEntryBlock;
+            if (bb->firstMIRInsn == NULL) {
+                continue;
+            } else {
+              setupLoopEntryBlock(cUnit, bb,
+                                  &labelList[bb->fallThrough->id]);
+            }
+        } else if (bb->blockType == kExitBlock) {
+            labelList[i].opcode = kMipsPseudoExitBlock;
+            goto gen_fallthrough;
+        } else if (bb->blockType == kDalvikByteCode) {
+            if (bb->hidden == true) continue;
+            labelList[i].opcode = kMipsPseudoNormalBlockLabel;
+            /* Reset the register state */
+            dvmCompilerResetRegPool(cUnit);
+            dvmCompilerClobberAllRegs(cUnit);
+            dvmCompilerResetNullCheck(cUnit);
+        } else {
+            switch (bb->blockType) {
+                case kChainingCellNormal:
+                    labelList[i].opcode = kMipsPseudoChainingCellNormal;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellNormal], i);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellInvokeSingleton;
+                    labelList[i].operands[0] =
+                        (int) bb->containingMethod;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokeSingleton], i);
+                    break;
+                case kChainingCellInvokePredicted:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellInvokePredicted;
+                    /*
+                     * Move the cached method pointer from operand 1 to 0.
+                     * Operand 0 was clobbered earlier in this routine to store
+                     * the block starting offset, which is not applicable to
+                     * predicted chaining cell.
+                     */
+                    labelList[i].operands[0] = labelList[i].operands[1];
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokePredicted], i);
+                    break;
+                case kChainingCellHot:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellHot;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellHot], i);
+                    break;
+                case kPCReconstruction:
+                    /* Make sure exception handling block is next */
+                    labelList[i].opcode =
+                        kMipsPseudoPCReconstructionBlockLabel;
+                    handlePCReconstruction(cUnit,
+                                           &labelList[cUnit->puntBlock->id]);
+                    break;
+                case kExceptionHandling:
+                    labelList[i].opcode = kMipsPseudoEHBlockLabel;
+                    if (cUnit->pcReconstructionList.numUsed) {
+                        loadWordDisp(cUnit, rSELF, offsetof(Thread,
+                                     jitToInterpEntries.dvmJitToInterpPunt),
+                                     r_A1);
+                        opReg(cUnit, kOpBlx, r_A1);
+                    }
+                    break;
+                case kChainingCellBackwardBranch:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellBackwardBranch;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellBackwardBranch],
+                        i);
+                    break;
+                default:
+                    break;
+            }
+            continue;
+        }
+
+        /*
+         * Try to build a longer optimization unit. Currently if the previous
+         * block ends with a goto, we continue adding instructions and don't
+         * reset the register allocation pool.
+         */
+        for (BasicBlock *nextBB = bb; nextBB != NULL; nextBB = cUnit->nextCodegenBlock) {
+            bb = nextBB;
+            bb->visited = true;
+            cUnit->nextCodegenBlock = NULL;
+
+            for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+
+                dvmCompilerResetRegPool(cUnit);
+                if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+                    dvmCompilerClobberAllRegs(cUnit);
+                }
+
+                if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+                    dvmCompilerResetDefTracking(cUnit);
+                }
+
+                if ((int)mir->dalvikInsn.opcode >= (int)kMirOpFirst) {
+                    handleExtendedMIR(cUnit, mir);
+                    continue;
+                }
+
+                Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+                InstructionFormat dalvikFormat =
+                    dexGetFormatFromOpcode(dalvikOpcode);
+                const char *note;
+                if (mir->OptimizationFlags & MIR_INLINED) {
+                    note = " (I)";
+                } else if (mir->OptimizationFlags & MIR_INLINED_PRED) {
+                    note = " (PI)";
+                } else if (mir->OptimizationFlags & MIR_CALLEE) {
+                    note = " (C)";
+                } else {
+                    note = NULL;
+                }
+
+                MipsLIR *boundaryLIR =
+                    newLIR2(cUnit, kMipsPseudoDalvikByteCodeBoundary,
+                            mir->offset,
+                            (int) dvmCompilerGetDalvikDisassembly(&mir->dalvikInsn,
+                                                                  note));
+                if (mir->ssaRep) {
+                    char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+                    newLIR1(cUnit, kMipsPseudoSSARep, (int) ssaString);
+                }
+
+                /* Remember the first LIR for this block */
+                if (headLIR == NULL) {
+                    headLIR = boundaryLIR;
+                    /* Set the first boundaryLIR as a scheduling barrier */
+                    headLIR->defMask = ENCODE_ALL;
+                }
+
+                bool notHandled;
+                /*
+                 * Debugging: screen the opcode first to see if it is in the
+                 * do[-not]-compile list
+                 */
+                bool singleStepMe = SINGLE_STEP_OP(dalvikOpcode);
+#if defined(WITH_SELF_VERIFICATION)
+              if (singleStepMe == false) {
+                  singleStepMe = selfVerificationPuntOps(mir);
+              }
+#endif
+                if (singleStepMe || cUnit->allSingleStep) {
+                    notHandled = false;
+                    genInterpSingleStep(cUnit, mir);
+                } else {
+                    opcodeCoverage[dalvikOpcode]++;
+                    switch (dalvikFormat) {
+                        case kFmt10t:
+                        case kFmt20t:
+                        case kFmt30t:
+                            notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit,
+                                      mir, bb, labelList);
+                            break;
+                        case kFmt10x:
+                            notHandled = handleFmt10x(cUnit, mir);
+                            break;
+                        case kFmt11n:
+                        case kFmt31i:
+                            notHandled = handleFmt11n_Fmt31i(cUnit, mir);
+                            break;
+                        case kFmt11x:
+                            notHandled = handleFmt11x(cUnit, mir);
+                            break;
+                        case kFmt12x:
+                            notHandled = handleFmt12x(cUnit, mir);
+                            break;
+                        case kFmt20bc:
+                            notHandled = handleFmt20bc(cUnit, mir);
+                            break;
+                        case kFmt21c:
+                        case kFmt31c:
+                            notHandled = handleFmt21c_Fmt31c(cUnit, mir);
+                            break;
+                        case kFmt21h:
+                            notHandled = handleFmt21h(cUnit, mir);
+                            break;
+                        case kFmt21s:
+                            notHandled = handleFmt21s(cUnit, mir);
+                            break;
+                        case kFmt21t:
+                            notHandled = handleFmt21t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22b:
+                        case kFmt22s:
+                            notHandled = handleFmt22b_Fmt22s(cUnit, mir);
+                            break;
+                        case kFmt22c:
+                            notHandled = handleFmt22c(cUnit, mir);
+                            break;
+                        case kFmt22cs:
+                            notHandled = handleFmt22cs(cUnit, mir);
+                            break;
+                        case kFmt22t:
+                            notHandled = handleFmt22t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22x:
+                        case kFmt32x:
+                            notHandled = handleFmt22x_Fmt32x(cUnit, mir);
+                            break;
+                        case kFmt23x:
+                            notHandled = handleFmt23x(cUnit, mir);
+                            break;
+                        case kFmt31t:
+                            notHandled = handleFmt31t(cUnit, mir);
+                            break;
+                        case kFmt3rc:
+                        case kFmt35c:
+                            notHandled = handleFmt35c_3rc(cUnit, mir, bb,
+                                                          labelList);
+                            break;
+                        case kFmt3rms:
+                        case kFmt35ms:
+                            notHandled = handleFmt35ms_3rms(cUnit, mir,bb,
+                                                            labelList);
+                            break;
+                        case kFmt35mi:
+                        case kFmt3rmi:
+                            notHandled = handleExecuteInline(cUnit, mir);
+                            break;
+                        case kFmt51l:
+                            notHandled = handleFmt51l(cUnit, mir);
+                            break;
+                        default:
+                            notHandled = true;
+                            break;
+                    }
+                }
+                if (notHandled) {
+                    ALOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled",
+                         mir->offset,
+                         dalvikOpcode, dexGetOpcodeName(dalvikOpcode),
+                         dalvikFormat);
+                    dvmCompilerAbort(cUnit);
+                    break;
+                }
+            }
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToBody);
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToPCR);
+        }
+
+        if (headLIR) {
+            /*
+             * Eliminate redundant loads/stores and delay stores into later
+             * slots
+             */
+            dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                               cUnit->lastLIRInsn);
+            /* Reset headLIR which is also the optimization boundary */
+            headLIR = NULL;
+        }
+
+gen_fallthrough:
+        /*
+         * Check if the block is terminated due to trace length constraint -
+         * insert an unconditional branch to the chaining cell.
+         */
+        if (bb->needFallThroughBranch) {
+            genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+        }
+    }
+
+    /* Handle the chaining cells in predefined order */
+    for (i = 0; i < kChainingCellGap; i++) {
+        size_t j;
+        int *blockIdList = (int *) chainingListByType[i].elemList;
+
+        cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+        /* No chaining cells of this type */
+        if (cUnit->numChainingCells[i] == 0)
+            continue;
+
+        /* Record the first LIR for a new type of chaining cell */
+        cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+
+        for (j = 0; j < chainingListByType[i].numUsed; j++) {
+            int blockId = blockIdList[j];
+            BasicBlock *chainingBlock =
+                (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                         blockId);
+
+            /* Align this chaining cell first */
+            newLIR0(cUnit, kMipsPseudoPseudoAlign4);
+
+            /* Insert the pseudo chaining instruction */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+            switch (chainingBlock->blockType) {
+                case kChainingCellNormal:
+                    handleNormalChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    handleInvokeSingletonChainingCell(cUnit,
+                        chainingBlock->containingMethod);
+                    break;
+                case kChainingCellInvokePredicted:
+                    handleInvokePredictedChainingCell(cUnit);
+                    break;
+                case kChainingCellHot:
+                    handleHotChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellBackwardBranch:
+                    handleBackwardBranchChainingCell(cUnit,
+                        chainingBlock->startOffset);
+                    break;
+                default:
+                    ALOGE("Bad blocktype %d", chainingBlock->blockType);
+                    dvmCompilerAbort(cUnit);
+            }
+        }
+    }
+
+    /* Mark the bottom of chaining cells */
+    cUnit->chainingCellBottom = (LIR *) newLIR0(cUnit, kMipsChainingCellBottom);
+
+    /*
+     * Generate the branch to the dvmJitToInterpNoChain entry point at the end
+     * of all chaining cells for the overflow cases.
+     */
+    if (cUnit->switchOverflowPad) {
+        loadConstant(cUnit, r_A0, (int) cUnit->switchOverflowPad);
+        loadWordDisp(cUnit, rSELF, offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpNoChain), r_A2);
+        opRegReg(cUnit, kOpAdd, r_A1, r_A1);
+        opRegRegReg(cUnit, kOpAdd, r4PC, r_A0, r_A1);
+#if defined(WITH_JIT_TUNING)
+        loadConstant(cUnit, r_A0, kSwitchOverflow);
+#endif
+        opReg(cUnit, kOpBlx, r_A2);
+    }
+
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+
+#if defined(WITH_SELF_VERIFICATION)
+    selfVerificationBranchInsertPass(cUnit);
+#endif
+}
+
+/*
+ * Accept the work and start compiling.  Returns true if compilation
+ * is attempted.
+ */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+    JitTraceDescription *desc;
+    bool isCompile;
+    bool success = true;
+
+    if (gDvmJit.codeCacheFull) {
+        return false;
+    }
+
+    switch (work->kind) {
+        case kWorkOrderTrace:
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            break;
+        case kWorkOrderTraceDebug: {
+            bool oldPrintMe = gDvmJit.printMe;
+            gDvmJit.printMe = true;
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            gDvmJit.printMe = oldPrintMe;
+            break;
+        }
+        case kWorkOrderProfileMode:
+            dvmJitChangeProfileMode((TraceProfilingModes)(int)work->info);
+            isCompile = false;
+            break;
+        default:
+            isCompile = false;
+            ALOGE("Jit: unknown work order type");
+            assert(0);  // Bail if debug build, discard otherwise
+    }
+    if (!success)
+        work->result.codeAddress = NULL;
+    return isCompile;
+}
+
+/* Architectural-specific debugging helpers go here */
+void dvmCompilerArchDump(void)
+{
+    /* Print compiled opcode in this VM instance */
+    int i, start, streak;
+    char buf[1024];
+
+    streak = i = 0;
+    buf[0] = 0;
+    while (opcodeCoverage[i] == 0 && i < 256) {
+        i++;
+    }
+    if (i == 256) {
+        return;
+    }
+    for (start = i++, streak = 1; i < 256; i++) {
+        if (opcodeCoverage[i]) {
+            streak++;
+        } else {
+            if (streak == 1) {
+                sprintf(buf+strlen(buf), "%x,", start);
+            } else {
+                sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
+            }
+            streak = 0;
+            while (opcodeCoverage[i] == 0 && i < 256) {
+                i++;
+            }
+            if (i < 256) {
+                streak = 1;
+                start = i;
+            }
+        }
+    }
+    if (streak) {
+        if (streak == 1) {
+            sprintf(buf+strlen(buf), "%x", start);
+        } else {
+            sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
+        }
+    }
+    if (strlen(buf)) {
+        ALOGD("dalvik.vm.jit.op = %s", buf);
+    }
+}
+
+/* Common initialization routine for an architecture family */
+bool dvmCompilerArchInit()
+{
+    int i;
+
+    for (i = 0; i < kMipsLast; i++) {
+        if (EncodingMap[i].opcode != i) {
+            ALOGE("Encoding order for %s is wrong: expecting %d, seeing %d",
+                 EncodingMap[i].name, i, EncodingMap[i].opcode);
+            dvmAbort();  // OK to dvmAbort - build error
+        }
+    }
+
+    return dvmCompilerArchVariantInit();
+}
+
+void *dvmCompilerGetInterpretTemplate()
+{
+      return (void*) ((int)gDvmJit.codeCache +
+                      templateEntryOffsets[TEMPLATE_INTERPRET]);
+}
+
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet()
+{
+    return DALVIK_JIT_MIPS;
+}
+
+/* Needed by the Assembler */
+void dvmCompilerSetupResourceMasks(MipsLIR *lir)
+{
+    setupResourceMasks(lir);
+}
+
+/* Needed by the ld/st optmizatons */
+MipsLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopyNoInsert(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+MipsLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopy(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                            int srcLo, int srcHi)
+{
+    genRegCopyWide(cUnit, destLo, destHi, srcLo, srcHi);
+}
+
+void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    storeBaseDisp(cUnit, rBase, displacement, rSrc, size);
+}
+
+void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    storeBaseDispWide(cUnit, rBase, displacement, rSrcLo, rSrcHi);
+}
diff --git a/vm/compiler/codegen/mips/CodegenFactory.cpp b/vm/compiler/codegen/mips/CodegenFactory.cpp
new file mode 100644
index 0000000..a1211cc
--- /dev/null
+++ b/vm/compiler/codegen/mips/CodegenFactory.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * Mips variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+
+/* Load a word at base + displacement.  Displacement must be word multiple */
+static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+                            int rDest)
+{
+    return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
+                        INVALID_SREG);
+}
+
+static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc)
+{
+    return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord);
+}
+
+/*
+ * Load a Dalvik register into a physical register.  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+                                int reg1)
+{
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopy(cUnit, reg1, rlSrc.lowReg);
+    } else  if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+        loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                     reg1);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * register.  Should be used when loading to a fixed register (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 int reg1)
+{
+    dvmCompilerClobber(cUnit, reg1);
+    dvmCompilerMarkInUse(cUnit, reg1);
+    loadValueDirect(cUnit, rlSrc, reg1);
+}
+
+/*
+ * Load a Dalvik register pair into a physical register[s].  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                int regLo, int regHi)
+{
+    rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg);
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
+                         regLo, regHi, INVALID_SREG);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+            loadBaseDispWide(cUnit, NULL, rFP,
+                             dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                             regLo, regHi, INVALID_SREG);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * registers.  Should be used when loading to a fixed registers (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                     int regLo, int regHi)
+{
+    dvmCompilerClobber(cUnit, regLo);
+    dvmCompilerClobber(cUnit, regHi);
+    dvmCompilerMarkInUse(cUnit, regLo);
+    dvmCompilerMarkInUse(cUnit, regHi);
+    loadValueDirectWide(cUnit, rlSrc, regLo, regHi);
+}
+
+static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc,
+                             RegisterClass opKind)
+{
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirect(cUnit, rlSrc, rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+    } else if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+    }
+    return rlSrc;
+}
+
+static void storeValue(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(!rlDest.wide);
+    assert(!rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirect(cUnit, rlSrc, rlDest.lowReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                      rlDest.lowReg, kWord);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    } else {
+        dvmCompilerResetDefLoc(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
+
+static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 RegisterClass opKind)
+{
+    assert(rlSrc.wide);
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+        dvmCompilerMarkLive(cUnit, rlSrc.highReg,
+                            dvmCompilerSRegHi(rlSrc.sRegLow));
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
+                         rlSrc.lowReg, rlSrc.highReg, INVALID_SREG);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        dvmCompilerClobber(cUnit, rlSrc.highReg);
+    }
+    return rlSrc;
+}
+
+static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg));
+    assert(rlDest.wide);
+    assert(rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            dvmCompilerIsLive(cUnit, rlSrc.highReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg,
+                           rlSrc.lowReg, rlSrc.highReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            rlDest.highReg = rlSrc.highReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+            dvmCompilerClobber(cUnit, rlSrc.highReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg,
+                            rlDest.highReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkLive(cUnit, rlDest.highReg,
+                        dvmCompilerSRegHi(rlDest.sRegLow));
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+    dvmCompilerMarkDirty(cUnit, rlDest.highReg);
+    dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                          rlDest.lowReg, rlDest.highReg);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        dvmCompilerResetDefLocWide(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) ||
+            dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            assert((vReg+1) == dvmCompilerS2VReg(cUnit,
+                                     dvmCompilerSRegHi(rlDest.sRegLow)));
+            storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg,
+                              rlDest.highReg);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            dvmCompilerMarkClean(cUnit, rlDest.highReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
+/*
+ * Perform null-check on a register. sReg is the ssa register being checked,
+ * and mReg is the machine register holding the actual value. If internal state
+ * indicates that sReg has been checked before the check request is ignored.
+ */
+static MipsLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
+                                int dOffset, MipsLIR *pcrLabel)
+{
+    /* This particular Dalvik register has been null-checked */
+    if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
+        return pcrLabel;
+    }
+    dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg);
+    return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+
+
+/*
+ * Perform a "reg cmp reg" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static MipsLIR *genRegRegCheck(CompilationUnit *cUnit,
+                              MipsConditionCode cond,
+                              int reg1, int reg2, int dOffset,
+                              MipsLIR *pcrLabel)
+{
+    MipsLIR *res = NULL;
+    if (cond == kMipsCondGe) { /* signed >= case */
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        res = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
+        MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
+        genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    } else if (cond == kMipsCondCs) {  /* unsigned >= case */
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        res = newLIR3(cUnit, kMipsSltu, tReg, reg1, reg2);
+        MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
+        genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    } else {
+        ALOGE("Unexpected condition in genRegRegCheck: %d\n", (int) cond);
+        dvmAbort();
+    }
+    return res;
+}
+
+/*
+ * Perform zero-check on a register. Similar to genNullCheck but the value being
+ * checked does not have a corresponding Dalvik register.
+ */
+static MipsLIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
+                                int dOffset, MipsLIR *pcrLabel)
+{
+    return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/* Perform bound check on two registers */
+static MipsLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+                                  int rBound, int dOffset, MipsLIR *pcrLabel)
+{
+    return genRegRegCheck(cUnit, kMipsCondCs, rIndex, rBound, dOffset,
+                            pcrLabel);
+}
+
+/*
+ * Jump to the out-of-line handler to finish executing the
+ * remaining of more complex instructions.
+ */
+static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opCode)
+{
+    /*
+     * We're jumping from a trace to a template. Using jal is preferable to jalr,
+     * but we need to ensure source and target addresses allow the use of jal.
+     * This should almost always be the case, but if source and target are in
+     * different 256mb regions then use jalr.  The test below is very conservative
+     * since we don't have a source address yet, but this is ok for now given that
+     * we expect this case to be very rare. The test can be made less conservative
+     * as needed in the future in coordination with address assignment during
+     * the assembly process.
+     */
+    dvmCompilerClobberHandlerRegs(cUnit);
+    int targetAddr = (int) gDvmJit.codeCache + templateEntryOffsets[opCode];
+    int maxSourceAddr = (int) gDvmJit.codeCache + gDvmJit.codeCacheSize;
+
+    if ((targetAddr & 0xF0000000) == (maxSourceAddr & 0xF0000000)) {
+        newLIR1(cUnit, kMipsJal, targetAddr);
+    } else {
+        loadConstant(cUnit, r_T9, targetAddr);
+        newLIR2(cUnit, kMipsJalr, r_RA, r_T9);
+    }
+}
diff --git a/vm/compiler/codegen/mips/FP/MipsFP.cpp b/vm/compiler/codegen/mips/FP/MipsFP.cpp
new file mode 100644
index 0000000..cf44b0e
--- /dev/null
+++ b/vm/compiler/codegen/mips/FP/MipsFP.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+extern void dvmCompilerFlushRegWideForV5TEVFP(CompilationUnit *cUnit,
+                                              int reg1, int reg2);
+extern void dvmCompilerFlushRegForV5TEVFP(CompilationUnit *cUnit, int reg);
+
+/* First, flush any registers associated with this value */
+static void loadValueAddress(CompilationUnit *cUnit, RegLocation rlSrc,
+                             int rDest)
+{
+     rlSrc = rlSrc.wide ? dvmCompilerUpdateLocWide(cUnit, rlSrc) :
+                          dvmCompilerUpdateLoc(cUnit, rlSrc);
+     if (rlSrc.location == kLocPhysReg) {
+         if (rlSrc.wide) {
+             dvmCompilerFlushRegWideForV5TEVFP(cUnit, rlSrc.lowReg,
+                                               rlSrc.highReg);
+         } else {
+             dvmCompilerFlushRegForV5TEVFP(cUnit, rlSrc.lowReg);
+         }
+     }
+     opRegRegImm(cUnit, kOpAdd, rDest, rFP,
+                 dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+#ifdef __mips_hard_float
+    RegLocation rlResult = LOC_C_RETURN_WIDE_ALT;
+#else
+    RegLocation rlResult = LOC_C_RETURN_WIDE;
+#endif
+    RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+    loadValueAddress(cUnit, rlSrc, r_A2);
+    genDispatchToHandler(cUnit, TEMPLATE_SQRT_DOUBLE_VFP);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * TUNING: On some implementations, it is quicker to pass addresses
+ * to the handlers rather than load the operands into core registers
+ * and then move the values to FP regs in the handlers.  Other implementations
+ * may prefer passing data in registers (and the latter approach would
+ * yeild cleaner register handling - avoiding the requirement that operands
+ * be flushed to memory prior to the call).
+ */
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+#ifdef __mips_hard_float
+    int op = kMipsNop;
+    RegLocation rlResult;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            op = kMipsFadds;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            op = kMipsFsubs;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            op = kMipsFdivs;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            op = kMipsFmuls;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR3(cUnit, (MipsOpCode)op, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+
+    return false;
+#else
+    TemplateOpcode opcode;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            opcode = TEMPLATE_ADD_FLOAT_VFP;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            opcode = TEMPLATE_SUB_FLOAT_VFP;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            opcode = TEMPLATE_DIV_FLOAT_VFP;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            opcode = TEMPLATE_MUL_FLOAT_VFP;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddress(cUnit, rlDest, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc1, r_A1);
+    dvmCompilerClobber(cUnit, r_A1);
+    loadValueAddress(cUnit, rlSrc2, r_A2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    }
+    return false;
+#endif
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+#ifdef __mips_hard_float
+    int op = kMipsNop;
+    RegLocation rlResult;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            op = kMipsFaddd;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            op = kMipsFsubd;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            op = kMipsFdivd;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            op = kMipsFmuld;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+    assert(rlSrc1.wide);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+    assert(rlSrc2.wide);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    assert(rlDest.wide);
+    assert(rlResult.wide);
+    newLIR3(cUnit, (MipsOpCode)op, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc1.lowReg, rlSrc1.highReg),
+            S2D(rlSrc2.lowReg, rlSrc2.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+#else
+    TemplateOpcode opcode;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            opcode = TEMPLATE_ADD_DOUBLE_VFP;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            opcode = TEMPLATE_SUB_DOUBLE_VFP;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            opcode = TEMPLATE_DIV_DOUBLE_VFP;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            opcode = TEMPLATE_MUL_DOUBLE_VFP;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+                                               rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddress(cUnit, rlDest, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc1, r_A1);
+    dvmCompilerClobber(cUnit, r_A1);
+    loadValueAddress(cUnit, rlSrc2, r_A2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    }
+    return false;
+#endif
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    bool longSrc = false;
+    bool longDest = false;
+    RegLocation rlSrc;
+    RegLocation rlDest;
+#ifdef __mips_hard_float
+    int op = kMipsNop;
+    int srcReg;
+    RegLocation rlResult;
+
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            op = kMipsFcvtsw;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            op = kMipsFcvtsd;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            op = kMipsFcvtds;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            op = kMipsFcvtdw;
+            break;
+        case OP_FLOAT_TO_INT:
+        case OP_DOUBLE_TO_INT:
+        case OP_LONG_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+            return genConversionPortable(cUnit, mir);
+        default:
+            return true;
+    }
+    if (longSrc) {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+        srcReg = S2D(rlSrc.lowReg, rlSrc.highReg);
+    } else {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+        rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+        srcReg = rlSrc.lowReg;
+    }
+    if (longDest) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+        newLIR2(cUnit, (MipsOpCode)op, S2D(rlResult.lowReg, rlResult.highReg), srcReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+        newLIR2(cUnit, (MipsOpCode)op, rlResult.lowReg, srcReg);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+    return false;
+#else
+    TemplateOpcode templateOpcode;
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_INT_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_INT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_FLOAT_TO_INT_VFP;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_FLOAT_TO_DOUBLE_VFP;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_INT_TO_DOUBLE_VFP;
+            break;
+        case OP_DOUBLE_TO_INT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_INT_VFP;
+            break;
+        case OP_LONG_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+            return genConversionPortable(cUnit, mir);
+        default:
+            return true;
+    }
+
+    if (longSrc) {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    } else {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    }
+
+    if (longDest) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    loadValueAddress(cUnit, rlDest, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc, r_A1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    if (rlDest.wide) {
+        rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    }
+    dvmCompilerClobber(cUnit, rlDest.lowReg);
+    return false;
+#endif
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    TemplateOpcode templateOpcode;
+    RegLocation rlResult = dvmCompilerGetReturn(cUnit);
+    bool wide = true;
+
+    switch(mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            templateOpcode = TEMPLATE_CMPL_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPG_FLOAT:
+            templateOpcode = TEMPLATE_CMPG_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPL_DOUBLE:
+            templateOpcode = TEMPLATE_CMPL_DOUBLE_VFP;
+            break;
+        case OP_CMPG_DOUBLE:
+            templateOpcode = TEMPLATE_CMPG_DOUBLE_VFP;
+            break;
+        default:
+            return true;
+    }
+    loadValueAddress(cUnit, rlSrc1, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc2, r_A1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/mips/GlobalOptimizations.cpp b/vm/compiler/codegen/mips/GlobalOptimizations.cpp
new file mode 100644
index 0000000..189d818
--- /dev/null
+++ b/vm/compiler/codegen/mips/GlobalOptimizations.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "MipsLIR.h"
+
+/*
+ * Identify unconditional branches that jump to the immediate successor of the
+ * branch itself.
+ */
+static void applyRedundantBranchElimination(CompilationUnit *cUnit)
+{
+    MipsLIR *thisLIR;
+
+    for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Branch to the next instruction */
+        if (!thisLIR->flags.isNop && thisLIR->opcode == kMipsB) {
+            MipsLIR *nextLIR = thisLIR;
+
+            while (true) {
+                nextLIR = NEXT_LIR(nextLIR);
+
+                /*
+                 * Is the branch target the next instruction?
+                 */
+                if (nextLIR == (MipsLIR *) thisLIR->generic.target) {
+                    thisLIR->flags.isNop = true;
+                    break;
+                }
+
+                /*
+                 * Found real useful stuff between the branch and the target.
+                 * Need to explicitly check the lastLIRInsn here since with
+                 * method-based JIT the branch might be the last real
+                 * instruction.
+                 */
+                if (!isPseudoOpCode(nextLIR->opcode) ||
+                    (nextLIR = (MipsLIR *) cUnit->lastLIRInsn))
+                    break;
+            }
+        }
+    }
+}
+
+/*
+ * Do simple a form of copy propagation and elimination.
+ */
+static void applyCopyPropagation(CompilationUnit *cUnit)
+{
+    MipsLIR *thisLIR;
+
+    /* look for copies to possibly eliminate */
+    for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        if (thisLIR->flags.isNop || thisLIR->opcode != kMipsMove)
+            continue;
+
+        const int max_insns = 10;
+        MipsLIR *savedLIR[max_insns];
+        int srcRedefined = 0;
+        int insnCount = 0;
+        MipsLIR *nextLIR;
+
+        /* look for and record all uses of reg defined by the copy */
+        for (nextLIR = (MipsLIR *) NEXT_LIR(thisLIR);
+             nextLIR != (MipsLIR *) cUnit->lastLIRInsn;
+             nextLIR = NEXT_LIR(nextLIR)) {
+
+            if (nextLIR->flags.isNop || nextLIR->opcode == kMips32BitData)
+                continue;
+
+            if (isPseudoOpCode(nextLIR->opcode)) {
+                if (nextLIR->opcode == kMipsPseudoDalvikByteCodeBoundary ||
+                    nextLIR->opcode == kMipsPseudoBarrier ||
+                    nextLIR->opcode == kMipsPseudoExtended ||
+                    nextLIR->opcode == kMipsPseudoSSARep)
+                    continue; /* these pseudos don't pose problems */
+                else if (nextLIR->opcode == kMipsPseudoTargetLabel ||
+                         nextLIR->opcode == kMipsPseudoEntryBlock ||
+                         nextLIR->opcode == kMipsPseudoExitBlock)
+                    insnCount = 0;  /* give up for these pseudos */
+                break; /* reached end for copy propagation */
+            }
+
+            /* Since instructions with IS_BRANCH flag set will have its */
+            /* useMask and defMask set to ENCODE_ALL, any checking of   */
+            /* these flags must come after the branching checks.        */
+
+            /* don't propagate across branch/jump and link case
+               or jump via register */
+            if (EncodingMap[nextLIR->opcode].flags & REG_DEF_LR ||
+                nextLIR->opcode == kMipsJalr ||
+                nextLIR->opcode == kMipsJr) {
+                insnCount = 0;
+                break;
+            }
+
+            /* branches with certain targets ok while others aren't */
+            if (EncodingMap[nextLIR->opcode].flags & IS_BRANCH) {
+                MipsLIR *targetLIR =  (MipsLIR *) nextLIR->generic.target;
+                if (targetLIR->opcode != kMipsPseudoEHBlockLabel &&
+                    targetLIR->opcode != kMipsPseudoChainingCellHot &&
+                    targetLIR->opcode != kMipsPseudoChainingCellNormal &&
+                    targetLIR->opcode != kMipsPseudoChainingCellInvokePredicted &&
+                    targetLIR->opcode != kMipsPseudoChainingCellInvokeSingleton &&
+                    targetLIR->opcode != kMipsPseudoPCReconstructionBlockLabel &&
+                    targetLIR->opcode != kMipsPseudoPCReconstructionCell) {
+                    insnCount = 0;
+                    break;
+                }
+                /* FIXME - for now don't propagate across any branch/jump. */
+                insnCount = 0;
+                break;
+            }
+
+            /* copy def reg used here, so record insn for copy propagation */
+            if (thisLIR->defMask & nextLIR->useMask) {
+                if (insnCount == max_insns || srcRedefined) {
+                    insnCount = 0;
+                    break; /* just give up if too many or not possible */
+                }
+                savedLIR[insnCount++] = nextLIR;
+            }
+
+            if (thisLIR->defMask & nextLIR->defMask) {
+		if (nextLIR->opcode == kMipsMovz)
+		    insnCount = 0; /* movz relies on thisLIR setting dst reg so abandon propagation*/
+                break;
+            }
+
+            /* copy src reg redefined here, so can't propagate further */
+            if (thisLIR->useMask & nextLIR->defMask) {
+                if (insnCount == 0)
+                    break; /* nothing to propagate */
+                srcRedefined = 1;
+            }
+       }
+
+        /* conditions allow propagation and copy elimination */
+        if (insnCount) {
+            int i;
+            for (i = 0; i < insnCount; i++) {
+                int flags = EncodingMap[savedLIR[i]->opcode].flags;
+                savedLIR[i]->useMask &= ~(1 << thisLIR->operands[0]);
+                savedLIR[i]->useMask |= 1 << thisLIR->operands[1];
+                if ((flags & REG_USE0) &&
+                    savedLIR[i]->operands[0] == thisLIR->operands[0])
+                    savedLIR[i]->operands[0] = thisLIR->operands[1];
+                if ((flags & REG_USE1) &&
+                    savedLIR[i]->operands[1] == thisLIR->operands[0])
+                    savedLIR[i]->operands[1] = thisLIR->operands[1];
+                if ((flags & REG_USE2) &&
+                    savedLIR[i]->operands[2] == thisLIR->operands[0])
+                    savedLIR[i]->operands[2] = thisLIR->operands[1];
+                if ((flags & REG_USE3) &&
+                    savedLIR[i]->operands[3] == thisLIR->operands[0])
+                    savedLIR[i]->operands[3] = thisLIR->operands[1];
+            }
+            thisLIR->flags.isNop = true;
+        }
+    }
+}
+
+#ifdef __mips_hard_float
+/*
+ * Look for pairs of mov.s instructions that can be combined into mov.d
+ */
+static void mergeMovs(CompilationUnit *cUnit)
+{
+  MipsLIR *movsLIR = NULL;
+  MipsLIR *thisLIR;
+
+  for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+       thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+       thisLIR = NEXT_LIR(thisLIR)) {
+    if (thisLIR->flags.isNop)
+      continue;
+
+    if (isPseudoOpCode(thisLIR->opcode)) {
+      if (thisLIR->opcode == kMipsPseudoDalvikByteCodeBoundary ||
+                thisLIR->opcode == kMipsPseudoExtended ||
+	  thisLIR->opcode == kMipsPseudoSSARep)
+	continue;  /* ok to move across these pseudos */
+      movsLIR = NULL; /* don't merge across other pseudos */
+      continue;
+    }
+
+    /* merge pairs of mov.s instructions */
+    if (thisLIR->opcode == kMipsFmovs) {
+      if (movsLIR == NULL)
+	movsLIR = thisLIR;
+      else if (((movsLIR->operands[0] & 1) == 0) &&
+	       ((movsLIR->operands[1] & 1) == 0) &&
+	       ((movsLIR->operands[0] + 1) == thisLIR->operands[0]) &&
+	       ((movsLIR->operands[1] + 1) == thisLIR->operands[1])) {
+	/* movsLIR is handling even register - upgrade to mov.d */
+	movsLIR->opcode = kMipsFmovd;
+	movsLIR->operands[0] = S2D(movsLIR->operands[0], movsLIR->operands[0]+1);
+	movsLIR->operands[1] = S2D(movsLIR->operands[1], movsLIR->operands[1]+1);
+	thisLIR->flags.isNop = true;
+	movsLIR = NULL;
+      }
+      else if (((movsLIR->operands[0] & 1) == 1) &&
+	       ((movsLIR->operands[1] & 1) == 1) &&
+	       ((movsLIR->operands[0] - 1) == thisLIR->operands[0]) &&
+	       ((movsLIR->operands[1] - 1) == thisLIR->operands[1])) {
+	/* thissLIR is handling even register - upgrade to mov.d */
+	thisLIR->opcode = kMipsFmovd;
+	thisLIR->operands[0] = S2D(thisLIR->operands[0], thisLIR->operands[0]+1);
+	thisLIR->operands[1] = S2D(thisLIR->operands[1], thisLIR->operands[1]+1);
+	movsLIR->flags.isNop = true;
+	movsLIR = NULL;
+      }
+      else
+	/* carry on searching from here */
+	movsLIR = thisLIR;
+      continue;
+    }
+
+    /* intervening instruction - start search from scratch */
+    movsLIR = NULL;
+  }
+}
+#endif
+
+
+/*
+ * Look back first and then ahead to try to find an instruction to move into
+ * the branch delay slot.  If the analysis can be done cheaply enough, it may be
+ * be possible to tune this routine to be more beneficial (e.g., being more
+ * particular about what instruction is speculated).
+ */
+static MipsLIR *delaySlotLIR(MipsLIR *firstLIR, MipsLIR *branchLIR)
+{
+    int isLoad;
+    int loadVisited = 0;
+    int isStore;
+    int storeVisited = 0;
+    u8 useMask = branchLIR->useMask;
+    u8 defMask = branchLIR->defMask;
+    MipsLIR *thisLIR;
+    MipsLIR *newLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+
+    for (thisLIR = PREV_LIR(branchLIR);
+         thisLIR != firstLIR;
+         thisLIR = PREV_LIR(thisLIR)) {
+        if (thisLIR->flags.isNop)
+            continue;
+
+        if (isPseudoOpCode(thisLIR->opcode)) {
+            if (thisLIR->opcode == kMipsPseudoDalvikByteCodeBoundary ||
+                thisLIR->opcode == kMipsPseudoExtended ||
+                thisLIR->opcode == kMipsPseudoSSARep)
+                continue;  /* ok to move across these pseudos */
+            break; /* don't move across all other pseudos */
+        }
+
+        /* give up on moving previous instruction down into slot */
+        if (thisLIR->opcode == kMipsNop ||
+            thisLIR->opcode == kMips32BitData ||
+            EncodingMap[thisLIR->opcode].flags & IS_BRANCH)
+            break;
+
+        /* don't reorder loads/stores (the alias info could
+           possibly be used to allow as a future enhancement) */
+        isLoad = EncodingMap[thisLIR->opcode].flags & IS_LOAD;
+        isStore = EncodingMap[thisLIR->opcode].flags & IS_STORE;
+
+        if (!(thisLIR->useMask & defMask) &&
+            !(thisLIR->defMask & useMask) &&
+            !(thisLIR->defMask & defMask) &&
+            !(isLoad && storeVisited) &&
+            !(isStore && loadVisited) &&
+            !(isStore && storeVisited)) {
+            *newLIR = *thisLIR;
+            thisLIR->flags.isNop = true;
+            return newLIR; /* move into delay slot succeeded */
+        }
+
+        loadVisited |= isLoad;
+        storeVisited |= isStore;
+
+        /* accumulate def/use constraints */
+        useMask |= thisLIR->useMask;
+        defMask |= thisLIR->defMask;
+    }
+
+    /* for unconditional branches try to copy the instruction at the
+       branch target up into the delay slot and adjust the branch */
+    if (branchLIR->opcode == kMipsB) {
+        MipsLIR *targetLIR;
+        for (targetLIR = (MipsLIR *) branchLIR->generic.target;
+             targetLIR;
+             targetLIR = NEXT_LIR(targetLIR)) {
+            if (!targetLIR->flags.isNop &&
+                (!isPseudoOpCode(targetLIR->opcode) || /* can't pull predicted up */
+                 targetLIR->opcode == kMipsPseudoChainingCellInvokePredicted))
+                break; /* try to get to next real op at branch target */
+        }
+        if (targetLIR && !isPseudoOpCode(targetLIR->opcode) &&
+            !(EncodingMap[targetLIR->opcode].flags & IS_BRANCH)) {
+            *newLIR = *targetLIR;
+            branchLIR->generic.target = (LIR *) NEXT_LIR(targetLIR);
+            return newLIR;
+        }
+    } else if (branchLIR->opcode >= kMipsBeq && branchLIR->opcode <= kMipsBne) {
+        /* for conditional branches try to fill branch delay slot
+           via speculative execution when safe */
+        MipsLIR *targetLIR;
+        for (targetLIR = (MipsLIR *) branchLIR->generic.target;
+             targetLIR;
+             targetLIR = NEXT_LIR(targetLIR)) {
+            if (!targetLIR->flags.isNop && !isPseudoOpCode(targetLIR->opcode))
+                break; /* try to get to next real op at branch target */
+        }
+
+        MipsLIR *nextLIR;
+        for (nextLIR = NEXT_LIR(branchLIR);
+             nextLIR;
+             nextLIR = NEXT_LIR(nextLIR)) {
+            if (!nextLIR->flags.isNop && !isPseudoOpCode(nextLIR->opcode))
+                break; /* try to get to next real op for fall thru */
+        }
+
+        if (nextLIR && targetLIR) {
+            int flags = EncodingMap[nextLIR->opcode].flags;
+            int isLoad = flags & IS_LOAD;
+
+            /* common branch and fall thru to normal chaining cells case */
+            if (isLoad && nextLIR->opcode == targetLIR->opcode &&
+                nextLIR->operands[0] == targetLIR->operands[0] &&
+                nextLIR->operands[1] == targetLIR->operands[1] &&
+                nextLIR->operands[2] == targetLIR->operands[2]) {
+                *newLIR = *targetLIR;
+                branchLIR->generic.target = (LIR *) NEXT_LIR(targetLIR);
+                return newLIR;
+            }
+
+            /* try prefetching (maybe try speculating instructions along the
+               trace like dalvik frame load which is common and may be safe) */
+            int isStore = flags & IS_STORE;
+            if (isLoad || isStore) {
+                newLIR->opcode = kMipsPref;
+                newLIR->operands[0] = isLoad ? 0 : 1;
+                newLIR->operands[1] = nextLIR->operands[1];
+                newLIR->operands[2] = nextLIR->operands[2];
+                newLIR->defMask = nextLIR->defMask;
+                newLIR->useMask = nextLIR->useMask;
+                return newLIR;
+            }
+        }
+    }
+
+    /* couldn't find a useful instruction to move into the delay slot */
+    newLIR->opcode = kMipsNop;
+    return newLIR;
+}
+
+/*
+ * The branch delay slot has been ignored thus far.  This is the point where
+ * a useful instruction is moved into it or a nop is inserted.  Leave existing
+ * NOPs alone -- these came from sparse and packed switch ops and are needed
+ * to maintain the proper offset to the jump table.
+ */
+static void introduceBranchDelaySlot(CompilationUnit *cUnit)
+{
+    MipsLIR *thisLIR;
+    MipsLIR *firstLIR =(MipsLIR *) cUnit->firstLIRInsn;
+    MipsLIR *lastLIR =(MipsLIR *) cUnit->lastLIRInsn;
+
+    for (thisLIR = lastLIR; thisLIR != firstLIR; thisLIR = PREV_LIR(thisLIR)) {
+        if (thisLIR->flags.isNop ||
+            isPseudoOpCode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & IS_BRANCH)) {
+            continue;
+        } else if (thisLIR == lastLIR) {
+            dvmCompilerAppendLIR(cUnit,
+                (LIR *) delaySlotLIR(firstLIR, thisLIR));
+        } else if (NEXT_LIR(thisLIR)->opcode != kMipsNop) {
+            dvmCompilerInsertLIRAfter((LIR *) thisLIR,
+                (LIR *) delaySlotLIR(firstLIR, thisLIR));
+        }
+    }
+
+    if (!thisLIR->flags.isNop &&
+        !isPseudoOpCode(thisLIR->opcode) &&
+        EncodingMap[thisLIR->opcode].flags & IS_BRANCH) {
+        /* nothing available to move, so insert nop */
+        MipsLIR *nopLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        nopLIR->opcode = kMipsNop;
+        dvmCompilerInsertLIRAfter((LIR *) thisLIR, (LIR *) nopLIR);
+    }
+}
+
+void dvmCompilerApplyGlobalOptimizations(CompilationUnit *cUnit)
+{
+    applyRedundantBranchElimination(cUnit);
+    applyCopyPropagation(cUnit);
+#ifdef __mips_hard_float
+    mergeMovs(cUnit);
+#endif
+    introduceBranchDelaySlot(cUnit);
+}
diff --git a/vm/compiler/codegen/mips/LocalOptimizations.cpp b/vm/compiler/codegen/mips/LocalOptimizations.cpp
new file mode 100644
index 0000000..1ef0d17
--- /dev/null
+++ b/vm/compiler/codegen/mips/LocalOptimizations.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "MipsLIR.h"
+#include "Codegen.h"
+
+#define DEBUG_OPT(X)
+
+/* Check RAW, WAR, and WAR dependency on the register operands */
+#define CHECK_REG_DEP(use, def, check) ((def & check->useMask) || \
+                                        ((use | def) & check->defMask))
+
+/* Scheduler heuristics */
+#define MAX_HOIST_DISTANCE 20
+#define LDLD_DISTANCE 4
+#define LD_LATENCY 2
+
+static inline bool isDalvikRegisterClobbered(MipsLIR *lir1, MipsLIR *lir2)
+{
+    int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->aliasInfo);
+    int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->aliasInfo);
+    int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->aliasInfo);
+    int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->aliasInfo);
+
+    return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
+}
+
+#if 0
+/* Debugging utility routine */
+static void dumpDependentInsnPair(MipsLIR *thisLIR, MipsLIR *checkLIR,
+                                  const char *optimization)
+{
+    LOGD("************ %s ************", optimization);
+    dvmDumpLIRInsn((LIR *) thisLIR, 0);
+    dvmDumpLIRInsn((LIR *) checkLIR, 0);
+}
+#endif
+
+/* Convert a more expensive instruction (ie load) into a move */
+static void convertMemOpIntoMove(CompilationUnit *cUnit, MipsLIR *origLIR,
+                                 int dest, int src)
+{
+    /* Insert a move to replace the load */
+    MipsLIR *moveLIR;
+    moveLIR = dvmCompilerRegCopyNoInsert( cUnit, dest, src);
+    /*
+     * Insert the converted instruction after the original since the
+     * optimization is scannng in the top-down order and the new instruction
+     * will need to be re-checked (eg the new dest clobbers the src used in
+     * thisLIR).
+     */
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) moveLIR);
+}
+
+/*
+ * Perform a pass of top-down walk, from the second-last instruction in the
+ * superblock, to eliminate redundant loads and stores.
+ *
+ * An earlier load can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * An earlier store can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * A later store can be eliminated by an earlier store iff
+ *   1) They are must-aliases
+ *   2) The memory location is not written to in between
+ */
+static void applyLoadStoreElimination(CompilationUnit *cUnit,
+                                      MipsLIR *headLIR,
+                                      MipsLIR *tailLIR)
+{
+    MipsLIR *thisLIR;
+
+    if (headLIR == tailLIR) return;
+
+    for (thisLIR = PREV_LIR(tailLIR);
+         thisLIR != headLIR;
+         thisLIR = PREV_LIR(thisLIR)) {
+        int sinkDistance = 0;
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpCode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & (IS_LOAD | IS_STORE))) {
+            continue;
+        }
+
+        int nativeRegId = thisLIR->operands[0];
+        bool isThisLIRLoad = EncodingMap[thisLIR->opcode].flags & IS_LOAD;
+        MipsLIR *checkLIR;
+        /* Use the mem mask to determine the rough memory location */
+        u8 thisMemMask = (thisLIR->useMask | thisLIR->defMask) & ENCODE_MEM;
+
+        /*
+         * Currently only eliminate redundant ld/st for constant and Dalvik
+         * register accesses.
+         */
+        if (!(thisMemMask & (ENCODE_LITERAL | ENCODE_DALVIK_REG))) continue;
+
+        /*
+         * Add r15 (pc) to the resource mask to prevent this instruction
+         * from sinking past branch instructions. Also take out the memory
+         * region bits since stopMask is used to check data/control
+         * dependencies.
+         */
+        u8 stopUseRegMask = (ENCODE_REG_PC | thisLIR->useMask) &
+                            ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        for (checkLIR = NEXT_LIR(thisLIR);
+             checkLIR != tailLIR;
+             checkLIR = NEXT_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = (checkLIR->useMask | checkLIR->defMask) &
+                              ENCODE_MEM;
+            u8 aliasCondition = thisMemMask & checkMemMask;
+            bool stopHere = false;
+
+            /*
+             * Potential aliases seen - check the alias relations
+             */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                bool isCheckLIRLoad = EncodingMap[checkLIR->opcode].flags &
+                                      IS_LOAD;
+                if  (aliasCondition == ENCODE_LITERAL) {
+                    /*
+                     * Should only see literal loads in the instruction
+                     * stream.
+                     */
+                    assert(!(EncodingMap[checkLIR->opcode].flags &
+                             IS_STORE));
+                    /* Same value && same register type */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo &&
+                        REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId)){
+                        /*
+                         * Different destination register - insert
+                         * a move
+                         */
+                        if (checkLIR->operands[0] != nativeRegId) {
+                            convertMemOpIntoMove(cUnit, checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                        }
+                        checkLIR->flags.isNop = true;
+                    }
+                } else if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo) {
+                        /* Only optimize compatible registers */
+                        bool regCompatible =
+                            REGTYPE(checkLIR->operands[0]) ==
+                            REGTYPE(nativeRegId);
+                        if ((isThisLIRLoad && isCheckLIRLoad) ||
+                            (!isThisLIRLoad && isCheckLIRLoad)) {
+                            /* RAR or RAW */
+                            if (regCompatible) {
+                                /*
+                                 * Different destination register -
+                                 * insert a move
+                                 */
+                                if (checkLIR->operands[0] !=
+                                    nativeRegId) {
+                                    convertMemOpIntoMove(cUnit,
+                                                 checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                                }
+                                checkLIR->flags.isNop = true;
+                            } else {
+                                /*
+                                 * Destinaions are of different types -
+                                 * something complicated going on so
+                                 * stop looking now.
+                                 */
+                                stopHere = true;
+                            }
+                        } else if (isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAR - register value is killed */
+                            stopHere = true;
+                        } else if (!isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAW - nuke the earlier store */
+                            thisLIR->flags.isNop = true;
+                            stopHere = true;
+                        }
+                    /* Partial overlap */
+                    } else if (isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        /*
+                         * It is actually ok to continue if checkLIR
+                         * is a read. But it is hard to make a test
+                         * case for this so we just stop here to be
+                         * conservative.
+                         */
+                        stopHere = true;
+                    }
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    break;
+                /* The checkLIR has been transformed - check the next one */
+                } else if (checkLIR->flags.isNop) {
+                    continue;
+                }
+            }
+
+
+            /*
+             * this and check LIRs have no memory dependency. Now check if
+             * their register operands have any RAW, WAR, and WAW
+             * dependencies. If so, stop looking.
+             */
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+                                                "REG CLOBBERED"));
+                /* Only sink store instructions */
+                if (sinkDistance && !isThisLIRLoad) {
+                    MipsLIR *newStoreLIR =
+                        (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+                    *newStoreLIR = *thisLIR;
+                    /*
+                     * Stop point found - insert *before* the checkLIR
+                     * since the instruction list is scanned in the
+                     * top-down order.
+                     */
+                    dvmCompilerInsertLIRBefore((LIR *) checkLIR,
+                                               (LIR *) newStoreLIR);
+                    thisLIR->flags.isNop = true;
+                }
+                break;
+            } else if (!checkLIR->flags.isNop) {
+                sinkDistance++;
+            }
+        }
+    }
+}
+
+/*
+ * Perform a pass of bottom-up walk, from the second instruction in the
+ * superblock, to try to hoist loads to earlier slots.
+ */
+static void applyLoadHoisting(CompilationUnit *cUnit,
+                              MipsLIR *headLIR,
+                              MipsLIR *tailLIR)
+{
+    MipsLIR *thisLIR, *checkLIR;
+    /*
+     * Store the list of independent instructions that can be hoisted past.
+     * Will decide the best place to insert later.
+     */
+    MipsLIR *prevInstList[MAX_HOIST_DISTANCE];
+
+    /* Empty block */
+    if (headLIR == tailLIR) return;
+
+    /* Start from the second instruction */
+    for (thisLIR = NEXT_LIR(headLIR);
+         thisLIR != tailLIR;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpCode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & IS_LOAD)) {
+            continue;
+        }
+
+        u8 stopUseAllMask = thisLIR->useMask;
+
+        /*
+         * Branches for null/range checks are marked with the true resource
+         * bits, and loads to Dalvik registers, constant pools, and non-alias
+         * locations are safe to be hoisted. So only mark the heap references
+         * conservatively here.
+         */
+        if (stopUseAllMask & ENCODE_HEAP_REF) {
+            stopUseAllMask |= ENCODE_REG_PC;
+        }
+
+        /* Similar as above, but just check for pure register dependency */
+        u8 stopUseRegMask = stopUseAllMask & ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        int nextSlot = 0;
+        bool stopHere = false;
+
+        /* Try to hoist the load to a good spot */
+        for (checkLIR = PREV_LIR(thisLIR);
+             checkLIR != headLIR;
+             checkLIR = PREV_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = checkLIR->defMask & ENCODE_MEM;
+            u8 aliasCondition = stopUseAllMask & checkMemMask;
+            stopHere = false;
+
+            /* Potential WAR alias seen - check the exact relation */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                /* We can fully disambiguate Dalvik references */
+                if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias or partually overlap */
+                    if ((checkLIR->aliasInfo == thisLIR->aliasInfo) ||
+                        isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        stopHere = true;
+                    }
+                /* Conservatively treat all heap refs as may-alias */
+                } else {
+                    assert(aliasCondition == ENCODE_HEAP_REF);
+                    stopHere = true;
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    prevInstList[nextSlot++] = checkLIR;
+                    break;
+                }
+            }
+
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            /*
+             * Store the dependent or non-pseudo/indepedent instruction to the
+             * list.
+             */
+            if (stopHere || !isPseudoOpCode(checkLIR->opcode)) {
+                prevInstList[nextSlot++] = checkLIR;
+                if (nextSlot == MAX_HOIST_DISTANCE) break;
+            }
+
+            /* Found a new place to put the load - move it here */
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(checkLIR, thisLIR
+                                                "HOIST STOP"));
+                break;
+            }
+        }
+
+        /*
+         * Reached the top - use headLIR as the dependent marker as all labels
+         * are barriers.
+         */
+        if (stopHere == false && nextSlot < MAX_HOIST_DISTANCE) {
+            prevInstList[nextSlot++] = headLIR;
+        }
+
+        /*
+         * At least one independent instruction is found. Scan in the reversed
+         * direction to find a beneficial slot.
+         */
+        if (nextSlot >= 2) {
+            int firstSlot = nextSlot - 2;
+            int slot;
+            MipsLIR *depLIR = prevInstList[nextSlot-1];
+            /* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */
+            if (!isPseudoOpCode(depLIR->opcode) &&
+                (EncodingMap[depLIR->opcode].flags & IS_LOAD)) {
+                firstSlot -= LDLD_DISTANCE;
+            }
+            /*
+             * Make sure we check slot >= 0 since firstSlot may be negative
+             * when the loop is first entered.
+             */
+            for (slot = firstSlot; slot >= 0; slot--) {
+                MipsLIR *curLIR = prevInstList[slot];
+                MipsLIR *prevLIR = prevInstList[slot+1];
+
+                /*
+                 * Check the highest instruction.
+                 * ENCODE_ALL represents a scheduling barrier.
+                 */
+                if (prevLIR->defMask == ENCODE_ALL) {
+                    /*
+                     * If the first instruction is a load, don't hoist anything
+                     * above it since it is unlikely to be beneficial.
+                     */
+                    if (EncodingMap[curLIR->opcode].flags & IS_LOAD) continue;
+                    /*
+                     * Need to unconditionally break here even if the hoisted
+                     * distance is greater than LD_LATENCY (ie more than enough
+                     * cycles are inserted to hide the load latency) since theu
+                     * subsequent code doesn't expect to compare against a
+                     * pseudo opcode (whose opcode value is negative).
+                     */
+                    break;
+                }
+
+                /*
+                 * NOTE: now prevLIR is guaranteed to be a non-pseudo
+                 * instruction (ie accessing EncodingMap[prevLIR->opcode] is
+                 * safe).
+                 *
+                 * Try to find two instructions with load/use dependency until
+                 * the remaining instructions are less than LD_LATENCY.
+                 */
+                if (((curLIR->useMask & prevLIR->defMask) &&
+                     (EncodingMap[prevLIR->opcode].flags & IS_LOAD)) ||
+                    (slot < LD_LATENCY)) {
+                    break;
+                }
+            }
+
+            /* Found a slot to hoist to */
+            if (slot >= 0) {
+                MipsLIR *curLIR = prevInstList[slot];
+                MipsLIR *newLoadLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR),
+                                                               true);
+                *newLoadLIR = *thisLIR;
+                /*
+                 * Insertion is guaranteed to succeed since checkLIR
+                 * is never the first LIR on the list
+                 */
+                dvmCompilerInsertLIRBefore((LIR *) curLIR,
+                                           (LIR *) newLoadLIR);
+                thisLIR->flags.isNop = true;
+            }
+        }
+    }
+}
+
+void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR,
+                                        LIR *tailLIR)
+{
+    if (!(gDvmJit.disableOpt & (1 << kLoadStoreElimination))) {
+        applyLoadStoreElimination(cUnit, (MipsLIR *) headLIR,
+                                  (MipsLIR *) tailLIR);
+    }
+    if (!(gDvmJit.disableOpt & (1 << kLoadHoisting))) {
+        applyLoadHoisting(cUnit, (MipsLIR *) headLIR, (MipsLIR *) tailLIR);
+    }
+}
diff --git a/vm/compiler/codegen/mips/Mips32/Factory.cpp b/vm/compiler/codegen/mips/Mips32/Factory.cpp
new file mode 100644
index 0000000..c6d7775
--- /dev/null
+++ b/vm/compiler/codegen/mips/Mips32/Factory.cpp
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, r_T0, r_T1, r_T2,
+                          r_T3, r_T4, r_T5, r_T6, r_T7, r_T8, r_T9, r_S0, r_S4};
+#ifdef __mips_hard_float
+static int fpTemps[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7,
+                        r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15};
+#endif
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg,
+                      int highReg);
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
+static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+                            int rDest);
+static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc);
+static MipsLIR *genRegRegCheck(CompilationUnit *cUnit,
+                              MipsConditionCode cond,
+                              int reg1, int reg2, int dOffset,
+                              MipsLIR *pcrLabel);
+static MipsLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);
+
+#ifdef __mips_hard_float
+static MipsLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    MipsLIR* res = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    } else {
+        /* must be both DOUBLE or both not DOUBLE */
+        assert(DOUBLEREG(rDest) == DOUBLEREG(rSrc));
+        if (DOUBLEREG(rDest)) {
+            res->opcode = kMipsFmovd;
+        } else {
+            if (SINGLEREG(rDest)) {
+                if (SINGLEREG(rSrc)) {
+                    res->opcode = kMipsFmovs;
+                } else {
+                    /* note the operands are swapped for the mtc1 instr */
+                    res->opcode = kMipsMtc1;
+                    res->operands[0] = rSrc;
+                    res->operands[1] = rDest;
+                }
+            } else {
+                assert(SINGLEREG(rSrc));
+                res->opcode = kMipsMfc1;
+            }
+        }
+    }
+    setupResourceMasks(res);
+    return res;
+}
+#endif
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.  If target is
+ * a high register, build constant into a low register and copy.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static MipsLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+                                     int value)
+{
+    MipsLIR *res;
+
+#ifdef __mips_hard_float
+    int rDestSave = rDest;
+    int isFpReg = FPREG(rDest);
+    if (isFpReg) {
+        assert(SINGLEREG(rDest));
+        rDest = dvmCompilerAllocTemp(cUnit);
+    }
+#endif
+
+    /* See if the value can be constructed cheaply */
+    if (value == 0) {
+        res = newLIR2(cUnit, kMipsMove, rDest, r_ZERO);
+    } else if ((value > 0) && (value <= 65535)) {
+        res = newLIR3(cUnit, kMipsOri, rDest, r_ZERO, value);
+    } else if ((value < 0) && (value >= -32768)) {
+        res = newLIR3(cUnit, kMipsAddiu, rDest, r_ZERO, value);
+    } else {
+        res = newLIR2(cUnit, kMipsLui, rDest, value>>16);
+        if (value & 0xffff)
+	    newLIR3(cUnit, kMipsOri, rDest, rDest, value);
+    }
+
+#ifdef __mips_hard_float
+    if (isFpReg) {
+        newLIR2(cUnit, kMipsMtc1, rDest, rDestSave);
+        dvmCompilerFreeTemp(cUnit, rDest);
+    }
+#endif
+
+    return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static MipsLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+/*
+ * Load a class pointer value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static MipsLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
+{
+    MipsLIR *res;
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    res = newLIR2(cUnit, kMipsLui, rDest, value>>16);
+    if (value & 0xffff)
+        newLIR3(cUnit, kMipsOri, rDest, rDest, value);
+    return res;
+}
+
+static MipsLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpUncondBr:
+            opcode = kMipsB;
+            break;
+        default:
+            ALOGE("Jit: bad case in opNone");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR0(cUnit, opcode);
+    return res;
+}
+
+static MipsLIR *opCompareBranch(CompilationUnit *cUnit, MipsOpCode opc, int rs, int rt)
+{
+    MipsLIR *res;
+    if (rt < 0) {
+      assert(opc >= kMipsBeqz && opc <= kMipsBnez);
+      res = newLIR1(cUnit, opc, rs);
+    } else  {
+      assert(opc == kMipsBeq || opc == kMipsBne);
+      res = newLIR2(cUnit, opc, rs, rt);
+    }
+    return res;
+}
+
+static MipsLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);
+
+static MipsLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpBlx:
+            opcode = kMipsJalr;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR2(cUnit, opcode, r_RA, rDestSrc);
+}
+
+static MipsLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value);
+static MipsLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value)
+{
+    MipsLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    bool shortForm = (absValue & 0xff) == absValue;
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpAdd:
+            return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
+            break;
+        case kOpSub:
+            return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    if (shortForm)
+        res = newLIR2(cUnit, opcode, rDestSrc1, absValue);
+    else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, rScratch, value);
+        if (op == kOpCmp)
+            newLIR2(cUnit, opcode, rDestSrc1, rScratch);
+        else
+            newLIR3(cUnit, opcode, rDestSrc1, rDestSrc1, rScratch);
+    }
+    return res;
+}
+
+static MipsLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2)
+{
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpAdd:
+            opcode = kMipsAddu;
+            break;
+        case kOpSub:
+            opcode = kMipsSubu;
+            break;
+        case kOpAnd:
+            opcode = kMipsAnd;
+            break;
+        case kOpMul:
+            opcode = kMipsMul;
+            break;
+        case kOpOr:
+            opcode = kMipsOr;
+            break;
+        case kOpXor:
+            opcode = kMipsXor;
+            break;
+        case kOpLsl:
+            opcode = kMipsSllv;
+            break;
+        case kOpLsr:
+            opcode = kMipsSrlv;
+            break;
+        case kOpAsr:
+            opcode = kMipsSrav;
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegRegReg");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    return newLIR3(cUnit, opcode, rDest, rSrc1, rSrc2);
+}
+
+static MipsLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value)
+{
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    bool shortForm = true;
+
+    switch(op) {
+        case kOpAdd:
+            if (IS_SIMM16(value)) {
+                opcode = kMipsAddiu;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsAddu;
+            }
+            break;
+        case kOpSub:
+            if (IS_SIMM16((-value))) {
+                value = -value;
+                opcode = kMipsAddiu;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsSubu;
+            }
+            break;
+        case kOpLsl:
+                assert(value >= 0 && value <= 31);
+                opcode = kMipsSll;
+                break;
+        case kOpLsr:
+                assert(value >= 0 && value <= 31);
+                opcode = kMipsSrl;
+                break;
+        case kOpAsr:
+                assert(value >= 0 && value <= 31);
+                opcode = kMipsSra;
+                break;
+        case kOpAnd:
+            if (IS_UIMM16((value))) {
+                opcode = kMipsAndi;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsAnd;
+            }
+            break;
+        case kOpOr:
+            if (IS_UIMM16((value))) {
+                opcode = kMipsOri;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsOr;
+            }
+            break;
+        case kOpXor:
+            if (IS_UIMM16((value))) {
+                opcode = kMipsXori;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsXor;
+            }
+            break;
+        case kOpMul:
+            shortForm = false;
+            opcode = kMipsMul;
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+
+    if (shortForm)
+        res = newLIR3(cUnit, opcode, rDest, rSrc1, value);
+    else {
+        if (rDest != rSrc1) {
+            res = loadConstant(cUnit, rDest, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rDest);
+        } else {
+            int rScratch = dvmCompilerAllocTemp(cUnit);
+            res = loadConstant(cUnit, rScratch, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rScratch);
+        }
+    }
+    return res;
+}
+
+static MipsLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2)
+{
+    MipsOpCode opcode = kMipsNop;
+    MipsLIR *res;
+    switch (op) {
+        case kOpMov:
+            opcode = kMipsMove;
+            break;
+        case kOpMvn:
+            return newLIR3(cUnit, kMipsNor, rDestSrc1, rSrc2, r_ZERO);
+        case kOpNeg:
+            return newLIR3(cUnit, kMipsSubu, rDestSrc1, r_ZERO, rSrc2);
+        case kOpAdd:
+        case kOpAnd:
+        case kOpMul:
+        case kOpOr:
+        case kOpSub:
+        case kOpXor:
+            return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
+        case kOp2Byte:
+#if __mips_isa_rev>=2
+            res = newLIR2(cUnit, kMipsSeb, rDestSrc1, rSrc2);
+#else
+            res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
+            opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
+#endif
+            return res;
+        case kOp2Short:
+#if __mips_isa_rev>=2
+            res = newLIR2(cUnit, kMipsSeh, rDestSrc1, rSrc2);
+#else
+            res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+            opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
+#endif
+            return res;
+        case kOp2Char:
+             return newLIR3(cUnit, kMipsAndi, rDestSrc1, rSrc2, 0xFFFF);
+        default:
+            ALOGE("Jit: bad case in opRegReg");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
+}
+
+static MipsLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+                                     int rDestHi, int valLo, int valHi)
+{
+    MipsLIR *res;
+    res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+    loadConstantNoClobber(cUnit, rDestHi, valHi);
+    return res;
+}
+
+/* Load value from base + scaled index. */
+static MipsLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size)
+{
+    MipsLIR *first = NULL;
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    int tReg = dvmCompilerAllocTemp(cUnit);
+
+#ifdef __mips_hard_float
+    if (FPREG(rDest)) {
+        assert(SINGLEREG(rDest));
+        assert((size == kWord) || (size == kSingle));
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+#endif
+
+    if (!scale) {
+        first = newLIR3(cUnit, kMipsAddu, tReg , rBase, rIndex);
+    } else {
+        first = opRegRegImm(cUnit, kOpLsl, tReg, rIndex, scale);
+        newLIR3(cUnit, kMipsAddu, tReg , rBase, tReg);
+    }
+
+    switch (size) {
+#ifdef __mips_hard_float
+        case kSingle:
+            opcode = kMipsFlwc1;
+            break;
+#endif
+        case kWord:
+            opcode = kMipsLw;
+            break;
+        case kUnsignedHalf:
+            opcode = kMipsLhu;
+            break;
+        case kSignedHalf:
+            opcode = kMipsLh;
+            break;
+        case kUnsignedByte:
+            opcode = kMipsLbu;
+            break;
+        case kSignedByte:
+            opcode = kMipsLb;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+
+    res = newLIR3(cUnit, opcode, rDest, 0, tReg);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    dvmCompilerFreeTemp(cUnit, tReg);
+    return (first) ? first : res;
+}
+
+/* store value base base + scaled index. */
+static MipsLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+                                int rIndex, int rSrc, int scale, OpSize size)
+{
+    MipsLIR *first = NULL;
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    int rNewIndex = rIndex;
+    int tReg = dvmCompilerAllocTemp(cUnit);
+
+#ifdef __mips_hard_float
+    if (FPREG(rSrc)) {
+        assert(SINGLEREG(rSrc));
+        assert((size == kWord) || (size == kSingle));
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+#endif
+
+    if (!scale) {
+        first = newLIR3(cUnit, kMipsAddu, tReg , rBase, rIndex);
+    } else {
+        first = opRegRegImm(cUnit, kOpLsl, tReg, rIndex, scale);
+        newLIR3(cUnit, kMipsAddu, tReg , rBase, tReg);
+    }
+
+    switch (size) {
+#ifdef __mips_hard_float
+        case kSingle:
+            opcode = kMipsFswc1;
+            break;
+#endif
+        case kWord:
+            opcode = kMipsSw;
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = kMipsSh;
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = kMipsSb;
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opcode, rSrc, 0, tReg);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    dvmCompilerFreeTemp(cUnit, rNewIndex);
+    return first;
+}
+
+static MipsLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    int i;
+    int loadCnt = 0;
+    MipsLIR *res = NULL ;
+    genBarrier(cUnit);
+
+    for (i = 0; i < 8; i++, rMask >>= 1) {
+        if (rMask & 0x1) { /* map r0 to MIPS r_A0 */
+            newLIR3(cUnit, kMipsLw, i+r_A0, loadCnt*4, rBase);
+            loadCnt++;
+        }
+    }
+
+    if (loadCnt) {/* increment after */
+        newLIR3(cUnit, kMipsAddiu, rBase, rBase, loadCnt*4);
+    }
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res; /* NULL always returned which should be ok since no callers use it */
+}
+
+static MipsLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    int i;
+    int storeCnt = 0;
+    MipsLIR *res = NULL ;
+    genBarrier(cUnit);
+
+    for (i = 0; i < 8; i++, rMask >>= 1) {
+        if (rMask & 0x1) { /* map r0 to MIPS r_A0 */
+            newLIR3(cUnit, kMipsSw, i+r_A0, storeCnt*4, rBase);
+            storeCnt++;
+        }
+    }
+
+    if (storeCnt) { /* increment after */
+        newLIR3(cUnit, kMipsAddiu, rBase, rBase, storeCnt*4);
+    }
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res; /* NULL always returned which should be ok since no callers use it */
+}
+
+static MipsLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDest, int rDestHi,
+                                OpSize size, int sReg)
+/*
+ * Load value from base + displacement.  Optionally perform null check
+ * on base (which must have an associated sReg and MIR).  If not
+ * performing null check, incoming MIR can be null. IMPORTANT: this
+ * code must not allocate any new temps.  If a new register is needed
+ * and base and dest are the same, spill some other register to
+ * rlp and then restore.
+ */
+{
+    MipsLIR *res;
+    MipsLIR *load = NULL;
+    MipsLIR *load2 = NULL;
+    MipsOpCode opcode = kMipsNop;
+    bool shortForm = IS_SIMM16(displacement);
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            opcode = kMipsLw;
+#ifdef __mips_hard_float
+            if (FPREG(rDest)) {
+                opcode = kMipsFlwc1;
+                if (DOUBLEREG(rDest)) {
+                    rDest = rDest - FP_DOUBLE;
+                } else {
+                    assert(FPREG(rDestHi));
+                    assert(rDest == (rDestHi - 1));
+                }
+                rDestHi = rDest + 1;
+            }
+#endif
+            shortForm = IS_SIMM16_2WORD(displacement);
+            assert((displacement & 0x3) == 0);
+            break;
+        case kWord:
+        case kSingle:
+            opcode = kMipsLw;
+#ifdef __mips_hard_float
+            if (FPREG(rDest)) {
+                opcode = kMipsFlwc1;
+                assert(SINGLEREG(rDest));
+            }
+#endif
+            assert((displacement & 0x3) == 0);
+            break;
+        case kUnsignedHalf:
+            opcode = kMipsLhu;
+            assert((displacement & 0x1) == 0);
+            break;
+        case kSignedHalf:
+            opcode = kMipsLh;
+            assert((displacement & 0x1) == 0);
+            break;
+        case kUnsignedByte:
+            opcode = kMipsLbu;
+            break;
+        case kSignedByte:
+            opcode = kMipsLb;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+
+    if (shortForm) {
+        if (!pair) {
+            load = res = newLIR3(cUnit, opcode, rDest, displacement, rBase);
+        } else {
+            load = res = newLIR3(cUnit, opcode, rDest, displacement + LOWORD_OFFSET, rBase);
+            load2 = newLIR3(cUnit, opcode, rDestHi, displacement + HIWORD_OFFSET, rBase);
+        }
+    } else {
+        if (pair) {
+            int rTmp = dvmCompilerAllocFreeTemp(cUnit);
+            res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
+            load = newLIR3(cUnit, opcode, rDest, LOWORD_OFFSET, rTmp);
+            load2 = newLIR3(cUnit, opcode, rDestHi, HIWORD_OFFSET, rTmp);
+            dvmCompilerFreeTemp(cUnit, rTmp);
+        } else {
+            int rTmp = (rBase == rDest) ? dvmCompilerAllocFreeTemp(cUnit)
+                                        : rDest;
+            res = loadConstant(cUnit, rTmp, displacement);
+            load = newLIR3(cUnit, opcode, rDest, rBase, rTmp);
+            if (rTmp != rDest)
+                dvmCompilerFreeTemp(cUnit, rTmp);
+        }
+    }
+
+    if (rBase == rFP) {
+        if (load != NULL)
+            annotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+                                    true /* isLoad */);
+        if (load2 != NULL)
+            annotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2,
+                                    true /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (load != NULL && cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+    if (load2 != NULL && cUnit->heapMemOp)
+        load2->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static MipsLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+                            size, sReg);
+}
+
+static MipsLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDestLo, int rDestHi,
+                                int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+                            kLong, sReg);
+}
+
+static MipsLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrc, int rSrcHi,
+                                 OpSize size)
+{
+    MipsLIR *res;
+    MipsLIR *store = NULL;
+    MipsLIR *store2 = NULL;
+    MipsOpCode opcode = kMipsNop;
+    bool shortForm = IS_SIMM16(displacement);
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            opcode = kMipsSw;
+#ifdef __mips_hard_float
+            if (FPREG(rSrc)) {
+                opcode = kMipsFswc1;
+                if (DOUBLEREG(rSrc)) {
+                    rSrc = rSrc - FP_DOUBLE;
+                } else {
+                    assert(FPREG(rSrcHi));
+                    assert(rSrc == (rSrcHi - 1));
+                }
+                rSrcHi = rSrc + 1;
+            }
+#endif
+            shortForm = IS_SIMM16_2WORD(displacement);
+            assert((displacement & 0x3) == 0);
+            break;
+        case kWord:
+        case kSingle:
+            opcode = kMipsSw;
+#ifdef __mips_hard_float
+            if (FPREG(rSrc)) {
+                opcode = kMipsFswc1;
+                assert(SINGLEREG(rSrc));
+            }
+#endif
+            assert((displacement & 0x3) == 0);
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = kMipsSh;
+            assert((displacement & 0x1) == 0);
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = kMipsSb;
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+
+    if (shortForm) {
+        if (!pair) {
+            store = res = newLIR3(cUnit, opcode, rSrc, displacement, rBase);
+        } else {
+            store = res = newLIR3(cUnit, opcode, rSrc, displacement + LOWORD_OFFSET, rBase);
+            store2 = newLIR3(cUnit, opcode, rSrcHi, displacement + HIWORD_OFFSET, rBase);
+        }
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
+        if (!pair) {
+            store =  newLIR3(cUnit, opcode, rSrc, 0, rScratch);
+        } else {
+            store =  newLIR3(cUnit, opcode, rSrc, LOWORD_OFFSET, rScratch);
+            store2 = newLIR3(cUnit, opcode, rSrcHi, HIWORD_OFFSET, rScratch);
+        }
+        dvmCompilerFreeTemp(cUnit, rScratch);
+    }
+
+    if (rBase == rFP) {
+        if (store != NULL)
+            annotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+                                    false /* isLoad */);
+        if (store2 != NULL)
+            annotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2,
+                                    false /* isLoad */);
+    }
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (store != NULL && cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+    if (store2 != NULL && cUnit->heapMemOp)
+        store2->flags.insertWrapper = true;
+#endif
+    return res;
+}
+
+static MipsLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static MipsLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    storeWordDisp(cUnit, base, LOWORD_OFFSET, lowReg);
+    storeWordDisp(cUnit, base, HIWORD_OFFSET, highReg);
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    loadWordDisp(cUnit, base, LOWORD_OFFSET , lowReg);
+    loadWordDisp(cUnit, base, HIWORD_OFFSET , highReg);
+}
+
+static MipsLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    MipsLIR* res;
+    MipsOpCode opcode;
+#ifdef __mips_hard_float
+    if (FPREG(rDest) || FPREG(rSrc))
+        return fpRegCopy(cUnit, rDest, rSrc);
+#endif
+    res = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    opcode = kMipsMove;
+    assert(LOWREG(rDest) && LOWREG(rSrc));
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    res->opcode = opcode;
+    setupResourceMasks(res);
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    }
+    return res;
+}
+
+static MipsLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    MipsLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+    dvmCompilerAppendLIR(cUnit, (LIR*)res);
+    return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                           int srcLo, int srcHi)
+{
+#ifdef __mips_hard_float
+    bool destFP = FPREG(destLo) && FPREG(destHi);
+    bool srcFP = FPREG(srcLo) && FPREG(srcHi);
+    assert(FPREG(srcLo) == FPREG(srcHi));
+    assert(FPREG(destLo) == FPREG(destHi));
+    if (destFP) {
+        if (srcFP) {
+            genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
+        } else {
+           /* note the operands are swapped for the mtc1 instr */
+            newLIR2(cUnit, kMipsMtc1, srcLo, destLo);
+            newLIR2(cUnit, kMipsMtc1, srcHi, destHi);
+        }
+    } else {
+        if (srcFP) {
+            newLIR2(cUnit, kMipsMfc1, destLo, srcLo);
+            newLIR2(cUnit, kMipsMfc1, destHi, srcHi);
+        } else {
+            // Handle overlap
+            if (srcHi == destLo) {
+                genRegCopy(cUnit, destHi, srcHi);
+                genRegCopy(cUnit, destLo, srcLo);
+            } else {
+                genRegCopy(cUnit, destLo, srcLo);
+                genRegCopy(cUnit, destHi, srcHi);
+            }
+        }
+    }
+#else
+    // Handle overlap
+    if (srcHi == destLo) {
+        genRegCopy(cUnit, destHi, srcHi);
+        genRegCopy(cUnit, destLo, srcLo);
+    } else {
+        genRegCopy(cUnit, destLo, srcLo);
+        genRegCopy(cUnit, destHi, srcHi);
+    }
+#endif
+}
+
+static inline MipsLIR *genRegImmCheck(CompilationUnit *cUnit,
+                                     MipsConditionCode cond, int reg,
+                                     int checkValue, int dOffset,
+                                     MipsLIR *pcrLabel)
+{
+    MipsLIR *branch = NULL;
+
+    if (checkValue == 0) {
+        MipsOpCode opc = kMipsNop;
+        if (cond == kMipsCondEq) {
+            opc = kMipsBeqz;
+	} else if (cond == kMipsCondNe) {
+            opc = kMipsBnez;
+        } else if (cond == kMipsCondLt || cond == kMipsCondMi) {
+            opc = kMipsBltz;
+        } else if (cond == kMipsCondLe) {
+            opc = kMipsBlez;
+        } else if (cond == kMipsCondGt) {
+            opc = kMipsBgtz;
+        } else if (cond == kMipsCondGe) {
+            opc = kMipsBgez;
+        } else {
+            ALOGE("Jit: bad case in genRegImmCheck");
+            dvmCompilerAbort(cUnit);
+        }
+        branch = opCompareBranch(cUnit, opc, reg, -1);
+    } else if (IS_SIMM16(checkValue)) {
+        if (cond == kMipsCondLt) {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            newLIR3(cUnit, kMipsSlti, tReg, reg, checkValue);
+            branch = opCompareBranch(cUnit, kMipsBne, tReg, r_ZERO);
+            dvmCompilerFreeTemp(cUnit, tReg);
+        } else {
+            ALOGE("Jit: bad case in genRegImmCheck");
+            dvmCompilerAbort(cUnit);
+        }
+    } else {
+        ALOGE("Jit: bad case in genRegImmCheck");
+        dvmCompilerAbort(cUnit);
+    }
+
+    if (cUnit->jitMode == kJitMethod) {
+        BasicBlock *bb = cUnit->curBlock;
+        if (bb->taken) {
+            MipsLIR  *exceptionLabel = (MipsLIR *) cUnit->blockLabelList;
+            exceptionLabel += bb->taken->id;
+            branch->generic.target = (LIR *) exceptionLabel;
+            return exceptionLabel;
+        } else {
+            ALOGE("Catch blocks not handled yet");
+            dvmAbort();
+            return NULL;
+        }
+    } else {
+        return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    }
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
+                                         MipsLIR *origLIR) {
+// DOUGLAS - this still needs to be implemented for MIPS.
+#if 0
+    /*
+     * We need two separate pushes, since we want r5 to be pushed first.
+     * Store multiple will push LR first.
+     */
+    MipsLIR *pushFP = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pushFP->opcode = kThumbPush;
+    pushFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(pushFP);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushFP);
+
+    MipsLIR *pushLR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pushLR->opcode = kThumbPush;
+    /* Thumb push can handle LR, but is encoded differently at bit 8 */
+    pushLR->operands[0] = 1 << 8;
+    setupResourceMasks(pushLR);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushLR);
+#endif
+}
+
+static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
+                                         MipsLIR *origLIR) {
+// DOUGLAS - this still needs to be implemented for MIPS.
+#if 0
+    /*
+     * Since Thumb cannot pop memory content into LR, we have to pop LR
+     * to a temp first (r5 in this case). Then we move r5 to LR, then pop the
+     * original r5 from stack.
+     */
+    /* Pop memory content(LR) into r5 first */
+    MipsLIR *popForLR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    popForLR->opcode = kThumbPop;
+    popForLR->operands[0] = 1 << r5FP;
+    setupResourceMasks(popForLR);
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) popForLR);
+
+    MipsLIR *copy = genRegCopyNoInsert(cUnit, r14lr, r5FP);
+    dvmCompilerInsertLIRAfter((LIR *) popForLR, (LIR *) copy);
+
+    /* Now restore the original r5 */
+    MipsLIR *popFP = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    popFP->opcode = kThumbPop;
+    popFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(popFP);
+    dvmCompilerInsertLIRAfter((LIR *) copy, (LIR *) popFP);
+#endif
+}
+#endif
diff --git a/vm/compiler/codegen/mips/Mips32/Gen.cpp b/vm/compiler/codegen/mips/Mips32/Gen.cpp
new file mode 100644
index 0000000..29c7c5f
--- /dev/null
+++ b/vm/compiler/codegen/mips/Mips32/Gen.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Mips ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Reserve 8 bytes at the beginning of the trace
+ *        +----------------------------+
+ *        | prof count addr (4 bytes)  |
+ *        +----------------------------+
+ *        | chain cell offset (4 bytes)|
+ *        +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (24 bytes)
+ *       lahi  a0, addr    # get ptr to prof count addr into a0
+ *       lalo  a0, addr
+ *       lw    a0, 0(a0)   # read prof count addr into a0
+ *       lw    a1, 0(a0)   # read prof count into a1
+ *       addiu a1, a1, 1   # increment count
+ *       sw    a1, 0(a0)   # store count
+ *
+ * For periodic profiling (8 bytes)
+ *       call  TEMPLATE_PERIODIC_PROFILING
+ *       nop
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+    assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+    MipsLIR *executionCount = newLIR1(cUnit, kMips32BitData, addr);
+    cUnit->chainCellOffsetLIR =
+        (LIR *) newLIR1(cUnit, kMips32BitData, CHAIN_CELL_OFFSET_TAG);
+    cUnit->headerSize = 8;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        MipsLIR *loadAddr = newLIR2(cUnit, kMipsLahi, r_A0, 0);
+        loadAddr->generic.target = (LIR *) executionCount;
+        loadAddr = newLIR3(cUnit, kMipsLalo, r_A0, r_A0, 0);
+        loadAddr ->generic.target = (LIR *) executionCount;
+        newLIR3(cUnit, kMipsLw, r_A0, 0, r_A0);
+        newLIR3(cUnit, kMipsLw, r_A1, 0, r_A0);
+        newLIR3(cUnit, kMipsAddiu, r_A1, r_A1, 1);
+        newLIR3(cUnit, kMipsSw, r_A1, 0, r_A0);
+        return 24;
+    } else {
+        int opcode = TEMPLATE_PERIODIC_PROFILING;
+        newLIR1(cUnit, kMipsJal,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        newLIR0(cUnit, kMipsNop); /* delay slot */
+        return 8;
+    }
+}
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+                        RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.lowReg,
+                rlSrc.lowReg, 0x80000000);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+                         RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg,
+                        0x80000000);
+    genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+    genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG);
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static bool partialOverlap(int sreg1, int sreg2)
+{
+    return abs(sreg1 - sreg2) == 1;
+}
+
+static void withCarryHelper(CompilationUnit *cUnit, MipsOpCode opc,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2, int sltuSrc1, int sltuSrc2)
+{
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    newLIR3(cUnit, opc, rlDest.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    newLIR3(cUnit, kMipsSltu, tReg, sltuSrc1, sltuSrc2);
+    newLIR3(cUnit, opc, rlDest.highReg, rlSrc1.highReg, rlSrc2.highReg);
+    newLIR3(cUnit, opc, rlDest.highReg, rlDest.highReg, tReg);
+    dvmCompilerFreeTemp(cUnit, tReg);
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+                         OpKind secondOp, RegLocation rlDest,
+                         RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    int carryOp = (secondOp == kOpAdc || secondOp == kOpSbc);
+
+    if (partialOverlap(rlSrc1.sRegLow,rlSrc2.sRegLow) ||
+        partialOverlap(rlSrc1.sRegLow,rlDest.sRegLow) ||
+        partialOverlap(rlSrc2.sRegLow,rlDest.sRegLow)) {
+        // Rare case - not enough registers to properly handle
+        genInterpSingleStep(cUnit, mir);
+    } else if (rlDest.sRegLow == rlSrc1.sRegLow) {
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        if (!carryOp) {
+            opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlResult.lowReg, rlSrc2.lowReg);
+            opRegRegReg(cUnit, secondOp, rlResult.highReg, rlResult.highReg, rlSrc2.highReg);
+        } else if (secondOp == kOpAdc) {
+            withCarryHelper(cUnit, kMipsAddu, rlResult, rlResult, rlSrc2,
+                            rlResult.lowReg, rlSrc2.lowReg);
+        } else {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            newLIR2(cUnit, kMipsMove, tReg, rlResult.lowReg);
+            withCarryHelper(cUnit, kMipsSubu, rlResult, rlResult, rlSrc2,
+                            tReg, rlResult.lowReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+        }
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (rlDest.sRegLow == rlSrc2.sRegLow) {
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+        if (!carryOp) {
+            opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlResult.lowReg);
+            opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg, rlResult.highReg);
+        } else if (secondOp == kOpAdc) {
+            withCarryHelper(cUnit, kMipsAddu, rlResult, rlSrc1, rlResult,
+                            rlResult.lowReg, rlSrc1.lowReg);
+        } else {
+            withCarryHelper(cUnit, kMipsSubu, rlResult, rlSrc1, rlResult,
+                            rlSrc1.lowReg, rlResult.lowReg);
+        }
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        if (!carryOp) {
+            opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+            opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg, rlSrc2.highReg);
+        } else if (secondOp == kOpAdc) {
+            withCarryHelper(cUnit, kMipsAddu, rlResult, rlSrc1, rlSrc2,
+                            rlResult.lowReg, rlSrc1.lowReg);
+        } else {
+            withCarryHelper(cUnit, kMipsSubu, rlResult, rlSrc1, rlSrc2,
+                            rlSrc1.lowReg, rlResult.lowReg);
+        }
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+    int numTemps = sizeof(coreTemps)/sizeof(int);
+    RegisterPool *pool = (RegisterPool *) dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps =
+            (RegisterInfo *) dvmCompilerNew(numTemps * sizeof(*pool->coreTemps), true);
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+#ifdef __mips_hard_float
+    int numFPTemps = sizeof(fpTemps)/sizeof(int);
+    pool->numFPTemps = numFPTemps;
+    pool->FPTemps =
+            (RegisterInfo *) dvmCompilerNew(numFPTemps * sizeof(*pool->FPTemps), true);
+    dvmCompilerInitPool(pool->FPTemps, fpTemps, pool->numFPTemps);
+#else
+    pool->numFPTemps = 0;
+    pool->FPTemps = NULL;
+    dvmCompilerInitPool(pool->FPTemps, NULL, 0);
+#endif
+    pool->nullCheckedRegs =
+        dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static MipsLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+    MipsLIR *res;
+    int rDPC = dvmCompilerAllocTemp(cUnit);
+    int rAddr = dvmCompilerAllocTemp(cUnit);
+    int offset = offsetof(StackSaveArea, xtra.currentPc);
+    res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+    newLIR3(cUnit, kMipsAddiu, rAddr, rFP, -(sizeof(StackSaveArea) - offset));
+    storeWordDisp( cUnit, rAddr, 0, rDPC);
+    return res;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+    genMonitorPortable(cUnit, mir);
+}
+
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+    genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
+    rlResult = dvmCompilerGetReturn(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    int reg0 = loadValue(cUnit, rlSrc, kCoreReg).lowReg;
+#if __mips_isa_rev>=2
+    newLIR4(cUnit, kMipsExt, reg0, reg0, 0, 31-1 /* size-1 */);
+#else
+    newLIR2(cUnit, kMipsSll, reg0, 1);
+    newLIR2(cUnit, kMipsSrl, reg0, 1);
+#endif
+    storeWordDisp(cUnit, rSELF, offset, reg0);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reg0);
+    return false;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation regSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    int reglo = regSrc.lowReg;
+    int reghi = regSrc.highReg;
+    storeWordDisp(cUnit, rSELF, offset + LOWORD_OFFSET, reglo);
+#if __mips_isa_rev>=2
+    newLIR4(cUnit, kMipsExt, reghi, reghi, 0, 31-1 /* size-1 */);
+#else
+    newLIR2(cUnit, kMipsSll, reghi, 1);
+    newLIR2(cUnit, kMipsSrl, reghi, 1);
+#endif
+    storeWordDisp(cUnit, rSELF, offset + HIWORD_OFFSET, reghi);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reghi);
+    return false;
+}
+
+/* No select in thumb, so we need to branch.  Thumb2 will do better */
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    int reg0 = loadValue(cUnit, rlSrc1, kCoreReg).lowReg;
+    int reg1 = loadValue(cUnit, rlSrc2, kCoreReg).lowReg;
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    if (isMin) {
+       newLIR3(cUnit, kMipsSlt, tReg, reg0, reg1);
+    }
+    else {
+       newLIR3(cUnit, kMipsSlt, tReg, reg1, reg0);
+    }
+    newLIR3(cUnit, kMipsMovz, reg0, reg1, tReg);
+    dvmCompilerFreeTemp(cUnit, tReg);
+    newLIR3(cUnit, kMipsSw, reg0, offset, rSELF);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit,reg0);
+    return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit,
+        int firstBit, int secondBit)
+{
+    // We can't implement "add src, src, src, lsl#shift" on Thumb, so we have
+    // to do a regular multiply.
+    opRegRegImm(cUnit, kOpMul, rlResult.lowReg, rlSrc.lowReg, lit);
+}
diff --git a/vm/compiler/codegen/mips/Mips32/Ralloc.cpp b/vm/compiler/codegen/mips/Mips32/Ralloc.cpp
new file mode 100644
index 0000000..6810131
--- /dev/null
+++ b/vm/compiler/codegen/mips/Mips32/Ralloc.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Mips ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Alloc a pair of core registers, or a double.  Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit, bool fpHint,
+                                  int regClass)
+{
+    int highReg;
+    int lowReg;
+    int res = 0;
+
+#ifdef __mips_hard_float
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg)) {
+        lowReg = dvmCompilerAllocTempDouble(cUnit);
+        highReg = lowReg + 1;
+        res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+        return res;
+    }
+#endif
+
+    lowReg = dvmCompilerAllocTemp(cUnit);
+    highReg = dvmCompilerAllocTemp(cUnit);
+    res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+    return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint, int regClass)
+{
+#ifdef __mips_hard_float
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg))
+{
+        return dvmCompilerAllocTempFloat(cUnit);
+}
+#endif
+    return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/mips/MipsLIR.h b/vm/compiler/codegen/mips/MipsLIR.h
new file mode 100644
index 0000000..fc82da2
--- /dev/null
+++ b/vm/compiler/codegen/mips/MipsLIR.h
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_MIPS_MIPSLIR_H_
+#define DALVIK_VM_COMPILER_CODEGEN_MIPS_MIPSLIR_H_
+
+#include "Dalvik.h"
+#include "compiler/CompilerInternals.h"
+
+/*
+ * zero is always the value 0
+ * at is scratch for Jit (normally used as temp reg by assembler)
+ * v0, v1 are scratch for Jit (normally hold subroutine return values)
+ * a0-a3 are scratch for Jit (normally hold subroutine arguments)
+ * t0-t7 are scratch for Jit
+ * t8 is scratch for Jit
+ * t9 is scratch for Jit (normally used for function calls)
+ * s0 (rFP) is reserved [holds Dalvik frame pointer]
+ * s1 (rSELF) is reserved [holds current &Thread]
+ * s2 (rINST) is scratch for Jit
+ * s3 (rIBASE) is scratch for Jit
+ * s4-s7 are scratch for Jit
+ * k0, k1 are reserved for use by interrupt handlers
+ * gp is reserved for global pointer
+ * sp is reserved
+ * s8 is scratch for Jit
+ * ra is scratch for Jit (normally holds the return addr)
+ *
+ * Preserved across C calls: s0-s8
+ * Trashed across C calls: at, v0-v1, a0-a3, t0-t9, gp, ra
+ *
+ * Floating pointer registers
+ * NOTE: there are 32 fp registers (16 df pairs), but current Jit code
+ *       only support 16 fp registers (8 df pairs).
+ * f0-f15
+ * df0-df7, where df0={f0,f1}, df1={f2,f3}, ... , df7={f14,f15}
+ *
+ * f0-f15 (df0-df7) trashed across C calls
+ *
+ * For mips32 code use:
+ *      a0-a3 to hold operands
+ *      v0-v1 to hold results
+ *      t0-t9 for temps
+ *
+ * All jump/branch instructions have a delay slot after it.
+ *
+ */
+
+/* Offset to distingish FP regs */
+#define FP_REG_OFFSET 32
+/* Offset to distinguish DP FP regs */
+#define FP_DOUBLE 64
+/* Offset to distingish the extra regs */
+#define EXTRA_REG_OFFSET 128
+/* Reg types */
+#define REGTYPE(x) (x & (FP_REG_OFFSET | FP_DOUBLE))
+#define FPREG(x) ((x & FP_REG_OFFSET) == FP_REG_OFFSET)
+#define EXTRAREG(x) ((x & EXTRA_REG_OFFSET) == EXTRA_REG_OFFSET)
+#define LOWREG(x) ((x & 0x1f) == x)
+#define DOUBLEREG(x) ((x & FP_DOUBLE) == FP_DOUBLE)
+#define SINGLEREG(x) (FPREG(x) && !DOUBLEREG(x))
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area.  Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define S2D(x,y) ((x) | FP_DOUBLE)
+/* Mask to strip off fp flags */
+#define FP_REG_MASK (FP_REG_OFFSET-1)
+/* non-existent Dalvik register */
+#define vNone   (-1)
+/* non-existant physical register */
+#define rNone   (-1)
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define LOWORD_OFFSET 0
+#define HIWORD_OFFSET 4
+#define r_ARG0 r_A0
+#define r_ARG1 r_A1
+#define r_ARG2 r_A2
+#define r_ARG3 r_A3
+#define r_RESULT0 r_V0
+#define r_RESULT1 r_V1
+#else
+#define LOWORD_OFFSET 4
+#define HIWORD_OFFSET 0
+#define r_ARG0 r_A1
+#define r_ARG1 r_A0
+#define r_ARG2 r_A3
+#define r_ARG3 r_A2
+#define r_RESULT0 r_V1
+#define r_RESULT1 r_V0
+#endif
+
+/* These are the same for both big and little endian. */
+#define r_FARG0 r_F12
+#define r_FARG1 r_F13
+#define r_FRESULT0 r_F0
+#define r_FRESULT1 r_F1
+
+/* RegisterLocation templates return values (r_V0, or r_V0/r_V1) */
+#define LOC_C_RETURN {kLocPhysReg, 0, 0, r_V0, 0, -1}
+#define LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, r_RESULT0, r_RESULT1, -1}
+#define LOC_C_RETURN_ALT {kLocPhysReg, 0, 1, r_F0, 0, -1}
+#define LOC_C_RETURN_WIDE_ALT {kLocPhysReg, 1, 1, r_FRESULT0, r_FRESULT1, -1}
+/* RegisterLocation templates for interpState->retVal; */
+#define LOC_DALVIK_RETURN_VAL {kLocRetval, 0, 0, 0, 0, -1}
+#define LOC_DALVIK_RETURN_VAL_WIDE {kLocRetval, 1, 0, 0, 0, -1}
+
+ /*
+ * Data structure tracking the mapping between a Dalvik register (pair) and a
+ * native register (pair). The idea is to reuse the previously loaded value
+ * if possible, otherwise to keep the value in a native register as long as
+ * possible.
+ */
+typedef struct RegisterInfo {
+    int reg;                    // Reg number
+    bool inUse;                 // Has it been allocated?
+    bool pair;                  // Part of a register pair?
+    int partner;                // If pair, other reg of pair
+    bool live;                  // Is there an associated SSA name?
+    bool dirty;                 // If live, is it dirty?
+    int sReg;                   // Name of live value
+    struct LIR *defStart;       // Starting inst in last def sequence
+    struct LIR *defEnd;         // Ending inst in last def sequence
+} RegisterInfo;
+
+typedef struct RegisterPool {
+    BitVector *nullCheckedRegs; // Track which registers have been null-checked
+    int numCoreTemps;
+    RegisterInfo *coreTemps;
+    int nextCoreTemp;
+    int numFPTemps;
+    RegisterInfo *FPTemps;
+    int nextFPTemp;
+} RegisterPool;
+
+typedef enum ResourceEncodingPos {
+    kGPReg0     = 0,
+    kRegSP      = 29,
+    kRegLR      = 31,
+    kFPReg0     = 32, /* only 16 fp regs supported currently */
+    kFPRegEnd   = 48,
+    kRegHI      = kFPRegEnd,
+    kRegLO,
+    kRegPC,
+    kRegEnd     = 51,
+    kCCode      = kRegEnd,
+    kFPStatus,          // FP status word
+    // The following four bits are for memory disambiguation
+    kDalvikReg,         // 1 Dalvik Frame (can be fully disambiguated)
+    kLiteral,           // 2 Literal pool (can be fully disambiguated)
+    kHeapRef,           // 3 Somewhere on the heap (alias with any other heap)
+    kMustNotAlias,      // 4 Guaranteed to be non-alias (eg *(r6+x))
+} ResourceEncodingPos;
+
+#define ENCODE_REG_LIST(N)      ((u8) N)
+#define ENCODE_REG_SP           (1ULL << kRegSP)
+#define ENCODE_REG_LR           (1ULL << kRegLR)
+#define ENCODE_REG_PC           (1ULL << kRegPC)
+#define ENCODE_CCODE            (1ULL << kCCode)
+#define ENCODE_FP_STATUS        (1ULL << kFPStatus)
+
+/* Abstract memory locations */
+#define ENCODE_DALVIK_REG       (1ULL << kDalvikReg)
+#define ENCODE_LITERAL          (1ULL << kLiteral)
+#define ENCODE_HEAP_REF         (1ULL << kHeapRef)
+#define ENCODE_MUST_NOT_ALIAS   (1ULL << kMustNotAlias)
+
+#define ENCODE_ALL              (~0ULL)
+#define ENCODE_MEM              (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
+                                 ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
+
+#define DECODE_ALIAS_INFO_REG(X)        (X & 0xffff)
+#define DECODE_ALIAS_INFO_WIDE(X)       ((X & 0x80000000) ? 1 : 0)
+
+typedef enum OpSize {
+    kWord,
+    kLong,
+    kSingle,
+    kDouble,
+    kUnsignedHalf,
+    kSignedHalf,
+    kUnsignedByte,
+    kSignedByte,
+} OpSize;
+
+typedef enum OpKind {
+    kOpMov,
+    kOpMvn,
+    kOpCmp,
+    kOpLsl,
+    kOpLsr,
+    kOpAsr,
+    kOpRor,
+    kOpNot,
+    kOpAnd,
+    kOpOr,
+    kOpXor,
+    kOpNeg,
+    kOpAdd,
+    kOpAdc,
+    kOpSub,
+    kOpSbc,
+    kOpRsub,
+    kOpMul,
+    kOpDiv,
+    kOpRem,
+    kOpBic,
+    kOpCmn,
+    kOpTst,
+    kOpBkpt,
+    kOpBlx,
+    kOpPush,
+    kOpPop,
+    kOp2Char,
+    kOp2Short,
+    kOp2Byte,
+    kOpCondBr,
+    kOpUncondBr,
+} OpKind;
+
+/*
+ * Annotate special-purpose core registers:
+ *
+ * rPC, rFP, and rSELF are for architecture-independent code to use.
+ */
+typedef enum NativeRegisterPool {
+    r_ZERO = 0,
+    r_AT = 1,
+    r_V0 = 2,
+    r_V1 = 3,
+    r_A0 = 4,
+    r_A1 = 5,
+    r_A2 = 6,
+    r_A3 = 7,
+    r_T0 = 8,
+    r_T1 = 9,
+    r_T2 = 10,
+    r_T3 = 11,
+    r_T4 = 12,
+    r_T5 = 13,
+    r_T6 = 14,
+    r_T7 = 15,
+    r_S0 = 16,
+    r_S1 = 17,
+    r_S2 = 18,
+    r_S3 = 19,
+    r_S4 = 20,
+    r_S5 = 21,
+    r_S6 = 22,
+    r_S7 = 23,
+    r_T8 = 24,
+    r_T9 = 25,
+    r_K0 = 26,
+    r_K1 = 27,
+    r_GP = 28,
+    r_SP = 29,
+    r_FP = 30,
+    r_RA = 31,
+
+    r_F0 = 0 + FP_REG_OFFSET,
+    r_F1,
+    r_F2,
+    r_F3,
+    r_F4,
+    r_F5,
+    r_F6,
+    r_F7,
+    r_F8,
+    r_F9,
+    r_F10,
+    r_F11,
+    r_F12,
+    r_F13,
+    r_F14,
+    r_F15,
+#if 0 /* only 16 fp regs supported currently */
+    r_F16,
+    r_F17,
+    r_F18,
+    r_F19,
+    r_F20,
+    r_F21,
+    r_F22,
+    r_F23,
+    r_F24,
+    r_F25,
+    r_F26,
+    r_F27,
+    r_F28,
+    r_F29,
+    r_F30,
+    r_F31,
+#endif
+    r_DF0 = r_F0 + FP_DOUBLE,
+    r_DF1 = r_F2 + FP_DOUBLE,
+    r_DF2 = r_F4 + FP_DOUBLE,
+    r_DF3 = r_F6 + FP_DOUBLE,
+    r_DF4 = r_F8 + FP_DOUBLE,
+    r_DF5 = r_F10 + FP_DOUBLE,
+    r_DF6 = r_F12 + FP_DOUBLE,
+    r_DF7 = r_F14 + FP_DOUBLE,
+#if 0 /* only 16 fp regs supported currently */
+    r_DF8 = r_F16 + FP_DOUBLE,
+    r_DF9 = r_F18 + FP_DOUBLE,
+    r_DF10 = r_F20 + FP_DOUBLE,
+    r_DF11 = r_F22 + FP_DOUBLE,
+    r_DF12 = r_F24 + FP_DOUBLE,
+    r_DF13 = r_F26 + FP_DOUBLE,
+    r_DF14 = r_F28 + FP_DOUBLE,
+    r_DF15 = r_F30 + FP_DOUBLE,
+#endif
+    r_HI = EXTRA_REG_OFFSET,
+    r_LO,
+    r_PC,
+} NativeRegisterPool;
+
+
+/* must match gp offset used mterp/mips files */
+#define STACK_OFFSET_GP 84
+
+/* MIPSTODO: properly remap arm regs (dPC, dFP, dGLUE) and remove these mappings */
+#define r4PC r_S0
+#define rFP r_S1
+#define rSELF r_S2
+#define rINST r_S4
+
+/* Shift encodings */
+typedef enum MipsShiftEncodings {
+    kMipsLsl = 0x0,
+    kMipsLsr = 0x1,
+    kMipsAsr = 0x2,
+    kMipsRor = 0x3
+} MipsShiftEncodings;
+
+/* condition encodings */
+typedef enum MipsConditionCode {
+    kMipsCondEq = 0x0,    /* 0000 */
+    kMipsCondNe = 0x1,    /* 0001 */
+    kMipsCondCs = 0x2,    /* 0010 */
+    kMipsCondCc = 0x3,    /* 0011 */
+    kMipsCondMi = 0x4,    /* 0100 */
+    kMipsCondPl = 0x5,    /* 0101 */
+    kMipsCondVs = 0x6,    /* 0110 */
+    kMipsCondVc = 0x7,    /* 0111 */
+    kMipsCondHi = 0x8,    /* 1000 */
+    kMipsCondLs = 0x9,    /* 1001 */
+    kMipsCondGe = 0xa,    /* 1010 */
+    kMipsCondLt = 0xb,    /* 1011 */
+    kMipsCondGt = 0xc,    /* 1100 */
+    kMipsCondLe = 0xd,    /* 1101 */
+    kMipsCondAl = 0xe,    /* 1110 */
+    kMipsCondNv = 0xf,    /* 1111 */
+} MipsConditionCode;
+
+#define isPseudoOpCode(opCode) ((int)(opCode) < 0)
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding snippet positions will be defined in
+ * Assemble.c.
+ */
+typedef enum MipsOpCode {
+    kMipsChainingCellBottom = -18,
+    kMipsPseudoBarrier = -17,
+    kMipsPseudoExtended = -16,
+    kMipsPseudoSSARep = -15,
+    kMipsPseudoEntryBlock = -14,
+    kMipsPseudoExitBlock = -13,
+    kMipsPseudoTargetLabel = -12,
+    kMipsPseudoChainingCellBackwardBranch = -11,
+    kMipsPseudoChainingCellHot = -10,
+    kMipsPseudoChainingCellInvokePredicted = -9,
+    kMipsPseudoChainingCellInvokeSingleton = -8,
+    kMipsPseudoChainingCellNormal = -7,
+    kMipsPseudoDalvikByteCodeBoundary = -6,
+    kMipsPseudoPseudoAlign4 = -5,
+    kMipsPseudoPCReconstructionCell = -4,
+    kMipsPseudoPCReconstructionBlockLabel = -3,
+    kMipsPseudoEHBlockLabel = -2,
+    kMipsPseudoNormalBlockLabel = -1,
+
+    kMipsFirst,
+    kMips32BitData = kMipsFirst, /* data [31..0] */
+    kMipsAddiu,   /* addiu t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] */
+    kMipsAddu,    /* add d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100001] */
+    kMipsAnd,     /* and d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100100] */
+    kMipsAndi,    /* andi t,s,imm16 [001100] s[25..21] t[20..16] imm16[15..0] */
+    kMipsB,       /* b o   [0001000000000000] o[15..0] */
+    kMipsBal,     /* bal o [0000010000010001] o[15..0] */
+    /* NOTE: the code tests the range kMipsBeq thru kMipsBne, so
+             adding an instruction in this range may require updates */
+    kMipsBeq,     /* beq s,t,o [000100] s[25..21] t[20..16] o[15..0] */
+    kMipsBeqz,    /* beqz s,o [000100] s[25..21] [00000] o[15..0] */
+    kMipsBgez,    /* bgez s,o [000001] s[25..21] [00001] o[15..0] */
+    kMipsBgtz,    /* bgtz s,o [000111] s[25..21] [00000] o[15..0] */
+    kMipsBlez,    /* blez s,o [000110] s[25..21] [00000] o[15..0] */
+    kMipsBltz,    /* bltz s,o [000001] s[25..21] [00000] o[15..0] */
+    kMipsBnez,    /* bnez s,o [000101] s[25..21] [00000] o[15..0] */
+    kMipsBne,     /* bne s,t,o [000101] s[25..21] t[20..16] o[15..0] */
+    kMipsDiv,     /* div s,t [000000] s[25..21] t[20..16] [0000000000011010] */
+#if __mips_isa_rev>=2
+    kMipsExt,     /* ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000] */
+#endif
+    kMipsJal,     /* jal t [000011] t[25..0] */
+    kMipsJalr,    /* jalr d,s [000000] s[25..21] [00000] d[15..11]
+                                  hint[10..6] [001001] */
+    kMipsJr,      /* jr s [000000] s[25..21] [0000000000] hint[10..6] [001000] */
+    kMipsLahi,    /* lui t,imm16 [00111100000] t[20..16] imm16[15..0] load addr hi */
+    kMipsLalo,    /* ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] load addr lo */
+    kMipsLui,     /* lui t,imm16 [00111100000] t[20..16] imm16[15..0] */
+    kMipsLb,      /* lb t,o(b) [100000] b[25..21] t[20..16] o[15..0] */
+    kMipsLbu,     /* lbu t,o(b) [100100] b[25..21] t[20..16] o[15..0] */
+    kMipsLh,      /* lh t,o(b) [100001] b[25..21] t[20..16] o[15..0] */
+    kMipsLhu,     /* lhu t,o(b) [100101] b[25..21] t[20..16] o[15..0] */
+    kMipsLw,      /* lw t,o(b) [100011] b[25..21] t[20..16] o[15..0] */
+    kMipsMfhi,    /* mfhi d [0000000000000000] d[15..11] [00000010000] */
+    kMipsMflo,    /* mflo d [0000000000000000] d[15..11] [00000010010] */
+    kMipsMove,    /* move d,s [000000] s[25..21] [00000] d[15..11] [00000100101] */
+    kMipsMovz,    /* movz d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000001010] */
+    kMipsMul,     /* mul d,s,t [011100] s[25..21] t[20..16] d[15..11] [00000000010] */
+    kMipsNop,     /* nop [00000000000000000000000000000000] */
+    kMipsNor,     /* nor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100111] */
+    kMipsOr,      /* or d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100101] */
+    kMipsOri,     /* ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] */
+    kMipsPref,    /* pref h,o(b) [101011] b[25..21] h[20..16] o[15..0] */
+    kMipsSb,      /* sb t,o(b) [101000] b[25..21] t[20..16] o[15..0] */
+#if __mips_isa_rev>=2
+    kMipsSeb,     /* seb d,t [01111100000] t[20..16] d[15..11] [10000100000] */
+    kMipsSeh,     /* seh d,t [01111100000] t[20..16] d[15..11] [11000100000] */
+#endif
+    kMipsSh,      /* sh t,o(b) [101001] b[25..21] t[20..16] o[15..0] */
+    kMipsSll,     /* sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000] */
+    kMipsSllv,    /* sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100] */
+    kMipsSlt,     /* slt d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101010] */
+    kMipsSlti,    /* slti t,s,imm16 [001010] s[25..21] t[20..16] imm16[15..0] */
+    kMipsSltu,    /* sltu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101011] */
+    kMipsSra,     /* sra d,s,imm5 [00000000000] t[20..16] d[15..11] imm5[10..6] [000011] */
+    kMipsSrav,    /* srav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000111] */
+    kMipsSrl,     /* srl d,t,a [00000000000] t[20..16] d[20..16] a[10..6] [000010] */
+    kMipsSrlv,    /* srlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000110] */
+    kMipsSubu,    /* subu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100011] */
+    kMipsSw,      /* sw t,o(b) [101011] b[25..21] t[20..16] o[15..0] */
+    kMipsXor,     /* xor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100110] */
+    kMipsXori,    /* xori t,s,imm16 [001110] s[25..21] t[20..16] imm16[15..0] */
+#ifdef __mips_hard_float
+    kMipsFadds,   /* add.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000000] */
+    kMipsFsubs,   /* sub.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000001] */
+    kMipsFmuls,   /* mul.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000010] */
+    kMipsFdivs,   /* div.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000011] */
+    kMipsFaddd,   /* add.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000000] */
+    kMipsFsubd,   /* sub.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000001] */
+    kMipsFmuld,   /* mul.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000010] */
+    kMipsFdivd,   /* div.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000011] */
+    kMipsFcvtsd,  /* cvt.s.d d,s [01000110001] [00000] s[15..11] d[10..6] [100000] */
+    kMipsFcvtsw,  /* cvt.s.w d,s [01000110100] [00000] s[15..11] d[10..6] [100000] */
+    kMipsFcvtds,  /* cvt.d.s d,s [01000110000] [00000] s[15..11] d[10..6] [100001] */
+    kMipsFcvtdw,  /* cvt.d.w d,s [01000110100] [00000] s[15..11] d[10..6] [100001] */
+    kMipsFcvtws,  /* cvt.w.d d,s [01000110000] [00000] s[15..11] d[10..6] [100100] */
+    kMipsFcvtwd,  /* cvt.w.d d,s [01000110001] [00000] s[15..11] d[10..6] [100100] */
+    kMipsFmovs,   /* mov.s d,s [01000110000] [00000] s[15..11] d[10..6] [000110] */
+    kMipsFmovd,   /* mov.d d,s [01000110001] [00000] s[15..11] d[10..6] [000110] */
+    kMipsFlwc1,   /* lwc1 t,o(b) [110001] b[25..21] t[20..16] o[15..0] */
+    kMipsFldc1,   /* ldc1 t,o(b) [110101] b[25..21] t[20..16] o[15..0] */
+    kMipsFswc1,   /* swc1 t,o(b) [111001] b[25..21] t[20..16] o[15..0] */
+    kMipsFsdc1,   /* sdc1 t,o(b) [111101] b[25..21] t[20..16] o[15..0] */
+    kMipsMfc1,    /* mfc1 t,s [01000100000] t[20..16] s[15..11] [00000000000] */
+    kMipsMtc1,    /* mtc1 t,s [01000100100] t[20..16] s[15..11] [00000000000] */
+#endif
+    kMipsUndefined,  /* undefined [011001xxxxxxxxxxxxxxxx] */
+    kMipsLast
+} MipsOpCode;
+
+/* Bit flags describing the behavior of each native opcode */
+typedef enum MipsOpFeatureFlags {
+    kIsBranch = 0,
+    kRegDef0,
+    kRegDef1,
+    kRegDefSP,
+    kRegDefLR,
+    kRegDefList0,
+    kRegDefList1,
+    kRegUse0,
+    kRegUse1,
+    kRegUse2,
+    kRegUse3,
+    kRegUseSP,
+    kRegUsePC,
+    kRegUseList0,
+    kRegUseList1,
+    kNoOperand,
+    kIsUnaryOp,
+    kIsBinaryOp,
+    kIsTertiaryOp,
+    kIsQuadOp,
+    kIsIT,
+    kSetsCCodes,
+    kUsesCCodes,
+    kMemLoad,
+    kMemStore,
+} MipsOpFeatureFlags;
+
+#define IS_LOAD         (1 << kMemLoad)
+#define IS_STORE        (1 << kMemStore)
+#define IS_BRANCH       (1 << kIsBranch)
+#define REG_DEF0        (1 << kRegDef0)
+#define REG_DEF1        (1 << kRegDef1)
+#define REG_DEF_SP      (1 << kRegDefSP)
+#define REG_DEF_LR      (1 << kRegDefLR)
+#define REG_DEF_LIST0   (1 << kRegDefList0)
+#define REG_DEF_LIST1   (1 << kRegDefList1)
+#define REG_USE0        (1 << kRegUse0)
+#define REG_USE1        (1 << kRegUse1)
+#define REG_USE2        (1 << kRegUse2)
+#define REG_USE3        (1 << kRegUse3)
+#define REG_USE_SP      (1 << kRegUseSP)
+#define REG_USE_PC      (1 << kRegUsePC)
+#define REG_USE_LIST0   (1 << kRegUseList0)
+#define REG_USE_LIST1   (1 << kRegUseList1)
+#define NO_OPERAND      (1 << kNoOperand)
+#define IS_UNARY_OP     (1 << kIsUnaryOp)
+#define IS_BINARY_OP    (1 << kIsBinaryOp)
+#define IS_TERTIARY_OP  (1 << kIsTertiaryOp)
+#define IS_QUAD_OP      (1 << kIsQuadOp)
+#define IS_IT           (1 << kIsIT)
+#define SETS_CCODES     (1 << kSetsCCodes)
+#define USES_CCODES     (1 << kUsesCCodes)
+
+/* Common combo register usage patterns */
+#define REG_USE01       (REG_USE0 | REG_USE1)
+#define REG_USE02       (REG_USE0 | REG_USE2)
+#define REG_USE012      (REG_USE01 | REG_USE2)
+#define REG_USE12       (REG_USE1 | REG_USE2)
+#define REG_USE23       (REG_USE2 | REG_USE3)
+#define REG_DEF01       (REG_DEF0 | REG_DEF1)
+#define REG_DEF0_USE0   (REG_DEF0 | REG_USE0)
+#define REG_DEF0_USE1   (REG_DEF0 | REG_USE1)
+#define REG_DEF0_USE2   (REG_DEF0 | REG_USE2)
+#define REG_DEF0_USE01  (REG_DEF0 | REG_USE01)
+#define REG_DEF0_USE12  (REG_DEF0 | REG_USE12)
+#define REG_DEF01_USE2  (REG_DEF0 | REG_DEF1 | REG_USE2)
+
+/* Instruction assembly fieldLoc kind */
+typedef enum MipsEncodingKind {
+    kFmtUnused,
+    kFmtBitBlt,        /* Bit string using end/start */
+    kFmtDfp,           /* Double FP reg */
+    kFmtSfp,           /* Single FP reg */
+} MipsEncodingKind;
+
+/* Struct used to define the snippet positions for each Thumb opcode */
+typedef struct MipsEncodingMap {
+    u4 skeleton;
+    struct {
+        MipsEncodingKind kind;
+        int end;   /* end for kFmtBitBlt, 1-bit slice end for FP regs */
+        int start; /* start for kFmtBitBlt, 4-bit slice end for FP regs */
+    } fieldLoc[4];
+    MipsOpCode opcode;
+    int flags;
+    const char *name;
+    const char* fmt;
+    int size;
+} MipsEncodingMap;
+
+/* Keys for target-specific scheduling and other optimization hints */
+typedef enum MipsTargetOptHints {
+    kMaxHoistDistance,
+} MipsTargetOptHints;
+
+extern MipsEncodingMap EncodingMap[kMipsLast];
+
+/*
+ * Each instance of this struct holds a pseudo or real LIR instruction:
+ * - pseudo ones (eg labels and marks) and will be discarded by the assembler.
+ * - real ones will be assembled into Thumb instructions.
+ *
+ * Machine resources are encoded into a 64-bit vector, where the encodings are
+ * as following:
+ * - [ 0..15]: general purpose registers including PC, SP, and LR
+ * - [16..47]: floating-point registers where d0 is expanded to s[01] and s0
+ *   starts at bit 16
+ * - [48]: IT block
+ * - [49]: integer condition code
+ * - [50]: floatint-point status word
+ */
+typedef struct MipsLIR {
+    LIR generic;
+    MipsOpCode opcode;
+    int operands[4];            // [0..3] = [dest, src1, src2, extra]
+    struct {
+        bool isNop:1;           // LIR is optimized away
+        bool insertWrapper:1;   // insert branch to emulate memory accesses
+        unsigned int age:4;     // default is 0, set lazily by the optimizer
+        unsigned int size:3;    // bytes (2 for thumb, 2/4 for thumb2)
+        unsigned int unused:23;
+    } flags;
+    int aliasInfo;              // For Dalvik register access & litpool disambiguation
+    u8 useMask;                 // Resource mask for use
+    u8 defMask;                 // Resource mask for def
+} MipsLIR;
+
+/* Init values when a predicted chain is initially assembled */
+/* E7FE is branch to self */
+#define PREDICTED_CHAIN_BX_PAIR_INIT     0xe7fe
+#define PREDICTED_CHAIN_DELAY_SLOT_INIT  0
+#define PREDICTED_CHAIN_CLAZZ_INIT       0
+#define PREDICTED_CHAIN_METHOD_INIT      0
+#define PREDICTED_CHAIN_COUNTER_INIT     0
+
+/* Utility macros to traverse the LIR/MipsLIR list */
+#define NEXT_LIR(lir) ((MipsLIR *) lir->generic.next)
+#define PREV_LIR(lir) ((MipsLIR *) lir->generic.prev)
+
+#define NEXT_LIR_LVALUE(lir) (lir)->generic.next
+#define PREV_LIR_LVALUE(lir) (lir)->generic.prev
+
+#define CHAIN_CELL_OFFSET_TAG   0xcdabcdabL
+
+#define IS_UIMM16(v) ((0 <= (v)) && ((v) <= 65535))
+#define IS_SIMM16(v) ((-32768 <= (v)) && ((v) <= 32766))
+#define IS_SIMM16_2WORD(v) ((-32764 <= (v)) && ((v) <= 32763)) /* 2 offsets must fit */
+
+#define CHAIN_CELL_NORMAL_SIZE    16
+#define CHAIN_CELL_PREDICTED_SIZE 20
+
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_MIPS_MIPSLIR_H_
diff --git a/vm/compiler/codegen/mips/Ralloc.h b/vm/compiler/codegen/mips/Ralloc.h
new file mode 100644
index 0000000..33ad2fb
--- /dev/null
+++ b/vm/compiler/codegen/mips/Ralloc.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "compiler/codegen/mips/MipsLIR.h"
+
+/*
+ * Return most flexible allowed register class based on size.
+ * Bug: 2813841
+ * Must use a core register for data types narrower than word (due
+ * to possible unaligned load/store.
+ */
+static inline RegisterClass dvmCompilerRegClassBySize(OpSize size)
+{
+    return (size == kUnsignedHalf ||
+            size == kSignedHalf ||
+            size == kUnsignedByte ||
+            size == kSignedByte ) ? kCoreReg : kAnyReg;
+}
+
+static inline int dvmCompilerS2VReg(CompilationUnit *cUnit, int sReg)
+{
+    assert(sReg != INVALID_SREG);
+    return DECODE_REG(dvmConvertSSARegToDalvik(cUnit, sReg));
+}
+
+/* Reset the tracker to unknown state */
+static inline void dvmCompilerResetNullCheck(CompilationUnit *cUnit)
+{
+    dvmClearAllBits(cUnit->regPool->nullCheckedRegs);
+}
+
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+static inline int dvmCompilerSRegHi(int lowSreg) {
+    return (lowSreg == INVALID_SREG) ? INVALID_SREG : lowSreg + 1;
+}
+
+
+static inline bool dvmCompilerLiveOut(CompilationUnit *cUnit, int sReg)
+{
+    //TODO: fully implement
+    return true;
+}
+
+static inline int dvmCompilerSSASrc(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numUses > num);
+    return mir->ssaRep->uses[num];
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update);
+/* Mark a temp register as dead.  Does not affect allocation state. */
+extern void dvmCompilerClobber(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit,
+                                        RegLocation loc);
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc);
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg);
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg,
+                                int highReg);
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl);
+
+/* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num);
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                               LIR *start, LIR *finish);
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                                   LIR *start, LIR *finish);
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high);
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high);
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num);
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num);
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit);
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit);
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg);
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit);
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit);
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit);
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc);
+
+//FIXME - this needs to also check the preserved pool.
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg);
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit);
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit);
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg);
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit);
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl);
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit);
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
diff --git a/vm/compiler/codegen/mips/RallocUtil.cpp b/vm/compiler/codegen/mips/RallocUtil.cpp
new file mode 100644
index 0000000..b13dddf
--- /dev/null
+++ b/vm/compiler/codegen/mips/RallocUtil.cpp
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "MipsLIR.h"
+#include "Codegen.h"
+#include "Ralloc.h"
+
+#define SREG(c, s) ((c)->regLocation[(s)].sRegLow)
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i < cUnit->regPool->numCoreTemps; i++) {
+        cUnit->regPool->coreTemps[i].inUse = false;
+    }
+    for (i=0; i < cUnit->regPool->numFPTemps; i++) {
+        cUnit->regPool->FPTemps[i].inUse = false;
+    }
+}
+
+ /* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num)
+{
+    int i;
+    for (i=0; i < num; i++) {
+        regs[i].reg = regNums[i];
+        regs[i].inUse = false;
+        regs[i].pair = false;
+        regs[i].live = false;
+        regs[i].dirty = false;
+        regs[i].sReg = INVALID_SREG;
+    }
+}
+
+static void dumpRegPool(RegisterInfo *p, int numRegs)
+{
+    int i;
+    ALOGE("================================================");
+    for (i=0; i < numRegs; i++ ){
+        ALOGE("R[%d]: U:%d, P:%d, part:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+           p[i].reg, p[i].inUse, p[i].pair, p[i].partner, p[i].live,
+           p[i].dirty, p[i].sReg,(int)p[i].defStart, (int)p[i].defEnd);
+    }
+    ALOGE("================================================");
+}
+
+static RegisterInfo *getRegInfo(CompilationUnit *cUnit, int reg)
+{
+    int numTemps = cUnit->regPool->numCoreTemps;
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    ALOGE("Tried to get info on a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+    return NULL;
+}
+
+static void flushRegWide(CompilationUnit *cUnit, int reg1, int reg2)
+{
+    RegisterInfo *info1 = getRegInfo(cUnit, reg1);
+    RegisterInfo *info2 = getRegInfo(cUnit, reg2);
+    assert(info1 && info2 && info1->pair && info2->pair &&
+           (info1->partner == info2->reg) &&
+           (info2->partner == info1->reg));
+    if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+        info1->dirty = false;
+        info2->dirty = false;
+        if (dvmCompilerS2VReg(cUnit, info2->sReg) <
+            dvmCompilerS2VReg(cUnit, info1->sReg))
+            info1 = info2;
+        dvmCompilerFlushRegWideImpl(cUnit, rFP,
+                                    dvmCompilerS2VReg(cUnit, info1->sReg) << 2,
+                                    info1->reg, info1->partner);
+    }
+}
+
+static void flushReg(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if (info->live && info->dirty) {
+        info->dirty = false;
+        dvmCompilerFlushRegImpl(cUnit, rFP,
+                                dvmCompilerS2VReg(cUnit, info->sReg) << 2,
+                                reg, kWord);
+    }
+}
+
+/* return true if found reg to clobber */
+static bool clobberRegBody(CompilationUnit *cUnit, RegisterInfo *p,
+                           int numTemps, int reg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            if (p[i].live && p[i].dirty) {
+                if (p[i].pair) {
+                    flushRegWide(cUnit, p[i].reg, p[i].partner);
+                } else {
+                    flushReg(cUnit, p[i].reg);
+                }
+            }
+            p[i].live = false;
+            p[i].sReg = INVALID_SREG;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+            if (p[i].pair) {
+                p[i].pair = false;
+                /* partners should be in same pool */
+                clobberRegBody(cUnit, p, numTemps, p[i].partner);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Mark a temp register as dead.  Does not affect allocation state. */
+void dvmCompilerClobber(CompilationUnit *cUnit, int reg)
+{
+    if (!clobberRegBody(cUnit, cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps, reg)) {
+        clobberRegBody(cUnit, cUnit->regPool->FPTemps,
+                       cUnit->regPool->numFPTemps, reg);
+    }
+}
+
+static void clobberSRegBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].sReg == sReg) {
+            p[i].live = false;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+        }
+    }
+}
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg)
+{
+    clobberSRegBody(cUnit->regPool->coreTemps, cUnit->regPool->numCoreTemps,
+                    sReg);
+    clobberSRegBody(cUnit->regPool->FPTemps, cUnit->regPool->numFPTemps,
+                    sReg);
+}
+
+static int allocTempBody(CompilationUnit *cUnit, RegisterInfo *p, int numTemps,
+                         int *nextTemp, bool required)
+{
+    int i;
+    int next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next].live) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    if (required) {
+        ALOGE("No free temp registers");
+        dvmCompilerAbort(cUnit);
+    }
+    return -1;  // No register available
+}
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit)
+{
+    RegisterInfo *p = cUnit->regPool->FPTemps;
+    int numTemps = cUnit->regPool->numFPTemps;
+    /* Cleanup - not all targets need aligned regs */
+    int start = cUnit->regPool->nextFPTemp + (cUnit->regPool->nextFPTemp & 1);
+    int next = start;
+    int i;
+
+    for (i=0; i < numTemps; i+=2) {
+        if (next >= numTemps)
+            next = 0;
+        if ((!p[next].inUse && !p[next].live) &&
+            (!p[next+1].inUse && !p[next+1].live)) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    next = start;
+    for (i=0; i < numTemps; i+=2) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next+1].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    ALOGE("No free temp registers");
+    dvmCompilerAbort(cUnit);
+    return -1;
+}
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->FPTemps,
+                         cUnit->regPool->numFPTemps,
+                         &cUnit->regPool->nextFPTemp, true);
+}
+
+static RegisterInfo *allocLiveBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    if (sReg == -1)
+        return NULL;
+    for (i=0; i < numTemps; i++) {
+        if (p[i].live && (p[i].sReg == sReg)) {
+            p[i].inUse = true;
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+static RegisterInfo *allocLive(CompilationUnit *cUnit, int sReg,
+                               int regClass)
+{
+    RegisterInfo *res = NULL;
+    switch(regClass) {
+        case kAnyReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            if (res)
+                break;
+            /* Intentional fallthrough */
+        case kCoreReg:
+            res = allocLiveBody(cUnit->regPool->coreTemps,
+                                cUnit->regPool->numCoreTemps, sReg);
+            break;
+        case kFPReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            break;
+        default:
+            ALOGE("Invalid register type");
+            dvmCompilerAbort(cUnit);
+    }
+    return res;
+}
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    ALOGE("Tried to free a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+/*
+ * FIXME - this needs to also check the preserved pool once we start
+ * start using preserved registers.
+ */
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    return NULL;
+}
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    ALOGE("Tried to lock a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit)
+{
+    dvmCompilerClobber(cUnit, r_ZERO);
+    dvmCompilerClobber(cUnit, r_AT);
+    dvmCompilerClobber(cUnit, r_V0);
+    dvmCompilerClobber(cUnit, r_V1);
+    dvmCompilerClobber(cUnit, r_A0);
+    dvmCompilerClobber(cUnit, r_A1);
+    dvmCompilerClobber(cUnit, r_A2);
+    dvmCompilerClobber(cUnit, r_A3);
+    dvmCompilerClobber(cUnit, r_T0);
+    dvmCompilerClobber(cUnit, r_T1);
+    dvmCompilerClobber(cUnit, r_T2);
+    dvmCompilerClobber(cUnit, r_T3);
+    dvmCompilerClobber(cUnit, r_T4);
+    dvmCompilerClobber(cUnit, r_T5);
+    dvmCompilerClobber(cUnit, r_T6);
+    dvmCompilerClobber(cUnit, r_T7);
+    dvmCompilerClobber(cUnit, r_T8);
+    dvmCompilerClobber(cUnit, r_T9);
+    dvmCompilerClobber(cUnit, r_K0);
+    dvmCompilerClobber(cUnit, r_K1);
+    dvmCompilerClobber(cUnit, r_GP);
+    dvmCompilerClobber(cUnit, r_FP);
+    dvmCompilerClobber(cUnit, r_RA);
+    dvmCompilerClobber(cUnit, r_HI);
+    dvmCompilerClobber(cUnit, r_LO);
+    dvmCompilerClobber(cUnit, r_F0);
+    dvmCompilerClobber(cUnit, r_F1);
+    dvmCompilerClobber(cUnit, r_F2);
+    dvmCompilerClobber(cUnit, r_F3);
+    dvmCompilerClobber(cUnit, r_F4);
+    dvmCompilerClobber(cUnit, r_F5);
+    dvmCompilerClobber(cUnit, r_F6);
+    dvmCompilerClobber(cUnit, r_F7);
+    dvmCompilerClobber(cUnit, r_F8);
+    dvmCompilerClobber(cUnit, r_F9);
+    dvmCompilerClobber(cUnit, r_F10);
+    dvmCompilerClobber(cUnit, r_F11);
+    dvmCompilerClobber(cUnit, r_F12);
+    dvmCompilerClobber(cUnit, r_F13);
+    dvmCompilerClobber(cUnit, r_F14);
+    dvmCompilerClobber(cUnit, r_F15);
+}
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit)
+{
+    //TUNING: reduce the set of regs used by handlers.  Only a few need lots.
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r_S0);
+    dvmCompilerClobber(cUnit, r_S1);
+    dvmCompilerClobber(cUnit, r_S2);
+    dvmCompilerClobber(cUnit, r_S3);
+    dvmCompilerClobber(cUnit, r_S4);
+    dvmCompilerClobber(cUnit, r_S5);
+    dvmCompilerClobber(cUnit, r_S6);
+    dvmCompilerClobber(cUnit, r_S7);
+}
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = getRegInfo(cUnit, reg);
+    p->defStart = NULL;
+    p->defEnd = NULL;
+}
+
+static void nullifyRange(CompilationUnit *cUnit, LIR *start, LIR *finish,
+                         int sReg1, int sReg2)
+{
+    if (start && finish) {
+        LIR *p;
+        assert(sReg1 == sReg2);
+        for (p = start; ;p = p->next) {
+            ((MipsLIR *)p)->flags.isNop = true;
+            if (p == finish)
+                break;
+        }
+    }
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                    LIR *start, LIR *finish)
+{
+    assert(!rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                        LIR *start, LIR *finish)
+{
+    assert(rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);  // Only track low of pair
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl)
+{
+    assert(rl.wide);
+    if (rl.location == kLocPhysReg) {
+        RegisterInfo *infoLo = getRegInfo(cUnit, rl.lowReg);
+        RegisterInfo *infoHi = getRegInfo(cUnit, rl.highReg);
+        if (!infoLo->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoLo->pair);
+        }
+        if (!infoHi->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoHi->pair);
+        }
+        assert(infoLo->pair);
+        assert(infoHi->pair);
+        assert(infoLo->partner == infoHi->reg);
+        assert(infoHi->partner == infoLo->reg);
+        infoLo->pair = false;
+        infoHi->pair = false;
+        infoLo->defStart = NULL;
+        infoLo->defEnd = NULL;
+        infoHi->defStart = NULL;
+        infoHi->defEnd = NULL;
+    }
+#ifndef HAVE_LITTLE_ENDIAN
+    else if (rl.location == kLocDalvikFrame) {
+        rl.sRegLow = dvmCompilerSRegHi(rl.sRegLow);
+    }
+#endif
+
+    rl.wide = false;
+    return rl;
+}
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(!rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(!p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+}
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);
+}
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerLockTemp(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+}
+
+// Make sure nothing is live and dirty
+static void flushAllRegsBody(CompilationUnit *cUnit, RegisterInfo *info,
+                             int numRegs)
+{
+    int i;
+    for (i=0; i < numRegs; i++) {
+        if (info[i].live && info[i].dirty) {
+            if (info[i].pair) {
+                flushRegWide(cUnit, info[i].reg, info[i].partner);
+            } else {
+                flushReg(cUnit, info[i].reg);
+            }
+        }
+    }
+}
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit)
+{
+    flushAllRegsBody(cUnit, cUnit->regPool->coreTemps,
+                     cUnit->regPool->numCoreTemps);
+    flushAllRegsBody(cUnit, cUnit->regPool->FPTemps,
+                     cUnit->regPool->numFPTemps);
+    dvmCompilerClobberAllRegs(cUnit);
+}
+
+
+//TUNING: rewrite all of this reg stuff.  Probably use an attribute table
+static bool regClassMatches(int regClass, int reg)
+{
+    if (regClass == kAnyReg) {
+        return true;
+    } else if (regClass == kCoreReg) {
+        return !FPREG(reg);
+    } else {
+        return FPREG(reg);
+    }
+}
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if ((info->reg == reg) && (info->sReg == sReg) && info->live) {
+        return;  /* already live */
+    } else if (sReg != INVALID_SREG) {
+        dvmCompilerClobberSReg(cUnit, sReg);
+        info->live = true;
+    } else {
+        /* Can't be live if no associated sReg */
+        info->live = false;
+    }
+    info->sReg = sReg;
+}
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg, int highReg)
+{
+    RegisterInfo *infoLo = getRegInfo(cUnit, lowReg);
+    RegisterInfo *infoHi = getRegInfo(cUnit, highReg);
+    infoLo->pair = infoHi->pair = true;
+    infoLo->partner = highReg;
+    infoHi->partner = lowReg;
+}
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = false;
+}
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = true;
+}
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg)
+{
+      RegisterInfo *info = getRegInfo(cUnit, reg);
+          info->inUse = true;
+}
+
+void copyRegInfo(CompilationUnit *cUnit, int newReg, int oldReg)
+{
+    RegisterInfo *newInfo = getRegInfo(cUnit, newReg);
+    RegisterInfo *oldInfo = getRegInfo(cUnit, oldReg);
+    *newInfo = *oldInfo;
+    newInfo->reg = newReg;
+}
+
+/*
+ * Return an updated location record with current in-register status.
+ * If the value lives in live temps, reflect that fact.  No code
+ * is generated.  The the live value is part of an older pair,
+ * clobber both low and high.
+ * TUNING: clobbering both is a bit heavy-handed, but the alternative
+ * is a bit complex when dealing with FP regs.  Examine code to see
+ * if it's worthwhile trying to be more clever here.
+ */
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit, RegLocation loc)
+{
+    assert(!loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        if (infoLo) {
+            if (infoLo->pair) {
+                dvmCompilerClobber(cUnit, infoLo->reg);
+                dvmCompilerClobber(cUnit, infoLo->partner);
+            } else {
+                loc.lowReg = infoLo->reg;
+                loc.location = kLocPhysReg;
+            }
+        }
+    }
+
+    return loc;
+}
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc)
+{
+    assert(loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        // Are the dalvik regs already live in physical registers?
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        RegisterInfo *infoHi = allocLive(cUnit,
+              dvmCompilerSRegHi(loc.sRegLow), kAnyReg);
+        bool match = true;
+        match = match && (infoLo != NULL);
+        match = match && (infoHi != NULL);
+        // Are they both core or both FP?
+        match = match && (FPREG(infoLo->reg) == FPREG(infoHi->reg));
+        // If a pair of floating point singles, are they properly aligned?
+        if (match && FPREG(infoLo->reg)) {
+            match &= ((infoLo->reg & 0x1) == 0);
+            match &= ((infoHi->reg - infoLo->reg) == 1);
+        }
+        // If previously used as a pair, it is the same pair?
+        if (match && (infoLo->pair || infoHi->pair)) {
+            match = (infoLo->pair == infoHi->pair);
+            match &= ((infoLo->reg == infoHi->partner) &&
+                      (infoHi->reg == infoLo->partner));
+        }
+        if (match) {
+            // Can reuse - update the register usage info
+            loc.lowReg = infoLo->reg;
+            loc.highReg = infoHi->reg;
+            loc.location = kLocPhysReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+            return loc;
+        }
+        // Can't easily reuse - clobber any overlaps
+        if (infoLo) {
+            dvmCompilerClobber(cUnit, infoLo->reg);
+            if (infoLo->pair)
+                dvmCompilerClobber(cUnit, infoLo->partner);
+        }
+        if (infoHi) {
+            dvmCompilerClobber(cUnit, infoHi->reg);
+            if (infoHi->pair)
+                dvmCompilerClobber(cUnit, infoHi->partner);
+        }
+    }
+
+    return loc;
+}
+
+static RegLocation evalLocWide(CompilationUnit *cUnit, RegLocation loc,
+                               int regClass, bool update)
+{
+    assert(loc.wide);
+    int newRegs;
+    int lowReg;
+    int highReg;
+
+    loc = dvmCompilerUpdateLocWide(cUnit, loc);
+
+    /* If already in registers, we can assume proper form.  Right reg class? */
+    if (loc.location == kLocPhysReg) {
+        assert(FPREG(loc.lowReg) == FPREG(loc.highReg));
+        assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Reallocate and copy */
+            newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+            lowReg = newRegs & 0xff;
+            highReg = (newRegs >> 8) & 0xff;
+            dvmCompilerRegCopyWide(cUnit, lowReg, highReg, loc.lowReg,
+                                   loc.highReg);
+            copyRegInfo(cUnit, lowReg, loc.lowReg);
+            copyRegInfo(cUnit, highReg, loc.highReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.highReg);
+            loc.lowReg = lowReg;
+            loc.highReg = highReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+    assert((loc.location != kLocRetval) ||
+           (dvmCompilerSRegHi(loc.sRegLow) == INVALID_SREG));
+
+    newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+    loc.lowReg = newRegs & 0xff;
+    loc.highReg = (newRegs >> 8) & 0xff;
+
+    dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+        dvmCompilerMarkLive(cUnit, loc.highReg, dvmCompilerSRegHi(loc.sRegLow));
+    }
+    assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+    return loc;
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update)
+{
+    int newReg;
+    if (loc.wide)
+        return evalLocWide(cUnit, loc, regClass, update);
+    loc = dvmCompilerUpdateLoc(cUnit, loc);
+
+    if (loc.location == kLocPhysReg) {
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Realloc, copy and transfer ownership */
+            newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+            dvmCompilerRegCopy(cUnit, newReg, loc.lowReg);
+            copyRegInfo(cUnit, newReg, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            loc.lowReg = newReg;
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+
+    newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+    loc.lowReg = newReg;
+
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+    }
+    return loc;
+}
+
+static inline int getDestSSAName(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numDefs > num);
+    return mir->ssaRep->defs[num];
+}
+
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num)
+{
+    RegLocation loc = cUnit->regLocation[
+         SREG(cUnit, dvmCompilerSSASrc(mir, num))];
+    loc.fp = cUnit->regLocation[dvmCompilerSSASrc(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num)
+{
+    RegLocation loc = cUnit->regLocation[SREG(cUnit, getDestSSAName(mir, num))];
+    loc.fp = cUnit->regLocation[getDestSSAName(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+static RegLocation getLocWide(CompilationUnit *cUnit, MIR *mir,
+                              int low, int high, bool isSrc)
+{
+    RegLocation lowLoc;
+    RegLocation highLoc;
+    /* Copy loc record for low word and patch in data from high word */
+    if (isSrc) {
+        lowLoc = dvmCompilerGetSrc(cUnit, mir, low);
+        highLoc = dvmCompilerGetSrc(cUnit, mir, high);
+    } else {
+        lowLoc = dvmCompilerGetDest(cUnit, mir, low);
+        highLoc = dvmCompilerGetDest(cUnit, mir, high);
+    }
+    /* Avoid this case by either promoting both or neither. */
+    assert(lowLoc.location == highLoc.location);
+    if (lowLoc.location == kLocPhysReg) {
+        /* This case shouldn't happen if we've named correctly */
+        assert(lowLoc.fp == highLoc.fp);
+    }
+    lowLoc.wide = true;
+    lowLoc.highReg = highLoc.lowReg;
+    return lowLoc;
+}
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, false);
+}
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, true);
+}
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE;
+    dvmCompilerClobber(cUnit, r_V0);
+    dvmCompilerClobber(cUnit, r_V1);
+    dvmCompilerMarkInUse(cUnit, r_V0);
+    dvmCompilerMarkInUse(cUnit, r_V1);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN;
+    dvmCompilerClobber(cUnit, r_V0);
+    dvmCompilerMarkInUse(cUnit, r_V0);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE_ALT;
+    dvmCompilerClobber(cUnit, r_F0);
+    dvmCompilerClobber(cUnit, r_F1);
+    dvmCompilerMarkInUse(cUnit, r_F0);
+    dvmCompilerMarkInUse(cUnit, r_F1);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_ALT;
+    dvmCompilerClobber(cUnit, r_F0);
+    dvmCompilerMarkInUse(cUnit, r_F0);
+    return res;
+}
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc)
+{
+    if (loc.location != kLocRetval) {
+        assert(loc.sRegLow != INVALID_SREG);
+        dvmClearBit(cUnit->regPool->nullCheckedRegs, loc.sRegLow);
+        if (loc.wide) {
+            assert(dvmCompilerSRegHi(loc.sRegLow) != INVALID_SREG);
+            dvmClearBit(cUnit->regPool->nullCheckedRegs,
+                        dvmCompilerSRegHi(loc.sRegLow));
+        }
+    }
+}
+
+extern void dvmCompilerFlushRegWideForV5TEVFP(CompilationUnit *cUnit,
+                                              int reg1, int reg2)
+{
+    flushRegWide(cUnit, reg1, reg2);
+}
+
+extern void dvmCompilerFlushRegForV5TEVFP(CompilationUnit *cUnit, int reg)
+{
+    flushReg(cUnit, reg);
+}
diff --git a/vm/compiler/codegen/mips/mips/ArchVariant.cpp b/vm/compiler/codegen/mips/mips/ArchVariant.cpp
new file mode 100644
index 0000000..a5252c1
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/ArchVariant.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * This file is included by Codegen-mips.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_MIPS;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/mips/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/mips/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 9; // 512
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold != 0) {
+        gDvmJit.threshold = 200;
+    }
+    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_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2", make sure that the last
+     * offset from the struct is less than 128.
+     */
+    assert((offsetof(Thread, jitToInterpEntries) +
+            sizeof(struct JitToInterpEntries)) < 128);
+
+    /* FIXME - comment out the following to enable method-based JIT */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+    __asm__ __volatile__ ("" : : : "memory");
+}
diff --git a/vm/compiler/codegen/mips/mips/ArchVariant.h b/vm/compiler/codegen/mips/mips/ArchVariant.h
new file mode 100644
index 0000000..ec04dd8
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_MIPS_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_MIPS_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode{
+#include "../../../template/mips/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_MIPS_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/mips/mips/CallingConvention.S b/vm/compiler/codegen/mips/mips/CallingConvention.S
new file mode 100644
index 0000000..cfe2695
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/CallingConvention.S
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    a0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+#ifdef __mips_hard_float
+    /* For performance reasons, we are not using any "callee saved" */
+    /* fp registers, thus no need to save them.                     */
+#endif
+    jr $31
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+#ifdef __mips_hard_float
+    /* For performance reasons, we are not using any "callee saved" */
+    /* fp registers, thus no need to restore them.                  */
+#endif
+    jr $31
diff --git a/vm/compiler/codegen/mips/mips/Codegen.cpp b/vm/compiler/codegen/mips/mips/Codegen.cpp
new file mode 100644
index 0000000..2c7456e
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/Codegen.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ #define _CODEGEN_C
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/mips/MipsLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/mips/Ralloc.h"
+#include "compiler/codegen/mips/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Architectural independent building blocks */
+#include "../Mips32/Factory.cpp"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.cpp"
+
+/* Thumb-specific codegen routines */
+#include "../Mips32/Gen.cpp"
+/* Thumb+Portable FP codegen routines */
+#include "../FP/MipsFP.cpp"
+
+/* Thumb-specific register allocation */
+#include "../Mips32/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Dummy driver for method-based JIT */
+#include "MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/mips/mips/MethodCodegenDriver.cpp b/vm/compiler/codegen/mips/mips/MethodCodegenDriver.cpp
new file mode 100644
index 0000000..55c2d89
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/MethodCodegenDriver.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    ALOGE("Method-based JIT not supported for the Mips target");
+    dvmAbort();
+}
diff --git a/vm/compiler/codegen/x86/AnalysisO1.cpp b/vm/compiler/codegen/x86/AnalysisO1.cpp
new file mode 100644
index 0000000..a9fd5ce
--- /dev/null
+++ b/vm/compiler/codegen/x86/AnalysisO1.cpp
@@ -0,0 +1,5323 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file AnalysisO1.cpp
+  \brief This file implements register allocator, constant folding
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "interp/InterpState.h"
+#include "interp/InterpDefs.h"
+#include "libdex/Leb128.h"
+
+/* compilation flags to turn on debug printout */
+//#define DEBUG_COMPILE_TABLE
+//#define DEBUG_ALLOC_CONSTRAINT
+//#define DEBUG_REGALLOC
+//#define DEBUG_REG_USED
+//#define DEBUG_REFCOUNT
+//#define DEBUG_REACHING_DEF2
+//#define DEBUG_REACHING_DEF
+//#define DEBUG_LIVE_RANGE
+//#define DEBUG_MOVE_OPT
+//#define DEBUG_SPILL
+//#define DEBUG_ENDOFBB
+//#define DEBUG_CONST
+/*
+  #define DEBUG_XFER_POINTS
+  #define DEBUG_DSE
+  #define DEBUG_CFG
+  #define DEBUG_GLOBALTYPE
+  #define DEBUG_STATE
+  #define DEBUG_COMPILE_TABLE
+  #define DEBUG_VIRTUAL_INFO
+  #define DEBUG_MOVE_OPT
+  #define DEBUG_MERGE_ENTRY
+  #define DEBUG_INVALIDATE
+*/
+#include "AnalysisO1.h"
+
+void dumpCompileTable();
+
+/* There are 3 kinds of variables that are handled in this file:
+   1> virtual register (isVirtualReg())
+   2> temporary (!isVirtualReg() && regNum < PhysicalReg_GLUE_DVMDEX)
+   3> glue variables: regNum >= PhysicalReg_GLUE_DVMDEX
+*/
+/** check whether a variable is a virtual register
+ */
+bool isVirtualReg(int type) {
+    if((type & LowOpndRegType_virtual) != 0) return true;
+    return false;
+}
+bool isTemporary(int type, int regNum) {
+    if(!isVirtualReg(type) && regNum < PhysicalReg_GLUE_DVMDEX) return true;
+    return false;
+}
+
+/** convert type defined in lowering module to type defined in register allocator
+    in lowering module <type, isPhysical>
+    in register allocator: LowOpndRegType_hard LowOpndRegType_virtual LowOpndRegType_scratch
+*/
+int convertType(int type, int reg, bool isPhysical) {
+    int newType = type;
+    if(isPhysical) newType |= LowOpndRegType_hard;
+    if(isVirtualReg(type)) newType |= LowOpndRegType_virtual;
+    else {
+        /* reg number for a VR can exceed PhysicalReg_SCRATCH_1 */
+        if(reg >= PhysicalReg_SCRATCH_1 && reg < PhysicalReg_GLUE_DVMDEX)
+            newType |= LowOpndRegType_scratch;
+    }
+    return newType;
+}
+
+/** return the size of a variable
+ */
+OpndSize getRegSize(int type) {
+    if((type & MASK_FOR_TYPE) == LowOpndRegType_xmm) return OpndSize_64;
+    if((type & MASK_FOR_TYPE) == LowOpndRegType_fs) return OpndSize_64;
+    /* for type _gp, _fs_s, _ss */
+    return OpndSize_32;
+}
+
+/*
+   Overlapping cases between two variables A and B
+   layout for A,B   isAPartiallyOverlapB  isBPartiallyOverlapA
+   1> |__|  |____|         OVERLAP_ALIGN        OVERLAP_B_COVER_A
+      |__|  |____|
+   2> |____|           OVERLAP_B_IS_LOW_OF_A    OVERLAP_B_COVER_LOW_OF_A
+        |__|
+   3> |____|           OVERLAP_B_IS_HIGH_OF_A   OVERLAP_B_COVER_HIGH_OF_A
+      |__|
+   4> |____|      OVERLAP_LOW_OF_A_IS_HIGH_OF_B OVERLAP_B_COVER_LOW_OF_A
+         |____|
+   5>    |____|   OVERLAP_HIGH_OF_A_IS_LOW_OF_B OVERLAP_B_COVER_HIGH_OF_A
+      |____|
+   6>   |__|           OVERLAP_A_IS_LOW_OF_B    OVERLAP_B_COVER_A
+      |____|
+   7> |__|             OVERLAP_A_IS_HIGH_OF_B   OVERLAP_B_COVER_A
+      |____|
+*/
+/** determine the overlapping between variable B and A
+*/
+OverlapCase getBPartiallyOverlapA(int regB, LowOpndRegType tB, int regA, LowOpndRegType tA) {
+    if(getRegSize(tA) == getRegSize(tB) && regA == regB) return OVERLAP_B_COVER_A;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regA == regB) return OVERLAP_B_COVER_LOW_OF_A;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regB == regA + 1) return OVERLAP_B_COVER_HIGH_OF_A;
+    if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && (regA == regB || regA == regB+1)) return OVERLAP_B_COVER_A;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regA == regB+1) return OVERLAP_B_COVER_LOW_OF_A;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regB == regA+1) return OVERLAP_B_COVER_HIGH_OF_A;
+    return OVERLAP_NO;
+}
+
+/** determine overlapping between variable A and B
+*/
+OverlapCase getAPartiallyOverlapB(int regA, LowOpndRegType tA, int regB, LowOpndRegType tB) {
+    if(getRegSize(tA) == getRegSize(tB) && regA == regB) return OVERLAP_ALIGN;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regA == regB)
+        return OVERLAP_B_IS_LOW_OF_A;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regB == regA+1)
+        return OVERLAP_B_IS_HIGH_OF_A;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regA == regB+1)
+        return OVERLAP_LOW_OF_A_IS_HIGH_OF_B;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regB == regA+1)
+        return OVERLAP_HIGH_OF_A_IS_LOW_OF_B;
+    if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && regA == regB)
+        return OVERLAP_A_IS_LOW_OF_B;
+    if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && regA == regB+1)
+        return OVERLAP_A_IS_HIGH_OF_B;
+    return OVERLAP_NO;
+}
+
+/** determine whether variable A fully covers B
+ */
+bool isAFullyCoverB(int regA, LowOpndRegType tA, int regB, LowOpndRegType tB) {
+    if(getRegSize(tB) == OpndSize_32) return true;
+    if(getRegSize(tA) == getRegSize(tB) && regA == regB) return true;
+    return false;
+}
+
+/*
+   RegAccessType accessType
+   1> DefOrUse.accessType
+      can only be D(VR), L(low part of VR), H(high part of VR), N(none)
+      for def, it means which part of the VR is live
+      for use, it means which part of the VR comes from the def
+   2> VirtualRegInfo.accessType
+      for currentInfo, it can only be a combination of U & D
+      for entries in infoBasicBlock, it can be a combination of U & D|L|H
+*/
+
+/*
+   Key data structures used:
+   1> BasicBlock_O1
+      VirtualRegInfo infoBasicBlock[]
+      DefUsePair* defUseTable
+      XferPoint xferPoints[]
+   2> MemoryVRInfo memVRTable[]
+      LiveRange* ranges
+   3> compileTableEntry compileTable[]
+   4> VirtualRegInfo
+      DefOrUse reachingDefs[3]
+   5> DefUsePair, LiveRange
+*/
+
+//! one entry for each variable used
+
+//! a variable can be virtual register, or a temporary (can be hard-coded)
+compileTableEntry compileTable[COMPILE_TABLE_SIZE];
+int num_compile_entries;
+//! tables to save the states of register allocation
+regAllocStateEntry1 stateTable1_1[COMPILE_TABLE_SIZE];
+regAllocStateEntry1 stateTable1_2[COMPILE_TABLE_SIZE];
+regAllocStateEntry1 stateTable1_3[COMPILE_TABLE_SIZE];
+regAllocStateEntry1 stateTable1_4[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_1[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_2[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_3[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_4[COMPILE_TABLE_SIZE];
+
+//! array of VirtualRegInfo to store VRs accessed by a single bytecode
+VirtualRegInfo infoByteCode[MAX_REG_PER_BYTECODE];
+int num_regs_per_bytecode;
+//! array of TempRegInfo to store temporaries accessed by a single bytecode
+TempRegInfo infoByteCodeTemp[MAX_TEMP_REG_PER_BYTECODE];
+int num_temp_regs_per_bytecode;
+//! array of MemoryVRInfo to store whether a VR is in memory
+#define NUM_MEM_VR_ENTRY 140
+MemoryVRInfo memVRTable[NUM_MEM_VR_ENTRY];
+int num_memory_vr;
+
+CompilationUnit* currentUnit = NULL;
+
+//! the current basic block
+BasicBlock_O1* currentBB = NULL;
+//! array of RegisterInfo for all the physical registers
+RegisterInfo allRegs[PhysicalReg_GLUE+1]; //initialized in codeGen
+
+VirtualRegInfo currentInfo;
+VirtualRegInfo tmpInfo;
+
+//! this array says whether a spill location is used (0 means not used, 1 means used)
+int spillIndexUsed[MAX_SPILL_JIT_IA];
+int indexForGlue = -1;
+
+int num_bbs_for_method;
+//! array of basic blocks in a method in program order
+BasicBlock_O1* method_bbs_sorted[MAX_NUM_BBS_PER_METHOD];
+//! the entry basic block
+BasicBlock_O1* bb_entry;
+int pc_start = -1;
+int pc_end = -1;
+
+//!array of PCs for exception handlers
+int exceptionHandlers[10];
+int num_exception_handlers;
+
+bool canSpillReg[PhysicalReg_Null]; //physical registers that should not be spilled
+int inGetVR_num = -1;
+int inGetVR_type;
+
+///////////////////////////////////////////////////////////////////////////////
+// FORWARD FUNCTION DECLARATION
+void addExceptionHandler(s4 tmp);
+
+int createCFG(Method* method);
+int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb);
+void dumpVirtualInfoOfBasicBlock(BasicBlock_O1* bb);
+void setTypeOfVR();
+void insertGlueReg();
+void dumpVirtualInfoOfMethod();
+int codeGenBasicBlock(const Method* method, BasicBlock_O1* bb);
+
+//used in collectInfoOfBasicBlock: getVirtualRegInfo
+int mergeEntry2(BasicBlock_O1* bb);
+int sortAllocConstraint(RegAllocConstraint* allocConstraints,
+                        RegAllocConstraint* allocConstraintsSorted, bool fromHighToLow);
+
+//used in codeGenBasicBlock
+void insertFromVirtualInfo(BasicBlock_O1* bb, int k); //update compileTable
+void insertFromTempInfo(int k); //update compileTable
+int updateXferPoints();
+void updateLiveTable();
+void printDefUseTable();
+bool isFirstOfHandler(BasicBlock_O1* bb);
+
+//used in mergeEntry2
+//following functions will not update global data structure
+RegAccessType mergeAccess2(RegAccessType A, RegAccessType B, OverlapCase isBPartiallyOverlapA);
+RegAccessType updateAccess1(RegAccessType A, OverlapCase isAPartiallyOverlapB); //will not update global data structure
+RegAccessType updateAccess2(RegAccessType C1, RegAccessType C2);
+RegAccessType updateAccess3(RegAccessType C, RegAccessType B);
+
+void updateDefUseTable();
+void updateReachingDefA(int indexToA, OverlapCase isBPartiallyOverlapA);
+void updateReachingDefB1(int indexToA);
+void updateReachingDefB2();
+void updateReachingDefB3();
+
+RegAccessType insertAUse(DefUsePair* ptr, int offsetPC, int regNum, LowOpndRegType physicalType);
+DefUsePair* insertADef(int offsetPC, int regNum, LowOpndRegType pType, RegAccessType rType);
+RegAccessType insertDefUsePair(int reachingDefIndex);
+
+//used in updateXferPoints
+int fakeUsageAtEndOfBB(BasicBlock_O1* bb);
+void insertLoadXfer(int offset, int regNum, LowOpndRegType pType);
+int searchMemTable(int regNum);
+void mergeLiveRange(int tableIndex, int rangeStart, int rangeEnd);
+//used in updateLiveTable
+RegAccessType setAccessTypeOfUse(OverlapCase isDefPartiallyOverlapUse, RegAccessType reachingDefLive);
+DefUsePair* searchDefUseTable(int offsetPC, int regNum, LowOpndRegType pType);
+void insertAccess(int tableIndex, LiveRange* startP, int rangeStart);
+
+//register allocation
+int spillLogicalReg(int spill_index, bool updateTable);
+
+/** check whether the current bytecode is IF or GOTO or SWITCH
+ */
+bool isCurrentByteCodeJump() {
+    u2 inst_op = INST_INST(inst);
+    if(inst_op == OP_IF_EQ || inst_op == OP_IF_NE || inst_op == OP_IF_LT ||
+       inst_op == OP_IF_GE || inst_op == OP_IF_GT || inst_op == OP_IF_LE) return true;
+    if(inst_op == OP_IF_EQZ || inst_op == OP_IF_NEZ || inst_op == OP_IF_LTZ ||
+       inst_op == OP_IF_GEZ || inst_op == OP_IF_GTZ || inst_op == OP_IF_LEZ) return true;
+    if(inst_op == OP_GOTO || inst_op == OP_GOTO_16 || inst_op == OP_GOTO_32) return true;
+    if(inst_op == OP_PACKED_SWITCH || inst_op == OP_SPARSE_SWITCH) return true;
+    return false;
+}
+
+/* this function is called before code generation of basic blocks
+   initialize data structure allRegs, which stores information for each physical register,
+   whether it is used, when it was last freed, whether it is callee-saved */
+void initializeAllRegs() {
+    int k;
+    for(k = PhysicalReg_EAX; k <= PhysicalReg_EBP; k++) {
+        allRegs[k].physicalReg = (PhysicalReg) k;
+        if(k == PhysicalReg_EDI || k == PhysicalReg_ESP || k == PhysicalReg_EBP)
+            allRegs[k].isUsed = true;
+        else {
+            allRegs[k].isUsed = false;
+            allRegs[k].freeTimeStamp = -1;
+        }
+        if(k == PhysicalReg_EBX || k == PhysicalReg_EBP || k == PhysicalReg_ESI || k == PhysicalReg_EDI)
+            allRegs[k].isCalleeSaved = true;
+        else
+            allRegs[k].isCalleeSaved = false;
+    }
+    for(k = PhysicalReg_XMM0; k <= PhysicalReg_XMM7; k++) {
+        allRegs[k].physicalReg = (PhysicalReg) k;
+        allRegs[k].isUsed = false;
+        allRegs[k].freeTimeStamp = -1;
+        allRegs[k].isCalleeSaved = false;
+    }
+}
+
+/** sync up allRegs (isUsed & freeTimeStamp) with compileTable
+    global data: RegisterInfo allRegs[PhysicalReg_Null]
+    update allRegs[EAX to XMM7] except EDI,ESP,EBP
+    update RegisterInfo.isUsed & RegisterInfo.freeTimeStamp
+        if the physical register was used and is not used now
+*/
+void syncAllRegs() {
+    int k, k2;
+    for(k = PhysicalReg_EAX; k <= PhysicalReg_XMM7; k++) {
+        if(k == PhysicalReg_EDI || k == PhysicalReg_ESP || k == PhysicalReg_EBP)
+            continue;
+        //check whether the physical register is used by any logical register
+        bool stillUsed = false;
+        for(k2 = 0; k2 < num_compile_entries; k2++) {
+            if(compileTable[k2].physicalReg == k) {
+                stillUsed = true;
+                break;
+            }
+        }
+        if(stillUsed && !allRegs[k].isUsed) {
+            allRegs[k].isUsed = true;
+        }
+        if(!stillUsed && allRegs[k].isUsed) {
+            allRegs[k].isUsed = false;
+            allRegs[k].freeTimeStamp = lowOpTimeStamp;
+        }
+    }
+    return;
+}
+
+//!sync up spillIndexUsed with compileTable
+
+//!
+void updateSpillIndexUsed() {
+    int k;
+    for(k = 0; k <= MAX_SPILL_JIT_IA-1; k++) spillIndexUsed[k] = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(isVirtualReg(compileTable[k].physicalType)) continue;
+        if(compileTable[k].spill_loc_index >= 0) {
+            if(compileTable[k].spill_loc_index > 4*(MAX_SPILL_JIT_IA-1))
+                ALOGE("spill_loc_index is wrong for entry %d: %d",
+                      k, compileTable[k].spill_loc_index);
+            spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 1;
+        }
+    }
+}
+
+/* free memory used in all basic blocks */
+void freeCFG() {
+    int k;
+    for(k = 0; k < num_bbs_for_method; k++) {
+        /* free defUseTable for method_bbs_sorted[k] */
+        DefUsePair* ptr = method_bbs_sorted[k]->defUseTable;
+        while(ptr != NULL) {
+            DefUsePair* tmp = ptr->next;
+            /* free ptr->uses */
+            DefOrUseLink* ptrUse = ptr->uses;
+            while(ptrUse != NULL) {
+                DefOrUseLink* tmp2 = ptrUse->next;
+                free(ptrUse);
+                ptrUse = tmp2;
+            }
+            free(ptr);
+            ptr = tmp;
+        }
+        free(method_bbs_sorted[k]);
+    }
+}
+
+/* update compileTable.physicalReg, compileTable.spill_loc_index & allRegs.isUsed
+   for glue-related variables, they do not exist
+       not in a physical register (physicalReg is Null)
+       not in a spilled memory location (spill_loc_index is -1)
+*/
+void initializeRegStateOfBB(BasicBlock_O1* bb) {
+    //for GLUE variables, do not exist
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        /* trace-based JIT: there is no VR with GG type */
+        if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType == GLOBALTYPE_GG) {
+            if(bb->bb_index > 0) { //non-entry block
+                if(isFirstOfHandler(bb)) {
+                    /* at the beginning of an exception handler, GG VR is in the interpreted stack */
+                    compileTable[k].physicalReg = PhysicalReg_Null;
+#ifdef DEBUG_COMPILE_TABLE
+                    ALOGI("at the first basic block of an exception handler, GG VR %d type %d is in memory",
+                          compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+                } else {
+                    if(compileTable[k].physicalReg == PhysicalReg_Null) {
+                        /* GG VR is in a specific physical register */
+                        compileTable[k].physicalReg = compileTable[k].physicalReg_prev;
+                    }
+                    int tReg = compileTable[k].physicalReg;
+                    allRegs[tReg].isUsed = true;
+#ifdef DEBUG_REG_USED
+                    ALOGI("REGALLOC: physical reg %d is used by a GG VR %d %d at beginning of BB", tReg, compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+                }
+            } //non-entry block
+        } //if GG VR
+        if(compileTable[k].regNum != PhysicalReg_GLUE &&
+           compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX) {
+            /* glue related registers */
+            compileTable[k].physicalReg = PhysicalReg_Null;
+            compileTable[k].spill_loc_index = -1;
+        }
+    }
+}
+
+/* update memVRTable[].nullCheckDone */
+void initializeNullCheck(int indexToMemVR) {
+    bool found = false;
+#ifdef GLOBAL_NULLCHECK_OPT
+    /* search nullCheck_inB of the current Basic Block */
+    for(k = 0; k < nullCheck_inSize[currentBB->bb_index2]; k++) {
+        if(nullCheck_inB[currentBB->bb_index2][k] == memVRTable[indexToMemVR].regNum) {
+            found = true;
+            break;
+        }
+    }
+#endif
+    memVRTable[indexToMemVR].nullCheckDone = found;
+}
+
+/* initialize memVRTable */
+void initializeMemVRTable() {
+    num_memory_vr = 0;
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(!isVirtualReg(compileTable[k].physicalType)) continue;
+        /* VRs in compileTable */
+        bool setToInMemory = (compileTable[k].physicalReg == PhysicalReg_Null);
+        int regNum = compileTable[k].regNum;
+        OpndSize sizeVR = getRegSize(compileTable[k].physicalType);
+        /* search memVRTable for the VR in compileTable */
+        int kk;
+        int indexL = -1;
+        int indexH = -1;
+        for(kk = 0; kk < num_memory_vr; kk++) {
+            if(memVRTable[kk].regNum == regNum) {
+                indexL = kk;
+                continue;
+            }
+            if(memVRTable[kk].regNum == regNum+1 && sizeVR == OpndSize_64) {
+                indexH = kk;
+                continue;
+            }
+        }
+        if(indexL < 0) {
+            /* the low half of VR is not in memVRTable
+               add an entry for the low half in memVRTable */
+            if(num_memory_vr >= NUM_MEM_VR_ENTRY) {
+                ALOGE("exceeds size of memVRTable");
+                dvmAbort();
+            }
+            memVRTable[num_memory_vr].regNum = regNum;
+            memVRTable[num_memory_vr].inMemory = setToInMemory;
+            initializeNullCheck(num_memory_vr); //set nullCheckDone
+            memVRTable[num_memory_vr].boundCheck.checkDone = false;
+            memVRTable[num_memory_vr].num_ranges = 0;
+            memVRTable[num_memory_vr].ranges = NULL;
+            memVRTable[num_memory_vr].delayFreeFlags = VRDELAY_NONE;
+            num_memory_vr++;
+        }
+        if(sizeVR == OpndSize_64 && indexH < 0) {
+            /* the high half of VR is not in memVRTable
+               add an entry for the high half in memVRTable */
+            if(num_memory_vr >= NUM_MEM_VR_ENTRY) {
+                ALOGE("exceeds size of memVRTable");
+                dvmAbort();
+            }
+            memVRTable[num_memory_vr].regNum = regNum+1;
+            memVRTable[num_memory_vr].inMemory = setToInMemory;
+            initializeNullCheck(num_memory_vr);
+            memVRTable[num_memory_vr].boundCheck.checkDone = false;
+            memVRTable[num_memory_vr].num_ranges = 0;
+            memVRTable[num_memory_vr].ranges = NULL;
+            memVRTable[num_memory_vr].delayFreeFlags = VRDELAY_NONE;
+            num_memory_vr++;
+        }
+    }
+}
+
+/* create a O1 basic block from basic block constructed in JIT MIR */
+BasicBlock_O1* createBasicBlockO1(BasicBlock* bb) {
+    BasicBlock_O1* bb1 = createBasicBlock(0, -1);
+    bb1->jitBasicBlock = bb;
+    return bb1;
+}
+
+/* a basic block in JIT MIR can contain bytecodes that are not in program order
+   for example, a "goto" bytecode will be followed by the goto target */
+void preprocessingBB(BasicBlock* bb) {
+    currentBB = createBasicBlockO1(bb);
+    /* initialize currentBB->allocConstraints */
+    int ii;
+    for(ii = 0; ii < 8; ii++) {
+        currentBB->allocConstraints[ii].physicalReg = (PhysicalReg)ii;
+        currentBB->allocConstraints[ii].count = 0;
+    }
+    collectInfoOfBasicBlock(currentMethod, currentBB);
+#ifdef DEBUG_COMPILE_TABLE
+    dumpVirtualInfoOfBasicBlock(currentBB);
+#endif
+    currentBB = NULL;
+}
+
+void preprocessingTrace() {
+    int k, k2, k3, jj;
+    /* this is a simplified verson of setTypeOfVR()
+        all VRs are assumed to be GL, no VR will be GG
+    */
+    for(k = 0; k < num_bbs_for_method; k++)
+        for(jj = 0; jj < method_bbs_sorted[k]->num_regs; jj++)
+            method_bbs_sorted[k]->infoBasicBlock[jj].gType = GLOBALTYPE_GL;
+
+    /* insert a glue-related register GLUE_DVMDEX to compileTable */
+    insertGlueReg();
+
+    int compile_entries_old = num_compile_entries;
+    for(k2 = 0; k2 < num_bbs_for_method; k2++) {
+        currentBB = method_bbs_sorted[k2];
+        /* update compileTable with virtual register from currentBB */
+        for(k3 = 0; k3 < currentBB->num_regs; k3++) {
+            insertFromVirtualInfo(currentBB, k3);
+        }
+
+        /* for each GL|GG type VR, insert fake usage at end of basic block to keep it live */
+        int offsetPC_back = offsetPC;
+        offsetPC = PC_FOR_END_OF_BB;
+        for(k = 0; k < num_compile_entries; k++) {
+            currentInfo.regNum = compileTable[k].regNum;
+            currentInfo.physicalType = (LowOpndRegType)compileTable[k].physicalType;
+            if(isVirtualReg(compileTable[k].physicalType) &&
+               compileTable[k].gType == GLOBALTYPE_GL) {
+                /* update defUseTable by assuming a fake usage at END of a basic block for variable @ currentInfo */
+                fakeUsageAtEndOfBB(currentBB);
+            }
+            if(isVirtualReg(compileTable[k].physicalType) &&
+               compileTable[k].gType == GLOBALTYPE_GG) {
+                fakeUsageAtEndOfBB(currentBB);
+            }
+        }
+        offsetPC = offsetPC_back;
+        num_compile_entries = compile_entries_old;
+    }
+    /* initialize data structure allRegs */
+    initializeAllRegs();
+#ifdef DEBUG_COMPILE_TABLE
+    dumpCompileTable();
+#endif
+    currentBB = NULL;
+}
+
+void printJitTraceInfoAtRunTime(const Method* method, int offset) {
+    ALOGI("execute trace for %s%s at offset %x", method->clazz->descriptor, method->name, offset);
+}
+
+void startOfTraceO1(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId, CompilationUnit *cUnit) {
+    num_exception_handlers = 0;
+    num_compile_entries = 0;
+    currentBB = NULL;
+    pc_start = -1;
+    bb_entry = NULL;
+    num_bbs_for_method = 0;
+    currentUnit = cUnit;
+    lowOpTimeStamp = 0;
+
+// dumpDebuggingInfo is gone in CompilationUnit struct
+#if 0
+    /* add code to dump debugging information */
+    if(cUnit->dumpDebuggingInfo) {
+        move_imm_to_mem(OpndSize_32, cUnit->startOffset, -4, PhysicalReg_ESP, true); //2nd argument: offset
+        move_imm_to_mem(OpndSize_32, (int)currentMethod, -8, PhysicalReg_ESP, true); //1st argument: method
+        load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+        typedef void (*vmHelper)(const Method*, int);
+        vmHelper funcPtr = printJitTraceInfoAtRunTime;
+        move_imm_to_reg(OpndSize_32, (int)funcPtr, PhysicalReg_ECX, true);
+        call_reg(PhysicalReg_ECX, true);
+
+        load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    }
+#endif
+}
+
+
+/* Code generation for a basic block defined for JIT
+   We have two data structures for a basic block:
+       BasicBlock defined in vm/compiler by JIT
+       BasicBlock_O1 defined in o1 */
+int codeGenBasicBlockJit(const Method* method, BasicBlock* bb) {
+    /* search method_bbs_sorted to find the O1 basic block corresponding to bb */
+    int k;
+    for(k = 0; k < num_bbs_for_method; k++) {
+        if(method_bbs_sorted[k]->jitBasicBlock == bb) {
+            lowOpTimeStamp = 0; //reset time stamp at start of a basic block
+            currentBB = method_bbs_sorted[k];
+            int cg_ret = codeGenBasicBlock(method, currentBB);
+            currentBB = NULL;
+            return cg_ret;
+        }
+    }
+    ALOGE("can't find the corresponding O1 basic block for id %d type %d",
+         bb->id, bb->blockType);
+    return -1;
+}
+void endOfBasicBlock(BasicBlock* bb) {
+    isScratchPhysical = true;
+    currentBB = NULL;
+}
+void endOfTraceO1() {
+     freeCFG();
+}
+
+/** entry point to collect information about virtual registers used in a basic block
+    Initialize data structure BasicBlock_O1
+    The usage information of virtual registers is stoerd in bb->infoBasicBlock
+
+    Global variables accessed: offsetPC, rPC
+*/
+int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb) {
+    bb->num_regs = 0;
+    bb->num_defs = 0;
+    bb->defUseTable = NULL;
+    bb->defUseTail = NULL;
+    u2* rPC_start = (u2*)method->insns;
+    int kk;
+    bb->endsWithReturn = false;
+    bb->hasAccessToGlue = false;
+
+    MIR* mir;
+    int seqNum = 0;
+    /* traverse the MIR in basic block
+       sequence number is used to make sure next bytecode will have a larger sequence number */
+    for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
+        offsetPC = seqNum;
+        mir->seqNum = seqNum++;
+        rPC = rPC_start + mir->offset;
+#ifdef WITH_JIT_INLINING
+        if(mir->dalvikInsn.opcode >= kMirOpFirst &&
+           mir->dalvikInsn.opcode != kMirOpCheckInlinePrediction) continue;
+        if(ir->dalvikInsn.opcode == kMirOpCheckInlinePrediction) { //TODO
+        }
+#else
+        if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) continue;
+#endif
+        inst = FETCH(0);
+        u2 inst_op = INST_INST(inst);
+        /* update bb->hasAccessToGlue */
+        if((inst_op >= OP_MOVE_RESULT && inst_op <= OP_RETURN_OBJECT) ||
+           (inst_op >= OP_MONITOR_ENTER && inst_op <= OP_INSTANCE_OF) ||
+           (inst_op == OP_FILLED_NEW_ARRAY) ||
+           (inst_op == OP_FILLED_NEW_ARRAY_RANGE) ||
+           (inst_op == OP_THROW) ||
+           (inst_op >= OP_INVOKE_VIRTUAL && inst_op <= OP_INVOKE_INTERFACE_RANGE) ||
+           (inst_op >= OP_THROW_VERIFICATION_ERROR &&
+            inst_op <= OP_EXECUTE_INLINE_RANGE) ||
+           (inst_op >= OP_INVOKE_VIRTUAL_QUICK && inst_op <= OP_INVOKE_SUPER_QUICK_RANGE))
+            bb->hasAccessToGlue = true;
+        /* update bb->endsWithReturn */
+        if(inst_op == OP_RETURN_VOID || inst_op == OP_RETURN || inst_op == OP_RETURN_VOID_BARRIER ||
+           inst_op == OP_RETURN_OBJECT || inst_op == OP_RETURN_WIDE)
+            bb->endsWithReturn = true;
+
+        /* get virtual register usage in current bytecode */
+        getVirtualRegInfo(infoByteCode);
+        int num_regs = num_regs_per_bytecode;
+        for(kk = 0; kk < num_regs; kk++) {
+            currentInfo = infoByteCode[kk];
+#ifdef DEBUG_MERGE_ENTRY
+            ALOGI("call mergeEntry2 at offsetPC %x kk %d VR %d %d", offsetPC, kk,
+                  currentInfo.regNum, currentInfo.physicalType);
+#endif
+            mergeEntry2(bb); //update defUseTable of the basic block
+        }
+
+        //dumpVirtualInfoOfBasicBlock(bb);
+    }//for each bytecode
+
+    bb->pc_end = seqNum;
+
+    //sort allocConstraints of each basic block
+    for(kk = 0; kk < bb->num_regs; kk++) {
+#ifdef DEBUG_ALLOC_CONSTRAINT
+        ALOGI("sort virtual reg %d type %d -------", bb->infoBasicBlock[kk].regNum,
+              bb->infoBasicBlock[kk].physicalType);
+#endif
+        sortAllocConstraint(bb->infoBasicBlock[kk].allocConstraints,
+                            bb->infoBasicBlock[kk].allocConstraintsSorted, true);
+    }
+#ifdef DEBUG_ALLOC_CONSTRAINT
+    ALOGI("sort constraints for BB %d --------", bb->bb_index);
+#endif
+    sortAllocConstraint(bb->allocConstraints, bb->allocConstraintsSorted, false);
+    return 0;
+}
+
+/** entry point to generate native code for a O1 basic block
+    There are 3 kinds of virtual registers in a O1 basic block:
+    1> L VR: local within the basic block
+    2> GG VR: is live in other basic blocks,
+              its content is in a pre-defined GPR at the beginning of a basic block
+    3> GL VR: is live in other basic blocks,
+              its content is in the interpreted stack at the beginning of a basic block
+    compileTable is updated with infoBasicBlock at the start of the basic block;
+    Before lowering each bytecode, compileTable is updated with infoByteCodeTemp;
+    At end of the basic block, right before the jump instruction, handles constant VRs and GG VRs
+*/
+int codeGenBasicBlock(const Method* method, BasicBlock_O1* bb) {
+    /* we assume at the beginning of each basic block,
+       all GL VRs reside in memory and all GG VRs reside in predefined physical registers,
+       so at the end of a basic block, recover a spilled GG VR, store a GL VR to memory */
+    /* update compileTable with entries in bb->infoBasicBlock */
+    int k;
+    for(k = 0; k < bb->num_regs; k++) {
+        insertFromVirtualInfo(bb, k);
+    }
+    updateXferPoints(); //call fakeUsageAtEndOfBB
+#ifdef DEBUG_REACHING_DEF
+    printDefUseTable();
+#endif
+#ifdef DSE_OPT
+    removeDeadDefs();
+    printDefUseTable();
+#endif
+    //clear const section of compileTable
+    for(k = 0; k < num_compile_entries; k++) compileTable[k].isConst = false;
+    num_const_vr = 0;
+#ifdef DEBUG_COMPILE_TABLE
+    ALOGI("At start of basic block %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
+    dumpCompileTable();
+#endif
+    initializeRegStateOfBB(bb);
+    initializeMemVRTable();
+    updateLiveTable();
+    freeReg(true);  //before code gen of a basic block, also called at end of a basic block?
+#ifdef DEBUG_COMPILE_TABLE
+    ALOGI("At start of basic block %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
+#endif
+
+    u2* rPC_start = (u2*)method->insns;
+    bool lastByteCodeIsJump = false;
+    MIR* mir;
+    for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
+        offsetPC = mir->seqNum;
+        rPC = rPC_start + mir->offset;
+#ifdef WITH_JIT_INLINING
+        if(mir->dalvikInsn.opcode >= kMirOpFirst &&
+           mir->dalvikInsn.opcode != kMirOpCheckInlinePrediction) {
+#else
+        if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) {
+#endif
+            handleExtendedMIR(currentUnit, mir);
+            continue;
+        }
+
+        inst = FETCH(0);
+        //before handling a bytecode, import info of temporary registers to compileTable including refCount
+        num_temp_regs_per_bytecode = getTempRegInfo(infoByteCodeTemp);
+        for(k = 0; k < num_temp_regs_per_bytecode; k++) {
+            if(infoByteCodeTemp[k].versionNum > 0) continue;
+            insertFromTempInfo(k);
+        }
+        startNativeCode(-1, -1);
+        for(k = 0; k <= MAX_SPILL_JIT_IA-1; k++) spillIndexUsed[k] = 0;
+        //update spillIndexUsed if a glue variable was spilled
+        for(k = 0; k < num_compile_entries; k++) {
+            if(compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX) {
+                if(compileTable[k].spill_loc_index >= 0)
+                    spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 1;
+            }
+        }
+#ifdef DEBUG_COMPILE_TABLE
+        ALOGI("compile table size after importing temporary info %d", num_compile_entries);
+        ALOGI("before one bytecode %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
+#endif
+        //set isConst to true for CONST & MOVE MOVE_OBJ?
+        //clear isConst to true for MOVE, MOVE_OBJ, MOVE_RESULT, MOVE_EXCEPTION ...
+        bool isConst = getConstInfo(bb); //will reset isConst if a VR is updated by the bytecode
+        bool isDeadStmt = false;
+#ifdef DSE_OPT
+        for(k = 0; k < num_dead_pc; k++) {
+            if(deadPCs[k] == offsetPC) {
+                isDeadStmt = true;
+                break;
+            }
+        }
+#endif
+        getVirtualRegInfo(infoByteCode);
+        //call something similar to mergeEntry2, but only update refCount
+        //clear refCount
+        for(k = 0; k < num_regs_per_bytecode; k++) {
+            int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
+                                            infoByteCode[k].regNum);
+            if(indexT >= 0)
+                compileTable[indexT].refCount = 0;
+        }
+        for(k = 0; k < num_regs_per_bytecode; k++) {
+            int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
+                                            infoByteCode[k].regNum);
+            if(indexT >= 0)
+                compileTable[indexT].refCount += infoByteCode[k].refCount;
+        } //for k
+#ifdef DSE_OPT
+        if(isDeadStmt) { //search compileTable
+            getVirtualRegInfo(infoByteCode);
+#ifdef DEBUG_DSE
+            ALOGI("DSE: stmt at offsetPC %d is dead", offsetPC);
+#endif
+            for(k = 0; k < num_regs_per_bytecode; k++) {
+                int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
+                                                infoByteCode[k].regNum);
+                if(indexT >= 0)
+                    compileTable[indexT].refCount -= infoByteCode[k].refCount;
+            }
+        }
+#endif
+        lastByteCodeIsJump = false;
+        if(!isConst && !isDeadStmt)  //isDeadStmt is false when DSE_OPT is not enabled
+        {
+#ifdef DEBUG_COMPILE_TABLE
+            dumpCompileTable();
+#endif
+            globalShortMap = NULL;
+            if(isCurrentByteCodeJump()) lastByteCodeIsJump = true;
+            //lowerByteCode will call globalVREndOfBB if it is jump
+            int retCode = lowerByteCodeJit(method, rPC, mir);
+            if(gDvmJit.codeCacheByteUsed + (stream - streamStart) +
+                 CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+                 ALOGE("JIT code cache full");
+                 gDvmJit.codeCacheFull = true;
+                 return -1;
+            }
+
+            if (retCode == 1) {
+                // We always fall back to the interpreter for OP_INVOKE_OBJECT_INIT_RANGE,
+                // but any other failure is unexpected and should be logged.
+                if (mir->dalvikInsn.opcode != OP_INVOKE_OBJECT_INIT_RANGE) {
+                    ALOGE("JIT couldn't compile %s%s dex_pc=%d opcode=%d",
+                          method->clazz->descriptor,
+                          method->name,
+                          offsetPC,
+                          mir->dalvikInsn.opcode);
+                }
+                return -1;
+            }
+            updateConstInfo(bb);
+            freeShortMap();
+            if(retCode < 0) {
+                ALOGE("error in lowering the bytecode");
+                return retCode;
+            }
+            freeReg(true); //may dump GL VR to memory (this is necessary)
+            //after each bytecode, make sure non-VRs have refCount of zero
+            for(k = 0; k < num_compile_entries; k++) {
+                if(isTemporary(compileTable[k].physicalType, compileTable[k].regNum)) {
+#ifdef PRINT_WARNING
+                    if(compileTable[k].refCount > 0) {
+                        ALOGW("refCount for a temporary reg %d %d is %d after a bytecode", compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
+                    }
+#endif
+                    compileTable[k].refCount = 0;
+                }
+            }
+        } else { //isConst || isDeadStmt
+            //if this bytecode is the target of a jump, the mapFromBCtoNCG should be updated
+            offsetNCG = stream - streamMethodStart;
+            mapFromBCtoNCG[offsetPC] = offsetNCG;
+#ifdef DEBUG_COMPILE_TABLE
+            ALOGI("this bytecode generates a constant and has no side effect");
+#endif
+            freeReg(true); //may dump GL VR to memory (this is necessary)
+        }
+#ifdef DEBUG_COMPILE_TABLE
+        ALOGI("after one bytecode BB %d (num of VRs %d)", bb->bb_index, bb->num_regs);
+#endif
+    }//for each bytecode
+#ifdef DEBUG_COMPILE_TABLE
+    dumpCompileTable();
+#endif
+    if(!lastByteCodeIsJump) constVREndOfBB();
+    //at end of a basic block, get spilled GG VR & dump GL VR
+    if(!lastByteCodeIsJump) globalVREndOfBB(method);
+    //remove entries for temporary registers, L VR and GL VR
+    int jj;
+    for(k = 0; k < num_compile_entries; ) {
+        bool removeEntry = false;
+        if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType != GLOBALTYPE_GG) {
+            removeEntry = true;
+        }
+        if(isTemporary(compileTable[k].physicalType, compileTable[k].regNum))
+            removeEntry = true;
+        if(removeEntry) {
+#ifdef PRINT_WARNING
+            if(compileTable[k].refCount > 0)
+                ALOGW("refCount for REG %d %d is %d at end of a basic block", compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
+#endif
+            compileTable[k].refCount = 0;
+            for(jj = k+1; jj < num_compile_entries; jj++) {
+                compileTable[jj-1] = compileTable[jj];
+            }
+            num_compile_entries--;
+        } else {
+            k++;
+        }
+    }
+    freeReg(true);
+    //free LIVE TABLE
+    for(k = 0; k < num_memory_vr; k++) {
+        LiveRange* ptr2 = memVRTable[k].ranges;
+        while(ptr2 != NULL) {
+            LiveRange* tmpP = ptr2->next;
+            free(ptr2->accessPC);
+            free(ptr2);
+            ptr2 = tmpP;
+        }
+    }
+#ifdef DEBUG_COMPILE_TABLE
+    ALOGI("At end of basic block -------");
+    dumpCompileTable();
+#endif
+    return 0;
+}
+
+/** update infoBasicBlock & defUseTable
+    input: currentInfo
+    side effect: update currentInfo.reachingDefs
+
+    update entries in infoBasicBlock by calling updateReachingDefA
+    if there is no entry in infoBasicBlock for B, an entry will be created and inserted to infoBasicBlock
+
+    defUseTable is updated to account for the access at currentInfo
+    if accessType of B is U or UD, we call updateReachingDefB to update currentInfo.reachingDefs
+        in order to correctly insert the usage to defUseTable
+*/
+int mergeEntry2(BasicBlock_O1* bb) {
+    LowOpndRegType typeB = currentInfo.physicalType;
+    int regB = currentInfo.regNum;
+    int jj, k;
+    int jjend = bb->num_regs;
+    bool isMerged = false;
+    bool hasAlias = false;
+    OverlapCase isBPartiallyOverlapA, isAPartiallyOverlapB;
+    RegAccessType tmpType = REGACCESS_N;
+    currentInfo.num_reaching_defs = 0;
+
+    /* traverse variable A in infoBasicBlock */
+    for(jj = 0; jj < jjend; jj++) {
+        int regA = bb->infoBasicBlock[jj].regNum;
+        LowOpndRegType typeA = bb->infoBasicBlock[jj].physicalType;
+        isBPartiallyOverlapA = getBPartiallyOverlapA(regB, typeB, regA, typeA);
+        isAPartiallyOverlapB = getAPartiallyOverlapB(regA, typeA, regB, typeB);
+        if(regA == regB && typeA == typeB) {
+            /* variable A and B are aligned */
+            bb->infoBasicBlock[jj].accessType = mergeAccess2(bb->infoBasicBlock[jj].accessType, currentInfo.accessType,
+                                                             OVERLAP_B_COVER_A);
+            bb->infoBasicBlock[jj].refCount += currentInfo.refCount;
+            /* copy reaching defs of variable B from variable A */
+            currentInfo.num_reaching_defs = bb->infoBasicBlock[jj].num_reaching_defs;
+            for(k = 0; k < currentInfo.num_reaching_defs; k++)
+                currentInfo.reachingDefs[k] = bb->infoBasicBlock[jj].reachingDefs[k];
+            updateDefUseTable(); //use currentInfo to update defUseTable
+            updateReachingDefA(jj, OVERLAP_B_COVER_A); //update reachingDefs of A
+            isMerged = true;
+            hasAlias = true;
+            if(typeB == LowOpndRegType_gp) {
+                //merge allocConstraints
+                for(k = 0; k < 8; k++) {
+                    bb->infoBasicBlock[jj].allocConstraints[k].count += currentInfo.allocConstraints[k].count;
+                }
+            }
+        }
+        else if(isBPartiallyOverlapA != OVERLAP_NO) {
+            tmpType = updateAccess2(tmpType, updateAccess1(bb->infoBasicBlock[jj].accessType, isAPartiallyOverlapB));
+            bb->infoBasicBlock[jj].accessType = mergeAccess2(bb->infoBasicBlock[jj].accessType, currentInfo.accessType,
+                                                             isBPartiallyOverlapA);
+#ifdef DEBUG_MERGE_ENTRY
+            ALOGI("update accessType in case 2: VR %d %d accessType %d", regA, typeA, bb->infoBasicBlock[jj].accessType);
+#endif
+            hasAlias = true;
+            if(currentInfo.accessType == REGACCESS_U || currentInfo.accessType == REGACCESS_UD) {
+                /* update currentInfo.reachingDefs */
+                updateReachingDefB1(jj);
+                updateReachingDefB2();
+            }
+            updateReachingDefA(jj, isBPartiallyOverlapA);
+        }
+        else {
+            //even if B does not overlap with A, B can affect the reaching defs of A
+            //for example, B is a def of "v0", A is "v1"
+            //  B can kill some reaching defs of A or affect the accessType of a reaching def
+            updateReachingDefA(jj, OVERLAP_NO); //update reachingDefs of A
+        }
+    }//for each variable A in infoBasicBlock
+    if(!isMerged) {
+        /* create a new entry in infoBasicBlock */
+        bb->infoBasicBlock[bb->num_regs].refCount = currentInfo.refCount;
+        bb->infoBasicBlock[bb->num_regs].physicalType = typeB;
+        if(hasAlias)
+            bb->infoBasicBlock[bb->num_regs].accessType = updateAccess3(tmpType, currentInfo.accessType);
+        else
+            bb->infoBasicBlock[bb->num_regs].accessType = currentInfo.accessType;
+#ifdef DEBUG_MERGE_ENTRY
+        ALOGI("update accessType in case 3: VR %d %d accessType %d", regB, typeB, bb->infoBasicBlock[bb->num_regs].accessType);
+#endif
+        bb->infoBasicBlock[bb->num_regs].regNum = regB;
+        for(k = 0; k < 8; k++)
+            bb->infoBasicBlock[bb->num_regs].allocConstraints[k] = currentInfo.allocConstraints[k];
+#ifdef DEBUG_MERGE_ENTRY
+        ALOGI("isMerged is false, call updateDefUseTable");
+#endif
+        updateDefUseTable(); //use currentInfo to update defUseTable
+        updateReachingDefB3(); //update currentInfo.reachingDefs if currentInfo defines variable B
+
+        //copy from currentInfo.reachingDefs to bb->infoBasicBlock[bb->num_regs]
+        bb->infoBasicBlock[bb->num_regs].num_reaching_defs = currentInfo.num_reaching_defs;
+        for(k = 0; k < currentInfo.num_reaching_defs; k++)
+            bb->infoBasicBlock[bb->num_regs].reachingDefs[k] = currentInfo.reachingDefs[k];
+#ifdef DEBUG_MERGE_ENTRY
+        ALOGI("try to update reaching defs for VR %d %d", regB, typeB);
+        for(k = 0; k < bb->infoBasicBlock[bb->num_regs].num_reaching_defs; k++)
+            ALOGI("reaching def %d @ %d for VR %d %d access %d", k, currentInfo.reachingDefs[k].offsetPC,
+                  currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType,
+                  currentInfo.reachingDefs[k].accessType);
+#endif
+        bb->num_regs++;
+        if(bb->num_regs >= MAX_REG_PER_BASICBLOCK) {
+            ALOGE("too many VRs in a basic block");
+            dvmAbort();
+        }
+        return -1;
+    }
+    return 0;
+}
+
+//!update reaching defs for infoBasicBlock[indexToA]
+
+//!use currentInfo.reachingDefs to update reaching defs for variable A
+void updateReachingDefA(int indexToA, OverlapCase isBPartiallyOverlapA) {
+    if(indexToA < 0) return;
+    int k, k2;
+    OverlapCase isBPartiallyOverlapDef;
+    if(currentInfo.accessType == REGACCESS_U) {
+        return; //no update to reachingDefs of the VR
+    }
+    /* access in currentInfo is DU, D, or UD */
+    if(isBPartiallyOverlapA == OVERLAP_B_COVER_A) {
+        /* from this point on, the reachingDefs for variable A is a single def to currentInfo at offsetPC */
+        currentBB->infoBasicBlock[indexToA].num_reaching_defs = 1;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].offsetPC = offsetPC;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].regNum = currentInfo.regNum;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].physicalType = currentInfo.physicalType;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].accessType = REGACCESS_D;
+#ifdef DEBUG_REACHING_DEF
+        ALOGI("single reaching def @ %d for VR %d %d", offsetPC, currentInfo.regNum, currentInfo.physicalType);
+#endif
+        return;
+    }
+    /* update reachingDefs for variable A to get rid of dead defs */
+    /* Bug fix: it is possible that more than one reaching defs need to be removed
+                after one reaching def is removed, num_reaching_defs--, but k should not change
+    */
+    for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; ) {
+        /* remove one reaching def in one interation of the loop */
+        //check overlapping between def & B
+        isBPartiallyOverlapDef = getBPartiallyOverlapA(currentInfo.regNum, currentInfo.physicalType,
+                                                       currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+                                                       currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType);
+#ifdef DEBUG_REACHING_DEF
+        ALOGI("DEBUG B %d %d def %d %d %d", currentInfo.regNum, currentInfo.physicalType,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType);
+#endif
+        /* cases where one def nees to be removed:
+           if B fully covers def, def is removed
+           if B overlaps high half of def & def's accessType is H, def is removed
+           if B overlaps low half of def & def's accessType is L, def is removed
+        */
+        if((isBPartiallyOverlapDef == OVERLAP_B_COVER_HIGH_OF_A &&
+            currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType == REGACCESS_H) ||
+           (isBPartiallyOverlapDef == OVERLAP_B_COVER_LOW_OF_A &&
+            currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType == REGACCESS_L) ||
+           isBPartiallyOverlapDef == OVERLAP_B_COVER_A
+           ) { //remove def
+            //shift from k+1 to end
+            for(k2 = k+1; k2 < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k2++)
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k2-1] = currentBB->infoBasicBlock[indexToA].reachingDefs[k2];
+            currentBB->infoBasicBlock[indexToA].num_reaching_defs--;
+        }
+        /*
+           if B overlaps high half of def & def's accessType is not H --> update accessType of def
+        */
+        else if(isBPartiallyOverlapDef == OVERLAP_B_COVER_HIGH_OF_A &&
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType != REGACCESS_H) {
+            //low half is still valid
+            if(getRegSize(currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType) == OpndSize_32)
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_D;
+            else
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_L;
+#ifdef DEBUG_REACHING_DEF
+            ALOGI("DEBUG: set accessType of def to L");
+#endif
+            k++;
+        }
+        /*
+           if B overlaps low half of def & def's accessType is not L --> update accessType of def
+        */
+        else if(isBPartiallyOverlapDef == OVERLAP_B_COVER_LOW_OF_A &&
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType != REGACCESS_L) {
+            //high half of def is still valid
+            currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_H;
+#ifdef DEBUG_REACHING_DEF
+            ALOGI("DEBUG: set accessType of def to H");
+#endif
+            k++;
+        }
+        else {
+            k++;
+        }
+    }//for k
+    if(isBPartiallyOverlapA != OVERLAP_NO) {
+        //insert the def to variable @ currentInfo
+        k = currentBB->infoBasicBlock[indexToA].num_reaching_defs;
+        if(k >= 3) {
+          ALOGE("more than 3 reaching defs");
+        }
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].offsetPC = offsetPC;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum = currentInfo.regNum;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType = currentInfo.physicalType;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_D;
+        currentBB->infoBasicBlock[indexToA].num_reaching_defs++;
+    }
+#ifdef DEBUG_REACHING_DEF2
+    ALOGI("IN updateReachingDefA for VR %d %d", currentBB->infoBasicBlock[indexToA].regNum,
+          currentBB->infoBasicBlock[indexToA].physicalType);
+    for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k++)
+        ALOGI("reaching def %d @ %d for VR %d %d access %d", k,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].offsetPC,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType);
+#endif
+}
+
+/** Given a variable B @currentInfo,
+    updates its reaching defs by checking reaching defs of variable A @currentBB->infoBasicBlock[indexToA]
+    The result is stored in tmpInfo.reachingDefs
+*/
+void updateReachingDefB1(int indexToA) {
+    if(indexToA < 0) return;
+    int k;
+    tmpInfo.num_reaching_defs = 0;
+    for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k++) {
+        /* go through reachingDefs of variable A @currentBB->infoBasicBlock[indexToA]
+           for each def, check whether it overlaps with variable B @currentInfo
+               if the def overlaps with variable B, insert it to tmpInfo.reachingDefs
+        */
+        OverlapCase isDefPartiallyOverlapB = getAPartiallyOverlapB(
+                                                 currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+                                                 currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
+                                                 currentInfo.regNum, currentInfo.physicalType
+                                                 );
+        bool insert1 = false; //whether to insert the def to tmpInfo.reachingDefs
+        if(isDefPartiallyOverlapB == OVERLAP_ALIGN ||
+           isDefPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B ||
+           isDefPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B) {
+            /* B aligns with def */
+            /* def is low half of B, def is high half of B
+               in these two cases, def is 32 bits */
+            insert1 = true;
+        }
+        RegAccessType deftype = currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType;
+        if(isDefPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A ||
+           isDefPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B) {
+            /* B is the low half of def */
+            /* the low half of def is the high half of B */
+            if(deftype != REGACCESS_H) insert1 = true;
+        }
+        if(isDefPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A ||
+           isDefPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B) {
+            /* B is the high half of def */
+            /* the high half of def is the low half of B */
+            if(deftype != REGACCESS_L) insert1 = true;
+        }
+        if(insert1) {
+            if(tmpInfo.num_reaching_defs >= 3) {
+                ALOGE("more than 3 reaching defs for tmpInfo");
+            }
+            tmpInfo.reachingDefs[tmpInfo.num_reaching_defs] = currentBB->infoBasicBlock[indexToA].reachingDefs[k];
+            tmpInfo.num_reaching_defs++;
+#ifdef DEBUG_REACHING_DEF2
+            ALOGI("insert from entry %d %d: index %d", currentBB->infoBasicBlock[indexToA].regNum,
+                  currentBB->infoBasicBlock[indexToA].physicalType, k);
+#endif
+        }
+    }
+}
+
+/** update currentInfo.reachingDefs by merging currentInfo.reachingDefs with tmpInfo.reachingDefs
+*/
+void updateReachingDefB2() {
+    int k, k2;
+    for(k2 = 0; k2 < tmpInfo.num_reaching_defs; k2++ ) {
+        bool merged = false;
+        for(k = 0; k < currentInfo.num_reaching_defs; k++) {
+            /* check whether it is the same def, if yes, do nothing */
+            if(currentInfo.reachingDefs[k].regNum == tmpInfo.reachingDefs[k2].regNum &&
+               currentInfo.reachingDefs[k].physicalType == tmpInfo.reachingDefs[k2].physicalType) {
+                merged = true;
+                if(currentInfo.reachingDefs[k].offsetPC != tmpInfo.reachingDefs[k2].offsetPC) {
+                    ALOGE("defs on the same VR %d %d with different offsetPC %d vs %d",
+                          currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType,
+                          currentInfo.reachingDefs[k].offsetPC, tmpInfo.reachingDefs[k2].offsetPC);
+                }
+                if(currentInfo.reachingDefs[k].accessType != tmpInfo.reachingDefs[k2].accessType)
+                    ALOGE("defs on the same VR %d %d with different accessType",
+                          currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType);
+                break;
+            }
+        }
+        if(!merged) {
+            if(currentInfo.num_reaching_defs >= 3) {
+               ALOGE("more than 3 reaching defs for currentInfo");
+            }
+            currentInfo.reachingDefs[currentInfo.num_reaching_defs] = tmpInfo.reachingDefs[k2];
+            currentInfo.num_reaching_defs++;
+        }
+    }
+}
+
+//!update currentInfo.reachingDefs with currentInfo if variable is defined in currentInfo
+
+//!
+void updateReachingDefB3() {
+    if(currentInfo.accessType == REGACCESS_U) {
+        return; //no need to update currentInfo.reachingDefs
+    }
+    currentInfo.num_reaching_defs = 1;
+    currentInfo.reachingDefs[0].regNum = currentInfo.regNum;
+    currentInfo.reachingDefs[0].physicalType = currentInfo.physicalType;
+    currentInfo.reachingDefs[0].offsetPC = offsetPC;
+    currentInfo.reachingDefs[0].accessType = REGACCESS_D;
+}
+
+/** update defUseTable by checking currentInfo
+*/
+void updateDefUseTable() {
+    /* no access */
+    if(currentInfo.accessType == REGACCESS_N) return;
+    /* define then use, or define only */
+    if(currentInfo.accessType == REGACCESS_DU || currentInfo.accessType == REGACCESS_D) {
+        /* insert a definition at offsetPC to variable @ currentInfo */
+        DefUsePair* ptr = insertADef(offsetPC, currentInfo.regNum, currentInfo.physicalType, REGACCESS_D);
+        if(currentInfo.accessType != REGACCESS_D) {
+             /* if access is define then use, insert a use at offsetPC */
+            insertAUse(ptr, offsetPC, currentInfo.regNum, currentInfo.physicalType);
+        }
+        return;
+    }
+    /* use only or use then define
+       check the reaching defs for the usage */
+    int k;
+    bool isLCovered = false, isHCovered = false, isDCovered = false;
+    for(k = 0; k < currentInfo.num_reaching_defs; k++) {
+        /* insert a def currentInfo.reachingDefs[k] and a use of variable at offsetPC */
+        RegAccessType useType = insertDefUsePair(k);
+        if(useType == REGACCESS_D) isDCovered = true;
+        if(useType == REGACCESS_L) isLCovered = true;
+        if(useType == REGACCESS_H) isHCovered = true;
+    }
+    OpndSize useSize = getRegSize(currentInfo.physicalType);
+    if((!isDCovered) && (!isLCovered)) {
+        /* the low half of variable is not defined in the basic block
+           so insert a def to the low half at START of the basic block */
+        insertDefUsePair(-1);
+    }
+    if(useSize == OpndSize_64 && (!isDCovered) && (!isHCovered)) {
+        /* the high half of variable is not defined in the basic block
+           so insert a def to the high half at START of the basic block */
+        insertDefUsePair(-2);
+    }
+    if(currentInfo.accessType == REGACCESS_UD) {
+        /* insert a def at offsetPC to variable @ currentInfo */
+        insertADef(offsetPC, currentInfo.regNum, currentInfo.physicalType, REGACCESS_D);
+        return;
+    }
+}
+
+//! insert a use at offsetPC of given variable at end of DefUsePair
+
+//!
+RegAccessType insertAUse(DefUsePair* ptr, int offsetPC, int regNum, LowOpndRegType physicalType) {
+    DefOrUseLink* tLink = (DefOrUseLink*)malloc(sizeof(DefOrUseLink));
+    if(tLink == NULL) {
+        ALOGE("Memory allocation failed");
+        return REGACCESS_UNKNOWN;
+    }
+    tLink->offsetPC = offsetPC;
+    tLink->regNum = regNum;
+    tLink->physicalType = physicalType;
+    tLink->next = NULL;
+    if(ptr->useTail != NULL)
+        ptr->useTail->next = tLink;
+    ptr->useTail = tLink;
+    if(ptr->uses == NULL)
+        ptr->uses = tLink;
+    ptr->num_uses++;
+
+    //check whether the def is partially overlapping with the variable
+    OverlapCase isDefPartiallyOverlapB = getBPartiallyOverlapA(ptr->def.regNum,
+                                                       ptr->def.physicalType,
+                                                       regNum, physicalType);
+    RegAccessType useType = setAccessTypeOfUse(isDefPartiallyOverlapB, ptr->def.accessType);
+    tLink->accessType = useType;
+    return useType;
+}
+
+//! insert a def to currentBB->defUseTable
+
+//! update currentBB->defUseTail if necessary
+DefUsePair* insertADef(int offsetPC, int regNum, LowOpndRegType pType, RegAccessType rType) {
+    DefUsePair* ptr = (DefUsePair*)malloc(sizeof(DefUsePair));
+    if(ptr == NULL) {
+        ALOGE("Memory allocation failed");
+        return NULL;
+    }
+    ptr->next = NULL;
+    ptr->def.offsetPC = offsetPC;
+    ptr->def.regNum = regNum;
+    ptr->def.physicalType = pType;
+    ptr->def.accessType = rType;
+    ptr->num_uses = 0;
+    ptr->useTail = NULL;
+    ptr->uses = NULL;
+    if(currentBB->defUseTail != NULL) {
+        currentBB->defUseTail->next = ptr;
+    }
+    currentBB->defUseTail = ptr;
+    if(currentBB->defUseTable == NULL)
+        currentBB->defUseTable = ptr;
+    currentBB->num_defs++;
+#ifdef DEBUG_REACHING_DEF
+    ALOGI("insert a def at %d to defUseTable for VR %d %d", offsetPC,
+          regNum, pType);
+#endif
+    return ptr;
+}
+
+/** insert a def to defUseTable, then insert a use of variable @ currentInfo
+    if reachingDefIndex >= 0, the def is currentInfo.reachingDefs[index]
+    if reachingDefIndex is -1, the low half is defined at START of the basic block
+    if reachingDefIndex is -2, the high half is defined at START of the basic block
+*/
+RegAccessType insertDefUsePair(int reachingDefIndex) {
+    int k = reachingDefIndex;
+    DefUsePair* tableIndex = NULL;
+    DefOrUse theDef;
+    theDef.regNum = 0;
+    if(k < 0) {
+        /* def at start of the basic blcok */
+        theDef.offsetPC = PC_FOR_START_OF_BB;
+        theDef.accessType = REGACCESS_D;
+        if(k == -1) //low half of variable
+            theDef.regNum = currentInfo.regNum;
+        if(k == -2) //high half of variable
+            theDef.regNum = currentInfo.regNum+1;
+        theDef.physicalType = LowOpndRegType_gp;
+    }
+    else {
+        theDef = currentInfo.reachingDefs[k];
+    }
+    tableIndex = searchDefUseTable(theDef.offsetPC, theDef.regNum, theDef.physicalType);
+    if(tableIndex == NULL) //insert an entry
+        tableIndex = insertADef(theDef.offsetPC, theDef.regNum, theDef.physicalType, theDef.accessType);
+    else
+        tableIndex->def.accessType = theDef.accessType;
+    RegAccessType useType = insertAUse(tableIndex, offsetPC, currentInfo.regNum, currentInfo.physicalType);
+    return useType;
+}
+
+//! insert a XFER_MEM_TO_XMM to currentBB->xferPoints
+
+//!
+void insertLoadXfer(int offset, int regNum, LowOpndRegType pType) {
+    //check whether it is already in currentBB->xferPoints
+    int k;
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        if(currentBB->xferPoints[k].xtype == XFER_MEM_TO_XMM &&
+           currentBB->xferPoints[k].offsetPC == offset &&
+           currentBB->xferPoints[k].regNum == regNum &&
+           currentBB->xferPoints[k].physicalType == pType)
+            return;
+    }
+    currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_MEM_TO_XMM;
+    currentBB->xferPoints[currentBB->num_xfer_points].regNum = regNum;
+    currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = offset;
+    currentBB->xferPoints[currentBB->num_xfer_points].physicalType = pType;
+#ifdef DEBUG_XFER_POINTS
+    ALOGI("insert to xferPoints %d: XFER_MEM_TO_XMM of VR %d %d at %d", currentBB->num_xfer_points, regNum, pType, offset);
+#endif
+    currentBB->num_xfer_points++;
+    if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
+        ALOGE("too many xfer points");
+        dvmAbort();
+    }
+}
+
+/** update defUseTable by assuming a fake usage at END of a basic block for variable @ currentInfo
+    create a fake usage at end of a basic block for variable B (currentInfo.physicalType, currentInfo.regNum)
+    get reaching def info for variable B and store the info in currentInfo.reachingDefs
+        for each virtual register (variable A) accessed in the basic block
+            update reaching defs of B by checking reaching defs of variable A
+    update defUseTable
+*/
+int fakeUsageAtEndOfBB(BasicBlock_O1* bb) {
+    currentInfo.accessType = REGACCESS_U;
+    LowOpndRegType typeB = currentInfo.physicalType;
+    int regB = currentInfo.regNum;
+    int jj, k;
+    currentInfo.num_reaching_defs = 0;
+    for(jj = 0; jj < bb->num_regs; jj++) {
+        int regA = bb->infoBasicBlock[jj].regNum;
+        LowOpndRegType typeA = bb->infoBasicBlock[jj].physicalType;
+        OverlapCase isBPartiallyOverlapA = getBPartiallyOverlapA(regB, typeB, regA, typeA);
+        if(regA == regB && typeA == typeB) {
+            /* copy reachingDefs from variable A */
+            currentInfo.num_reaching_defs = bb->infoBasicBlock[jj].num_reaching_defs;
+            for(k = 0; k < currentInfo.num_reaching_defs; k++)
+                currentInfo.reachingDefs[k] = bb->infoBasicBlock[jj].reachingDefs[k];
+            break;
+        }
+        else if(isBPartiallyOverlapA != OVERLAP_NO) {
+            /* B overlaps with A */
+            /* update reaching defs of variable B by checking reaching defs of bb->infoBasicBlock[jj] */
+            updateReachingDefB1(jj);
+            updateReachingDefB2(); //merge currentInfo with tmpInfo
+        }
+    }
+    /* update defUseTable by checking currentInfo */
+    updateDefUseTable();
+    return 0;
+}
+
+/** update xferPoints of currentBB
+    Traverse currentBB->defUseTable
+*/
+int updateXferPoints() {
+    int k = 0;
+    currentBB->num_xfer_points = 0;
+    DefUsePair* ptr = currentBB->defUseTable;
+    DefOrUseLink* ptrUse = NULL;
+    /* traverse the def use chain of the basic block */
+    while(ptr != NULL) {
+        LowOpndRegType defType = ptr->def.physicalType;
+        //if definition is for a variable of 32 bits
+        if(getRegSize(defType) == OpndSize_32) {
+            /* check usages of the definition, whether it reaches a GPR, a XMM, a FS, or a SS */
+            bool hasGpUsage = false;
+            bool hasGpUsage2 = false; //not a fake usage
+            bool hasXmmUsage = false;
+            bool hasFSUsage = false;
+            bool hasSSUsage = false;
+            ptrUse = ptr->uses;
+            while(ptrUse != NULL) {
+                if(ptrUse->physicalType == LowOpndRegType_gp) {
+                    hasGpUsage = true;
+                    if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
+                        hasGpUsage2 = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_ss) hasSSUsage = true;
+                if(ptrUse->physicalType == LowOpndRegType_fs ||
+                   ptrUse->physicalType == LowOpndRegType_fs_s)
+                    hasFSUsage = true;
+                if(ptrUse->physicalType == LowOpndRegType_xmm) {
+                    hasXmmUsage = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_xmm ||
+                   ptrUse->physicalType == LowOpndRegType_ss) {
+                    /* if a 32-bit definition reaches a xmm usage or a SS usage,
+                       insert a XFER_MEM_TO_XMM */
+                    insertLoadXfer(ptrUse->offsetPC,
+                                   ptrUse->regNum, LowOpndRegType_xmm);
+                }
+                ptrUse = ptrUse->next;
+            }
+            if(((hasXmmUsage || hasFSUsage || hasSSUsage) && defType == LowOpndRegType_gp) ||
+               (hasGpUsage && defType == LowOpndRegType_fs) ||
+               (defType == LowOpndRegType_ss && (hasGpUsage || hasXmmUsage || hasFSUsage))) {
+                /* insert a transfer if def is on a GPR, usage is on a XMM, FS or SS
+                                     if def is on a FS, usage is on a GPR
+                                     if def is on a SS, usage is on a GPR, XMM or FS
+                   transfer type is XFER_DEF_TO_GP_MEM if a real GPR usage exisits
+                   transfer type is XFER_DEF_TO_GP otherwise*/
+                currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = ptr->def.offsetPC;
+                currentBB->xferPoints[currentBB->num_xfer_points].regNum = ptr->def.regNum;
+                currentBB->xferPoints[currentBB->num_xfer_points].physicalType = ptr->def.physicalType;
+                if(hasGpUsage2) { //create an entry XFER_DEF_TO_GP_MEM
+                    currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_TO_GP_MEM;
+                }
+                else { //create an entry XFER_DEF_TO_MEM
+                    currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_TO_MEM;
+                }
+                currentBB->xferPoints[currentBB->num_xfer_points].tableIndex = k;
+#ifdef DEBUG_XFER_POINTS
+                ALOGI("insert XFER %d at def %d: V%d %d", currentBB->num_xfer_points, ptr->def.offsetPC, ptr->def.regNum, defType);
+#endif
+                currentBB->num_xfer_points++;
+                if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
+                    ALOGE("too many xfer points");
+                    dvmAbort();
+                }
+            }
+        }
+        else { /* def is on 64 bits */
+            bool hasGpUsageOfL = false; //exist a GPR usage of the low half
+            bool hasGpUsageOfH = false; //exist a GPR usage of the high half
+            bool hasGpUsageOfL2 = false;
+            bool hasGpUsageOfH2 = false;
+            bool hasMisaligned = false;
+            bool hasAligned = false;
+            bool hasFSUsage = false;
+            bool hasSSUsage = false;
+            ptrUse = ptr->uses;
+            while(ptrUse != NULL) {
+                if(ptrUse->physicalType == LowOpndRegType_gp &&
+                   ptrUse->regNum == ptr->def.regNum) {
+                    hasGpUsageOfL = true;
+                    if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
+                        hasGpUsageOfL2 = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_gp &&
+                   ptrUse->regNum == ptr->def.regNum + 1) {
+                    hasGpUsageOfH = true;
+                    if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
+                        hasGpUsageOfH2 = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_xmm &&
+                   ptrUse->regNum == ptr->def.regNum) {
+                    hasAligned = true;
+                    /* if def is on FS and use is on XMM, insert a XFER_MEM_TO_XMM */
+                    if(defType == LowOpndRegType_fs)
+                        insertLoadXfer(ptrUse->offsetPC,
+                                       ptrUse->regNum, LowOpndRegType_xmm);
+                }
+                if(ptrUse->physicalType == LowOpndRegType_fs ||
+                   ptrUse->physicalType == LowOpndRegType_fs_s)
+                    hasFSUsage = true;
+                if(ptrUse->physicalType == LowOpndRegType_xmm &&
+                   ptrUse->regNum != ptr->def.regNum) {
+                    hasMisaligned = true;
+                    /* if use is on XMM and use and def are misaligned, insert a XFER_MEM_TO_XMM */
+                    insertLoadXfer(ptrUse->offsetPC,
+                                   ptrUse->regNum, LowOpndRegType_xmm);
+                }
+                if(ptrUse->physicalType == LowOpndRegType_ss) {
+                    hasSSUsage = true;
+                    /* if use is on SS, insert a XFER_MEM_TO_XMM */
+                    insertLoadXfer(ptrUse->offsetPC,
+                                   ptrUse->regNum, LowOpndRegType_ss);
+                }
+                ptrUse = ptrUse->next;
+            }
+            if(defType == LowOpndRegType_fs && !hasGpUsageOfL && !hasGpUsageOfH) {
+                ptr = ptr->next;
+                continue;
+            }
+            if(defType == LowOpndRegType_xmm && !hasFSUsage &&
+               !hasGpUsageOfL && !hasGpUsageOfH && !hasMisaligned && !hasSSUsage) {
+                ptr = ptr->next;
+                continue;
+            }
+            /* insert a XFER_DEF_IS_XMM */
+            currentBB->xferPoints[currentBB->num_xfer_points].regNum = ptr->def.regNum;
+            currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = ptr->def.offsetPC;
+            currentBB->xferPoints[currentBB->num_xfer_points].physicalType = ptr->def.physicalType;
+            currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_IS_XMM;
+            currentBB->xferPoints[currentBB->num_xfer_points].vr_gpl = -1;
+            currentBB->xferPoints[currentBB->num_xfer_points].vr_gph = -1;
+            if(hasGpUsageOfL2) currentBB->xferPoints[currentBB->num_xfer_points].vr_gpl = ptr->def.regNum;
+            if(hasGpUsageOfH2) currentBB->xferPoints[currentBB->num_xfer_points].vr_gph = ptr->def.regNum+1;
+            currentBB->xferPoints[currentBB->num_xfer_points].dumpToMem = true;
+            currentBB->xferPoints[currentBB->num_xfer_points].dumpToXmm = false; //not used in updateVirtualReg
+            if(hasAligned) currentBB->xferPoints[currentBB->num_xfer_points].dumpToXmm = true;
+            currentBB->xferPoints[currentBB->num_xfer_points].tableIndex = k;
+#ifdef DEBUG_XFER_POINTS
+            ALOGI("insert XFER %d at def %d: V%d %d", currentBB->num_xfer_points, ptr->def.offsetPC, ptr->def.regNum, defType);
+#endif
+            currentBB->num_xfer_points++;
+            if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
+                ALOGE("too many xfer points");
+                dvmAbort();
+            }
+        }
+        ptr = ptr->next;
+    } //while ptr
+#ifdef DEBUG_XFER_POINTS
+    ALOGI("XFER points for current basic block ------");
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        ALOGI("  at offset %x, VR %d %d: type %d, vr_gpl %d, vr_gph %d, dumpToMem %d, dumpToXmm %d",
+              currentBB->xferPoints[k].offsetPC, currentBB->xferPoints[k].regNum,
+              currentBB->xferPoints[k].physicalType, currentBB->xferPoints[k].xtype,
+              currentBB->xferPoints[k].vr_gpl, currentBB->xferPoints[k].vr_gph,
+              currentBB->xferPoints[k].dumpToMem, currentBB->xferPoints[k].dumpToXmm);
+    }
+#endif
+    return -1;
+}
+
+//! update memVRTable[].ranges by browsing the defUseTable
+
+//! each virtual register has a list of live ranges, and each live range has a list of PCs that access the VR
+void updateLiveTable() {
+    DefUsePair* ptr = currentBB->defUseTable;
+    while(ptr != NULL) {
+        bool updateUse = false;
+        if(ptr->num_uses == 0) {
+            ptr->num_uses = 1;
+            ptr->uses = (DefOrUseLink*)malloc(sizeof(DefOrUseLink));
+            if(ptr->uses == NULL) {
+                ALOGE("Memory allocation failed");
+                return;
+            }
+            ptr->uses->accessType = REGACCESS_D;
+            ptr->uses->regNum = ptr->def.regNum;
+            ptr->uses->offsetPC = ptr->def.offsetPC;
+            ptr->uses->physicalType = ptr->def.physicalType;
+            ptr->uses->next = NULL;
+            ptr->useTail = ptr->uses;
+            updateUse = true;
+        }
+        DefOrUseLink* ptrUse = ptr->uses;
+        while(ptrUse != NULL) {
+            RegAccessType useType = ptrUse->accessType;
+            if(useType == REGACCESS_L || useType == REGACCESS_D) {
+                int indexL = searchMemTable(ptrUse->regNum);
+                if(indexL >= 0)
+                    mergeLiveRange(indexL, ptr->def.offsetPC,
+                                   ptrUse->offsetPC); //tableIndex, start PC, end PC
+            }
+            if(getRegSize(ptrUse->physicalType) == OpndSize_64 &&
+               (useType == REGACCESS_H || useType == REGACCESS_D)) {
+                int indexH = searchMemTable(ptrUse->regNum+1);
+                if(indexH >= 0)
+                    mergeLiveRange(indexH, ptr->def.offsetPC,
+                                   ptrUse->offsetPC);
+            }
+            ptrUse = ptrUse->next;
+        }//while ptrUse
+        if(updateUse) {
+            ptr->num_uses = 0;
+            free(ptr->uses);
+            ptr->uses = NULL;
+            ptr->useTail = NULL;
+        }
+        ptr = ptr->next;
+    }//while ptr
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVE TABLE");
+    for(int k = 0; k < num_memory_vr; k++) {
+        ALOGI("VR %d live ", memVRTable[k].regNum);
+        LiveRange* ptr = memVRTable[k].ranges;
+        while(ptr != NULL) {
+            ALOGI("[%x %x] (", ptr->start, ptr->end);
+            for(int k3 = 0; k3 < ptr->num_access; k3++)
+                ALOGI("%x ", ptr->accessPC[k3]);
+            ALOGI(") ");
+            ptr = ptr->next;
+        }
+        ALOGI("");
+    }
+#endif
+}
+
+//!add a live range [rangeStart, rangeEnd] to ranges of memVRTable, merge to existing live ranges if necessary
+
+//!ranges are in increasing order of startPC
+void mergeLiveRange(int tableIndex, int rangeStart, int rangeEnd) {
+    if(rangeStart == PC_FOR_START_OF_BB) rangeStart = currentBB->pc_start;
+    if(rangeEnd == PC_FOR_END_OF_BB) rangeEnd = currentBB->pc_end;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE call mergeLiveRange on tableIndex %d with [%x %x]", tableIndex, rangeStart, rangeEnd);
+#endif
+    int startIndex = -1, endIndex = -1;
+    bool startBeforeRange = false, endBeforeRange = false; //before the index or in the range
+    bool startDone = false, endDone = false;
+    LiveRange* ptr = memVRTable[tableIndex].ranges;
+    LiveRange* ptrStart = NULL;
+    LiveRange* ptrStart_prev = NULL;
+    LiveRange* ptrEnd = NULL;
+    LiveRange* ptrEnd_prev = NULL;
+    int k = 0;
+    while(ptr != NULL) {
+        if(!startDone) {
+            if(ptr->start <= rangeStart &&
+               ptr->end >= rangeStart) {
+                startIndex = k;
+                ptrStart = ptr;
+                startBeforeRange = false;
+                startDone = true;
+            }
+            else if(ptr->start > rangeStart) {
+                startIndex = k;
+                ptrStart = ptr;
+                startBeforeRange = true;
+                startDone = true;
+            }
+        }
+        if(!startDone) ptrStart_prev = ptr;
+        if(!endDone) {
+            if(ptr->start <= rangeEnd &&
+               ptr->end >= rangeEnd) {
+                endIndex = k;
+                ptrEnd = ptr;
+                endBeforeRange = false;
+                endDone = true;
+            }
+            else if(ptr->start > rangeEnd) {
+                endIndex = k;
+                ptrEnd = ptr;
+                endBeforeRange = true;
+                endDone = true;
+            }
+        }
+        if(!endDone) ptrEnd_prev = ptr;
+        ptr = ptr->next;
+        k++;
+    } //while
+    if(!startDone) { //both can be NULL
+        startIndex = memVRTable[tableIndex].num_ranges;
+        ptrStart = NULL; //ptrStart_prev should be the last live range
+        startBeforeRange = true;
+    }
+    //if endDone, ptrEnd is not NULL, ptrEnd_prev can be NULL
+    if(!endDone) { //both can be NULL
+        endIndex = memVRTable[tableIndex].num_ranges;
+        ptrEnd = NULL;
+        endBeforeRange = true;
+    }
+    if(startIndex == endIndex && startBeforeRange && endBeforeRange) { //insert at startIndex
+        //3 cases depending on BeforeRange when startIndex == endIndex
+        //insert only if both true
+        //merge otherwise
+        /////////// insert before ptrStart
+        LiveRange* currRange = (LiveRange *)malloc(sizeof(LiveRange));
+        if(ptrStart_prev == NULL) {
+            currRange->next = memVRTable[tableIndex].ranges;
+            memVRTable[tableIndex].ranges = currRange;
+        } else {
+            currRange->next = ptrStart_prev->next;
+            ptrStart_prev->next = currRange;
+        }
+        currRange->start = rangeStart;
+        currRange->end = rangeEnd;
+        currRange->accessPC = (int *)malloc(sizeof(int) * NUM_ACCESS_IN_LIVERANGE);
+        currRange->num_alloc = NUM_ACCESS_IN_LIVERANGE;
+        if(rangeStart != rangeEnd) {
+            currRange->num_access = 2;
+            currRange->accessPC[0] = rangeStart;
+            currRange->accessPC[1] = rangeEnd;
+        } else {
+            currRange->num_access = 1;
+            currRange->accessPC[0] = rangeStart;
+        }
+        memVRTable[tableIndex].num_ranges++;
+#ifdef DEBUG_LIVE_RANGE
+        ALOGI("LIVERANGE insert one live range [%x %x] to tableIndex %d", rangeStart, rangeEnd, tableIndex);
+#endif
+        return;
+    }
+    if(!endBeforeRange) { //here ptrEnd is not NULL
+        endIndex++; //next
+        ptrEnd_prev = ptrEnd; //ptrEnd_prev is not NULL
+        ptrEnd = ptrEnd->next; //ptrEnd can be NULL
+    }
+    if(endIndex < startIndex+1) ALOGE("mergeLiveRange endIndex %d startIndex %d", endIndex, startIndex);
+    ///////// use ptrStart & ptrEnd_prev
+    if(ptrStart == NULL || ptrEnd_prev == NULL) {
+        ALOGE("mergeLiveRange ptr is NULL");
+        return;
+    }
+    //endIndex > startIndex (merge the ranges between startIndex and endIndex-1)
+    //update ptrStart
+    if(ptrStart->start > rangeStart)
+        ptrStart->start = rangeStart; //min of old start & rangeStart
+    ptrStart->end = ptrEnd_prev->end; //max of old end & rangeEnd
+    if(rangeEnd > ptrStart->end)
+        ptrStart->end = rangeEnd;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE merge entries for tableIndex %d from %d to %d", tableIndex, startIndex+1, endIndex-1);
+#endif
+    if(ptrStart->num_access <= 0) ALOGE("mergeLiveRange number of access");
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE tableIndex %d startIndex %d num_access %d (", tableIndex, startIndex, ptrStart->num_access);
+    for(k = 0; k < ptrStart->num_access; k++)
+        ALOGI("%x ", ptrStart->accessPC[k]);
+    ALOGI(")");
+#endif
+    ///// go through pointers from ptrStart->next to ptrEnd
+    //from startIndex+1 to endIndex-1
+    ptr = ptrStart->next;
+    while(ptr != NULL && ptr != ptrEnd) {
+        int k2;
+        for(k2 = 0; k2 < ptr->num_access; k2++) { //merge to startIndex
+            insertAccess(tableIndex, ptrStart, ptr->accessPC[k2]);
+        }//k2
+        ptr = ptr->next;
+    }
+    insertAccess(tableIndex, ptrStart, rangeStart);
+    insertAccess(tableIndex, ptrStart, rangeEnd);
+    //remove startIndex+1 to endIndex-1
+    if(startIndex+1 < endIndex) {
+        ptr = ptrStart->next;
+        while(ptr != NULL && ptr != ptrEnd) {
+            LiveRange* tmpP = ptr->next;
+            free(ptr->accessPC);
+            free(ptr);
+            ptr = tmpP;
+        }
+        ptrStart->next = ptrEnd;
+    }
+    memVRTable[tableIndex].num_ranges -= (endIndex - startIndex - 1);
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("num_ranges for VR %d: %d", memVRTable[tableIndex].regNum, memVRTable[tableIndex].num_ranges);
+#endif
+}
+//! insert an access to a given live range, in order
+
+//!
+void insertAccess(int tableIndex, LiveRange* startP, int rangeStart) {
+    int k3, k4;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE insertAccess %d %x", tableIndex, rangeStart);
+#endif
+    int insertIndex = -1;
+    for(k3 = 0; k3 < startP->num_access; k3++) {
+        if(startP->accessPC[k3] == rangeStart) {
+            return;
+        }
+        if(startP->accessPC[k3] > rangeStart) {
+            insertIndex = k3;
+            break;
+        }
+    }
+
+    //insert here
+    k3 = insertIndex;
+    if(insertIndex == -1) {
+        k3 = startP->num_access;
+    }
+    if(startP->num_access == startP->num_alloc) {
+        int currentAlloc = startP->num_alloc;
+        startP->num_alloc += NUM_ACCESS_IN_LIVERANGE;
+        int* tmpPtr = (int *)malloc(sizeof(int) * startP->num_alloc);
+        for(k4 = 0; k4 < currentAlloc; k4++)
+            tmpPtr[k4] = startP->accessPC[k4];
+        free(startP->accessPC);
+        startP->accessPC = tmpPtr;
+    }
+    //insert accessPC
+    for(k4 = startP->num_access-1; k4 >= k3; k4--)
+        startP->accessPC[k4+1] = startP->accessPC[k4];
+    startP->accessPC[k3] = rangeStart;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE insert %x to tableIndex %d", rangeStart, tableIndex);
+#endif
+    startP->num_access++;
+    return;
+}
+
+/////////////////////////////////////////////////////////////////////
+bool isInMemory(int regNum, OpndSize size);
+void setVRToMemory(int regNum, OpndSize size);
+bool isVRLive(int vA);
+int getSpillIndex(bool isGLUE, OpndSize size);
+void clearVRToMemory(int regNum, OpndSize size);
+void clearVRNullCheck(int regNum, OpndSize size);
+
+inline int getSpillLocDisp(int offset) {
+#ifdef SPILL_IN_THREAD
+    return offset+offsetof(Thread, spillRegion);;
+#else
+    return offset+offEBP_spill;
+#endif
+}
+#if 0
+/* used if we keep self pointer in a physical register */
+inline int getSpillLocReg(int offset) {
+    return PhysicalReg_Glue;
+}
+#endif
+#ifdef SPILL_IN_THREAD
+inline void loadFromSpillRegion_with_self(OpndSize size, int reg_self, bool selfPhysical, int reg, int offset) {
+    /* only 1 instruction is generated by move_mem_to_reg_noalloc */
+    move_mem_to_reg_noalloc(size,
+                            getSpillLocDisp(offset), reg_self, selfPhysical,
+                            MemoryAccess_SPILL, offset,
+                            reg, true);
+}
+inline void loadFromSpillRegion(OpndSize size, int reg, int offset) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    int reg_self = registerAlloc(LowOpndRegType_scratch, C_SCRATCH_1, isScratchPhysical, false);
+    /* only 1 instruction is generated by move_mem_to_reg_noalloc */
+    move_mem_to_reg_noalloc(size,
+                            getSpillLocDisp(offset), reg_self, true,
+                            MemoryAccess_SPILL, offset,
+                            reg, true);
+}
+inline void saveToSpillRegion_with_self(OpndSize size, int selfReg, bool selfPhysical, int reg, int offset) {
+    move_reg_to_mem_noalloc(size,
+                            reg, true,
+                            getSpillLocDisp(offset), selfReg, selfPhysical,
+                            MemoryAccess_SPILL, offset);
+}
+inline void saveToSpillRegion(OpndSize size, int reg, int offset) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    int reg_self = registerAlloc(LowOpndRegType_scratch, C_SCRATCH_1, isScratchPhysical, false);
+    move_reg_to_mem_noalloc(size,
+                            reg, true,
+                            getSpillLocDisp(offset), reg_self, true,
+                            MemoryAccess_SPILL, offset);
+}
+#else
+inline void loadFromSpillRegion(OpndSize size, int reg, int offset) {
+    /* only 1 instruction is generated by move_mem_to_reg_noalloc */
+    move_mem_to_reg_noalloc(size,
+                            getSpillLocDisp(offset), PhysicalReg_EBP, true,
+                            MemoryAccess_SPILL, offset,
+                            reg, true);
+}
+inline void saveToSpillRegion(OpndSize size, int reg, int offset) {
+    move_reg_to_mem_noalloc(size,
+                            reg, true,
+                            getSpillLocDisp(offset), PhysicalReg_EBP, true,
+                            MemoryAccess_SPILL, offset);
+}
+#endif
+
+//! dump an immediate to memory, set inMemory to true
+
+//!
+void dumpImmToMem(int vrNum, OpndSize size, int value) {
+    if(isInMemory(vrNum, size)) {
+#ifdef DEBUG_SPILL
+        ALOGI("Skip dumpImmToMem vA %d size %d", vrNum, size);
+#endif
+        return;
+    }
+    set_VR_to_imm_noalloc(vrNum, size, value);
+    setVRToMemory(vrNum, size);
+}
+//! dump content of a VR to memory, set inMemory to true
+
+//!
+void dumpToMem(int vrNum, LowOpndRegType type, int regAll) { //ss,gp,xmm
+    if(isInMemory(vrNum, getRegSize(type))) {
+#ifdef DEBUG_SPILL
+        ALOGI("Skip dumpToMem vA %d type %d", vrNum, type);
+#endif
+        return;
+    }
+    if(type == LowOpndRegType_gp || type == LowOpndRegType_xmm)
+        set_virtual_reg_noalloc(vrNum, getRegSize(type), regAll, true);
+    if(type == LowOpndRegType_ss)
+        move_ss_reg_to_mem_noalloc(regAll, true,
+                                   4*vrNum, PhysicalReg_FP, true,
+                                   MemoryAccess_VR, vrNum);
+    setVRToMemory(vrNum, getRegSize(type));
+}
+//! dump part of a 64-bit VR to memory and update inMemory
+
+//! isLow tells whether low half or high half is dumped
+void dumpPartToMem(int reg /*xmm physical reg*/, int vA, bool isLow) {
+    if(isLow) {
+        if(isInMemory(vA, OpndSize_32)) {
+#ifdef DEBUG_SPILL
+            ALOGI("Skip dumpPartToMem isLow %d vA %d", isLow, vA);
+#endif
+            return;
+        }
+    }
+    else {
+        if(isInMemory(vA+1, OpndSize_32)) {
+#ifdef DEBUG_SPILL
+            ALOGI("Skip dumpPartToMem isLow %d vA %d", isLow, vA);
+#endif
+            return;
+        }
+    }
+    if(isLow) {
+        if(!isVRLive(vA)) return;
+    }
+    else {
+        if(!isVRLive(vA+1)) return;
+    }
+    //move part to vA or vA+1
+    if(isLow) {
+        move_ss_reg_to_mem_noalloc(reg, true,
+                                   4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+    } else {
+        int k = getSpillIndex(false, OpndSize_64);
+        //H, L in 4*k+4 & 4*k
+#ifdef SPILL_IN_THREAD
+        get_self_pointer(PhysicalReg_SCRATCH_1, isScratchPhysical);
+        saveToSpillRegion_with_self(OpndSize_64, PhysicalReg_SCRATCH_1, isScratchPhysical, reg, 4*k);
+        //update low 32 bits of xmm reg from 4*k+4
+        move_ss_mem_to_reg(NULL,
+                                   getSpillLocDisp(4*k+4), PhysicalReg_SCRATCH_1, isScratchPhysical,
+                                   reg, true);
+#else
+        saveToSpillRegion(OpndSize_64, reg, 4*k);
+        //update low 32 bits of xmm reg from 4*k+4
+        move_ss_mem_to_reg_noalloc(
+                                   getSpillLocDisp(4*k+4), PhysicalReg_EBP, true,
+                                   MemoryAccess_SPILL, 4*k+4,
+                                   reg, true);
+#endif
+        //move low 32 bits of xmm reg to vA+1
+        move_ss_reg_to_mem_noalloc(reg, true, 4*(vA+1), PhysicalReg_FP, true, MemoryAccess_VR, vA+1);
+    }
+    if(isLow)
+        setVRToMemory(vA, OpndSize_32);
+    else
+        setVRToMemory(vA+1, OpndSize_32);
+}
+void clearVRBoundCheck(int regNum, OpndSize size);
+//! the content of a VR is no longer in memory or in physical register if the latest content of a VR is constant
+
+//! clear nullCheckDone; if another VR is overlapped with the given VR, the content of that VR is no longer in physical register
+void invalidateVRDueToConst(int reg, OpndSize size) {
+    clearVRToMemory(reg, size); //memory content is out-dated
+    clearVRNullCheck(reg, size);
+    clearVRBoundCheck(reg, size);
+    //check reg,gp reg,ss reg,xmm reg-1,xmm
+    //if size is 64: check reg+1,gp|ss reg+1,xmm
+    int index;
+    //if VR is xmm, check whether we need to dump part of VR to memory
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_xmm);
+#endif
+        if(size == OpndSize_32)
+            dumpPartToMem(compileTable[index].physicalReg, reg, false); //dump high of xmm to memory
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg-1);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg-1, LowOpndRegType_xmm);
+#endif
+        dumpPartToMem(compileTable[index].physicalReg, reg-1, true); //dump low of xmm to memory
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_gp);
+#endif
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_ss);
+#endif
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    if(size == OpndSize_64) {
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_xmm);
+#endif
+            dumpPartToMem(compileTable[index].physicalReg, reg+1, false); //dump high of xmm to memory
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_gp);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_ss);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+}
+//! check which physical registers hold out-dated content if there is a def
+
+//! if another VR is overlapped with the given VR, the content of that VR is no longer in physical register
+//! should we update inMemory?
+void invalidateVR(int reg, LowOpndRegType pType) {
+    //def at fs: content of xmm & gp & ss are out-dated (reg-1,xmm reg,xmm reg+1,xmm) (reg,gp|ss reg+1,gp|ss)
+    //def at xmm: content of misaligned xmm & gp are out-dated (reg-1,xmm reg+1,xmm) (reg,gp|ss reg+1,gp|ss)
+    //def at fs_s: content of xmm & gp are out-dated (reg-1,xmm reg,xmm) (reg,gp|ss)
+    //def at gp:   content of xmm is out-dated (reg-1,xmm reg,xmm) (reg,ss)
+    //def at ss:   content of xmm & gp are out-dated (reg-1,xmm reg,xmm) (reg,gp)
+    int index;
+    if(pType != LowOpndRegType_xmm) { //check xmm @reg
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_xmm);
+#endif
+            if(getRegSize(pType) == OpndSize_32)
+                dumpPartToMem(compileTable[index].physicalReg, reg, false); //dump high of xmm to memory
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    //check misaligned xmm @ reg-1
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg-1);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg-1, LowOpndRegType_xmm);
+#endif
+        dumpPartToMem(compileTable[index].physicalReg, reg-1, true); //dump low of xmm to memory
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    //check misaligned xmm @ reg+1
+    if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
+        //check reg+1,xmm
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_xmm);
+#endif
+            dumpPartToMem(compileTable[index].physicalReg, reg+1, false); //dump high of xmm to memory
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType != LowOpndRegType_gp) {
+        //check reg,gp
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_gp);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
+        //check reg+1,gp
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_gp);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType != LowOpndRegType_ss) {
+        //check reg,ss
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_ss);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
+        //check reg+1,ss
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_ss);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+}
+//! bookkeeping when a VR is updated
+
+//! invalidate contents of some physical registers, clear nullCheckDone, and update inMemory;
+//! check whether there exist tranfer points for this bytecode, if yes, perform the transfer
+int updateVirtualReg(int reg, LowOpndRegType pType) {
+    int k;
+    OpndSize size = getRegSize(pType);
+    //WAS only invalidate xmm VRs for the following cases:
+    //if def reaches a use of vA,xmm and (the def is not xmm or is misaligned xmm)
+    //  invalidate "vA,xmm"
+    invalidateVR(reg, pType);
+    clearVRNullCheck(reg, size);
+    clearVRBoundCheck(reg, size);
+    if(pType == LowOpndRegType_fs || pType == LowOpndRegType_fs_s)
+        setVRToMemory(reg, size);
+    else {
+        clearVRToMemory(reg, size);
+    }
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        if(currentBB->xferPoints[k].offsetPC == offsetPC &&
+           currentBB->xferPoints[k].regNum == reg &&
+           currentBB->xferPoints[k].physicalType == pType &&
+           currentBB->xferPoints[k].xtype != XFER_MEM_TO_XMM) {
+            //perform the corresponding action for the def
+            PhysicalReg regAll;
+            if(currentBB->xferPoints[k].xtype == XFER_DEF_IS_XMM) {
+                //def at fs: content of xmm is out-dated
+                //def at xmm: content of misaligned xmm is out-dated
+                //invalidateXmmVR(currentBB->xferPoints[k].tableIndex);
+#ifdef DEBUG_XFER_POINTS
+                if(currentBB->xferPoints[k].dumpToXmm) ALOGI("XFER set_virtual_reg to xmm: xmm VR %d", reg);
+#endif
+                if(pType == LowOpndRegType_xmm)  {
+#ifdef DEBUG_XFER_POINTS
+                    ALOGI("XFER set_virtual_reg to memory: xmm VR %d", reg);
+#endif
+                    PhysicalReg regAll = (PhysicalReg)checkVirtualReg(reg, LowOpndRegType_xmm, 0 /* do not update*/);
+                    dumpToMem(reg, LowOpndRegType_xmm, regAll);
+                }
+                if(currentBB->xferPoints[k].vr_gpl >= 0) { //
+                }
+                if(currentBB->xferPoints[k].vr_gph >= 0) {
+                }
+            }
+            if((pType == LowOpndRegType_gp || pType == LowOpndRegType_ss) &&
+               (currentBB->xferPoints[k].xtype == XFER_DEF_TO_MEM ||
+                currentBB->xferPoints[k].xtype == XFER_DEF_TO_GP_MEM)) {
+                //the defined gp VR already in register
+                //invalidateXmmVR(currentBB->xferPoints[k].tableIndex);
+                regAll = (PhysicalReg)checkVirtualReg(reg, pType, 0 /* do not update*/);
+                dumpToMem(reg, pType, regAll);
+#ifdef DEBUG_XFER_POINTS
+                ALOGI("XFER set_virtual_reg to memory: gp VR %d", reg);
+#endif
+            }
+            if((pType == LowOpndRegType_fs_s || pType == LowOpndRegType_ss) &&
+               currentBB->xferPoints[k].xtype == XFER_DEF_TO_GP_MEM) {
+            }
+        }
+    }
+    return -1;
+}
+////////////////////////////////////////////////////////////////
+//REGISTER ALLOCATION
+int spillForHardReg(int regNum, int type);
+void decreaseRefCount(int index);
+int getFreeReg(int type, int reg, int indexToCompileTable);
+PhysicalReg spillForLogicalReg(int type, int reg, int indexToCompileTable);
+int unspillLogicalReg(int spill_index, int physicalReg);
+int searchVirtualInfoOfBB(LowOpndRegType type, int regNum, BasicBlock_O1* bb);
+bool isTemp8Bit(int type, int reg);
+bool matchType(int typeA, int typeB);
+int getNextAccess(int compileIndex);
+void dumpCompileTable();
+
+//! allocate a register for a variable
+
+//!if no physical register is free, call spillForLogicalReg to free up a physical register;
+//!if the variable is a temporary and it was spilled, call unspillLogicalReg to load from spill location to the allocated physical register;
+//!if updateRefCount is true, reduce reference count of the variable by 1
+int registerAlloc(int type, int reg, bool isPhysical, bool updateRefCount) {
+#ifdef DEBUG_REGALLOC
+    ALOGI("%p: try to allocate register %d type %d isPhysical %d", currentBB, reg, type, isPhysical);
+#endif
+    if(currentBB == NULL) {
+        if(type & LowOpndRegType_virtual) return PhysicalReg_Null;
+        if(isPhysical) return reg; //for helper functions
+        return PhysicalReg_Null;
+    }
+    //ignore EDI, ESP, EBP (glue)
+    if(isPhysical && (reg == PhysicalReg_EDI || reg == PhysicalReg_ESP ||
+                      reg == PhysicalReg_EBP || reg == PhysicalReg_Null))
+        return reg;
+
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int tIndex = searchCompileTable(newType, reg);
+    if(tIndex < 0) {
+      ALOGE("reg %d type %d not found in registerAlloc", reg, newType);
+      return PhysicalReg_Null;
+    }
+
+    //physical register
+    if(isPhysical) {
+        if(allRegs[reg].isUsed) { //if used by a non hard-coded register
+            spillForHardReg(reg, newType);
+        }
+        allRegs[reg].isUsed = true;
+#ifdef DEBUG_REG_USED
+        ALOGI("REGALLOC: allocate a reg %d", reg);
+#endif
+        compileTable[tIndex].physicalReg = reg;
+        if(updateRefCount)
+            decreaseRefCount(tIndex);
+#ifdef DEBUG_REGALLOC
+        ALOGI("REGALLOC: allocate register %d for logical register %d %d",
+               compileTable[tIndex].physicalReg, reg, newType);
+#endif
+        return reg;
+    }
+    //already allocated
+    if(compileTable[tIndex].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_REGALLOC
+        ALOGI("already allocated to physical register %d", compileTable[tIndex].physicalReg);
+#endif
+        if(updateRefCount)
+            decreaseRefCount(tIndex);
+        return compileTable[tIndex].physicalReg;
+    }
+
+    //at this point, the logical register is not hard-coded and is mapped to Reg_Null
+    //first check whether there is a free reg
+    //if not, call spillForLogicalReg
+    int index = getFreeReg(newType, reg, tIndex);
+    if(index >= 0 && index < PhysicalReg_Null) {
+        //update compileTable & allRegs
+        compileTable[tIndex].physicalReg = allRegs[index].physicalReg;
+        allRegs[index].isUsed = true;
+#ifdef DEBUG_REG_USED
+        ALOGI("REGALLOC: register %d is free", allRegs[index].physicalReg);
+#endif
+    } else {
+        PhysicalReg allocR = spillForLogicalReg(newType, reg, tIndex);
+        compileTable[tIndex].physicalReg = allocR;
+    }
+    if(compileTable[tIndex].spill_loc_index >= 0) {
+        unspillLogicalReg(tIndex, compileTable[tIndex].physicalReg);
+    }
+    if(updateRefCount)
+        decreaseRefCount(tIndex);
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: allocate register %d for logical register %d %d",
+           compileTable[tIndex].physicalReg, reg, newType);
+#endif
+    return compileTable[tIndex].physicalReg;
+}
+//!a variable will use a physical register allocated for another variable
+
+//!This is used when MOVE_OPT is on, it tries to alias a virtual register with a temporary to remove a move
+int registerAllocMove(int reg, int type, bool isPhysical, int srcReg) {
+    if(srcReg == PhysicalReg_EDI || srcReg == PhysicalReg_ESP || srcReg == PhysicalReg_EBP)
+        ALOGE("can't move from srcReg EDI or ESP or EBP");
+#ifdef DEBUG_REGALLOC
+    ALOGI("in registerAllocMove: reg %d type %d srcReg %d", reg, type, srcReg);
+#endif
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index = searchCompileTable(newType, reg);
+    if(index < 0) {
+        ALOGE("reg %d type %d not found in registerAllocMove", reg, newType);
+        return -1;
+    }
+
+    decreaseRefCount(index);
+    compileTable[index].physicalReg = srcReg;
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: registerAllocMove %d for logical register %d %d",
+           compileTable[index].physicalReg, reg, newType);
+#endif
+    return srcReg;
+}
+
+//! check whether a physical register is available to be used by a variable
+
+//! data structures accessed:
+//! 1> currentBB->infoBasicBlock[index].allocConstraintsSorted
+//!    sorted from high count to low count
+//! 2> currentBB->allocConstraintsSorted
+//!    sorted from low count to high count
+//! 3> allRegs: whether a physical register is available, indexed by PhysicalReg
+//! NOTE: if a temporary variable is 8-bit, only %eax, %ebx, %ecx, %edx can be used
+int getFreeReg(int type, int reg, int indexToCompileTable) {
+    syncAllRegs();
+    /* handles requests for xmm or ss registers */
+    int k;
+    if(((type & MASK_FOR_TYPE) == LowOpndRegType_xmm) ||
+       ((type & MASK_FOR_TYPE) == LowOpndRegType_ss)) {
+        for(k = PhysicalReg_XMM0; k <= PhysicalReg_XMM7; k++) {
+            if(!allRegs[k].isUsed) return k;
+        }
+        return -1;
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("USED registers: ");
+    for(k = 0; k < 8; k++)
+        ALOGI("%d used: %d time freed: %d callee-saveld: %d", k, allRegs[k].isUsed,
+             allRegs[k].freeTimeStamp, allRegs[k].isCalleeSaved);
+    ALOGI("");
+#endif
+
+    /* a VR is requesting a physical register */
+    if(isVirtualReg(type)) { //find a callee-saved register
+        /* if VR is type GG, check the pre-allocated physical register first */
+        bool isGGVR = compileTable[indexToCompileTable].gType == GLOBALTYPE_GG;
+        if(isGGVR) {
+            int regCandidateT = compileTable[indexToCompileTable].physicalReg_prev;
+            if(!allRegs[regCandidateT].isUsed) return regCandidateT;
+        }
+
+        int index = searchVirtualInfoOfBB((LowOpndRegType)(type&MASK_FOR_TYPE), reg, currentBB);
+        if(index < 0) {
+            ALOGE("VR %d %d not found in infoBasicBlock of currentBB %d (num of VRs %d)",
+                  reg, type, currentBB->bb_index, currentBB->num_regs);
+            dvmAbort();
+        }
+
+        /* check allocConstraints for this VR,
+           return an available physical register with the highest constraint > 0 */
+        for(k = 0; k < 8; k++) {
+            if(currentBB->infoBasicBlock[index].allocConstraintsSorted[k].count == 0) break;
+            int regCandidateT = currentBB->infoBasicBlock[index].allocConstraintsSorted[k].physicalReg;
+            assert(regCandidateT < PhysicalReg_Null);
+            if(!allRegs[regCandidateT].isUsed) return regCandidateT;
+        }
+
+        /* WAS: return an available physical register with the lowest constraint
+           NOW: consider a new factor (freeTime) when there is a tie
+                if 2 available physical registers have the same number of constraints
+                choose the one with smaller free time stamp */
+        int currentCount = -1;
+        int index1 = -1;
+        int smallestTime = -1;
+        for(k = 0; k < 8; k++) {
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            assert(regCandidateT < PhysicalReg_Null);
+            if(index1 >= 0 && currentBB->allocConstraintsSorted[k].count > currentCount)
+                break; //candidate has higher count than index1
+            if(!allRegs[regCandidateT].isUsed) {
+                if(index1 < 0) {
+                    index1 = k;
+                    currentCount = currentBB->allocConstraintsSorted[k].count;
+                    smallestTime = allRegs[regCandidateT].freeTimeStamp;
+                } else if(allRegs[regCandidateT].freeTimeStamp < smallestTime) {
+                    index1 = k;
+                    smallestTime = allRegs[regCandidateT].freeTimeStamp;
+                }
+            }
+        }
+        if(index1 >= 0) return currentBB->allocConstraintsSorted[index1].physicalReg;
+        return -1;
+    }
+    /* handle request from a temporary variable or a glue variable */
+    else {
+        bool is8Bit = isTemp8Bit(type, reg);
+
+        /* if the temporary variable is linked to a VR and
+              the VR is not yet allocated to any physical register */
+        int vr_num = compileTable[indexToCompileTable].linkageToVR;
+        if(vr_num >= 0) {
+            int index3 = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_virtual, vr_num);
+            if(index3 < 0) {
+                ALOGE("2 in tracing linkage to VR %d", vr_num);
+                dvmAbort();
+            }
+
+            if(compileTable[index3].physicalReg == PhysicalReg_Null) {
+                int index2 = searchVirtualInfoOfBB(LowOpndRegType_gp, vr_num, currentBB);
+                if(index2 < 0) {
+                    ALOGE("1 in tracing linkage to VR %d", vr_num);
+                    dvmAbort();
+                }
+#ifdef DEBUG_REGALLOC
+                ALOGI("in getFreeReg for temporary reg %d, trace the linkage to VR %d",
+                     reg, vr_num);
+#endif
+
+                /* check allocConstraints on the VR
+                   return an available physical register with the highest constraint > 0
+                */
+                for(k = 0; k < 8; k++) {
+                    if(currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].count == 0) break;
+                    int regCandidateT = currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].physicalReg;
+#ifdef DEBUG_REGALLOC
+                    ALOGI("check register %d with count %d", regCandidateT,
+                          currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].count);
+#endif
+                    /* if the requesting variable is 8 bit */
+                    if(is8Bit && regCandidateT > PhysicalReg_EDX) continue;
+                    assert(regCandidateT < PhysicalReg_Null);
+                    if(!allRegs[regCandidateT].isUsed) return regCandidateT;
+                }
+            }
+        }
+        /* check allocConstraints of the basic block
+           if 2 available physical registers have the same constraint count,
+              return the non callee-saved physical reg */
+        /* enhancement: record the time when a register is freed (freeTimeStamp)
+                        the purpose is to reduce false dependency
+           priority: constraint count, non callee-saved, time freed
+               let x be the lowest constraint count
+               set A be available callee-saved physical registers with count == x
+               set B be available non callee-saved physical registers with count == x
+               if set B is not null, return the one with smallest free time
+               otherwise, return the one in A with smallest free time
+           To ignore whether it is callee-saved, add all candidates to set A
+        */
+        int setAIndex[8];
+        int num_A = 0;
+        int setBIndex[8];
+        int num_B = 0;
+        int index1 = -1; //points to an available physical reg with lowest count
+        int currentCount = -1;
+        for(k = 0; k < 8; k++) {
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            if(is8Bit && regCandidateT > PhysicalReg_EDX) continue;
+
+            if(index1 >= 0 && currentBB->allocConstraintsSorted[k].count > currentCount)
+                break; //candidate has higher count than index1
+            assert(regCandidateT < PhysicalReg_Null);
+            if(!allRegs[regCandidateT].isUsed) {
+                /*To ignore whether it is callee-saved, add all candidates to set A */
+                if(false) {//!allRegs[regCandidateT].isCalleeSaved) { //add to set B
+                    setBIndex[num_B++] = k;
+                } else { //add to set A
+                    setAIndex[num_A++] = k;
+                }
+                if(index1 < 0) {
+                    /* index1 points to a physical reg with lowest count */
+                    index1 = k;
+                    currentCount = currentBB->allocConstraintsSorted[k].count;
+                }
+            }
+        }
+
+        int kk;
+        int smallestTime = -1;
+        index1 = -1;
+        for(kk = 0; kk < num_B; kk++) {
+            k = setBIndex[kk];
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            assert(regCandidateT < PhysicalReg_Null);
+            if(kk == 0 || allRegs[regCandidateT].freeTimeStamp < smallestTime) {
+                index1 = k;
+                smallestTime = allRegs[regCandidateT].freeTimeStamp;
+            }
+        }
+        if(index1 >= 0)
+            return currentBB->allocConstraintsSorted[index1].physicalReg;
+        index1 = -1;
+        for(kk = 0; kk < num_A; kk++) {
+            k = setAIndex[kk];
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            if(kk == 0 || allRegs[regCandidateT].freeTimeStamp < smallestTime) {
+                index1 = k;
+                smallestTime = allRegs[regCandidateT].freeTimeStamp;
+            }
+        }
+        if(index1 >= 0) return currentBB->allocConstraintsSorted[index1].physicalReg;
+        return -1;
+    }
+    return -1;
+}
+
+//! find a candidate physical register for a variable and spill all variables that are mapped to the candidate
+
+//!
+PhysicalReg spillForLogicalReg(int type, int reg, int indexToCompileTable) {
+    //choose a used register to spill
+    //when a GG virtual register is spilled, write it to interpretd stack, set physicalReg to Null
+    //  at end of the basic block, load spilled GG VR to physical reg
+    //when other types of VR is spilled, write it to interpreted stack, set physicalReg to Null
+    //when a temporary (non-virtual) register is spilled, write it to stack, set physicalReg to Null
+    //can we spill a hard-coded temporary register? YES
+    int k, k2;
+    PhysicalReg allocR;
+
+    //do not try to free a physical reg that is used by more than one logical registers
+    //fix on sep 28, 2009
+    //do not try to spill a hard-coded logical register
+    //do not try to free a physical reg that is outside of the range for 8-bit logical reg
+    /* for each physical register,
+       collect number of non-hardcode entries that are mapped to the physical register */
+    int numOfUses[PhysicalReg_Null];
+    for(k = PhysicalReg_EAX; k < PhysicalReg_Null; k++)
+        numOfUses[k] = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if((compileTable[k].physicalReg != PhysicalReg_Null) &&
+           matchType(type, compileTable[k].physicalType) &&
+           (compileTable[k].physicalType & LowOpndRegType_hard) == 0) {
+            numOfUses[compileTable[k].physicalReg]++;
+        }
+    }
+
+    /* candidates: all non-hardcode entries that are mapped to
+           a physical register that is used by only one entry*/
+    bool is8Bit = isTemp8Bit(type, reg);
+    int candidates[COMPILE_TABLE_SIZE];
+    int num_cand = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(matchType(type, compileTable[k].physicalType) &&
+           compileTable[k].physicalReg != PhysicalReg_Null) {
+            if(is8Bit && compileTable[k].physicalReg > PhysicalReg_EDX) continue; //not a candidate
+            if(!canSpillReg[compileTable[k].physicalReg]) continue; //not a candidate
+            if((compileTable[k].physicalType & LowOpndRegType_hard) == 0 &&
+               numOfUses[compileTable[k].physicalReg] <= 1) {
+                candidates[num_cand++] = k;
+            }
+        }
+    }
+
+    /* go through all candidates:
+       first check GLUE-related entries */
+    int spill_index = -1;
+    for(k2 = 0; k2 < num_cand; k2++) {
+        k = candidates[k2];
+        if((compileTable[k].physicalReg != PhysicalReg_Null) &&
+           matchType(type, compileTable[k].physicalType) &&
+           (compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
+            compileTable[k].regNum != PhysicalReg_GLUE)) {
+            allocR = (PhysicalReg)spillLogicalReg(k, true);
+#ifdef DEBUG_REGALLOC
+            ALOGI("SPILL register used by num %d type %d it is a GLUE register with refCount %d",
+                  compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
+#endif
+            return allocR;
+        }
+    }
+
+    /* out of the candates, find a VR that has the furthest next use */
+    int furthestUse = offsetPC;
+    for(k2 = 0; k2 < num_cand; k2++) {
+        k = candidates[k2];
+        if((compileTable[k].physicalReg != PhysicalReg_Null) &&
+           matchType(type, compileTable[k].physicalType) &&
+           isVirtualReg(compileTable[k].physicalType)) {
+            int nextUse = getNextAccess(k);
+            if(spill_index < 0 || nextUse > furthestUse) {
+                spill_index = k;
+                furthestUse = nextUse;
+            }
+        }
+    }
+
+    /* spill the VR with the furthest next use */
+    if(spill_index >= 0) {
+        allocR = (PhysicalReg)spillLogicalReg(spill_index, true);
+        return allocR; //the register is still being used
+    }
+
+    /* spill an entry with the smallest refCount */
+    int baseLeftOver = 0;
+    int index = -1;
+    for(k2 = 0; k2 < num_cand; k2++) {
+        k = candidates[k2];
+        if(k != indexForGlue &&
+           (compileTable[k].physicalReg != PhysicalReg_Null) &&
+           (compileTable[k].physicalType & LowOpndRegType_hard) == 0 && //not hard-coded
+           matchType(type, compileTable[k].physicalType)) {
+            if((index < 0) || (compileTable[k].refCount < baseLeftOver)) {
+                baseLeftOver = compileTable[k].refCount;
+                index = k;
+            }
+        }
+    }
+    if(index < 0) {
+        dumpCompileTable();
+        ALOGE("no register to spill for logical %d %d", reg, type);
+        dvmAbort();
+    }
+    allocR = (PhysicalReg)spillLogicalReg(index, true);
+#ifdef DEBUG_REGALLOC
+    ALOGI("SPILL register used by num %d type %d it is a temporary register with refCount %d",
+           compileTable[index].regNum, compileTable[index].physicalType, compileTable[index].refCount);
+#endif
+    return allocR;
+}
+//!spill a variable to memory, the variable is specified by an index to compileTable
+
+//!If the variable is a temporary, get a spill location that is not in use and spill the content to the spill location;
+//!If updateTable is true, set physicalReg to Null;
+//!Return the physical register that was allocated to the variable
+int spillLogicalReg(int spill_index, bool updateTable) {
+    if((compileTable[spill_index].physicalType & LowOpndRegType_hard) != 0) {
+        ALOGE("can't spill a hard-coded register");
+        dvmAbort();
+    }
+    int physicalReg = compileTable[spill_index].physicalReg;
+    if(!canSpillReg[physicalReg]) {
+#ifdef PRINT_WARNING
+        ALOGW("can't spill register %d", physicalReg);
+#endif
+        //dvmAbort(); //this happens in get_virtual_reg where VR is allocated to the same reg as the hardcoded temporary
+    }
+    if(isVirtualReg(compileTable[spill_index].physicalType)) {
+        //spill back to memory
+        dumpToMem(compileTable[spill_index].regNum,
+                  (LowOpndRegType)(compileTable[spill_index].physicalType&MASK_FOR_TYPE),
+                  compileTable[spill_index].physicalReg);
+    }
+    else {
+        //update spill_loc_index
+        int k = getSpillIndex(spill_index == indexForGlue,
+                    getRegSize(compileTable[spill_index].physicalType));
+        compileTable[spill_index].spill_loc_index = 4*k;
+        if(k >= 0)
+            spillIndexUsed[k] = 1;
+        saveToSpillRegion(getRegSize(compileTable[spill_index].physicalType),
+                          compileTable[spill_index].physicalReg, 4*k);
+    }
+    //compileTable[spill_index].physicalReg_prev = compileTable[spill_index].physicalReg;
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: SPILL logical reg %d %d with refCount %d allocated to %d",
+           compileTable[spill_index].regNum,
+           compileTable[spill_index].physicalType, compileTable[spill_index].refCount,
+           compileTable[spill_index].physicalReg);
+#endif
+    if(!updateTable) return PhysicalReg_Null;
+
+    int allocR = compileTable[spill_index].physicalReg;
+    compileTable[spill_index].physicalReg = PhysicalReg_Null;
+    return allocR;
+}
+//! load a varible from memory to physical register, the variable is specified with an index to compileTable
+
+//!If the variable is a temporary, load from spill location and set the flag for the spill location to not used
+int unspillLogicalReg(int spill_index, int physicalReg) {
+    //can't un-spill to %eax in afterCall!!!
+    //what if GG VR is allocated to %eax!!!
+    if(isVirtualReg(compileTable[spill_index].physicalType)) {
+        get_virtual_reg_noalloc(compileTable[spill_index].regNum,
+                                getRegSize(compileTable[spill_index].physicalType),
+                                physicalReg, true);
+    }
+    else {
+        loadFromSpillRegion(getRegSize(compileTable[spill_index].physicalType),
+                            physicalReg, compileTable[spill_index].spill_loc_index);
+        spillIndexUsed[compileTable[spill_index].spill_loc_index >> 2] = 0;
+        compileTable[spill_index].spill_loc_index = -1;
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: UNSPILL logical reg %d %d with refCount %d", compileTable[spill_index].regNum,
+           compileTable[spill_index].physicalType, compileTable[spill_index].refCount);
+#endif
+    return PhysicalReg_Null;
+}
+
+//!spill a virtual register to memory
+
+//!if the current value of a VR is constant, write immediate to memory;
+//!if the current value of a VR is in a physical register, call spillLogicalReg to dump content of the physical register to memory;
+//!ifupdateTable is true, set the physical register for VR to Null and decrease reference count of the virtual register
+int spillVirtualReg(int vrNum, LowOpndRegType type, bool updateTable) {
+    int index = searchCompileTable(type | LowOpndRegType_virtual, vrNum);
+    if(index < 0) {
+        ALOGE("can't find VR %d %d in spillVirtualReg", vrNum, type);
+        return -1;
+    }
+    //check whether it is const
+    int value[2];
+    int isConst = isVirtualRegConstant(vrNum, type, value, false); //do not update refCount
+    if(isConst == 1 || isConst == 3) {
+        dumpImmToMem(vrNum, OpndSize_32, value[0]);
+    }
+    if(getRegSize(type) == OpndSize_64 && (isConst == 2 || isConst == 3)) {
+        dumpImmToMem(vrNum+1, OpndSize_32, value[1]);
+    }
+    if(isConst != 3 && compileTable[index].physicalReg != PhysicalReg_Null)
+        spillLogicalReg(index, updateTable);
+    if(updateTable) decreaseRefCount(index);
+    return -1;
+}
+
+//! spill variables that are mapped to physical register (regNum)
+
+//!
+int spillForHardReg(int regNum, int type) {
+    //find an entry that uses the physical register
+    int spill_index = -1;
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(k != indexForGlue &&
+           compileTable[k].physicalReg == regNum &&
+           matchType(type, compileTable[k].physicalType)) {
+            spill_index = k;
+            if(compileTable[k].regNum == regNum && compileTable[k].physicalType == type)
+                continue;
+            if(inGetVR_num >= 0 && compileTable[k].regNum == inGetVR_num && compileTable[k].physicalType == (type | LowOpndRegType_virtual))
+                continue;
+#ifdef DEBUG_REGALLOC
+            ALOGI("SPILL logical reg %d %d to free hard-coded reg %d %d",
+                   compileTable[spill_index].regNum, compileTable[spill_index].physicalType,
+                   regNum, type);
+            if(compileTable[spill_index].physicalType & LowOpndRegType_hard) dumpCompileTable();
+#endif
+            assert(spill_index < COMPILE_TABLE_SIZE);
+            spillLogicalReg(spill_index, true);
+        }
+    }
+    return regNum;
+}
+////////////////////////////////////////////////////////////////
+//! update allocConstraints of the current basic block
+
+//! allocConstraints specify how many times a hardcoded register is used in this basic block
+void updateCurrentBBWithConstraints(PhysicalReg reg) {
+    if(reg > PhysicalReg_EBP) {
+        ALOGE("register %d out of range in updateCurrentBBWithConstraints", reg);
+    }
+    currentBB->allocConstraints[reg].count++;
+}
+//! sort allocConstraints and save the result in allocConstraintsSorted
+
+//! allocConstraints specify how many times a virtual register is linked to a hardcode register
+//! it is updated in getVirtualRegInfo and merged by mergeEntry2
+int sortAllocConstraint(RegAllocConstraint* allocConstraints,
+                        RegAllocConstraint* allocConstraintsSorted, bool fromHighToLow) {
+    int ii, jj;
+    int num_sorted = 0;
+    for(jj = 0; jj < 8; jj++) {
+        //figure out where to insert allocConstraints[jj]
+        int count = allocConstraints[jj].count;
+        int regT = allocConstraints[jj].physicalReg;
+        assert(regT < PhysicalReg_Null);
+        int insertIndex = -1;
+        for(ii = 0; ii < num_sorted; ii++) {
+            int regT2 = allocConstraintsSorted[ii].physicalReg;
+            assert(regT2 < PhysicalReg_Null);
+            if(allRegs[regT].isCalleeSaved &&
+               count == allocConstraintsSorted[ii].count) {
+                insertIndex = ii;
+                break;
+            }
+            if((!allRegs[regT].isCalleeSaved) &&
+               count == allocConstraintsSorted[ii].count &&
+               (!allRegs[regT2].isCalleeSaved)) { //skip until found one that is not callee-saved
+                insertIndex = ii;
+                break;
+            }
+            if((fromHighToLow && count > allocConstraintsSorted[ii].count) ||
+               ((!fromHighToLow) && count < allocConstraintsSorted[ii].count)) {
+                insertIndex = ii;
+                break;
+            }
+        }
+        if(insertIndex < 0) {
+            allocConstraintsSorted[num_sorted].physicalReg = (PhysicalReg)regT;
+            allocConstraintsSorted[num_sorted].count = count;
+            num_sorted++;
+        } else {
+            for(ii = num_sorted-1; ii >= insertIndex; ii--) {
+                allocConstraintsSorted[ii+1] = allocConstraintsSorted[ii];
+            }
+            allocConstraintsSorted[insertIndex] = allocConstraints[jj];
+            num_sorted++;
+        }
+    } //for jj
+#ifdef DEBUG_ALLOC_CONSTRAINT
+    for(jj = 0; jj < 8; jj++) {
+        if(allocConstraintsSorted[jj].count > 0)
+            ALOGI("%d: register %d has count %d", jj, allocConstraintsSorted[jj].physicalReg, allocConstraintsSorted[jj].count);
+    }
+#endif
+    return 0;
+}
+//! find the entry for a given virtual register in compileTable
+
+//!
+int findVirtualRegInTable(u2 vA, LowOpndRegType type, bool printError) {
+    int k = searchCompileTable(type | LowOpndRegType_virtual, vA);
+    if(k < 0 && printError) {
+        ALOGE("findVirtualRegInTable virtual register %d type %d", vA, type);
+        dvmAbort();
+    }
+    return k;
+}
+
+//! check whether a virtual register is constant
+
+//! the value of the constant is stored in valuePtr; if updateRefCount is true & the VR is constant, reference count for the VR will be reduced by 1
+int isVirtualRegConstant(int regNum, LowOpndRegType type, int* valuePtr, bool updateRefCount) {
+
+    OpndSize size = getRegSize(type);
+    int k;
+    int indexL = -1;
+    int indexH = -1;
+    for(k = 0; k < num_const_vr; k++) {
+#ifdef DEBUG_CONST
+        ALOGI("constVRTable VR %d isConst %d value %x", constVRTable[k].regNum, constVRTable[k].isConst, constVRTable[k].value);
+#endif
+        if(constVRTable[k].regNum == regNum) {
+            indexL = k;
+            continue;
+        }
+        if(constVRTable[k].regNum == regNum + 1 && size == OpndSize_64) {
+            indexH = k;
+            continue;
+        }
+    }
+    bool isConstL = false;
+    bool isConstH = false;
+    if(indexL >= 0) {
+        isConstL = constVRTable[indexL].isConst;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        isConstH = constVRTable[indexH].isConst;
+    }
+
+    if((isConstL || isConstH)) {
+        if(size == OpndSize_64 && isConstH)
+            valuePtr[1] = constVRTable[indexH].value;
+        if(isConstL)
+            *valuePtr = constVRTable[indexL].value;
+    }
+    if((isConstL && size == OpndSize_32) || (isConstL && isConstH)) {
+        if(updateRefCount) {
+            int indexOrig = searchCompileTable(type | LowOpndRegType_virtual, regNum);
+            if(indexOrig < 0) ALOGE("can't find VR in isVirtualRegConstant num %d type %d", regNum, type);
+            decreaseRefCount(indexOrig);
+        }
+#ifdef DEBUG_CONST
+        ALOGI("VR %d %d is const case", regNum, type);
+#endif
+        return 3;
+    }
+    if(size == OpndSize_32) return 0;
+    if(isConstL) return 1;
+    if(isConstH) return 2;
+    return 0;
+}
+
+//!update RegAccessType of virtual register vB given RegAccessType of vA
+
+//!RegAccessType can be D, L, H
+//!D means full definition, L means only lower-half is defined, H means only higher half is defined
+//!we say a VR has no exposed usage in a basic block if the accessType is D or DU
+//!we say a VR has exposed usage in a basic block if the accessType is not D nor DU
+//!we say a VR has exposed usage in other basic blocks (hasOtherExposedUsage) if
+//!  there exists another basic block where VR has exposed usage in that basic block
+//!A can be U, D, L, H, UD, UL, UH, DU, LU, HU (merged result)
+//!B can be U, D, UD, DU (an entry for the current bytecode)
+//!input isAPartiallyOverlapB can be any value between -1 to 6
+//!if A is xmm: gp B lower half of A, (isAPartiallyOverlapB is 1)
+//!             gp B higher half of A, (isAPartiallyOverlapB is 2)
+//!             lower half of A covers the higher half of xmm B  (isAPartiallyOverlapB is 4)
+//!             higher half of A covers the lower half of xmm B   (isAPartiallyOverlapB is 3)
+//!if A is gp:  A covers the lower half of xmm B, (isAPartiallyOverlapB is 5)
+//!             A covers the higher half of xmm B (isAPartiallyOverlapB is 6)
+RegAccessType updateAccess1(RegAccessType A, OverlapCase isAPartiallyOverlapB) {
+    if(A == REGACCESS_D || A == REGACCESS_DU || A == REGACCESS_UD) {
+        if(isAPartiallyOverlapB == OVERLAP_ALIGN) return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A || isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A)
+            return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
+            return REGACCESS_L;
+        return REGACCESS_H;
+    }
+    if(A == REGACCESS_L || A == REGACCESS_LU || A == REGACCESS_UL) {
+        if(isAPartiallyOverlapB == OVERLAP_ALIGN || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
+            return REGACCESS_L;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A) return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A || isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B)
+            return REGACCESS_N;
+        if(isAPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B)
+            return REGACCESS_H;
+    }
+    if(A == REGACCESS_H || A == REGACCESS_HU || A == REGACCESS_UH) {
+        if(isAPartiallyOverlapB == OVERLAP_ALIGN || isAPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B)
+            return REGACCESS_H;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A || isAPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B)
+            return REGACCESS_N;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A) return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
+            return REGACCESS_L;
+    }
+    return REGACCESS_N;
+}
+//! merge RegAccessType C1 with RegAccessType C2
+
+//!C can be N,L,H,D
+RegAccessType updateAccess2(RegAccessType C1, RegAccessType C2) {
+    if(C1 == REGACCESS_D || C2 == REGACCESS_D) return REGACCESS_D;
+    if(C1 == REGACCESS_N) return C2;
+    if(C2 == REGACCESS_N) return C1;
+    if(C1 == REGACCESS_L && C2 == REGACCESS_H) return REGACCESS_D;
+    if(C1 == REGACCESS_H && C2 == REGACCESS_L) return REGACCESS_D;
+    return C1;
+}
+//! merge RegAccessType C with RegAccessType B
+
+//!C can be N,L,H,D
+//!B can be U, D, UD, DU
+RegAccessType updateAccess3(RegAccessType C, RegAccessType B) {
+    if(B == REGACCESS_D || B == REGACCESS_DU) return B; //no exposed usage
+    if(B == REGACCESS_U || B == REGACCESS_UD) {
+        if(C == REGACCESS_N) return B;
+        if(C == REGACCESS_L) return REGACCESS_LU;
+        if(C == REGACCESS_H) return REGACCESS_HU;
+        if(C == REGACCESS_D) return REGACCESS_DU;
+    }
+    return B;
+}
+//! merge RegAccessType A with RegAccessType B
+
+//!argument isBPartiallyOverlapA can be any value between -1 and 2
+//!0 means fully overlapping, 1 means B is the lower half, 2 means B is the higher half
+RegAccessType mergeAccess2(RegAccessType A, RegAccessType B, OverlapCase isBPartiallyOverlapA) {
+    if(A == REGACCESS_UD || A == REGACCESS_UL || A == REGACCESS_UH ||
+       A == REGACCESS_DU || A == REGACCESS_LU || A == REGACCESS_HU) return A;
+    if(A == REGACCESS_D) {
+        if(B == REGACCESS_D) return REGACCESS_D;
+        if(B == REGACCESS_U) return REGACCESS_DU;
+        if(B == REGACCESS_UD) return REGACCESS_DU;
+        if(B == REGACCESS_DU) return B;
+    }
+    if(A == REGACCESS_U) {
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
+        if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
+        if(B == REGACCESS_U) return A;
+        if(B == REGACCESS_UD && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
+        if(B == REGACCESS_UD && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
+        if(B == REGACCESS_UD && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
+        if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
+    }
+    if(A == REGACCESS_L) {
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_L;
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_D;
+        if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_D;
+        if(B == REGACCESS_U) return REGACCESS_LU;
+        if(B == REGACCESS_UD) return REGACCESS_LU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_LU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_DU;
+        if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_DU;
+    }
+    if(A == REGACCESS_H) {
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_D;
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_H;
+        if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_D;
+        if(B == REGACCESS_U) return REGACCESS_HU;
+        if(B == REGACCESS_UD) return REGACCESS_HU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_DU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_HU;
+        if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_DU;
+    }
+    return REGACCESS_N;
+}
+
+//!determines which part of a use is from a given definition
+
+//!reachingDefLive tells us which part of the def is live at this point
+//!isDefPartiallyOverlapUse can be any value between -1 and 2
+RegAccessType setAccessTypeOfUse(OverlapCase isDefPartiallyOverlapUse, RegAccessType reachingDefLive) {
+    if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_A)
+        return reachingDefLive;
+    if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_LOW_OF_A) { //def covers the low half of use
+        return REGACCESS_L;
+    }
+    if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_HIGH_OF_A) {
+        return REGACCESS_H;
+    }
+    return REGACCESS_N;
+}
+
+//! search currentBB->defUseTable to find a def for regNum at offsetPC
+
+//!
+DefUsePair* searchDefUseTable(int offsetPC, int regNum, LowOpndRegType pType) {
+    DefUsePair* ptr = currentBB->defUseTable;
+    while(ptr != NULL) {
+        if(ptr->def.offsetPC == offsetPC &&
+           ptr->def.regNum == regNum &&
+           ptr->def.physicalType == pType) {
+            return ptr;
+        }
+        ptr = ptr->next;
+    }
+    return NULL;
+}
+void printDefUseTable() {
+    ALOGI("PRINT defUseTable --------");
+    DefUsePair* ptr = currentBB->defUseTable;
+    while(ptr != NULL) {
+        ALOGI("  def @ %x of VR %d %d has %d uses", ptr->def.offsetPC,
+              ptr->def.regNum, ptr->def.physicalType,
+              ptr->num_uses);
+        DefOrUseLink* ptr2 = ptr->uses;
+        while(ptr2 != NULL) {
+            ALOGI("    use @ %x of VR %d %d accessType %d", ptr2->offsetPC,
+                  ptr2->regNum,
+                  ptr2->physicalType,
+                  ptr2->accessType);
+            ptr2 = ptr2->next;
+        }
+        ptr = ptr->next;
+    }
+}
+//! when a VR is used, check whether a transfer from memory to XMM is necessary
+
+//!
+int updateVRAtUse(int reg, LowOpndRegType pType, int regAll) {
+    int k;
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        if(currentBB->xferPoints[k].offsetPC == offsetPC &&
+           currentBB->xferPoints[k].xtype == XFER_MEM_TO_XMM &&
+           currentBB->xferPoints[k].regNum == reg &&
+           currentBB->xferPoints[k].physicalType == pType) {
+#ifdef DEBUG_XFER_POINTS
+            ALOGI("XFER from memory to xmm %d", reg);
+#endif
+            move_mem_to_reg_noalloc(OpndSize_64,
+                                    4*currentBB->xferPoints[k].regNum, PhysicalReg_FP, true,
+                                    MemoryAccess_VR, currentBB->xferPoints[k].regNum,
+                                    regAll, true);
+        }
+    }
+    return 0;
+}
+///////////////////////////////////////////////////////////////////////////////
+// DEAD/USELESS STATEMENT ELMINATION
+// bytecodes can be removed if a bytecode has no side effect and the defs are not used
+// this optimization is guarded with DSE_OPT
+// currently, this optimization is not on, since it does not provide observable performance improvement
+//     and it increases compilation time
+
+/* we remove a maximal of 40 bytecodes within a single basic block */
+#define MAX_NUM_DEAD_PC_IN_BB 40
+int deadPCs[MAX_NUM_DEAD_PC_IN_BB];
+int num_dead_pc = 0;
+//! collect all PCs that can be removed
+
+//! traverse each byte code in the current basic block and check whether it can be removed, if yes, update deadPCs
+void getDeadStmts() {
+    BasicBlock_O1* bb = currentBB;
+    int k;
+    num_dead_pc = 0;
+    //traverse each bytecode in the basic block
+    //update offsetPC, rPC & inst
+    u2* rPC_start = (u2*)currentMethod->insns;
+    MIR* mir;
+    for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
+        offsetPC = mir->seqNum;
+        rPC = rPC_start + mir->offset;
+        if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) continue;
+#ifdef DEBUG_DSE
+        ALOGI("DSE: offsetPC %x", offsetPC);
+#endif
+        inst = FETCH(0);
+        bool isDeadStmt = true;
+        getVirtualRegInfo(infoByteCode);
+        u2 inst_op = INST_INST(inst);
+	//skip bytecodes with side effect
+        if(inst_op != OP_CONST_STRING && inst_op != OP_CONST_STRING_JUMBO &&
+           inst_op != OP_MOVE && inst_op != OP_MOVE_OBJECT &&
+           inst_op != OP_MOVE_FROM16 && inst_op != OP_MOVE_OBJECT_FROM16 &&
+           inst_op != OP_MOVE_16 && inst_op != OP_CONST_CLASS &&
+           inst_op != OP_MOVE_OBJECT_16 && inst_op != OP_MOVE_WIDE &&
+           inst_op != OP_MOVE_WIDE_FROM16 && inst_op != OP_MOVE_WIDE_16 &&
+           inst_op != OP_MOVE_RESULT && inst_op != OP_MOVE_RESULT_OBJECT) {
+            continue;
+        }
+        //some statements do not define any VR!!!
+        int num_defs = 0;
+        for(k = 0; k < num_regs_per_bytecode; k++) {
+            if(infoByteCode[k].accessType == REGACCESS_D ||
+               infoByteCode[k].accessType == REGACCESS_UD ||
+               infoByteCode[k].accessType == REGACCESS_DU) { //search defUseTable
+                num_defs++;
+                DefUsePair* indexT = searchDefUseTable(offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
+                if(indexT == NULL) {
+                    ALOGE("def at %x of VR %d %d not in table",
+                           offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
+                    return;
+                }
+                if(indexT->num_uses > 0) {
+                    isDeadStmt = false;
+                    break;
+                } else {
+#ifdef DEBUG_DSE
+                    ALOGI("DSE: num_uses is %d for def at %d for VR %d %d", indexT->num_uses,
+                          offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
+#endif
+                }
+            }
+        } //for k
+        if(num_defs == 0) isDeadStmt = false;
+        if(isDeadStmt && num_dead_pc < MAX_NUM_DEAD_PC_IN_BB) {
+#ifdef DEBUG_DSE
+            ALOGI("DSE: stmt at %x is dead", offsetPC);
+#endif
+            deadPCs[num_dead_pc++] = offsetPC;
+        }
+    } //for offsetPC
+#ifdef DEBUG_DSE
+    ALOGI("Dead Stmts: ");
+    for(k = 0; k < num_dead_pc; k++) ALOGI("%x ", deadPCs[k]);
+    ALOGI("");
+#endif
+}
+//! entry point to remove dead statements
+
+//! recursively call getDeadStmts and remove uses in defUseTable that are from a dead PC
+//! until there is no change to number of dead PCs
+void removeDeadDefs() {
+    int k;
+    int deadPCs_2[MAX_NUM_DEAD_PC_IN_BB];
+    int num_dead_pc_2 = 0;
+    getDeadStmts();
+    if(num_dead_pc == 0) return;
+    DefUsePair* ptr = NULL;
+    DefOrUseLink* ptrUse = NULL;
+    DefOrUseLink* ptrUse_prev = NULL;
+    while(true) {
+        //check all the uses in defUseTable and remove any use that is from a dead PC
+        ptr = currentBB->defUseTable;
+        while(ptr != NULL) {
+            int k3;
+            ptrUse = ptr->uses;
+            ptrUse_prev = NULL;
+            while(ptrUse != NULL) {
+                bool isIn = false;
+                for(k3 = 0; k3 < num_dead_pc; k3++) {
+                    if(ptrUse->offsetPC == deadPCs[k3]) {
+                        isIn = true;
+                        break;
+                    }
+                }//k3
+                if(!isIn) {
+                    ptrUse_prev = ptrUse;
+                    ptrUse = ptrUse->next; //next use
+                }
+                else {
+                    //go to next use and remove ptrUse
+#ifdef DEBUG_DSE
+                    ALOGI("DSE: remove usage at offsetPC %d reached by def at %d", ptrUse->offsetPC,
+                           ptr->def.offsetPC);
+#endif
+                    DefOrUseLink* nextP = ptrUse->next;
+                    if(ptrUse == ptr->useTail) ptr->useTail = ptrUse_prev;
+                    free(ptrUse);
+                    if(ptrUse_prev == NULL) {
+                        ptr->uses = nextP;
+                    } else {
+                        ptrUse_prev->next = nextP;
+                    }
+                    ptrUse = nextP; //do not update ptrUse_prev
+                    ptr->num_uses--;
+                }
+            }//while ptrUse
+            ptr = ptr->next;
+        }//while ptr
+	//save deadPCs in deadPCs_2
+        num_dead_pc_2 = num_dead_pc;
+        for(k = 0; k < num_dead_pc_2; k++)
+            deadPCs_2[k] = deadPCs[k];
+	//update deadPCs
+        getDeadStmts();
+	//if no change to number of dead PCs, break out of the while loop
+        if(num_dead_pc_2 == num_dead_pc) break;
+    }//while
+#ifdef DEBUG_DSE
+    ALOGI("DSE: DEAD STMTS: ");
+    for(k = 0; k < num_dead_pc; k++) {
+        ALOGI("%d ", deadPCs[k]);
+    }
+    ALOGI("");
+#endif
+}
+/////////////////////////////////////////////////////////////
+//!search memVRTable for a given virtual register
+
+//!
+int searchMemTable(int regNum) {
+    int k;
+    for(k = 0; k < num_memory_vr; k++) {
+        if(memVRTable[k].regNum == regNum) {
+            return k;
+        }
+    }
+    ALOGW("in searchMemTable can't find VR %d num_memory_vr %d", regNum, num_memory_vr);
+    return -1;
+}
+/////////////////////////////////////////////////////////////////////////
+// A VR is already in memory && NULL CHECK
+//!check whether the latest content of a VR is in memory
+
+//!
+bool isInMemory(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL < 0) return false;
+    if(size == OpndSize_64 && indexH < 0) return false;
+    if(!memVRTable[indexL].inMemory) return false;
+    if(size == OpndSize_64 && (!memVRTable[indexH].inMemory)) return false;
+    return true;
+}
+//!set field inMemory of memVRTable to true
+
+//!
+void setVRToMemory(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL < 0) {
+        ALOGE("VR %d not in memVRTable", regNum);
+        return;
+    }
+    memVRTable[indexL].inMemory = true;
+    if(size == OpndSize_64) {
+        if(indexH < 0) {
+            ALOGE("VR %d not in memVRTable", regNum+1);
+            return;
+        }
+        memVRTable[indexH].inMemory = true;
+    }
+}
+//! check whether null check for a VR is performed previously
+
+//!
+bool isVRNullCheck(int regNum, OpndSize size) {
+    if(size != OpndSize_32) {
+        ALOGE("isVRNullCheck size should be 32");
+        dvmAbort();
+    }
+    int indexL = searchMemTable(regNum);
+    if(indexL < 0) {
+        ALOGE("VR %d not in memVRTable", regNum);
+        return false;
+    }
+    return memVRTable[indexL].nullCheckDone;
+}
+bool isVRBoundCheck(int vr_array, int vr_index) {
+    int indexL = searchMemTable(vr_array);
+    if(indexL < 0) {
+        ALOGE("isVRBoundCheck: VR %d not in memVRTable", vr_array);
+        return false;
+    }
+    if(memVRTable[indexL].boundCheck.indexVR == vr_index)
+        return memVRTable[indexL].boundCheck.checkDone;
+    return false;
+}
+//! set nullCheckDone in memVRTable to true
+
+//!
+void setVRNullCheck(int regNum, OpndSize size) {
+    if(size != OpndSize_32) {
+        ALOGE("setVRNullCheck size should be 32");
+        dvmAbort();
+    }
+    int indexL = searchMemTable(regNum);
+    if(indexL < 0) {
+        ALOGE("VR %d not in memVRTable", regNum);
+        return;
+    }
+    memVRTable[indexL].nullCheckDone = true;
+}
+void setVRBoundCheck(int vr_array, int vr_index) {
+    int indexL = searchMemTable(vr_array);
+    if(indexL < 0) {
+        ALOGE("setVRBoundCheck: VR %d not in memVRTable", vr_array);
+        return;
+    }
+    memVRTable[indexL].boundCheck.indexVR = vr_index;
+    memVRTable[indexL].boundCheck.checkDone = true;
+}
+void clearVRBoundCheck(int regNum, OpndSize size) {
+    int k;
+    for(k = 0; k < num_memory_vr; k++) {
+        if(memVRTable[k].regNum == regNum ||
+           (size == OpndSize_64 && memVRTable[k].regNum == regNum+1)) {
+            memVRTable[k].boundCheck.checkDone = false;
+        }
+        if(memVRTable[k].boundCheck.indexVR == regNum ||
+           (size == OpndSize_64 && memVRTable[k].boundCheck.indexVR == regNum+1)) {
+            memVRTable[k].boundCheck.checkDone = false;
+        }
+    }
+}
+//! set inMemory of memVRTable to false
+
+//!
+void clearVRToMemory(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL >= 0) {
+        memVRTable[indexL].inMemory = false;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        memVRTable[indexH].inMemory = false;
+    }
+}
+//! set nullCheckDone of memVRTable to false
+
+//!
+void clearVRNullCheck(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL >= 0) {
+        memVRTable[indexL].nullCheckDone = false;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        memVRTable[indexH].nullCheckDone = false;
+    }
+}
+
+//! Extend Virtual Register life
+
+//! Requests that the life of a specific virtual register be extended. This ensures
+//! that its mapping to a physical register won't be canceled while the extension
+//! request is valid. NOTE: This does not support 64-bit values (when two adjacent
+//! VRs are used)
+//! @see cancelVRFreeDelayRequest
+//! @see getVRFreeDelayRequested
+//! @see VRFreeDelayFlags
+//! @param regNum is the VR number
+//! @param reason explains why freeing must be delayed. A single or combination
+//! of VRFreeDelayFlags should be used.
+//! @return negative value if request failed
+int requestVRFreeDelay(int regNum, u4 reason) {
+    //TODO Add 64-bit operand support when needed
+    int indexL = searchMemTable(regNum);
+    if(indexL >= 0) {
+        memVRTable[indexL].delayFreeFlags |= reason;
+    } else {
+        ALOGE("requestVRFreeDelay: VR %d not in memVRTable", regNum);
+    }
+    return indexL;
+}
+
+//! Cancel request for virtual register life extension
+
+//! Cancels any outstanding requests to extended liveness of VR. Additionally,
+//! this ensures that if the VR is no longer life after this point, it will
+//! no longer be associated with a physical register which can then be reused.
+//! NOTE: This does not support 64-bit values (when two adjacent VRs are used)
+//! @see requestVRFreeDelay
+//! @see getVRFreeDelayRequested
+//! @see VRFreeDelayFlags
+//! @param regNum is the VR number
+//! @param reason explains what freeing delay request should be canceled. A single
+//! or combination of VRFreeDelayFlags should be used.
+void cancelVRFreeDelayRequest(int regNum, u4 reason) {
+    //TODO Add 64-bit operand support when needed
+    bool needCallToFreeReg = false;
+    int indexL = searchMemTable(regNum);
+    if(indexL >= 0) {
+        if((memVRTable[indexL].delayFreeFlags & reason) != VRDELAY_NONE) { // don't cancel delay if it wasn't requested
+            memVRTable[indexL].delayFreeFlags ^= reason; // only cancel this particular reason, not all others
+            if(memVRTable[indexL].delayFreeFlags == VRDELAY_NONE)
+                needCallToFreeReg = true; // freeReg might want to free this VR now if there is no longer a valid delay
+        }
+    }
+    if(needCallToFreeReg)
+        freeReg(true);
+}
+
+//! Gets status of virtual register free delay request
+
+//! Finds out if there was a delay request for freeing this VR.
+//! NOTE: This does not support 64-bit values (when two adjacent VRs are used)
+//! @see requestVRFreeDelay
+//! @see cancelVRFreeDelayRequest
+//! @param regNum is the VR number
+//! @return true if VR has an active delay request
+bool getVRFreeDelayRequested(int regNum) {
+    //TODO Add 64-bit operand support when needed
+    int indexL = searchMemTable(regNum);
+    if(indexL >= 0) {
+        if(memVRTable[indexL].delayFreeFlags != VRDELAY_NONE)
+            return true;
+        return false;
+    }
+    return false;
+}
+
+//! find the basic block that a bytecode is in
+
+//!
+BasicBlock_O1* findForOffset(int offset) {
+    int k;
+    for(k = 0; k < num_bbs_for_method; k++) {
+        if(method_bbs_sorted[k]->pc_start <= offset && method_bbs_sorted[k]->pc_end > offset)
+            return method_bbs_sorted[k];
+    }
+    return NULL;
+}
+void dump_CFG(Method* method);
+
+int current_bc_size = -1;
+
+//! check whether a virtual register is used in a basic block
+
+//!
+bool isUsedInBB(int regNum, int type, BasicBlock_O1* bb) {
+    int k;
+    for(k = 0; k < bb->num_regs; k++) {
+        if(bb->infoBasicBlock[k].physicalType == (type&MASK_FOR_TYPE) && bb->infoBasicBlock[k].regNum == regNum)
+            return true;
+    }
+    return false;
+}
+//! return the index to infoBasicBlock for a given virtual register
+
+//! return -1 if not found
+int searchVirtualInfoOfBB(LowOpndRegType type, int regNum, BasicBlock_O1* bb) {
+    int k;
+    for(k = 0; k < bb->num_regs; k++) {
+        if(bb->infoBasicBlock[k].physicalType == type && bb->infoBasicBlock[k].regNum == regNum)
+            return k;
+    }
+    return -1;
+}
+//! return the index to compileTable for a given virtual register
+
+//! return -1 if not found
+int searchCompileTable(int type, int regNum) { //returns the index
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(compileTable[k].physicalType == type && compileTable[k].regNum == regNum)
+            return k;
+    }
+    return -1;
+}
+//!check whether a physical register for a variable with typeA will work for another variable with typeB
+
+//!Type LowOpndRegType_ss is compatible with type LowOpndRegType_xmm
+bool matchType(int typeA, int typeB) {
+    if((typeA & MASK_FOR_TYPE) == (typeB & MASK_FOR_TYPE)) return true;
+    if((typeA & MASK_FOR_TYPE) == LowOpndRegType_ss &&
+       (typeB & MASK_FOR_TYPE) == LowOpndRegType_xmm) return true;
+    if((typeA & MASK_FOR_TYPE) == LowOpndRegType_xmm &&
+       (typeB & MASK_FOR_TYPE) == LowOpndRegType_ss) return true;
+    return false;
+}
+//!check whether a virtual register is used in the current bytecode
+
+//!
+bool isUsedInByteCode(int regNum, int type) {
+    getVirtualRegInfo(infoByteCode);
+    int k;
+    for(k = 0; k < num_regs_per_bytecode; k++) {
+        if(infoByteCode[k].physicalType == (type&MASK_FOR_TYPE) && infoByteCode[k].regNum == regNum)
+            return true;
+    }
+    return false;
+}
+//! obsolete
+bool defineFirst(int atype) {
+    if(atype == REGACCESS_D || atype == REGACCESS_L || atype == REGACCESS_H || atype == REGACCESS_DU)
+        return true;
+    return false;
+}
+//!check whether a virtual register is updated in a basic block
+
+//!
+bool notUpdated(RegAccessType atype) {
+    if(atype == REGACCESS_U) return true;
+    return false;
+}
+//!check whether a virtual register has exposed usage within a given basic block
+
+//!
+bool hasExposedUsage2(BasicBlock_O1* bb, int index) {
+    RegAccessType atype = bb->infoBasicBlock[index].accessType;
+    if(atype == REGACCESS_D || atype == REGACCESS_L || atype == REGACCESS_H || atype == REGACCESS_DU)
+        return false;
+    return true;
+}
+//! return the spill location that is not used
+
+//!
+int getSpillIndex(bool isGLUE, OpndSize size) {
+    if(isGLUE) return 0;
+    int k;
+    for(k = 1; k <= MAX_SPILL_JIT_IA-1; k++) {
+        if(size == OpndSize_64) {
+            if(k < MAX_SPILL_JIT_IA-1 && spillIndexUsed[k] == 0 && spillIndexUsed[k+1] == 0)
+                return k;
+        }
+        else if(spillIndexUsed[k] == 0) {
+            return k;
+        }
+    }
+    ALOGE("can't find spill position in spillLogicalReg");
+    return -1;
+}
+//!this is called before generating a native code, it sets entries in array canSpillReg to true
+
+//!startNativeCode must be paired with endNativeCode
+void startNativeCode(int vr_num, int vr_type) {
+    int k;
+    for(k = 0; k < PhysicalReg_Null; k++) {
+        canSpillReg[k] = true;
+    }
+    inGetVR_num = vr_num;
+    inGetVR_type = vr_type;
+}
+//! called right after generating a native code
+
+//!It sets entries in array canSpillReg to true and reset inGetVR_num to -1
+void endNativeCode() {
+    int k;
+    for(k = 0; k < PhysicalReg_Null; k++) {
+        canSpillReg[k] = true;
+    }
+    inGetVR_num = -1;
+}
+//! set canSpillReg[physicalReg] to false
+
+//!
+void donotSpillReg(int physicalReg) {
+    canSpillReg[physicalReg] = false;
+}
+//! set canSpillReg[physicalReg] to true
+
+//!
+void doSpillReg(int physicalReg) {
+    canSpillReg[physicalReg] = true;
+}
+//! touch hardcoded register %ecx and reduce its reference count
+
+//!
+int touchEcx() {
+    //registerAlloc will spill logical reg that is mapped to ecx
+    //registerAlloc will reduce refCount
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_ECX, true, true);
+    return 0;
+}
+//! touch hardcoded register %eax and reduce its reference count
+
+//!
+int touchEax() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_EAX, true, true);
+    return 0;
+}
+int touchEsi() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_ESI, true, true);
+    return 0;
+}
+int touchXmm1() {
+    registerAlloc(LowOpndRegType_xmm, XMM_1, true, true);
+    return 0;
+}
+int touchEbx() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_EBX, true, true);
+    return 0;
+}
+
+//! touch hardcoded register %edx and reduce its reference count
+
+//!
+int touchEdx() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_EDX, true, true);
+    return 0;
+}
+
+#ifdef HACK_FOR_DEBUG
+//for debugging purpose, instructions are added at a certain place
+bool hacked = false;
+void hackBug() {
+  if(!hacked && iget_obj_inst == 13) {
+#if 0
+    move_reg_to_reg_noalloc(OpndSize_32, PhysicalReg_EBX, true, PhysicalReg_ECX, true);
+    //move from ebx to ecx & update compileTable for v3
+    int tIndex = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, 3);
+    if(tIndex < 0) ALOGE("hack can't find VR3");
+    compileTable[tIndex].physicalReg = PhysicalReg_ECX;
+#else
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EBX, true, 12, PhysicalReg_FP, true);
+#endif
+  }
+}
+void hackBug2() {
+  if(!hacked && iget_obj_inst == 13) {
+    dump_imm_mem_noalloc(Mnemonic_MOV, OpndSize_32, 0, 12, PhysicalReg_FP, true);
+    hacked = true;
+  }
+}
+#endif
+
+//! this function is called before calling a helper function or a vm function
+int beforeCall(const char* target) { //spill all live registers
+    if(currentBB == NULL) return -1;
+
+    /* special case for ncgGetEIP: this function only updates %edx */
+    if(!strcmp(target, "ncgGetEIP")) {
+        touchEdx();
+        return -1;
+    }
+
+    /* these functions use %eax for the return value */
+    if((!strcmp(target, "dvmInstanceofNonTrivial")) ||
+       (!strcmp(target, "dvmUnlockObject")) ||
+       (!strcmp(target, "dvmAllocObject")) ||
+       (!strcmp(target, "dvmAllocArrayByClass")) ||
+       (!strcmp(target, "dvmAllocPrimitiveArray")) ||
+       (!strcmp(target, "dvmInterpHandleFillArrayData")) ||
+       (!strcmp(target, "dvmFindInterfaceMethodInCache")) ||
+       (!strcmp(target, "dvmNcgHandlePackedSwitch")) ||
+       (!strcmp(target, "dvmNcgHandleSparseSwitch")) ||
+       (!strcmp(target, "dvmCanPutArrayElement")) ||
+       (!strcmp(target, "moddi3")) || (!strcmp(target, "divdi3")) ||
+       (!strcmp(target, "execute_inline"))
+       || (!strcmp(target, "dvmJitToPatchPredictedChain"))
+       || (!strcmp(target, "dvmJitHandlePackedSwitch"))
+       || (!strcmp(target, "dvmJitHandleSparseSwitch"))
+       ) {
+        touchEax();
+    }
+
+    //these two functions also use %edx for the return value
+    if((!strcmp(target, "moddi3")) || (!strcmp(target, "divdi3"))) {
+        touchEdx();
+    }
+    if((!strcmp(target, ".new_instance_helper"))) {
+        touchEsi(); touchEax();
+    }
+#if defined(ENABLE_TRACING)
+    if((!strcmp(target, "common_periodicChecks4"))) {
+        touchEdx();
+    }
+#endif
+    if((!strcmp(target, ".const_string_helper"))) {
+        touchEcx(); touchEax();
+    }
+    if((!strcmp(target, ".check_cast_helper"))) {
+        touchEbx(); touchEsi();
+    }
+    if((!strcmp(target, ".instance_of_helper"))) {
+        touchEbx(); touchEsi(); touchEcx();
+    }
+    if((!strcmp(target, ".monitor_enter_helper"))) {
+        touchEbx();
+    }
+    if((!strcmp(target, ".monitor_exit_helper"))) {
+        touchEbx();
+    }
+    if((!strcmp(target, ".aget_wide_helper"))) {
+        touchEbx(); touchEcx(); touchXmm1();
+    }
+    if((!strcmp(target, ".aget_helper")) || (!strcmp(target, ".aget_char_helper")) ||
+       (!strcmp(target, ".aget_short_helper")) || (!strcmp(target, ".aget_bool_helper")) ||
+       (!strcmp(target, ".aget_byte_helper"))) {
+        touchEbx(); touchEcx(); touchEdx();
+    }
+    if((!strcmp(target, ".aput_helper")) || (!strcmp(target, ".aput_char_helper")) ||
+       (!strcmp(target, ".aput_short_helper")) || (!strcmp(target, ".aput_bool_helper")) ||
+       (!strcmp(target, ".aput_byte_helper")) || (!strcmp(target, ".aput_wide_helper"))) {
+        touchEbx(); touchEcx(); touchEdx();
+    }
+    if((!strcmp(target, ".sput_helper")) || (!strcmp(target, ".sput_wide_helper"))) {
+        touchEdx(); touchEax();
+    }
+    if((!strcmp(target, ".sget_helper"))) {
+        touchEdx(); touchEcx();
+    }
+    if((!strcmp(target, ".sget_wide_helper"))) {
+        touchEdx(); touchXmm1();
+    }
+    if((!strcmp(target, ".aput_obj_helper"))) {
+        touchEdx(); touchEcx(); touchEax();
+    }
+    if((!strcmp(target, ".iput_helper")) || (!strcmp(target, ".iput_wide_helper"))) {
+        touchEbx(); touchEcx(); touchEsi();
+    }
+    if((!strcmp(target, ".iget_helper"))) {
+        touchEbx(); touchEcx(); touchEdx();
+    }
+    if((!strcmp(target, ".iget_wide_helper"))) {
+        touchEbx(); touchEcx(); touchXmm1();
+    }
+    if((!strcmp(target, ".new_array_helper"))) {
+        touchEbx(); touchEdx(); touchEax();
+    }
+    if((!strcmp(target, ".invoke_virtual_helper"))) {
+        touchEbx(); touchEcx();
+    }
+    if((!strcmp(target, ".invoke_direct_helper"))) {
+        touchEsi(); touchEcx();
+    }
+    if((!strcmp(target, ".invoke_super_helper"))) {
+        touchEbx(); touchEcx();
+    }
+    if((!strcmp(target, ".invoke_interface_helper"))) {
+        touchEbx(); touchEcx();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_5_helper")) ||
+       (!strcmp(target, ".invokeMethodNoRange_4_helper"))) {
+        touchEbx(); touchEsi(); touchEax(); touchEdx();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_3_helper"))) {
+        touchEbx(); touchEsi(); touchEax();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_2_helper"))) {
+        touchEbx(); touchEsi();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_1_helper"))) {
+        touchEbx();
+    }
+    if((!strcmp(target, ".invokeMethodRange_helper"))) {
+        touchEdx(); touchEsi();
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("enter beforeCall");
+#endif
+    if(!strncmp(target, ".invokeArgsDone", 15)) resetGlue(PhysicalReg_GLUE_DVMDEX);
+
+    freeReg(true); //to avoid spilling dead logical registers
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        /* before throwing an exception, if GLUE is spilled, load to %ebp
+           this should happen at last */
+        if(k == indexForGlue) continue;
+        if(compileTable[k].physicalReg != PhysicalReg_Null &&
+           (compileTable[k].physicalType & LowOpndRegType_hard) == 0) {
+            /* handles non hardcoded variables that are in physical registers */
+            if(!strcmp(target, "exception")) {
+                /* before throwing an exception
+                   update contents of all VRs in Java stack */
+                if(!isVirtualReg(compileTable[k].physicalType)) continue;
+                /* to have correct GC, we should update contents for L VRs as well */
+                //if(compileTable[k].gType == GLOBALTYPE_L) continue;
+            }
+            if((!strcmp(target, ".const_string_resolve")) ||
+               (!strcmp(target, ".static_field_resolve")) ||
+               (!strcmp(target, ".inst_field_resolve")) ||
+               (!strcmp(target, ".class_resolve")) ||
+               (!strcmp(target, ".direct_method_resolve")) ||
+               (!strcmp(target, ".virtual_method_resolve")) ||
+               (!strcmp(target, ".static_method_resolve"))) {
+               /* physical register %ebx will keep its content
+                  but to have correct GC, we should dump content of a VR
+                     that is mapped to %ebx */
+                if(compileTable[k].physicalReg == PhysicalReg_EBX &&
+                   (!isVirtualReg(compileTable[k].physicalType)))
+                    continue;
+            }
+            if((!strncmp(target, "dvm", 3)) || (!strcmp(target, "moddi3")) ||
+               (!strcmp(target, "divdi3")) ||
+               (!strcmp(target, "fmod")) || (!strcmp(target, "fmodf"))) {
+                /* callee-saved registers (%ebx, %esi, %ebp, %edi) will keep the content
+                   but to have correct GC, we should dump content of a VR
+                      that is mapped to a callee-saved register */
+                if((compileTable[k].physicalReg == PhysicalReg_EBX ||
+                    compileTable[k].physicalReg == PhysicalReg_ESI) &&
+                   (!isVirtualReg(compileTable[k].physicalType)))
+                    continue;
+            }
+#ifdef DEBUG_REGALLOC
+            ALOGI("SPILL logical register %d %d in beforeCall",
+                  compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+            spillLogicalReg(k, true);
+        }
+    }
+    if(indexForGlue >= 0 && !strcmp(target, "exception") &&
+       compileTable[indexForGlue].physicalReg == PhysicalReg_Null) {
+        unspillLogicalReg(indexForGlue, PhysicalReg_EBP); //load %ebp
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("exit beforeCall");
+#endif
+    return 0;
+}
+int getFreeReg(int type, int reg, int indexToCompileTable);
+//! after calling a helper function or a VM function
+
+//!
+int afterCall(const char* target) { //un-spill
+    if(currentBB == NULL) return -1;
+    if(!strcmp(target, "ncgGetEIP")) return -1;
+
+    return 0;
+}
+//! check whether a temporary is 8-bit
+
+//!
+bool isTemp8Bit(int type, int reg) {
+    if(currentBB == NULL) return false;
+    if(!isTemporary(type, reg)) return false;
+    int k;
+    for(k = 0; k < num_temp_regs_per_bytecode; k++) {
+        if(infoByteCodeTemp[k].physicalType == type &&
+           infoByteCodeTemp[k].regNum == reg) {
+            return infoByteCodeTemp[k].is8Bit;
+        }
+    }
+    ALOGE("isTemp8Bit %d %d", type, reg);
+    return false;
+}
+
+/* functions to access live ranges of a VR
+   Live range info is stored in memVRTable[].ranges, which is a linked list
+*/
+//! check whether a VR is live at the current bytecode
+
+//!
+bool isVRLive(int vA) {
+    int index = searchMemTable(vA);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", vA);
+        return false;
+    }
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start && offsetPC <= ptr->end) return true;
+        ptr = ptr->next;
+    }
+    return false;
+}
+
+//! check whether the current bytecode is the last access to a VR within a live range
+
+//!for 64-bit VR, return true only when true for both low half and high half
+bool isLastByteCodeOfLiveRange(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index;
+    LiveRange* ptr = NULL;
+    if(tSize == OpndSize_32) {
+        /* check live ranges for the VR */
+        index = searchMemTable(compileTable[k].regNum);
+        if(index < 0) {
+            ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+            return false;
+        }
+        ptr = memVRTable[index].ranges;
+        while(ptr != NULL) {
+            if(offsetPC == ptr->end) return true;
+            ptr = ptr->next;
+        }
+        return false;
+    }
+    /* size of the VR is 64 */
+    /* check live ranges of the low half */
+    index = searchMemTable(compileTable[k].regNum);
+    bool tmpB = false;
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC == ptr->end) {
+            tmpB = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!tmpB) return false;
+    /* check live ranges of the high half */
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC == ptr->end) {
+            return true;
+        }
+        ptr = ptr->next;
+    }
+    return false;
+}
+
+//! check whether the current bytecode is in a live range that extends to end of a basic block
+
+//!for 64 bit, return true if true for both low half and high half
+bool reachEndOfBB(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index;
+    bool retCode = false;
+    /* check live ranges of the low half */
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return false;
+    }
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            if(ptr->end == currentBB->pc_end) {
+                retCode = true;
+            }
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!retCode) return false;
+    if(tSize == OpndSize_32) return true;
+    /* check live ranges of the high half */
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            if(ptr->end == currentBB->pc_end) return true;
+            return false;
+        }
+        ptr = ptr->next;
+    }
+#ifdef PRINT_WARNING
+    ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum+1);
+#endif
+    return false;
+}
+
+//!check whether the current bytecode is the next to last access to a VR within a live range
+
+//!for 64 bit, return true if true for both low half and high half
+bool isNextToLastAccess(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index;
+    /* check live ranges for the low half */
+    bool retCode = false;
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return false;
+    }
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        int num_access = ptr->num_access;
+
+        if(num_access < 2) {
+           ptr = ptr->next;
+           continue;
+        }
+
+        if(offsetPC == ptr->accessPC[num_access-2]) {
+           retCode = true;
+           break;
+        }
+        ptr = ptr->next;
+    }
+    if(!retCode) return false;
+    if(tSize == OpndSize_32) return true;
+    /* check live ranges for the high half */
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        int num_access = ptr->num_access;
+
+        if(num_access < 2) {
+           ptr = ptr->next;
+           continue;
+        }
+
+        if(offsetPC == ptr->accessPC[num_access-2]) return true;
+        ptr = ptr->next;
+    }
+    return false;
+}
+
+/** return the start of the next live range
+    if there does not exist a next live range, return pc_end of the basic block
+    for 64 bits, return the larger one for low half and high half
+    Assume live ranges are sorted in order
+*/
+int getNextLiveRange(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    /* check live ranges of the low half */
+    int index;
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return offsetPC;
+    }
+    bool found = false;
+    int nextUse = offsetPC;
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(ptr->start > offsetPC) {
+            nextUse = ptr->start;
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!found) return currentBB->pc_end;
+    if(tSize == OpndSize_32) return nextUse;
+
+    /* check live ranges of the high half */
+    found = false;
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return offsetPC;
+    }
+    int nextUse2 = offsetPC;
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(ptr->start > offsetPC) {
+            nextUse2 = ptr->start;
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!found) return currentBB->pc_end;
+    /* return the larger one */
+    return (nextUse2 > nextUse ? nextUse2 : nextUse);
+}
+
+/** return the next access to a variable
+    If variable is 64-bit, get the next access to the lower half and the high half
+        return the eariler one
+*/
+int getNextAccess(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index, k3;
+    /* check live ranges of the low half */
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return offsetPC;
+    }
+    bool found = false;
+    int nextUse = offsetPC;
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            /* offsetPC belongs to this live range */
+            for(k3 = 0; k3 < ptr->num_access; k3++) {
+                if(ptr->accessPC[k3] > offsetPC) {
+                    nextUse = ptr->accessPC[k3];
+                    break;
+                }
+            }
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+#ifdef PRINT_WARNING
+    if(!found)
+        ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum);
+#endif
+    if(tSize == OpndSize_32) return nextUse;
+
+    /* check live ranges of the high half */
+    found = false;
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return offsetPC;
+    }
+    int nextUse2 = offsetPC;
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            for(k3 = 0; k3 < ptr->num_access; k3++) {
+                if(ptr->accessPC[k3] > offsetPC) {
+                    nextUse2 = ptr->accessPC[k3];
+                    break;
+                }
+            }
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+#ifdef PRINT_WARNING
+    if(!found) ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum+1);
+#endif
+    /* return the earlier one */
+    if(nextUse2 < nextUse) return nextUse2;
+    return nextUse;
+}
+
+/** free variables that are no longer in use
+    free a temporary with reference count of zero
+    will dump content of a GL VR to memory if necessary
+*/
+int freeReg(bool spillGL) {
+    if(currentBB == NULL) return 0;
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(compileTable[k].refCount == 0 && compileTable[k].physicalReg != PhysicalReg_Null) {
+            /* check entries with reference count of zero and is mapped to a physical register */
+            bool typeA = !isVirtualReg(compileTable[k].physicalType);
+            bool freeCrit = true, delayFreeing = false;
+            bool typeC = false, typeB = false, reachEnd = false;
+            if(isVirtualReg(compileTable[k].physicalType)) {
+                /* VRs in the compile table */
+
+                /* Check if delay for freeing was requested for this VR */
+                delayFreeing = getVRFreeDelayRequested(compileTable[k].regNum);
+
+                freeCrit = isLastByteCodeOfLiveRange(k); /* last bytecode of a live range */
+                reachEnd = reachEndOfBB(k); /* in a live range that extends to end of a basic block */
+#ifdef DEBUG_LIVE_RANGE
+                ALOGI("IN freeReg: VR %d offsetPC %x freecrit %d reachEnd %d nextToLast %d", compileTable[k].regNum, offsetPC, freeCrit, reachEnd, isNextToLastAccess(k));
+#endif
+                /* Bug: spilling of VRs after edi(rFP) is updated in RETURN bytecode
+                        will cause variables for callee to be spilled to the caller stack frame and
+                                                        to overwrite varaibles for caller
+                */
+                /* last bytecode of a live range reaching end of BB if not counting the fake usage at end */
+                bool boolB = reachEnd && isNextToLastAccess(k);
+                /* Bug: when a GG VR is checked at end of a basic block,
+                        freeCrit will be true and physicalReg will be set to Null
+                   Fix: change free condition from freeCrit to (freeCrit && offsetPC != currentBB->pc_end)
+                */
+                /* conditions to free a GG VR:
+                       last bytecode of a live range reaching end of BB if not counting the fake usage at end && endsWithReturn
+                       or
+                       last bytecode of a live range && offsetPC != currentBB->pc_end
+                           -> last bytecode of a live range not reaching end
+                */
+                typeC = ((freeCrit && offsetPC != currentBB->pc_end) ||
+                         (currentBB->endsWithReturn && boolB)) &&
+                        compileTable[k].gType == GLOBALTYPE_GG &&
+                        !delayFreeing;
+                /* conditions to free a L|GL VR:
+                       last bytecode of a live range
+                       or
+                       last bytecode of a live range reaching end of BB if not counting the fake usage at end
+                */
+                typeB = (freeCrit || boolB) &&
+                        (compileTable[k].gType != GLOBALTYPE_GG) &&
+                        !delayFreeing;
+            }
+            if(typeA || typeB || typeC) {
+#ifdef DEBUG_REGALLOC
+                if(typeA)
+                    ALOGI("FREE TEMP %d with type %d allocated to %d",
+                           compileTable[k].regNum, compileTable[k].physicalType,
+                           compileTable[k].physicalReg);
+                else if(typeB)
+                    ALOGI("FREE VR L|GL %d with type %d allocated to %d",
+                           compileTable[k].regNum, compileTable[k].physicalType,
+                           compileTable[k].physicalReg);
+                else if(typeC)
+                    ALOGI("FREE VR GG %d with type %d allocated to %d",
+                           compileTable[k].regNum, compileTable[k].physicalType,
+                           compileTable[k].physicalReg);
+#endif
+                bool dumpGL = false;
+                if(compileTable[k].gType == GLOBALTYPE_GL && !reachEnd) {
+                    /* if the live range does not reach end of basic block
+                       and there exists a try block from offsetPC to the next live range
+                           dump VR to interpreted stack */
+                    int tmpPC = getNextLiveRange(k);
+                    if(existATryBlock(currentMethod, offsetPC, tmpPC)) dumpGL = true;
+                }
+                /* if the live range reach end of basic block, dump VR to interpreted stack */
+                if(compileTable[k].gType == GLOBALTYPE_GL && reachEnd) dumpGL = true;
+                if(dumpGL) {
+                    if(spillGL) {
+#ifdef DEBUG_REGALLOC
+                        ALOGI("SPILL VR GL %d %d", compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+                        spillLogicalReg(k, true); //will dump VR to memory & update physicalReg
+                    }
+                }
+                else
+                     compileTable[k].physicalReg = PhysicalReg_Null;
+            }
+            if(typeA) {
+                if(compileTable[k].spill_loc_index >= 0) {
+                    /* update spill info for temporaries */
+                    spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 0;
+                    compileTable[k].spill_loc_index = -1;
+                    ALOGE("free a temporary register with TRSTATE_SPILLED");
+                }
+            }
+        }
+    }
+    syncAllRegs(); //sync up allRegs (isUsed & freeTimeStamp) with compileTable
+    return 0;
+}
+
+//! reduce the reference count by 1
+
+//! input: index to compileTable
+void decreaseRefCount(int index) {
+#ifdef DEBUG_REFCOUNT
+    ALOGI("REFCOUNT: %d in decreaseRefCount %d %d", compileTable[index].refCount,
+            compileTable[index].regNum, compileTable[index].physicalType);
+#endif
+    compileTable[index].refCount--;
+    if(compileTable[index].refCount < 0) {
+        ALOGE("refCount is negative for REG %d %d", compileTable[index].regNum, compileTable[index].physicalType);
+        dvmAbort();
+    }
+}
+//! reduce the reference count of a VR by 1
+
+//! input: reg & type
+int updateRefCount(int reg, LowOpndRegType type) {
+    if(currentBB == NULL) return 0;
+    int index = searchCompileTable(LowOpndRegType_virtual | type, reg);
+    if(index < 0) {
+        ALOGE("virtual reg %d type %d not found in updateRefCount", reg, type);
+        return -1;
+    }
+    decreaseRefCount(index);
+    return 0;
+}
+//! reduce the reference count of a variable by 1
+
+//! The variable is named with lowering module's naming mechanism
+int updateRefCount2(int reg, int type, bool isPhysical) {
+    if(currentBB == NULL) return 0;
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index = searchCompileTable(newType, reg);
+    if(index < 0) {
+        ALOGE("reg %d type %d not found in updateRefCount", reg, newType);
+        return -1;
+    }
+    decreaseRefCount(index);
+    return 0;
+}
+//! check whether a glue variable is in physical register or spilled
+
+//!
+bool isGlueHandled(int glue_reg) {
+    if(currentBB == NULL) return false;
+    int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
+    if(index < 0) {
+        ALOGE("glue reg %d not found in isGlueHandled", glue_reg);
+        return -1;
+    }
+    if(compileTable[index].spill_loc_index >= 0 ||
+       compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_GLUE
+        ALOGI("GLUE isGlueHandled for %d returns true", glue_reg);
+#endif
+        return true;
+    }
+#ifdef DEBUG_GLUE
+    ALOGI("GLUE isGlueHandled for %d returns false", glue_reg);
+#endif
+    return false;
+}
+//! reset the state of a glue variable to not existant (not in physical register nor spilled)
+
+//!
+void resetGlue(int glue_reg) {
+    if(currentBB == NULL) return;
+    int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
+    if(index < 0) {
+        ALOGE("glue reg %d not found in resetGlue", glue_reg);
+        return;
+    }
+#ifdef DEBUG_GLUE
+    ALOGI("GLUE reset for %d", glue_reg);
+#endif
+    compileTable[index].physicalReg = PhysicalReg_Null;
+    if(compileTable[index].spill_loc_index >= 0)
+        spillIndexUsed[compileTable[index].spill_loc_index >> 2] = 0;
+    compileTable[index].spill_loc_index = -1;
+}
+//! set a glue variable in a physical register allocated for a variable
+
+//! Variable is using lowering module's naming convention
+void updateGlue(int reg, bool isPhysical, int glue_reg) {
+    if(currentBB == NULL) return;
+    int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
+    if(index < 0) {
+        ALOGE("glue reg %d not found in updateGlue", glue_reg);
+        return;
+    }
+    /* find the compileTable entry for variable <reg, isPhysical> */
+    int newType = convertType(LowOpndRegType_gp, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index2 = searchCompileTable(newType, reg);
+    if(index2 < 0 || compileTable[index2].physicalReg == PhysicalReg_Null) {
+        ALOGE("updateGlue reg %d type %d", reg, newType);
+        return;
+    }
+#ifdef DEBUG_GLUE
+    ALOGI("physical register for GLUE %d set to %d", glue_reg, compileTable[index2].physicalReg);
+#endif
+    compileTable[index].physicalReg = compileTable[index2].physicalReg;
+    compileTable[index].spill_loc_index = -1;
+}
+
+//! check whether a virtual register is in a physical register
+
+//! If updateRefCount is 0, do not update reference count;
+//!If updateRefCount is 1, update reference count only when VR is in a physical register
+//!If updateRefCount is 2, update reference count
+int checkVirtualReg(int reg, LowOpndRegType type, int updateRefCount) {
+    if(currentBB == NULL) return PhysicalReg_Null;
+    int index = searchCompileTable(LowOpndRegType_virtual | type, reg);
+    if(index < 0) {
+        ALOGE("virtual reg %d type %d not found in checkVirtualReg", reg, type);
+        return PhysicalReg_Null;
+    }
+    //reduce reference count
+    if(compileTable[index].physicalReg != PhysicalReg_Null) {
+        if(updateRefCount != 0) decreaseRefCount(index);
+        return compileTable[index].physicalReg;
+    }
+    if(updateRefCount == 2) decreaseRefCount(index);
+    return PhysicalReg_Null;
+}
+//!check whether a temporary can share the same physical register with a VR
+
+//!This is called in get_virtual_reg
+//!If this function returns false, new register will be allocated for this temporary
+bool checkTempReg2(int reg, int type, bool isPhysical, int physicalRegForVR) {
+    if(currentBB == NULL) return false;
+    if(isPhysical) return false;
+
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int k;
+    for(k = 0; k < num_temp_regs_per_bytecode; k++) {
+        if(infoByteCodeTemp[k].physicalType == newType &&
+           infoByteCodeTemp[k].regNum == reg) {
+#ifdef DEBUG_MOVE_OPT
+            ALOGI("MOVE_OPT checkTempRegs for %d %d returns %d %d",
+                   reg, newType, infoByteCodeTemp[k].shareWithVR, infoByteCodeTemp[k].is8Bit);
+#endif
+            if(!infoByteCodeTemp[k].is8Bit) return infoByteCodeTemp[k].shareWithVR;
+            //is8Bit true for gp type only
+            if(!infoByteCodeTemp[k].shareWithVR) return false;
+            //both true
+            if(physicalRegForVR >= PhysicalReg_EAX && physicalRegForVR <= PhysicalReg_EDX) return true;
+#ifdef DEBUG_MOVE_OPT
+            ALOGI("MOVE_OPT registerAllocMove not used for 8-bit register");
+#endif
+            return false;
+        }
+    }
+    ALOGE("checkTempReg2 %d %d", reg, newType);
+    return false;
+}
+//!check whether a temporary can share the same physical register with a VR
+
+//!This is called in set_virtual_reg
+int checkTempReg(int reg, int type, bool isPhysical, int vrNum) {
+    if(currentBB == NULL) return PhysicalReg_Null;
+
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index = searchCompileTable(newType, reg);
+    if(index < 0) {
+        ALOGE("temp reg %d type %d not found in checkTempReg", reg, newType);
+        return PhysicalReg_Null;
+    }
+
+    //a temporary register can share the same physical reg with a VR if registerAllocMove is called
+    //this will cause problem with move bytecode
+    //get_VR(v1, t1) t1 and v1 point to the same physical reg
+    //set_VR(t1, v2) t1 and v2 point to the same physical reg
+    //this will cause v1 and v2 point to the same physical reg
+    //FIX: if this temp reg shares a physical reg with another reg
+    if(compileTable[index].physicalReg != PhysicalReg_Null) {
+        int k;
+        for(k = 0; k < num_compile_entries; k++) {
+            if(k == index) continue;
+            if(compileTable[k].physicalReg == compileTable[index].physicalReg) {
+                return PhysicalReg_Null; //will allocate a register for VR
+            }
+        }
+        decreaseRefCount(index);
+        return compileTable[index].physicalReg;
+    }
+    if(compileTable[index].spill_loc_index >= 0) {
+        //registerAlloc will call unspillLogicalReg (load from memory)
+#ifdef DEBUG_REGALLOC
+        ALOGW("in checkTempReg, the temporary register %d %d was spilled", reg, type);
+#endif
+        int regAll = registerAlloc(type, reg, isPhysical, true/* updateRefCount */);
+        return regAll;
+    }
+    return PhysicalReg_Null;
+}
+//!check whether a variable has exposed usage in a basic block
+
+//!It calls hasExposedUsage2
+bool hasExposedUsage(LowOpndRegType type, int regNum, BasicBlock_O1* bb) {
+    int index = searchVirtualInfoOfBB(type, regNum, bb);
+    if(index >= 0 && hasExposedUsage2(bb, index)) {
+        return true;
+    }
+    return false;
+}
+//!check whether a variable has exposed usage in other basic blocks
+
+//!
+bool hasOtherExposedUsage(OpndSize size, int regNum, BasicBlock_O1* bb) {
+    return true; //assume the worst case
+}
+
+//! handles constant VRs at end of a basic block
+
+//!If a VR is constant at end of a basic block and (it has exposed usage in other basic blocks or reaches a GG VR), dump immediate to memory
+void constVREndOfBB() {
+    BasicBlock_O1* bb = currentBB;
+    int k, k2;
+    //go through GG VRs, update a bool array
+    int constUsedByGG[MAX_CONST_REG];
+    for(k = 0; k < num_const_vr; k++)
+        constUsedByGG[k] = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType == GLOBALTYPE_GG) {
+            OpndSize size = getRegSize(compileTable[k].physicalType);
+            int regNum = compileTable[k].regNum;
+            int indexL = -1;
+            int indexH = -1;
+            for(k2 = 0; k2 < num_const_vr; k2++) {
+                if(constVRTable[k2].regNum == regNum) {
+                    indexL = k2;
+                    continue;
+                }
+                if(constVRTable[k2].regNum == regNum + 1 && size == OpndSize_64) {
+                    indexH = k2;
+                    continue;
+                }
+            }
+            if(indexL >= 0) constUsedByGG[indexL] = 1;
+            if(indexH >= 0) constUsedByGG[indexH] = 1;
+        } //GG VR
+    }
+    for(k = 0; k < num_const_vr; k++) {
+        if(!constVRTable[k].isConst) continue;
+        bool hasExp = false;
+        if(constUsedByGG[k] == 0)
+            hasExp = hasOtherExposedUsage(OpndSize_32, constVRTable[k].regNum, bb);
+        if(constUsedByGG[k] != 0 || hasExp) {
+            dumpImmToMem(constVRTable[k].regNum, OpndSize_32, constVRTable[k].value);
+            setVRToMemory(constVRTable[k].regNum, OpndSize_32);
+#ifdef DEBUG_ENDOFBB
+            ALOGI("ENDOFBB: exposed VR %d is const %d (%x)",
+                  constVRTable[k].regNum, constVRTable[k].value, constVRTable[k].value);
+#endif
+        } else {
+#ifdef DEBUG_ENDOFBB
+            ALOGI("ENDOFBB: unexposed VR %d is const %d (%x)",
+                  constVRTable[k].regNum, constVRTable[k].value, constVRTable[k].value);
+#endif
+        }
+    }
+}
+
+//!handles GG VRs at end of a basic block
+
+//!make sure all GG VRs are in pre-defined physical registers
+void globalVREndOfBB(const Method* method) {
+    //fix: freeReg first to write LL VR back to memory to avoid it gets overwritten by GG VRs
+    freeReg(true);
+    int k;
+    //spill GG VR first if it is not mapped to the specific reg
+    //release GLUE regs
+    for(k = 0; k < num_compile_entries; k++) {
+        if(compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
+           compileTable[k].regNum != PhysicalReg_GLUE) {
+            compileTable[k].physicalReg = PhysicalReg_Null;
+            compileTable[k].spill_loc_index = -1;
+        }
+        //if part of a GG VR is const, the physical reg is set to null
+        if(isVirtualReg(compileTable[k].physicalType) &&
+           compileTable[k].gType == GLOBALTYPE_GG && compileTable[k].physicalReg != PhysicalReg_Null &&
+           compileTable[k].physicalReg != compileTable[k].physicalReg_prev) {
+#ifdef DEBUG_ENDOFBB
+            ALOGW("end of BB GG VR is not mapped to the specific reg: %d %d %d",
+                  compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg);
+            ALOGW("ENDOFBB SPILL VR %d %d", compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+            spillLogicalReg(k, true); //the next section will load VR from memory to the specific reg
+        }
+    }
+    syncAllRegs();
+    for(k = 0; k < num_compile_entries; k++) {
+        if(isVirtualReg(compileTable[k].physicalType)) {
+            if(compileTable[k].gType == GLOBALTYPE_GG &&
+               compileTable[k].physicalReg == PhysicalReg_Null && (!currentBB->endsWithReturn)) {
+#ifdef DEBUG_ENDOFBB
+                ALOGI("ENDOFBB GET GG VR %d %d to physical register %d", compileTable[k].regNum,
+                      compileTable[k].physicalType, compileTable[k].physicalReg_prev);
+#endif
+                compileTable[k].physicalReg = compileTable[k].physicalReg_prev;
+                if(allRegs[compileTable[k].physicalReg_prev].isUsed) {
+                    ALOGE("physical register for GG VR is still used");
+                }
+                get_virtual_reg_noalloc(compileTable[k].regNum,
+                                        getRegSize(compileTable[k].physicalType),
+                                        compileTable[k].physicalReg_prev,
+                                        true);
+            }
+        }//not const
+    }
+    if(indexForGlue >= 0 &&
+        compileTable[indexForGlue].physicalReg == PhysicalReg_Null) {
+        unspillLogicalReg(indexForGlue, PhysicalReg_EBP); //load %ebp
+    }
+}
+
+//! get ready for the next version of a hard-coded register
+
+//!set its physicalReg to Null and update its reference count
+int nextVersionOfHardReg(PhysicalReg pReg, int refCount) {
+    int indexT = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_hard, pReg);
+    if(indexT < 0)
+        return -1;
+    compileTable[indexT].physicalReg = PhysicalReg_Null;
+#ifdef DEBUG_REFCOUNT
+    ALOGI("REFCOUNT: to %d in nextVersionOfHardReg %d", refCount, pReg);
+#endif
+    compileTable[indexT].refCount = refCount;
+    return 0;
+}
+
+/** update compileTable with bb->infoBasicBlock[k]
+*/
+void insertFromVirtualInfo(BasicBlock_O1* bb, int k) {
+    int index = searchCompileTable(LowOpndRegType_virtual | bb->infoBasicBlock[k].physicalType, bb->infoBasicBlock[k].regNum);
+    if(index < 0) {
+        /* the virtual register is not in compileTable, insert it */
+        index = num_compile_entries;
+        compileTable[num_compile_entries].physicalType = (LowOpndRegType_virtual | bb->infoBasicBlock[k].physicalType);
+        compileTable[num_compile_entries].regNum = bb->infoBasicBlock[k].regNum;
+        compileTable[num_compile_entries].physicalReg = PhysicalReg_Null;
+        compileTable[num_compile_entries].bb = bb;
+        compileTable[num_compile_entries].indexToInfoBB = k;
+        compileTable[num_compile_entries].spill_loc_index = -1;
+        compileTable[num_compile_entries].gType = bb->infoBasicBlock[k].gType;
+        num_compile_entries++;
+        if(num_compile_entries >= COMPILE_TABLE_SIZE) {
+            ALOGE("compileTable overflow");
+            dvmAbort();
+        }
+    }
+    /* re-set reference count of all VRs */
+    compileTable[index].refCount = bb->infoBasicBlock[k].refCount;
+    compileTable[index].accessType = bb->infoBasicBlock[k].accessType;
+    if(compileTable[index].gType == GLOBALTYPE_GG)
+        compileTable[index].physicalReg_prev = bb->infoBasicBlock[k].physicalReg_GG;
+}
+
+/** update compileTable with infoByteCodeTemp[k]
+*/
+void insertFromTempInfo(int k) {
+    int index = searchCompileTable(infoByteCodeTemp[k].physicalType, infoByteCodeTemp[k].regNum);
+    if(index < 0) {
+        /* the temporary is not in compileTable, insert it */
+        index = num_compile_entries;
+        compileTable[num_compile_entries].physicalType = infoByteCodeTemp[k].physicalType;
+        compileTable[num_compile_entries].regNum = infoByteCodeTemp[k].regNum;
+        num_compile_entries++;
+        if(num_compile_entries >= COMPILE_TABLE_SIZE) {
+            ALOGE("compileTable overflow");
+            dvmAbort();
+        }
+    }
+    compileTable[index].physicalReg = PhysicalReg_Null;
+    compileTable[index].refCount = infoByteCodeTemp[k].refCount;
+    compileTable[index].linkageToVR = infoByteCodeTemp[k].linkageToVR;
+    compileTable[index].gType = GLOBALTYPE_L;
+    compileTable[index].spill_loc_index = -1;
+}
+
+/* insert a glue-related register GLUE_DVMDEX to compileTable */
+void insertGlueReg() {
+    compileTable[num_compile_entries].physicalType = LowOpndRegType_gp;
+    compileTable[num_compile_entries].regNum = PhysicalReg_GLUE_DVMDEX;
+    compileTable[num_compile_entries].refCount = 2;
+    compileTable[num_compile_entries].physicalReg = PhysicalReg_Null;
+    compileTable[num_compile_entries].bb = NULL;
+    compileTable[num_compile_entries].spill_loc_index = -1;
+    compileTable[num_compile_entries].accessType = REGACCESS_N;
+    compileTable[num_compile_entries].linkageToVR = -1;
+    compileTable[num_compile_entries].gType = GLOBALTYPE_L;
+
+    num_compile_entries++;
+    if(num_compile_entries >= COMPILE_TABLE_SIZE) {
+        ALOGE("compileTable overflow");
+        dvmAbort();
+    }
+}
+
+/** print infoBasicBlock of the given basic block
+*/
+void dumpVirtualInfoOfBasicBlock(BasicBlock_O1* bb) {
+    int jj;
+    ALOGI("Virtual Info for BB%d --------", bb->bb_index);
+    for(jj = 0; jj < bb->num_regs; jj++) {
+        ALOGI("regNum %d physicalType %d accessType %d refCount %d def ",
+               bb->infoBasicBlock[jj].regNum, bb->infoBasicBlock[jj].physicalType,
+               bb->infoBasicBlock[jj].accessType, bb->infoBasicBlock[jj].refCount);
+        int k;
+        for(k = 0; k < bb->infoBasicBlock[jj].num_reaching_defs; k++)
+            ALOGI("[%x %d %d %d] ", bb->infoBasicBlock[jj].reachingDefs[k].offsetPC,
+                   bb->infoBasicBlock[jj].reachingDefs[k].regNum,
+                   bb->infoBasicBlock[jj].reachingDefs[k].physicalType,
+                   bb->infoBasicBlock[jj].reachingDefs[k].accessType);
+        ALOGI("");
+    }
+}
+
+/** print compileTable
+*/
+void dumpCompileTable() {
+    int jj;
+    ALOGI("Compile Table for method ----------");
+    for(jj = 0; jj < num_compile_entries; jj++) {
+        ALOGI("regNum %d physicalType %d refCount %d isConst %d physicalReg %d type %d",
+               compileTable[jj].regNum, compileTable[jj].physicalType,
+               compileTable[jj].refCount, compileTable[jj].isConst, compileTable[jj].physicalReg, compileTable[jj].gType);
+    }
+}
+
+//!check whether a basic block is the start of an exception handler
+
+//!
+bool isFirstOfHandler(BasicBlock_O1* bb) {
+    int i;
+    for(i = 0; i < num_exception_handlers; i++) {
+        if(bb->pc_start == exceptionHandlers[i]) return true;
+    }
+    return false;
+}
+
+//! create a basic block that starts at src_pc and ends at end_pc
+
+//!
+BasicBlock_O1* createBasicBlock(int src_pc, int end_pc) {
+    BasicBlock_O1* bb = (BasicBlock_O1*)malloc(sizeof(BasicBlock_O1));
+    if(bb == NULL) {
+        ALOGE("out of memory");
+        return NULL;
+    }
+    bb->pc_start = src_pc;
+    bb->bb_index = num_bbs_for_method;
+    if(bb_entry == NULL) bb_entry = bb;
+
+    /* insert the basic block to method_bbs_sorted in ascending order of pc_start */
+    int k;
+    int index = -1;
+    for(k = 0; k < num_bbs_for_method; k++)
+        if(method_bbs_sorted[k]->pc_start > src_pc) {
+            index = k;
+            break;
+        }
+    if(index == -1)
+        method_bbs_sorted[num_bbs_for_method] = bb;
+    else {
+        /* push the elements from index by 1 */
+        for(k = num_bbs_for_method-1; k >= index; k--)
+            method_bbs_sorted[k+1] = method_bbs_sorted[k];
+        method_bbs_sorted[index] = bb;
+    }
+    num_bbs_for_method++;
+    if(num_bbs_for_method >= MAX_NUM_BBS_PER_METHOD) {
+        ALOGE("too many basic blocks");
+        dvmAbort();
+    }
+    return bb;
+}
+
+/* BEGIN code to handle state transfers */
+//! save the current state of register allocator to a state table
+
+//!
+void rememberState(int stateNum) {
+#ifdef DEBUG_STATE
+    ALOGI("STATE: remember state %d", stateNum);
+#endif
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(stateNum == 1) {
+            stateTable1_1[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_1[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else if(stateNum == 2) {
+            stateTable1_2[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_2[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else if(stateNum == 3) {
+            stateTable1_3[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_3[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else if(stateNum == 4) {
+            stateTable1_4[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_4[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else ALOGE("state table overflow");
+#ifdef DEBUG_STATE
+        ALOGI("logical reg %d %d mapped to physical reg %d with spill index %d refCount %d",
+               compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg,
+               compileTable[k].spill_loc_index, compileTable[k].refCount);
+#endif
+    }
+    for(k = 0; k < num_memory_vr; k++) {
+        if(stateNum == 1) {
+            stateTable2_1[k].regNum = memVRTable[k].regNum;
+            stateTable2_1[k].inMemory = memVRTable[k].inMemory;
+        }
+        else if(stateNum == 2) {
+            stateTable2_2[k].regNum = memVRTable[k].regNum;
+            stateTable2_2[k].inMemory = memVRTable[k].inMemory;
+        }
+        else if(stateNum == 3) {
+            stateTable2_3[k].regNum = memVRTable[k].regNum;
+            stateTable2_3[k].inMemory = memVRTable[k].inMemory;
+        }
+        else if(stateNum == 4) {
+            stateTable2_4[k].regNum = memVRTable[k].regNum;
+            stateTable2_4[k].inMemory = memVRTable[k].inMemory;
+        }
+        else ALOGE("state table overflow");
+#ifdef DEBUG_STATE
+        ALOGI("virtual reg %d in memory %d", memVRTable[k].regNum, memVRTable[k].inMemory);
+#endif
+    }
+}
+
+//!update current state of register allocator with a state table
+
+//!
+void goToState(int stateNum) {
+    int k;
+#ifdef DEBUG_STATE
+    ALOGI("STATE: go to state %d", stateNum);
+#endif
+    for(k = 0; k < num_compile_entries; k++) {
+        if(stateNum == 1) {
+            compileTable[k].physicalReg = stateTable1_1[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_1[k].spill_loc_index;
+        }
+        else if(stateNum == 2) {
+            compileTable[k].physicalReg = stateTable1_2[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_2[k].spill_loc_index;
+        }
+        else if(stateNum == 3) {
+            compileTable[k].physicalReg = stateTable1_3[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_3[k].spill_loc_index;
+        }
+        else if(stateNum == 4) {
+            compileTable[k].physicalReg = stateTable1_4[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_4[k].spill_loc_index;
+        }
+        else ALOGE("state table overflow");
+    }
+    updateSpillIndexUsed();
+    syncAllRegs(); //to sync up allRegs CAN'T call freeReg here
+    //since it will change the state!!!
+    for(k = 0; k < num_memory_vr; k++) {
+        if(stateNum == 1) {
+            memVRTable[k].regNum = stateTable2_1[k].regNum;
+            memVRTable[k].inMemory = stateTable2_1[k].inMemory;
+        }
+        else if(stateNum == 2) {
+            memVRTable[k].regNum = stateTable2_2[k].regNum;
+            memVRTable[k].inMemory = stateTable2_2[k].inMemory;
+        }
+        else if(stateNum == 3) {
+            memVRTable[k].regNum = stateTable2_3[k].regNum;
+            memVRTable[k].inMemory = stateTable2_3[k].inMemory;
+        }
+        else if(stateNum == 4) {
+            memVRTable[k].regNum = stateTable2_4[k].regNum;
+            memVRTable[k].inMemory = stateTable2_4[k].inMemory;
+        }
+        else ALOGE("state table overflow");
+    }
+}
+typedef struct TransferOrder {
+    int targetReg;
+    int targetSpill;
+    int compileIndex;
+} TransferOrder;
+#define MAX_NUM_DEST 20
+//! a source register is used as a source in transfer
+//! it can have a maximum of MAX_NUM_DEST destinations
+typedef struct SourceReg {
+    int physicalReg;
+    int num_dests; //check bound
+    TransferOrder dsts[MAX_NUM_DEST];
+} SourceReg;
+int num_src_regs = 0; //check bound
+//! physical registers that are used as a source in transfer
+//! we allow a maximum of MAX_NUM_DEST sources in a transfer
+SourceReg srcRegs[MAX_NUM_DEST];
+//! tell us whether a source register is handled already
+bool handledSrc[MAX_NUM_DEST];
+//! in what order should the source registers be handled
+int handledOrder[MAX_NUM_DEST];
+//! insert a source register with a single destination
+
+//!
+void insertSrcReg(int srcPhysical, int targetReg, int targetSpill, int index) {
+    int k = 0;
+    for(k = 0; k < num_src_regs; k++) {
+        if(srcRegs[k].physicalReg == srcPhysical) { //increase num_dests
+            if(srcRegs[k].num_dests >= MAX_NUM_DEST) {
+                ALOGE("exceed number dst regs for a source reg");
+                dvmAbort();
+            }
+            srcRegs[k].dsts[srcRegs[k].num_dests].targetReg = targetReg;
+            srcRegs[k].dsts[srcRegs[k].num_dests].targetSpill = targetSpill;
+            srcRegs[k].dsts[srcRegs[k].num_dests].compileIndex = index;
+            srcRegs[k].num_dests++;
+            return;
+        }
+    }
+    if(num_src_regs >= MAX_NUM_DEST) {
+        ALOGE("exceed number of source regs");
+        dvmAbort();
+    }
+    srcRegs[num_src_regs].physicalReg = srcPhysical;
+    srcRegs[num_src_regs].num_dests = 1;
+    srcRegs[num_src_regs].dsts[0].targetReg = targetReg;
+    srcRegs[num_src_regs].dsts[0].targetSpill = targetSpill;
+    srcRegs[num_src_regs].dsts[0].compileIndex = index;
+    num_src_regs++;
+}
+//! check whether a register is a source and the source is not yet handled
+
+//!
+bool dstStillInUse(int dstReg) {
+    if(dstReg == PhysicalReg_Null) return false;
+    int k;
+    int index = -1;
+    for(k = 0; k < num_src_regs; k++) {
+        if(dstReg == srcRegs[k].physicalReg) {
+            index = k;
+            break;
+        }
+    }
+    if(index < 0) return false; //not in use
+    if(handledSrc[index]) return false; //not in use
+    return true;
+}
+//! reset the state of glue variables in a state table
+
+//!
+void resetStateOfGlue(int stateNum, int k) {
+#ifdef DEBUG_STATE
+    ALOGI("resetStateOfGlue state %d regNum %d", stateNum, compileTable[k].regNum);
+#endif
+    if(stateNum == 1) {
+        stateTable1_1[k].physicalReg = PhysicalReg_Null;
+        stateTable1_1[k].spill_loc_index = -1;
+    }
+    else if(stateNum == 2) {
+        stateTable1_2[k].physicalReg = PhysicalReg_Null;
+        stateTable1_2[k].spill_loc_index = -1;
+    }
+    else if(stateNum == 3) {
+        stateTable1_3[k].physicalReg = PhysicalReg_Null;
+        stateTable1_3[k].spill_loc_index = -1;
+    }
+    else if(stateNum == 4) {
+        stateTable1_4[k].physicalReg = PhysicalReg_Null;
+        stateTable1_4[k].spill_loc_index = -1;
+    }
+}
+//! construct a legal order of the source registers in this transfer
+
+//!
+void constructSrcRegs(int stateNum) {
+    int k;
+    num_src_regs = 0;
+#ifdef DEBUG_STATE
+    ALOGI("IN constructSrcRegs");
+#endif
+
+    for(k = 0; k < num_compile_entries; k++) {
+#ifdef DEBUG_STATE
+        ALOGI("logical reg %d %d mapped to physical reg %d with spill index %d refCount %d",
+               compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg,
+               compileTable[k].spill_loc_index, compileTable[k].refCount);
+#endif
+
+        int pType = compileTable[k].physicalType;
+        //ignore hardcoded logical registers
+        if((pType & LowOpndRegType_hard) != 0) continue;
+        //ignore type _fs
+        if((pType & MASK_FOR_TYPE) == LowOpndRegType_fs) continue;
+        if((pType & MASK_FOR_TYPE) == LowOpndRegType_fs_s) continue;
+
+        //GL VR refCount is zero, can't ignore
+        //L VR refCount is zero, ignore
+        //GG VR refCount is zero, can't ignore
+        //temporary refCount is zero, ignore
+
+        //for GLUE variables, if they do not exist, reset the entries in state table
+        if(compileTable[k].physicalReg == PhysicalReg_Null &&
+           compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
+           compileTable[k].regNum != PhysicalReg_GLUE &&
+           compileTable[k].spill_loc_index < 0) {
+            resetStateOfGlue(stateNum, k);
+        }
+
+        /* get the target state */
+        int targetReg = PhysicalReg_Null;
+        int targetSpill = -1;
+        if(stateNum == 1) {
+            targetReg = stateTable1_1[k].physicalReg;
+            targetSpill = stateTable1_1[k].spill_loc_index;
+        }
+        else if(stateNum == 2) {
+            targetReg = stateTable1_2[k].physicalReg;
+            targetSpill = stateTable1_2[k].spill_loc_index;
+        }
+        else if(stateNum == 3) {
+            targetReg = stateTable1_3[k].physicalReg;
+            targetSpill = stateTable1_3[k].spill_loc_index;
+        }
+        else if(stateNum == 4) {
+            targetReg = stateTable1_4[k].physicalReg;
+            targetSpill = stateTable1_4[k].spill_loc_index;
+        }
+
+        /* there exists an ordering problem
+           for example:
+             for a VR, move from memory to a physical reg esi
+             for a temporary regsiter, from esi to ecx
+             if we handle VR first, content of the temporary reg. will be overwritten
+           there are 4 cases:
+             I: a variable is currently in memory and its target is in physical reg
+             II: a variable is currently in a register and its target is in memory
+             III: a variable is currently in a different register
+             IV: a variable is currently in a different memory location (for non-VRs)
+           for GLUE, since it can only be allocated to %ebp, we don't have case III
+           For now, case IV is not handled since it didn't show
+        */
+        if(compileTable[k].physicalReg != targetReg &&
+           isVirtualReg(compileTable[k].physicalType)) {
+            /* handles VR for case I to III */
+
+            if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles VR for case I:
+                   insert a xfer order from PhysicalReg_Null to targetReg */
+                insertSrcReg(PhysicalReg_Null, targetReg, targetSpill, k);
+#ifdef DEBUG_STATE
+                ALOGI("insert for VR Null %d %d %d", targetReg, targetSpill, k);
+#endif
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles VR for case III
+                   insert a xfer order from srcReg to targetReg */
+                insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                /* handles VR for case II
+                   insert a xfer order from srcReg to memory */
+                insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+            }
+        }
+
+        if(compileTable[k].physicalReg != targetReg &&
+           !isVirtualReg(compileTable[k].physicalType)) {
+            /* handles non-VR for case I to III */
+
+            if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles non-VR for case I */
+                if(compileTable[k].spill_loc_index < 0) {
+                    /* this variable is freed, no need to transfer */
+#ifdef DEBUG_STATE
+                    ALOGW("in transferToState spill_loc_index is negative for temporary %d", compileTable[k].regNum);
+#endif
+                } else {
+                    /* insert a xfer order from memory to targetReg */
+#ifdef DEBUG_STATE
+                    ALOGI("insert Null %d %d %d", targetReg, targetSpill, k);
+#endif
+                    insertSrcReg(PhysicalReg_Null, targetReg, targetSpill, k);
+                }
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles non-VR for case III
+                   insert a xfer order from srcReg to targetReg */
+                insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                /* handles non-VR for case II */
+                if(targetSpill < 0) {
+                    /* this variable is freed, no need to transfer */
+#ifdef DEBUG_STATE
+                    ALOGW("in transferToState spill_loc_index is negative for temporary %d", compileTable[k].regNum);
+#endif
+                } else {
+                    /* insert a xfer order from srcReg to memory */
+                    insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+                }
+            }
+
+        }
+    }//for compile entries
+
+    int k2;
+#ifdef DEBUG_STATE
+    for(k = 0; k < num_src_regs; k++) {
+        ALOGI("SRCREG %d: ", srcRegs[k].physicalReg);
+        for(k2 = 0; k2 < srcRegs[k].num_dests; k2++) {
+            int index = srcRegs[k].dsts[k2].compileIndex;
+            ALOGI("[%d %d %d: %d %d %d] ", srcRegs[k].dsts[k2].targetReg,
+                   srcRegs[k].dsts[k2].targetSpill, srcRegs[k].dsts[k2].compileIndex,
+                   compileTable[index].regNum, compileTable[index].physicalType,
+                   compileTable[index].spill_loc_index);
+        }
+        ALOGI("");
+    }
+#endif
+
+    /* construct an order: xfers from srcReg first, then xfers from memory */
+    int num_handled = 0;
+    int num_in_order = 0;
+    for(k = 0; k < num_src_regs; k++) {
+        if(srcRegs[k].physicalReg == PhysicalReg_Null) {
+            handledSrc[k] = true;
+            num_handled++;
+        } else {
+            handledSrc[k] = false;
+        }
+    }
+    while(num_handled < num_src_regs) {
+        int prev_handled = num_handled;
+        for(k = 0; k < num_src_regs; k++) {
+            if(handledSrc[k]) continue;
+            bool canHandleNow = true;
+            for(k2 = 0; k2 < srcRegs[k].num_dests; k2++) {
+                if(dstStillInUse(srcRegs[k].dsts[k2].targetReg)) {
+                    canHandleNow = false;
+                    break;
+                }
+            }
+            if(canHandleNow) {
+                handledSrc[k] = true;
+                num_handled++;
+                handledOrder[num_in_order] = k;
+                num_in_order++;
+            }
+        } //for k
+        if(num_handled == prev_handled) {
+            ALOGE("no progress in selecting order");
+            dvmAbort();
+        }
+    } //while
+    for(k = 0; k < num_src_regs; k++) {
+        if(srcRegs[k].physicalReg == PhysicalReg_Null) {
+            handledOrder[num_in_order] = k;
+            num_in_order++;
+        }
+    }
+    if(num_in_order != num_src_regs) {
+        ALOGE("num_in_order != num_src_regs");
+        dvmAbort();
+    }
+#ifdef DEBUG_STATE
+    ALOGI("ORDER: ");
+    for(k = 0; k < num_src_regs; k++) {
+        ALOGI("%d ", handledOrder[k]);
+    }
+    ALOGI("");
+#endif
+}
+//! transfer the state of register allocator to a state specified in a state table
+
+//!
+void transferToState(int stateNum) {
+    freeReg(false); //do not spill GL
+    int k;
+#ifdef DEBUG_STATE
+    ALOGI("STATE: transfer to state %d", stateNum);
+#endif
+    if(stateNum > 4 || stateNum < 1) ALOGE("state table overflow");
+    constructSrcRegs(stateNum);
+    int k4, k3;
+    for(k4 = 0; k4 < num_src_regs; k4++) {
+        int k2 = handledOrder[k4]; //index to srcRegs
+        for(k3 = 0; k3 < srcRegs[k2].num_dests; k3++) {
+            k = srcRegs[k2].dsts[k3].compileIndex;
+            int targetReg = srcRegs[k2].dsts[k3].targetReg;
+            int targetSpill = srcRegs[k2].dsts[k3].targetSpill;
+            if(compileTable[k].physicalReg != targetReg && isVirtualReg(compileTable[k].physicalType)) {
+                OpndSize oSize = getRegSize(compileTable[k].physicalType);
+                bool isSS = ((compileTable[k].physicalType & MASK_FOR_TYPE) == LowOpndRegType_ss);
+                if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    if(isSS)
+                        move_ss_mem_to_reg_noalloc(4*compileTable[k].regNum,
+                                                   PhysicalReg_FP, true,
+                                                   MemoryAccess_VR, compileTable[k].regNum,
+                                                   targetReg, true);
+                    else
+                        move_mem_to_reg_noalloc(oSize, 4*compileTable[k].regNum,
+                                                PhysicalReg_FP, true,
+                                                MemoryAccess_VR, compileTable[k].regNum,
+                                                targetReg, true);
+                }
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    move_reg_to_reg_noalloc((isSS ? OpndSize_64 : oSize),
+                                            compileTable[k].physicalReg, true,
+                                            targetReg, true);
+                }
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                    dumpToMem(compileTable[k].regNum, (LowOpndRegType)(compileTable[k].physicalType & MASK_FOR_TYPE),
+                              compileTable[k].physicalReg);
+                }
+            } //VR
+            if(compileTable[k].physicalReg != targetReg && !isVirtualReg(compileTable[k].physicalType)) {
+                OpndSize oSize = getRegSize(compileTable[k].physicalType);
+                if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    loadFromSpillRegion(oSize, targetReg,
+                                        compileTable[k].spill_loc_index);
+                }
+                //both are not null, move from one to the other
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    move_reg_to_reg_noalloc(oSize, compileTable[k].physicalReg, true,
+                                            targetReg, true);
+                }
+                //current is not null, target is null (move from reg to memory)
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                    saveToSpillRegion(oSize, compileTable[k].physicalReg, targetSpill);
+                }
+            } //temporary
+        }//for
+    }//for
+    for(k = 0; k < num_memory_vr; k++) {
+        bool targetBool = false;
+        int targetReg = -1;
+        if(stateNum == 1) {
+            targetReg = stateTable2_1[k].regNum;
+            targetBool = stateTable2_1[k].inMemory;
+        }
+        else if(stateNum == 2) {
+            targetReg = stateTable2_2[k].regNum;
+            targetBool = stateTable2_2[k].inMemory;
+        }
+        else if(stateNum == 3) {
+            targetReg = stateTable2_3[k].regNum;
+            targetBool = stateTable2_3[k].inMemory;
+        }
+        else if(stateNum == 4) {
+            targetReg = stateTable2_4[k].regNum;
+            targetBool = stateTable2_4[k].inMemory;
+        }
+        if(targetReg != memVRTable[k].regNum)
+            ALOGE("regNum mismatch in transferToState");
+        if(targetBool && (!memVRTable[k].inMemory)) {
+            //dump to memory, check entries in compileTable: vA gp vA xmm vA ss
+#ifdef DEBUG_STATE
+            ALOGW("inMemory mismatch for VR %d in transferToState", targetReg);
+#endif
+            bool doneXfer = false;
+            int index = searchCompileTable(LowOpndRegType_xmm | LowOpndRegType_virtual, targetReg);
+            if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                dumpToMem(targetReg, LowOpndRegType_xmm, compileTable[index].physicalReg);
+                doneXfer = true;
+            }
+            if(!doneXfer) { //vA-1, xmm
+                index = searchCompileTable(LowOpndRegType_xmm | LowOpndRegType_virtual, targetReg-1);
+                if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                    dumpToMem(targetReg-1, LowOpndRegType_xmm, compileTable[index].physicalReg);
+                    doneXfer = true;
+                }
+            }
+            if(!doneXfer) { //vA gp
+                index = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_virtual, targetReg);
+                if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                    dumpToMem(targetReg, LowOpndRegType_gp, compileTable[index].physicalReg);
+                    doneXfer = true;
+                }
+            }
+            if(!doneXfer) { //vA, ss
+                index = searchCompileTable(LowOpndRegType_ss | LowOpndRegType_virtual, targetReg);
+                if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                    dumpToMem(targetReg, LowOpndRegType_ss, compileTable[index].physicalReg);
+                    doneXfer = true;
+                }
+            }
+            if(!doneXfer) ALOGW("can't match inMemory of VR %d in transferToState", targetReg);
+        }
+        if((!targetBool) && memVRTable[k].inMemory) {
+            //do nothing
+        }
+    }
+#ifdef DEBUG_STATE
+    ALOGI("END transferToState %d", stateNum);
+#endif
+    goToState(stateNum);
+}
+/* END code to handle state transfers */
diff --git a/vm/compiler/codegen/x86/AnalysisO1.h b/vm/compiler/codegen/x86/AnalysisO1.h
new file mode 100644
index 0000000..7f436d9
--- /dev/null
+++ b/vm/compiler/codegen/x86/AnalysisO1.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file ncg_o1_data.h
+    \brief A header file to define data structures used by register allocator & const folding
+*/
+#ifndef _DALVIK_NCG_ANALYSISO1_H
+#define _DALVIK_NCG_ANALYSISO1_H
+
+#include "Dalvik.h"
+#include "enc_wrapper.h"
+#include "Lower.h"
+#ifdef WITH_JIT
+#include "compiler/CompilerIR.h"
+#endif
+
+//! maximal number of edges per basic block
+#define MAX_NUM_EDGE_PER_BB 300
+//! maximal number of basic blocks per method
+#define MAX_NUM_BBS_PER_METHOD 1000
+//! maximal number of virtual registers per basic block
+#define MAX_REG_PER_BASICBLOCK 140
+//! maximal number of virtual registers per bytecode
+#define MAX_REG_PER_BYTECODE 40
+//! maximal number of virtual registers per method
+#define MAX_REG_PER_METHOD 200
+//! maximal number of temporaries per bytecode
+#define MAX_TEMP_REG_PER_BYTECODE 30
+//! maximal number of GG GPR VRs in a method
+#define MAX_GLOBAL_VR      2
+//! maximal number of GG XMM VRs in a method
+#define MAX_GLOBAL_VR_XMM  4
+#define MAX_CONST_REG 150
+
+#define MASK_FOR_TYPE 7 //last 3 bits 111
+
+#define LOOP_COUNT 10
+//! maximal number of entries in compileTable
+#define COMPILE_TABLE_SIZE 200
+//! maximal number of transfer points per basic block
+#define MAX_XFER_PER_BB 1000  //on Jan 4
+#define PC_FOR_END_OF_BB -999
+#define PC_FOR_START_OF_BB -998
+
+//! various cases of overlapping between 2 variables
+typedef enum OverlapCase {
+  OVERLAP_ALIGN = 0,
+  OVERLAP_B_IS_LOW_OF_A,
+  OVERLAP_B_IS_HIGH_OF_A,
+  OVERLAP_LOW_OF_A_IS_HIGH_OF_B,
+  OVERLAP_HIGH_OF_A_IS_LOW_OF_B,
+  OVERLAP_A_IS_LOW_OF_B,
+  OVERLAP_A_IS_HIGH_OF_B,
+  OVERLAP_B_COVER_A,
+  OVERLAP_B_COVER_LOW_OF_A,
+  OVERLAP_B_COVER_HIGH_OF_A,
+  OVERLAP_NO
+} OverlapCase;
+
+//!access type of a variable
+typedef enum RegAccessType {
+  REGACCESS_D = 0,
+  REGACCESS_U,
+  REGACCESS_DU,
+  REGACCESS_UD,
+  REGACCESS_L,
+  REGACCESS_H,
+  REGACCESS_UL,
+  REGACCESS_UH,
+  REGACCESS_LU,
+  REGACCESS_HU,
+  REGACCESS_N, //no access
+  REGACCESS_UNKNOWN
+} RegAccessType;
+//! a variable can be local (L), globally local (GL) or global (GG)
+typedef enum GlobalType {
+  GLOBALTYPE_GG,
+  GLOBALTYPE_GL,
+  GLOBALTYPE_L
+} GlobalType;
+typedef enum VRState {
+  VRSTATE_SPILLED,
+  VRSTATE_UPDATED,
+  VRSTATE_CLEAN
+} VRState;
+//! helper state to determine if freeing VRs needs to be delayed
+enum VRDelayFreeFlags {
+  VRDELAY_NONE = 0, // used when VR can be freed from using physical register if needed
+  VRDELAY_NULLCHECK = 1 << 0, // used when VR is used for null check and freeing must be delayed
+  VRDELAY_BOUNDCHECK = 1 << 1 // used when VR is used for bound check and freeing must be delayed
+};
+typedef enum TRState { //state of temporary registers
+  TRSTATE_SPILLED,
+  TRSTATE_UNSPILLED,
+  TRSTATE_CLEAN
+} TRState;
+//!information about a physical register
+typedef struct RegisterInfo {
+  PhysicalReg physicalReg;
+  bool isUsed;
+  bool isCalleeSaved;
+  int freeTimeStamp;
+} RegisterInfo;
+typedef struct UniqueRegister {
+  LowOpndRegType physicalType;
+  int regNum;
+  int numExposedUsage;
+  PhysicalReg physicalReg;
+} UniqueRegister;
+//!specifies the weight of a VR allocated to a specific physical register
+//!it is used for GPR VR only
+typedef struct RegAllocConstraint {
+  PhysicalReg physicalReg;
+  int count;
+} RegAllocConstraint;
+
+typedef enum XferType {
+  XFER_MEM_TO_XMM, //for usage
+  XFER_DEF_TO_MEM, //def is gp
+  XFER_DEF_TO_GP_MEM,
+  XFER_DEF_TO_GP,
+  XFER_DEF_IS_XMM //def is xmm
+} XferType;
+typedef struct XferPoint {
+  int tableIndex; //generated from a def-use pair
+  XferType xtype;
+  int offsetPC;
+  int regNum; //get or set VR at offsetPC
+  LowOpndRegType physicalType;
+
+  //if XFER_DEF_IS_XMM
+  int vr_gpl; //a gp VR that uses the lower half of the def
+  int vr_gph;
+  bool dumpToXmm;
+  bool dumpToMem;
+} XferPoint;
+
+//!for def: accessType means which part of the VR defined at offestPC is live now
+//!for use: accessType means which part of the usage comes from the reachingDef
+typedef struct DefOrUse {
+  int offsetPC; //!the program point
+  int regNum; //!access the virtual reg
+  LowOpndRegType physicalType; //!xmm or gp or ss
+  RegAccessType accessType; //!D, L, H, N
+} DefOrUse;
+//!a link list of DefOrUse
+typedef struct DefOrUseLink {
+  int offsetPC;
+  int regNum; //access the virtual reg
+  LowOpndRegType physicalType; //xmm or gp
+  RegAccessType accessType; //D, L, H, N
+  struct DefOrUseLink* next;
+} DefOrUseLink;
+//!pair of def and uses
+typedef struct DefUsePair {
+  DefOrUseLink* uses;
+  DefOrUseLink* useTail;
+  int num_uses;
+  DefOrUse def;
+  struct DefUsePair* next;
+} DefUsePair;
+
+//!information associated with a virtual register
+//!the pair <regNum, physicalType> uniquely determines a variable
+typedef struct VirtualRegInfo {
+  int regNum;
+  LowOpndRegType physicalType;
+  int refCount;
+  RegAccessType accessType;
+  GlobalType gType;
+  int physicalReg_GG;
+  RegAllocConstraint allocConstraints[8];
+  RegAllocConstraint allocConstraintsSorted[8];
+
+  DefOrUse reachingDefs[3]; //!reaching defs to the virtual register
+  int num_reaching_defs;
+} VirtualRegInfo;
+//!information of whether a VR is constant and its value
+typedef struct ConstVRInfo {
+  int regNum;
+  int value;
+  bool isConst;
+} ConstVRInfo;
+#define NUM_ACCESS_IN_LIVERANGE 10
+//!specifies one live range
+typedef struct LiveRange {
+  int start;
+  int end; //inclusive
+  //all accesses in the live range
+  int num_access;
+  int num_alloc;
+  int* accessPC;
+  struct LiveRange* next;
+} LiveRange;
+typedef struct BoundCheckIndex {
+  int indexVR;
+  bool checkDone;
+} BoundCheckIndex;
+//!information for a virtual register such as live ranges, in memory
+typedef struct MemoryVRInfo {
+  int regNum;
+  bool inMemory;
+  bool nullCheckDone;
+  BoundCheckIndex boundCheck;
+  int num_ranges;
+  LiveRange* ranges;
+  u4 delayFreeFlags; //! for use with flags defined by VRDelayFreeFlags enum
+} MemoryVRInfo;
+//!information of a temporary
+//!the pair <regNum, physicalType> uniquely determines a variable
+typedef struct TempRegInfo {
+  int regNum;
+  int physicalType;
+  int refCount;
+  int linkageToVR;
+  int versionNum;
+  bool shareWithVR; //for temp. regs updated by get_virtual_reg
+  bool is8Bit;
+} TempRegInfo;
+struct BasicBlock_O1;
+//!all variables accessed
+//!the pair <regNum, physicalType> uniquely determines a variable
+typedef struct compileTableEntry {
+  int regNum;
+  int physicalType; //gp, xmm or scratch, virtual
+  int physicalReg;
+  int physicalReg_prev; //for spilled GG VR
+  RegAccessType accessType;
+
+  bool isConst;
+  int value[2]; //[0]: lower [1]: higher
+  int refCount;
+
+  int linkageToVR; //for temporary registers only
+  GlobalType gType;
+  struct BasicBlock_O1* bb; //bb VR belongs to
+  int indexToInfoBB;
+
+  VRState regState;
+  TRState trState; //for temporary registers only
+  int spill_loc_index; //for temporary registers only
+} compileTableEntry;
+//!to save the state of register allocator
+typedef struct regAllocStateEntry1 {
+  int spill_loc_index;
+  int physicalReg;
+} regAllocStateEntry1;
+typedef struct regAllocStateEntry2 {
+  int regNum; //
+  bool inMemory; //whether 4-byte virtual reg is in memory
+} regAllocStateEntry2;
+//!edge in control flow graph
+typedef struct Edge_O1 {
+  struct BasicBlock_O1* src;
+  struct BasicBlock_O1* dst;
+} Edge_O1;
+//!information associated with a basic block
+typedef struct BasicBlock_O1 {
+  int bb_index;
+  int bb_index2;
+  int pc_start;       //!inclusive
+#ifndef WITH_JIT
+  int pc_end;         //!exclusive
+  Edge_O1* in_edges[MAX_NUM_EDGE_PER_BB]; //array of Edge*
+  int num_in_edges;
+  Edge_O1* out_edges[MAX_NUM_EDGE_PER_BB];
+  int num_out_edges;
+#else
+  int pc_end;
+  BasicBlock* jitBasicBlock;
+#endif
+  VirtualRegInfo infoBasicBlock[MAX_REG_PER_BASICBLOCK];
+  int num_regs;
+
+  RegAllocConstraint allocConstraints[8]; //# of times a hardcoded register is used in this basic block
+  //a physical register that is used many times has a lower priority to get picked in getFreeReg
+  RegAllocConstraint allocConstraintsSorted[8]; //count from low to high
+
+  DefUsePair* defUseTable;
+  DefUsePair* defUseTail;
+  int num_defs;
+  XferPoint xferPoints[MAX_XFER_PER_BB]; //program points where the transfer is required
+  int num_xfer_points;
+
+  bool endsWithReturn;
+  bool hasAccessToGlue;
+} BasicBlock_O1;
+typedef struct CFG_O1 {
+  BasicBlock_O1* head;
+} CFG_O1;
+//!worklist to create a control flow graph
+typedef struct CFGWork {
+  BasicBlock_O1* bb_prev;
+  int targetOff;
+  struct CFGWork* nextItem;
+} CFGWork;
+
+/////////////////////////////////////////
+extern compileTableEntry compileTable[COMPILE_TABLE_SIZE];
+extern int num_compile_entries;
+extern VirtualRegInfo infoByteCode[MAX_REG_PER_BYTECODE];
+extern int num_regs_per_bytecode;
+extern TempRegInfo infoByteCodeTemp[MAX_TEMP_REG_PER_BYTECODE];
+extern int num_temp_regs_per_bytecode;
+extern VirtualRegInfo infoMethod[MAX_REG_PER_METHOD];
+extern int num_regs_per_method;
+extern BasicBlock_O1* currentBB;
+
+extern BasicBlock_O1* method_bbs[MAX_NUM_BBS_PER_METHOD];
+extern int num_bbs_for_method;
+extern BasicBlock_O1* method_bbs_sorted[MAX_NUM_BBS_PER_METHOD];
+extern BasicBlock_O1* bb_entry;
+extern int pc_start;
+extern int pc_end;
+extern int current_bc_size;
+extern int num_exception_handlers;
+extern int exceptionHandlers[10];
+
+extern int num_const_vr;
+extern ConstVRInfo constVRTable[MAX_CONST_REG];
+
+extern int genSet[MAX_REG_PER_BYTECODE];
+extern int killSet[MAX_REG_PER_BYTECODE];
+extern int num_regs_gen; //per bytecode
+extern int num_regs_kill; //per bytecode
+
+extern int genSetBB[MAX_NUM_BBS_PER_METHOD][40];
+extern int killSetBB[MAX_NUM_BBS_PER_METHOD][40]; //same as size of memVRTable
+extern int num_gen_bb[MAX_NUM_BBS_PER_METHOD];
+extern int num_kill_bb[MAX_NUM_BBS_PER_METHOD];
+
+extern int nullCheck_inB[MAX_NUM_BBS_PER_METHOD][40];
+extern int nullCheck_inSize[MAX_NUM_BBS_PER_METHOD];
+extern int nullCheck_outB[MAX_NUM_BBS_PER_METHOD][40];
+extern int nullCheck_outSize[MAX_NUM_BBS_PER_METHOD];
+
+typedef enum GlueVarType {
+  RES_CLASS = 0,
+  RES_METHOD,
+  RES_FIELD,
+  RES_STRING,
+  GLUE_DVMDEX,
+  GLUE_METHOD_CLASS,
+  GLUE_METHOD
+} GlueVarType;
+
+void forwardAnalysis(int type);
+
+//functions in bc_visitor.c
+int getByteCodeSize();
+bool getConstInfo(BasicBlock_O1* bb);
+int getVirtualRegInfo(VirtualRegInfo* infoArray);
+int getTempRegInfo(TempRegInfo* infoArray);
+int createCFGHandler(Method* method);
+
+int findVirtualRegInTable(u2 vA, LowOpndRegType type, bool printError);
+int searchCompileTable(int type, int regNum);
+BasicBlock_O1* createBasicBlock(int src_pc, int end_pc);
+void handleJump(BasicBlock_O1* bb_prev, int relOff);
+void connectBasicBlock(BasicBlock_O1* src, BasicBlock_O1* dst);
+int insertWorklist(BasicBlock_O1* bb_prev, int targetOff);
+
+int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb); //update bb->infoBasicBlock
+
+void updateCurrentBBWithConstraints(PhysicalReg reg);
+void updateConstInfo(BasicBlock_O1*);
+OpndSize getRegSize(int type);
+void invalidateVRDueToConst(int reg, OpndSize size);
+#endif
+
diff --git a/vm/compiler/codegen/x86/BytecodeVisitor.cpp b/vm/compiler/codegen/x86/BytecodeVisitor.cpp
new file mode 100644
index 0000000..1d3c70e
--- /dev/null
+++ b/vm/compiler/codegen/x86/BytecodeVisitor.cpp
@@ -0,0 +1,5468 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file BytecodeVisitor.cpp
+    \brief This file implements visitors of the bytecode
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "AnalysisO1.h"
+
+//! Returns size of the current bytecode in u2 unit
+
+//!
+int getByteCodeSize() { //uses inst, unit in u2
+    switch (INST_INST(inst)) {
+    case OP_NOP:
+        return 1;
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+        return 1;
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+        return 2;
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        return 3;
+    case OP_MOVE_WIDE:
+        return 1;
+    case OP_MOVE_WIDE_FROM16:
+        return 2;
+    case OP_MOVE_WIDE_16:
+        return 3;
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+        return 1;
+    case OP_MOVE_RESULT_WIDE:
+        return 1;
+    case OP_MOVE_EXCEPTION:
+        return 1;
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        return 1;
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        return 1;
+    case OP_RETURN_WIDE:
+        return 1;
+    case OP_CONST_4:
+        return 1;
+    case OP_CONST_16:
+        return 2;
+    case OP_CONST:
+        return 3;
+    case OP_CONST_HIGH16:
+        return 2;
+    case OP_CONST_WIDE_16:
+        return 2;
+    case OP_CONST_WIDE_32:
+        return 3;
+    case OP_CONST_WIDE:
+        return 5;
+    case OP_CONST_WIDE_HIGH16:
+        return 2;
+    case OP_CONST_STRING:
+        return 2;
+    case OP_CONST_STRING_JUMBO:
+        return 3;
+    case OP_CONST_CLASS:
+        return 2;
+    case OP_MONITOR_ENTER:
+        return 1;
+    case OP_MONITOR_EXIT:
+        return 1;
+    case OP_CHECK_CAST:
+        return 2;
+    case OP_INSTANCE_OF:
+        return 2;
+    case OP_ARRAY_LENGTH:
+        return 1;
+    case OP_NEW_INSTANCE:
+        return 2;
+    case OP_NEW_ARRAY:
+        return 2;
+    case OP_FILLED_NEW_ARRAY:
+        return 3;
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        return 3;
+    case OP_FILL_ARRAY_DATA:
+        return 3;
+    case OP_THROW:
+        return 1;
+    case OP_THROW_VERIFICATION_ERROR:
+        return 2;
+    case OP_GOTO:
+        return 1;
+    case OP_GOTO_16:
+        return 2;
+    case OP_GOTO_32:
+        return 3;
+    case OP_PACKED_SWITCH:
+        return 3;
+    case OP_SPARSE_SWITCH:
+        return 3;
+    case OP_CMPL_FLOAT:
+        return 2;
+    case OP_CMPG_FLOAT:
+        return 2;
+    case OP_CMPL_DOUBLE:
+        return 2;
+    case OP_CMPG_DOUBLE:
+        return 2;
+    case OP_CMP_LONG:
+        return 2;
+    case OP_IF_EQ:
+        return 2;
+    case OP_IF_NE:
+        return 2;
+    case OP_IF_LT:
+        return 2;
+    case OP_IF_GE:
+        return 2;
+    case OP_IF_GT:
+        return 2;
+    case OP_IF_LE:
+        return 2;
+    case OP_IF_EQZ:
+        return 2;
+    case OP_IF_NEZ:
+        return 2;
+    case OP_IF_LTZ:
+        return 2;
+    case OP_IF_GEZ:
+        return 2;
+    case OP_IF_GTZ:
+        return 2;
+    case OP_IF_LEZ:
+        return 2;
+    case OP_AGET:
+        return 2;
+    case OP_AGET_WIDE:
+        return 2;
+    case OP_AGET_OBJECT:
+        return 2;
+    case OP_AGET_BOOLEAN:
+        return 2;
+    case OP_AGET_BYTE:
+        return 2;
+    case OP_AGET_CHAR:
+        return 2;
+    case OP_AGET_SHORT:
+        return 2;
+    case OP_APUT:
+        return 2;
+    case OP_APUT_WIDE:
+        return 2;
+    case OP_APUT_OBJECT:
+        return 2;
+    case OP_APUT_BOOLEAN:
+        return 2;
+    case OP_APUT_BYTE:
+        return 2;
+    case OP_APUT_CHAR:
+        return 2;
+    case OP_APUT_SHORT:
+        return 2;
+    case OP_IGET:
+    case OP_IGET_WIDE:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IPUT:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+        return 2;
+    case OP_SGET:
+    case OP_SGET_WIDE:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+    case OP_SPUT:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+        return 2;
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        return 3;
+
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_NEG_FLOAT:
+    case OP_NEG_DOUBLE:
+    case OP_INT_TO_LONG:
+    case OP_INT_TO_FLOAT:
+    case OP_INT_TO_DOUBLE:
+    case OP_LONG_TO_INT:
+    case OP_LONG_TO_FLOAT:
+    case OP_LONG_TO_DOUBLE:
+    case OP_FLOAT_TO_INT:
+    case OP_FLOAT_TO_LONG:
+    case OP_FLOAT_TO_DOUBLE:
+    case OP_DOUBLE_TO_INT:
+    case OP_DOUBLE_TO_LONG:
+    case OP_DOUBLE_TO_FLOAT:
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        return 1;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_DIV_INT:
+    case OP_REM_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_MUL_LONG:
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        return 2;
+
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_MUL_LONG_2ADDR:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_REM_FLOAT_2ADDR:
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_DOUBLE_2ADDR:
+    case OP_REM_DOUBLE_2ADDR:
+        return 1;
+
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        return 2;
+
+    case OP_ADD_INT_LIT8:
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        return 2;
+
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+        return 3;
+#if FIXME
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        return 3;
+#endif
+
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+        return 2;
+
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        return 3;
+#ifdef SUPPORT_HLO
+    case kExtInstruction:
+        switch(inst) {
+        case OP_X_AGET_QUICK:
+        case OP_X_AGET_WIDE_QUICK:
+        case OP_X_AGET_OBJECT_QUICK:
+    case OP_X_AGET_BOOLEAN_QUICK:
+    case OP_X_AGET_BYTE_QUICK:
+    case OP_X_AGET_CHAR_QUICK:
+    case OP_X_AGET_SHORT_QUICK:
+    case OP_X_APUT_QUICK:
+    case OP_X_APUT_WIDE_QUICK:
+    case OP_X_APUT_OBJECT_QUICK:
+    case OP_X_APUT_BOOLEAN_QUICK:
+    case OP_X_APUT_BYTE_QUICK:
+    case OP_X_APUT_CHAR_QUICK:
+    case OP_X_APUT_SHORT_QUICK:
+        return 3;
+    case OP_X_DEREF_GET:
+    case OP_X_DEREF_GET_OBJECT:
+    case OP_X_DEREF_GET_WIDE:
+    case OP_X_DEREF_GET_BOOLEAN:
+    case OP_X_DEREF_GET_BYTE:
+    case OP_X_DEREF_GET_CHAR:
+    case OP_X_DEREF_GET_SHORT:
+    case OP_X_DEREF_PUT:
+    case OP_X_DEREF_PUT_WIDE:
+    case OP_X_DEREF_PUT_OBJECT:
+    case OP_X_DEREF_PUT_BOOLEAN:
+    case OP_X_DEREF_PUT_BYTE:
+    case OP_X_DEREF_PUT_CHAR:
+    case OP_X_DEREF_PUT_SHORT:
+        return 2;
+    case OP_X_ARRAY_CHECKS:
+    case OP_X_ARRAY_OBJECT_CHECKS:
+        return 3;
+    case OP_X_CHECK_BOUNDS:
+    case OP_X_CHECK_NULL:
+    case OP_X_CHECK_TYPE:
+        return 2;
+    }
+#endif
+    }
+    return -1;
+}
+//! reduces refCount of a virtual register
+
+//!
+void touchOneVR(u2 vA, LowOpndRegType type) {
+    int index = searchCompileTable(LowOpndRegType_virtual | type, vA);
+    if(index < 0) {
+        ALOGE("virtual reg %d type %d not found in touchOneVR", vA, type);
+        return;
+    }
+    compileTable[index].refCount--;
+}
+//! reduces refCount of two virtual registers
+
+//!
+void touchTwoVRs(u2 vA, u2 vB, LowOpndRegType type) {
+    int index = searchCompileTable(LowOpndRegType_virtual | type, vA);
+    if(index < 0) {
+        ALOGE("virtual reg vA %d type %d not found in touchTwoVRs", vA, type);
+        return;
+    }
+    compileTable[index].refCount--;
+    index = searchCompileTable(LowOpndRegType_virtual | type, vB);
+    if(index < 0) {
+        ALOGE("virtual reg vB %d type %d not found in touchTwoVRs", vB, type);
+        return;
+    }
+    compileTable[index].refCount--;
+}
+int num_const_worklist;
+//! worklist to update constVRTable later
+int constWorklist[10];
+
+int num_const_vr; //in a basic block
+//! table to store the constant information for virtual registers
+ConstVRInfo constVRTable[MAX_CONST_REG];
+//! update constVRTable for a given virtual register
+
+//! set "isConst" to false
+void setVRToNonConst(int regNum, OpndSize size) {
+    int k;
+    int indexL = -1;
+    int indexH = -1;
+    for(k = 0; k < num_const_vr; k++) {
+        if(constVRTable[k].regNum == regNum) {
+            indexL = k;
+            continue;
+        }
+        if(constVRTable[k].regNum == regNum + 1 && size == OpndSize_64) {
+            indexH = k;
+            continue;
+        }
+    }
+    if(indexL >= 0) {
+        //remove this entry??
+        constVRTable[indexL].isConst = false;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        constVRTable[indexH].isConst = false;
+    }
+}
+//! update constVRTable for a given virtual register
+
+//! set "isConst" to true
+void setVRToConst(int regNum, OpndSize size, int* tmpValue) {
+    int k;
+    int indexL = -1;
+    int indexH = -1;
+    for(k = 0; k < num_const_vr; k++) {
+        if(constVRTable[k].regNum == regNum) {
+            indexL = k;
+            continue;
+        }
+        if(constVRTable[k].regNum == regNum + 1 && size == OpndSize_64) {
+            indexH = k;
+            continue;
+        }
+    }
+    if(indexL < 0) {
+        indexL = num_const_vr;
+        constVRTable[indexL].regNum = regNum;
+        num_const_vr++;
+    }
+    constVRTable[indexL].isConst = true;
+    constVRTable[indexL].value = tmpValue[0];
+    if(size == OpndSize_64) {
+        if(indexH < 0) {
+            indexH = num_const_vr;
+            constVRTable[indexH].regNum = regNum+1;
+            num_const_vr++;
+        }
+        constVRTable[indexH].isConst = true;
+        constVRTable[indexH].value = tmpValue[1];
+    }
+    if(num_const_vr > MAX_CONST_REG) ALOGE("constVRTable overflows");
+    invalidateVRDueToConst(regNum, size);
+}
+
+//! perform work on constWorklist
+
+//!
+void updateConstInfo(BasicBlock_O1* bb) {
+    if(bb == NULL) return;
+    int k;
+    for(k = 0; k < num_const_worklist; k++) {
+        //int indexOrig = constWorklist[k];
+        //compileTable[indexOrig].isConst = false;
+        //int A = compileTable[indexOrig].regNum;
+        //LowOpndRegType type = compileTable[indexOrig].physicalType & MASK_FOR_TYPE;
+        setVRToNonConst(constWorklist[k], OpndSize_32);
+    }
+}
+//! check whether the current bytecode generates a const
+
+//! if yes, update constVRTable; otherwise, update constWorklist
+//! if a bytecode uses vA (const), and updates vA to non const, getConstInfo will return false and update constWorklist to make sure when lowering the bytecode, vA is treated as constant
+bool getConstInfo(BasicBlock_O1* bb) {
+    compileTableEntry* infoArray = compileTable;
+    u2 inst_op = INST_INST(inst);
+    u2 vA = 0, vB = 0, v1, v2;
+    u2 BBBB;
+    u2 tmp_u2;
+    s4 tmp_s4;
+    u4 tmp_u4;
+    int entry, tmpValue[2], tmpValue2[2];
+    num_const_worklist = 0;
+
+    switch(inst_op) {
+        //for other opcode, if update the register, set isConst to false
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        if(inst_op == OP_MOVE || inst_op == OP_MOVE_OBJECT) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+        }
+        else if(inst_op == OP_MOVE_FROM16 || inst_op == OP_MOVE_OBJECT_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+        }
+        else if(inst_op == OP_MOVE_16 || inst_op == OP_MOVE_OBJECT_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+        }
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            infoArray[entry].isConst = true;
+            infoArray[entry].value[0] = tmpValue[0];
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+            return true;
+        } else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+        }
+        return false;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        if(inst_op == OP_MOVE_WIDE) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+        }
+        else if(inst_op == OP_MOVE_WIDE_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+        }
+        else if(inst_op == OP_MOVE_WIDE_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+        }
+        if(isVirtualRegConstant(vB, LowOpndRegType_xmm, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_xmm, true);
+            setVRToConst(vA, OpndSize_64, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_xmm);
+            return true;
+        } else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            constWorklist[num_const_worklist] = vA+1;
+            num_const_worklist++;
+        }
+        return false;
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+    case OP_MOVE_EXCEPTION:
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+    case OP_CONST_CLASS:
+    case OP_NEW_INSTANCE:
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+    case OP_AGET:
+    case OP_AGET_OBJECT:
+    case OP_AGET_BOOLEAN:
+    case OP_AGET_BYTE:
+    case OP_AGET_CHAR:
+    case OP_AGET_SHORT:
+    case OP_SGET:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_MOVE_RESULT_WIDE:
+    case OP_AGET_WIDE:
+    case OP_SGET_WIDE:
+    case OP_SGET_WIDE_VOLATILE:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_INSTANCE_OF:
+    case OP_ARRAY_LENGTH:
+    case OP_NEW_ARRAY:
+    case OP_IGET:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_IGET_WIDE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_WIDE_QUICK:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+        //TODO: constant folding for float/double/long ALU
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_NEG_FLOAT:
+    case OP_INT_TO_FLOAT:
+    case OP_LONG_TO_FLOAT:
+    case OP_FLOAT_TO_INT:
+    case OP_DOUBLE_TO_INT:
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_REM_FLOAT_2ADDR:
+    case OP_DOUBLE_TO_FLOAT:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA; //change constWorklist to point to vA TODO
+        num_const_worklist++;
+        return false;
+    case OP_FLOAT_TO_LONG:
+    case OP_DOUBLE_TO_LONG:
+    case OP_FLOAT_TO_DOUBLE:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_NEG_DOUBLE:
+    case OP_INT_TO_DOUBLE: //fp stack
+    case OP_LONG_TO_DOUBLE:
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_DOUBLE_2ADDR:
+    case OP_REM_DOUBLE_2ADDR:
+        //ops on float, double
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_LONG_TO_INT:
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_NEG_INT)
+                infoArray[entry].value[0] = -tmpValue[0];
+            if(inst_op == OP_NOT_INT)
+                infoArray[entry].value[0] = ~tmpValue[0]; //CHECK
+            if(inst_op == OP_LONG_TO_INT)
+                infoArray[entry].value[0] = tmpValue[0];
+            if(inst_op == OP_INT_TO_BYTE)// sar
+                infoArray[entry].value[0] = (tmpValue[0] << 24) >> 24;
+            if(inst_op == OP_INT_TO_CHAR) //shr
+                infoArray[entry].value[0] = ((unsigned int)(tmpValue[0] << 16)) >> 16;
+            if(inst_op == OP_INT_TO_SHORT) //sar
+                infoArray[entry].value[0] = (tmpValue[0] << 16) >> 16;
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_INT_TO_LONG:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1; //fixed on 10/15/2009
+        num_const_worklist++;
+        return false;
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+    case OP_REM_INT_LIT16:
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT8:
+    case OP_DIV_INT_LIT8:
+    case OP_DIV_INT:
+    case OP_REM_INT:
+        if(inst_op == OP_DIV_INT || inst_op == OP_DIV_INT_LIT8 ||
+           inst_op == OP_REM_INT || inst_op == OP_REM_INT_LIT8)
+            vA = INST_AA(inst);
+        else
+            vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        if(isVirtualRegConstant(vA, LowOpndRegType_gp, tmpValue, false) == 3 &&
+           isVirtualRegConstant(v2, LowOpndRegType_gp, tmpValue2, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] + tmpValue2[0];
+            if(inst_op == OP_SUB_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] - tmpValue2[0];
+            if(inst_op == OP_MUL_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] * tmpValue2[0];
+            if(inst_op == OP_DIV_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] / tmpValue2[0];
+            if(inst_op == OP_REM_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] % tmpValue2[0];
+            if(inst_op == OP_AND_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] & tmpValue2[0];
+            if(inst_op == OP_OR_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] | tmpValue2[0];
+            if(inst_op == OP_XOR_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmpValue2[0];
+            if(inst_op == OP_SHL_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] << tmpValue2[0];
+            if(inst_op == OP_SHR_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] >> tmpValue2[0];
+            if(inst_op == OP_USHR_INT_2ADDR)
+                infoArray[entry].value[0] = (unsigned int)tmpValue[0] >> tmpValue2[0];
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(v2, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        tmp_s4 = (s2)FETCH(1);
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] + tmp_s4;
+            if(inst_op == OP_RSUB_INT)
+                infoArray[entry].value[0] = tmp_s4 - tmpValue[0];
+            if(inst_op == OP_MUL_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] * tmp_s4;
+            if(inst_op == OP_DIV_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] / tmp_s4;
+            if(inst_op == OP_REM_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] % tmp_s4;
+            if(inst_op == OP_AND_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] & tmp_s4;
+            if(inst_op == OP_OR_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] | tmp_s4;
+            if(inst_op == OP_XOR_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmp_s4;
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        if(isVirtualRegConstant(v1, LowOpndRegType_gp, tmpValue, false) == 3 &&
+           isVirtualRegConstant(v2, LowOpndRegType_gp, tmpValue2, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT)
+                infoArray[entry].value[0] = tmpValue[0] + tmpValue2[0];
+            if(inst_op == OP_SUB_INT)
+                infoArray[entry].value[0] = tmpValue[0] - tmpValue2[0];
+            if(inst_op == OP_MUL_INT)
+                infoArray[entry].value[0] = tmpValue[0] * tmpValue2[0];
+            if(inst_op == OP_DIV_INT)
+                infoArray[entry].value[0] = tmpValue[0] / tmpValue2[0];
+            if(inst_op == OP_REM_INT)
+                infoArray[entry].value[0] = tmpValue[0] % tmpValue2[0];
+            if(inst_op == OP_AND_INT)
+                infoArray[entry].value[0] = tmpValue[0] & tmpValue2[0];
+            if(inst_op == OP_OR_INT)
+                infoArray[entry].value[0] = tmpValue[0] | tmpValue2[0];
+            if(inst_op == OP_XOR_INT)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmpValue2[0];
+            if(inst_op == OP_SHL_INT)
+                infoArray[entry].value[0] = tmpValue[0] << tmpValue2[0];
+            if(inst_op == OP_SHR_INT)
+                infoArray[entry].value[0] = tmpValue[0] >> tmpValue2[0];
+            if(inst_op == OP_USHR_INT)
+                infoArray[entry].value[0] = (unsigned int)tmpValue[0] >> tmpValue2[0];
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(v1, LowOpndRegType_gp);
+            touchOneVR(v2, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_INT_LIT8: //INST_AA
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        vA = INST_AA(inst);
+        vB = (u2)FETCH(1) & 0xff;
+        tmp_s4 = (s2)FETCH(1) >> 8;
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] + tmp_s4;
+            if(inst_op == OP_RSUB_INT_LIT8)
+                infoArray[entry].value[0] = tmp_s4 - tmpValue[0];
+            if(inst_op == OP_MUL_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] * tmp_s4;
+            if(inst_op == OP_DIV_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] / tmp_s4;
+            if(inst_op == OP_REM_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] % tmp_s4;
+            if(inst_op == OP_AND_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] & tmp_s4;
+            if(inst_op == OP_OR_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] | tmp_s4;
+            if(inst_op == OP_XOR_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmp_s4;
+            if(inst_op == OP_SHL_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] << tmp_s4;
+            if(inst_op == OP_SHR_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] >> tmp_s4;
+            if(inst_op == OP_USHR_INT_LIT8)
+                infoArray[entry].value[0] = (unsigned int)tmpValue[0] >> tmp_s4;
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+    case OP_MUL_LONG:
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+        //TODO bytecode is not going to update state registers
+        //constant folding
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_CMP_LONG:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+    case OP_MUL_LONG_2ADDR:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_CONST_4:
+        vA = INST_A(inst);
+        tmp_s4 = (s4) (INST_B(inst) << 28) >> 28;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = tmp_s4;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, tmp_s4);
+#endif
+        return true;
+    case OP_CONST_16:
+        BBBB = FETCH(1);
+        vA = INST_AA(inst);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s2)BBBB;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_HIGH16:
+        vA = INST_AA(inst);
+        tmp_u2 = FETCH(1);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u2<<16;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE_16:
+        vA = INST_AA(inst);
+        tmp_u2 = FETCH(1);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s2)tmp_u2;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s2)tmp_u2>>31;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE_32:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4>>31;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u8)FETCH(2) << 16;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        tmp_u4 = (u8)FETCH(3);
+        tmp_u4 |= (u8)FETCH(4) << 16;
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE_HIGH16:
+        vA = INST_AA(inst);
+        tmp_u2 = FETCH(1);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = 0;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u2<<16;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+#ifdef SUPPORT_HLO
+    case OP_X_AGET_QUICK:
+    case OP_X_AGET_OBJECT_QUICK:
+    case OP_X_AGET_BOOLEAN_QUICK:
+    case OP_X_AGET_BYTE_QUICK:
+    case OP_X_AGET_CHAR_QUICK:
+    case OP_X_AGET_SHORT_QUICK:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_X_AGET_WIDE_QUICK:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_X_DEREF_GET:
+    case OP_X_DEREF_GET_OBJECT:
+    case OP_X_DEREF_GET_BOOLEAN:
+    case OP_X_DEREF_GET_BYTE:
+    case OP_X_DEREF_GET_CHAR:
+    case OP_X_DEREF_GET_SHORT:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_X_DEREF_GET_WIDE:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+#endif
+    }
+    return false;
+}
+
+//! This function updates infoArray with virtual registers accessed when lowering the bytecode, and returns size of the bytecode in unit of u2
+
+//! uses of virtual registers are added to infoArray first
+int getVirtualRegInfo(VirtualRegInfo* infoArray) {
+    u2 inst_op = INST_INST(inst);
+    u2 vA = 0, vB = 0, vref, vindex;
+    u2 v1, v2, length, vD, vG, vE, vF, count;
+    u4 v1_u4, v2_u4;
+    int kk, num, num_entry;
+    s4 tmp_s4;
+    s2 tmp_s2;
+    u4 tmp_u4;
+    int codeSize = 0;
+    num_regs_per_bytecode = 0;
+    //update infoArray[xx].allocConstraints
+    for(num = 0; num < MAX_REG_PER_BYTECODE; num++) {
+        for(kk = 0; kk < 8; kk++) {
+            infoArray[num].allocConstraints[kk].physicalReg = (PhysicalReg)kk;
+            infoArray[num].allocConstraints[kk].count = 0;
+        }
+    }
+
+    switch (inst_op) {
+    case OP_NOP:
+        codeSize = 1;
+        break;
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        if(inst_op == OP_MOVE || inst_op == OP_MOVE_OBJECT) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+            codeSize = 1;
+        }
+        else if(inst_op == OP_MOVE_FROM16 || inst_op == OP_MOVE_OBJECT_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+            codeSize = 2;
+        }
+        else if(inst_op == OP_MOVE_16 || inst_op == OP_MOVE_OBJECT_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+            codeSize = 3;
+        }
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        if(inst_op == OP_MOVE_WIDE) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+            codeSize = 1;
+        }
+        else if(inst_op == OP_MOVE_WIDE_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+            codeSize = 2;
+        }
+        else if(inst_op == OP_MOVE_WIDE_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+            codeSize = 3;
+        }
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_MOVE_RESULT: //access memory
+    case OP_MOVE_RESULT_OBJECT:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        codeSize = 1;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MOVE_RESULT_WIDE: //note: 2 destinations
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        codeSize = 1;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MOVE_EXCEPTION: //access memory
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        codeSize = 1;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        codeSize = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        vA = INST_AA(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_RETURN_WIDE:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 1;
+        codeSize = 1;
+        break;
+    case OP_CONST_4:
+        vA = INST_A(inst);
+        tmp_s4 = (s4) (INST_B(inst) << 28) >> 28;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 1;
+        break;
+    case OP_CONST_16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_CONST:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 3;
+        break;
+    case OP_CONST_HIGH16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_CONST_WIDE_16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        codeSize = 2;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_CONST_WIDE_32:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 3;
+        break;
+    case OP_CONST_WIDE:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u8)FETCH(2) << 16;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        tmp_u4 = (u8)FETCH(3);
+        tmp_u4 |= (u8)FETCH(4) << 16;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        codeSize = 5;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_CONST_WIDE_HIGH16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 2;
+        break;
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+    case OP_CONST_CLASS:
+        vA = INST_AA(inst);
+        if(inst_op == OP_CONST_STRING || inst_op == OP_CONST_CLASS)
+            codeSize = 2;
+        else if(inst_op == OP_CONST_STRING_JUMBO)
+            codeSize = 3;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MONITOR_ENTER:
+        vA = INST_AA(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MONITOR_EXIT:
+        vA = INST_AA(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX); //eax is used as return value from c function
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_CHECK_CAST:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_INSTANCE_OF:
+        codeSize = 2;
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_ARRAY_LENGTH:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        //%edx is used in this bytecode, update currentBB->allocConstraints
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_NEW_INSTANCE:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_NEW_ARRAY:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst); //length
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        codeSize = 2;
+        break;
+    case OP_FILLED_NEW_ARRAY: {//update return value
+        //can use up to 5 registers to fill the content of array
+        length = INST_B(inst);
+        u2 vv = FETCH(2);
+        v1 = vv & 0xf;
+        v2 = (vv >> 4) & 0xf;
+        u2 v3 = (vv >> 8) & 0xf;
+        u2 v4 = (vv >> 12) & 0xf;
+        u2 v5 = INST_A(inst);
+        if(length >= 1) {
+            infoArray[0].regNum = v1; //src
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 2) {
+            infoArray[1].regNum = v2; //src
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 3) {
+            infoArray[2].regNum = v3; //src
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 4) {
+            infoArray[3].regNum = v4; //src
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 5) {
+            infoArray[4].regNum = v5; //src
+            infoArray[4].refCount = 1;
+            infoArray[4].accessType = REGACCESS_U;
+            infoArray[4].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = length;
+        codeSize = 3;
+        break;
+    }
+    case OP_FILLED_NEW_ARRAY_RANGE: {//use "length" virtual registers
+        length = INST_AA(inst);
+        u4 vC = (u4)FETCH(2);
+        for(kk = 0; kk < length; kk++) {
+            infoArray[kk].regNum = vC+kk; //src
+            infoArray[kk].refCount = 1;
+            infoArray[kk].accessType = REGACCESS_U;
+            infoArray[kk].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = length;
+        codeSize = 3;
+        break;
+    }
+    case OP_FILL_ARRAY_DATA: //update content of array, read memory
+        vA = INST_AA(inst); //use virtual register, but has side-effect, update memory
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        codeSize = 3;
+        break;
+    case OP_THROW: //update glue->exception
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        codeSize = 1;
+        break;
+    case OP_THROW_VERIFICATION_ERROR:
+        num_regs_per_bytecode = 0;
+        codeSize = 2;
+        break;
+    case OP_GOTO:
+        codeSize = 1;
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_GOTO_16:
+        codeSize = 2;
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_GOTO_32:
+        codeSize = 3;
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_PACKED_SWITCH:
+    case OP_SPARSE_SWITCH:
+        vA = INST_AA(inst);
+        codeSize = 3;
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        break;
+
+    case OP_CMPL_FLOAT: //move 32 bits from memory to lower part of XMM register
+    case OP_CMPG_FLOAT:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        v1_u4 = FETCH(1) & 0xff;
+        v2_u4 = FETCH(1) >> 8;
+        num_regs_per_bytecode = 1;
+        infoArray[0].regNum = v1_u4; //use ss or sd CHECK
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = v2_u4; //use
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        num_regs_per_bytecode = 3;
+        num_entry = 2;
+        infoArray[num_entry].regNum = vA; //define
+        infoArray[num_entry].refCount = 1;
+        infoArray[num_entry].accessType = REGACCESS_D;
+        infoArray[num_entry].physicalType = LowOpndRegType_gp;
+        break;
+    case OP_CMPL_DOUBLE: //move 64 bits from memory to lower part of XMM register
+    case OP_CMPG_DOUBLE:
+    case OP_CMP_LONG: //load v1, v1+1, v2, v2+1 to gpr
+        codeSize = 2;
+        vA = INST_AA(inst);
+        v1_u4 = FETCH(1) & 0xff;
+        v2_u4 = FETCH(1) >> 8;
+        num_regs_per_bytecode = 1;
+        if(inst_op == OP_CMP_LONG) {
+            infoArray[0].regNum = v1_u4; //use
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+            infoArray[1].regNum = v1_u4 + 1; //use
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+            infoArray[2].regNum = v2_u4; //use
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+            infoArray[3].regNum = v2_u4 + 1; //use
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+            num_regs_per_bytecode = 5;
+            num_entry = 4;
+            infoArray[num_entry].regNum = vA; //define
+            infoArray[num_entry].refCount = 2;
+            infoArray[num_entry].accessType = REGACCESS_D;
+            infoArray[num_entry].physicalType = LowOpndRegType_gp;
+        }
+        else {
+            infoArray[0].regNum = v1_u4; //use ss or sd CHECK
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm;
+            infoArray[1].regNum = v2_u4; //use
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_xmm;
+            num_regs_per_bytecode = 3;
+            num_entry = 2;
+            infoArray[num_entry].regNum = vA; //define
+            infoArray[num_entry].refCount = 1;
+            infoArray[num_entry].accessType = REGACCESS_D;
+            infoArray[num_entry].physicalType = LowOpndRegType_gp;
+        }
+        break;
+    case OP_IF_EQ:
+    case OP_IF_NE:
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vB;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize =12;
+        break;
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_AGET:
+        codeSize = 2;
+    case OP_AGET_WIDE:
+        codeSize = 2;
+    case OP_AGET_OBJECT:
+        codeSize = 2;
+    case OP_AGET_BOOLEAN: //movez 8
+        codeSize = 2;
+    case OP_AGET_BYTE: //moves 8
+        codeSize = 2;
+    case OP_AGET_CHAR: //movez 16
+        codeSize = 2;
+    case OP_AGET_SHORT: //moves 16
+        codeSize = 2;
+        vA = INST_AA(inst);
+        vref = FETCH(1) & 0xff;
+        vindex = FETCH(1) >> 8;
+        if(inst_op == OP_AGET_WIDE) {
+            infoArray[2].regNum = vA;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_D;
+            infoArray[2].physicalType = LowOpndRegType_xmm; //64, 128 not used in lowering
+        } else {
+            infoArray[2].regNum = vA;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_D;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[0].regNum = vref; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vindex; //use
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_APUT:
+    case OP_APUT_WIDE:
+    case OP_APUT_OBJECT:
+    case OP_APUT_BOOLEAN:
+    case OP_APUT_BYTE:
+    case OP_APUT_CHAR:
+    case OP_APUT_SHORT:
+        vA = INST_AA(inst);
+        vref = FETCH(1) & 0xff;
+        vindex = FETCH(1) >> 8;
+        codeSize = 2;
+        if(inst_op == OP_APUT_WIDE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64, 128 not used in lowering
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[1].regNum = vref; //use
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = vindex; //use
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        if(inst_op == OP_APUT_OBJECT) {
+            updateCurrentBBWithConstraints(PhysicalReg_EAX);
+            updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        }
+        num_regs_per_bytecode = 3;
+        break;
+
+    case OP_IGET:
+    case OP_IGET_WIDE:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        if(inst_op == OP_IGET_WIDE || inst_op == OP_IGET_WIDE_QUICK) {
+            infoArray[1].regNum = vA;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_xmm; //64
+        } else if(inst_op == OP_IGET_WIDE_VOLATILE) {
+            infoArray[1].regNum = vA;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+            infoArray[2].regNum = vA+1;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_D;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        } else {
+            infoArray[1].regNum = vA;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[0].regNum = vB; //object instance
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        if(inst_op == OP_IGET_WIDE_VOLATILE)
+            num_regs_per_bytecode = 3;
+        else
+            num_regs_per_bytecode = 2;
+        break;
+    case OP_IPUT:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        if(inst_op == OP_IPUT_WIDE || inst_op == OP_IPUT_WIDE_QUICK || inst_op == OP_IPUT_WIDE_VOLATILE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[1].regNum = vB; //object instance
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_SGET:
+    case OP_SGET_WIDE:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+        vA = INST_AA(inst);
+        codeSize = 2;
+        if(inst_op == OP_SGET_WIDE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_D;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64
+        } else if(inst_op == OP_SGET_WIDE_VOLATILE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_D;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+            infoArray[1].regNum = vA+1;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_D;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(inst_op == OP_SGET_WIDE_VOLATILE)
+            num_regs_per_bytecode = 2;
+        else
+            num_regs_per_bytecode = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        break;
+    case OP_SPUT:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+        vA = INST_AA(inst);
+        codeSize = 2;
+        if(inst_op == OP_SPUT_WIDE || inst_op == OP_SPUT_WIDE_VOLATILE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 1;
+        break;
+
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_SUPER_QUICK:
+        codeSize = 3;
+        vD = FETCH(2) & 0xf; //object for virtual,direct & interface
+        count = INST_B(inst);
+        vE = (FETCH(2) >> 4) & 0xf;
+        vF = (FETCH(2) >> 8) & 0xf;
+        vG = (FETCH(2) >> 12) & 0xf;
+        vA = INST_A(inst); //5th argument
+        if(count == 0) {
+            if(inst_op == OP_INVOKE_VIRTUAL || inst_op == OP_INVOKE_DIRECT ||
+               inst_op == OP_INVOKE_INTERFACE || inst_op == OP_INVOKE_VIRTUAL_QUICK ||
+               inst_op == OP_INVOKE_SUPER_QUICK) {
+                infoArray[0].regNum = vD;
+                infoArray[0].refCount = 1;
+                infoArray[0].accessType = REGACCESS_U;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                num_regs_per_bytecode = 1;
+            }
+            num_regs_per_bytecode = 0;
+        }
+        else num_regs_per_bytecode = count;
+        if(count >= 1) {
+            infoArray[0].regNum = vD;
+            if(inst_op == OP_INVOKE_VIRTUAL_QUICK ||
+               inst_op == OP_INVOKE_SUPER_QUICK) {
+                infoArray[0].refCount = 2;
+            } else if(inst_op == OP_INVOKE_VIRTUAL || inst_op == OP_INVOKE_DIRECT || inst_op == OP_INVOKE_INTERFACE) {
+                infoArray[0].refCount = 2;
+            } else {
+                infoArray[0].refCount = 1;
+            }
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 2) {
+            infoArray[1].regNum = vE;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 3) {
+            infoArray[2].regNum = vF;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 4) {
+            infoArray[3].regNum = vG;
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 5) {
+            infoArray[4].regNum = vA;
+            infoArray[4].refCount = 1;
+            infoArray[4].accessType = REGACCESS_U;
+            infoArray[4].physicalType = LowOpndRegType_gp;
+        }
+        if(inst_op != OP_INVOKE_VIRTUAL_QUICK && inst_op != OP_INVOKE_SUPER_QUICK)
+            updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        break;
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE_RANGE:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        codeSize = 3;
+        vD = FETCH(2);
+        count = INST_AA(inst);
+        if(count == 0) {
+            if(inst_op == OP_INVOKE_VIRTUAL_RANGE || inst_op == OP_INVOKE_DIRECT_RANGE ||
+               inst_op == OP_INVOKE_INTERFACE_RANGE || inst_op == OP_INVOKE_VIRTUAL_QUICK_RANGE ||
+               inst_op == OP_INVOKE_SUPER_QUICK_RANGE) {
+                infoArray[0].regNum = vD;
+                infoArray[0].refCount = 1;
+                infoArray[0].accessType = REGACCESS_U;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+            }
+        }
+        if(count > 0) { //same for count > 10
+            for(kk = 0; kk < count; kk++) {
+                infoArray[kk].regNum = vD+kk; //src
+                if(kk == 0 && (inst_op == OP_INVOKE_VIRTUAL_QUICK_RANGE ||
+                               inst_op == OP_INVOKE_SUPER_QUICK_RANGE))
+                    infoArray[kk].refCount = 2;
+                else if(kk == 0 && (inst_op == OP_INVOKE_VIRTUAL_RANGE ||
+                                    inst_op == OP_INVOKE_DIRECT_RANGE ||
+                                    inst_op == OP_INVOKE_INTERFACE_RANGE))
+                    infoArray[kk].refCount = 2;
+                else
+                    infoArray[kk].refCount = 1;
+                infoArray[kk].accessType = REGACCESS_U;
+                infoArray[kk].physicalType = LowOpndRegType_gp;
+            }
+        }
+        if(inst_op != OP_INVOKE_VIRTUAL_QUICK_RANGE && inst_op != OP_INVOKE_SUPER_QUICK_RANGE)
+            updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = count;
+        break;
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_NEG_FLOAT:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        break;
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_NEG_DOUBLE:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_INT_TO_LONG: //hard-coded registers
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp; //save from %eax
+        infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        infoArray[2].regNum = vA+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[2].allocConstraints[PhysicalReg_EDX].count = 1;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_INT_TO_FLOAT: //32 to 32
+    case OP_INT_TO_DOUBLE: //32 to 64
+    case OP_LONG_TO_FLOAT: //64 to 32
+    case OP_LONG_TO_DOUBLE: //64 to 64
+    case OP_FLOAT_TO_DOUBLE: //32 to 64
+    case OP_DOUBLE_TO_FLOAT: //64 to 32
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        if(inst_op == OP_INT_TO_DOUBLE || inst_op == OP_LONG_TO_DOUBLE || inst_op == OP_FLOAT_TO_DOUBLE)
+            infoArray[1].physicalType = LowOpndRegType_fs;
+        else
+            infoArray[1].physicalType = LowOpndRegType_fs_s;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        if(inst_op == OP_INT_TO_FLOAT || inst_op == OP_INT_TO_DOUBLE || inst_op == OP_FLOAT_TO_DOUBLE)
+            infoArray[0].physicalType = LowOpndRegType_fs_s; //float
+        else
+            infoArray[0].physicalType = LowOpndRegType_fs;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_LONG_TO_INT:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        break;
+    case OP_FLOAT_TO_INT:
+    case OP_DOUBLE_TO_INT: //for reaching-def analysis
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 3;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_fs_s; //store_int_fp_stack_VR
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        if(inst_op == OP_DOUBLE_TO_INT)
+            infoArray[0].physicalType = LowOpndRegType_fs;
+        else
+            infoArray[0].physicalType = LowOpndRegType_fs_s;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_FLOAT_TO_LONG:
+    case OP_DOUBLE_TO_LONG:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 3;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_fs;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        if(inst_op == OP_DOUBLE_TO_LONG)
+            infoArray[0].physicalType = LowOpndRegType_fs;
+        else
+            infoArray[0].physicalType = LowOpndRegType_fs_s;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_DIV_INT:
+    case OP_REM_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 2;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1; //for v1
+        if(inst_op == OP_REM_INT)
+            infoArray[2].allocConstraints[PhysicalReg_EDX].count = 1;//vA
+        else
+            infoArray[2].allocConstraints[PhysicalReg_EAX].count = 1;//vA
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2; // in ecx
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].allocConstraints[PhysicalReg_ECX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_MUL_LONG: //used int
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v1+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = v2;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = v2+1;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_U;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = vA;
+        infoArray[4].refCount = 1;
+        infoArray[4].accessType = REGACCESS_D;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = vA+1;
+        infoArray[5].refCount = 1;
+        infoArray[5].accessType = REGACCESS_D;
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 6;
+        codeSize = 2;
+        break;
+    case OP_DIV_LONG: //v1: xmm v2,vA:
+    case OP_REM_LONG:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = v2+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_D;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = vA+1;
+        infoArray[4].refCount = 1;
+        infoArray[4].accessType = REGACCESS_D;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 5;
+        codeSize = 2;
+        break;
+    case OP_SHL_LONG: //v2: 32, move_ss; v1,vA: xmm CHECK
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        codeSize = 2;
+        break;
+    case OP_SHR_LONG: //v2: 32, move_ss; v1,vA: xmm CHECK
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        infoArray[2].regNum = v1+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_D;
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 4;
+        codeSize = 2;
+        break;
+    case OP_USHR_LONG: //v2: move_ss; v1,vA: move_sd
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm; //sd
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss; //ss
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm; //sd
+        num_regs_per_bytecode = 3;
+        codeSize = 2;
+        break;
+    case OP_ADD_FLOAT: //move_ss
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_ss;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_REM_FLOAT: //32 bit GPR, fp_stack for output
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_fs_s;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_ADD_DOUBLE: //move_sd
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_REM_DOUBLE: //64 bit XMM, fp_stack for output
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_fs;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        break;
+
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD; //use then define
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 3;
+        infoArray[1].accessType = REGACCESS_UD; //use then define
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1; //for v1 is vA
+        if(inst_op == OP_REM_INT_2ADDR)
+            infoArray[1].allocConstraints[PhysicalReg_EDX].count = 1;//vA
+        else
+            infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;//vA
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD; //use then define
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_ECX].count = 1; //v2
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_MUL_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        num_regs_per_bytecode = 4;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 2;
+        infoArray[2].accessType = REGACCESS_UD;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA+1;
+        infoArray[3].refCount = 2;
+        infoArray[3].accessType = REGACCESS_UD;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        break;
+    case OP_DIV_LONG_2ADDR: //vA used as xmm, then updated as gps
+    case OP_REM_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 5;
+        codeSize = 1;
+        infoArray[0].regNum = vA;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = v2+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_D;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = vA+1;
+        infoArray[4].refCount = 1;
+        infoArray[4].accessType = REGACCESS_D;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        break;
+    case OP_SHL_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        infoArray[0].regNum = v2; //ss
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        break;
+    case OP_SHR_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 3;
+        codeSize = 1;
+        infoArray[0].regNum = v2; //ss
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 2;
+        infoArray[2].accessType = REGACCESS_UD;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        break;
+    case OP_USHR_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss; //ss CHECK
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm; //sd
+        break;
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_REM_FLOAT_2ADDR: //load vA as GPR, store from fs
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_gp; //CHECK
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_DOUBLE_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_REM_DOUBLE_2ADDR: //load to xmm, store from fs
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm; //CHECK
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        tmp_s4 = (s2)FETCH(1);
+        tmp_s2 = tmp_s4;
+        if(tmp_s2 == 0) {
+            num_regs_per_bytecode = 0;
+            break;
+        }
+        infoArray[1].regNum = vA; //in edx for rem, in eax
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB; //in eax
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        if(inst_op == OP_DIV_INT_LIT16) {
+            int power = isPowerOfTwo(tmp_s2);
+            if(power >= 1) { /* divide by a power of 2 constant */
+                infoArray[1].refCount = 1;
+                break;
+            }
+        }
+        if(tmp_s2 == -1)
+            infoArray[1].refCount = 2;
+        else
+            infoArray[1].refCount = 1;
+        if(inst_op == OP_REM_INT_LIT16)
+            infoArray[1].allocConstraints[PhysicalReg_EDX].count = 1;
+        else
+            infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        break;
+    case OP_ADD_INT_LIT8:
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        vB = (u2)FETCH(1) & 0xff;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        vB = (u2)FETCH(1) & 0xff;
+        tmp_s2 = (s2)FETCH(1) >> 8;
+        if(tmp_s2 == 0) {
+            num_regs_per_bytecode = 0;
+            break;
+        }
+
+        infoArray[1].regNum = vA;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        if(inst_op == OP_DIV_INT_LIT8) {
+            int power = isPowerOfTwo(tmp_s2);
+            if(power >= 1) { /* divide by a power of 2 constant */
+                infoArray[1].refCount = 1;
+                break;
+            }
+        }
+
+        if(tmp_s2 == -1)
+            infoArray[1].refCount = 2;
+        else
+            infoArray[1].refCount = 1;
+        if(inst_op == OP_REM_INT_LIT8)
+            infoArray[1].allocConstraints[PhysicalReg_EDX].count = 1;
+        else
+            infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        break;
+    case OP_EXECUTE_INLINE: //update glue->retval
+    case OP_EXECUTE_INLINE_RANGE:
+        u4 vC;
+        if(inst_op == OP_EXECUTE_INLINE)
+            num = INST_B(inst);
+        else
+            num = INST_AA(inst);
+        if(inst_op == OP_EXECUTE_INLINE) {
+            vC = FETCH(2) & 0xf;
+            vD = (FETCH(2) >> 4) & 0xf;
+            vE = (FETCH(2) >> 8) & 0xf;
+            vF = FETCH(2) >> 12;
+        } else {
+            vC = FETCH(2);
+            vD = vC + 1;
+            vE = vC + 2;
+            vF = vC + 3;
+        }
+        codeSize = 3;
+        if(num >= 1) {
+            infoArray[0].regNum = vC;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 2) {
+            infoArray[1].regNum = vD;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 3) {
+            infoArray[2].regNum = vE;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 4) {
+            infoArray[3].regNum = vF;
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = num;
+        break;
+#if FIXME
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        codeSize = 3;
+        num_regs_per_bytecode = 0;
+        break;
+#endif
+    }
+    return codeSize;
+}
+//! Updates infoArray(TempRegInfo) with temporaries accessed by INVOKE_NO_RANGE
+
+//!
+int updateInvokeNoRange(TempRegInfo* infoArray, int startInd) {
+    int j = startInd;
+    //invokeMethodNoRange
+    int count = INST_B(inst);
+    if(count == 5) {
+        infoArray[j].regNum = 22;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 4) {
+        infoArray[j].regNum = 23;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 3) {
+        infoArray[j].regNum = 24;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 2) {
+        infoArray[j].regNum = 25;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 1) {
+        infoArray[j].regNum = 26;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    return j;
+}
+//! Updates infoArray(TempRegInfo) with temporaries accessed by INVOKE_RANGE
+
+//! LOOP_COUNT is used to indicate a variable is live through a loop
+int updateInvokeRange(TempRegInfo* infoArray, int startIndex) {
+    int j = startIndex;
+    int count = INST_AA(inst);
+    infoArray[j].regNum = 21;
+    if(count <= 10) {
+        infoArray[j].refCount = 1+count; //DU
+    } else {
+        infoArray[j].refCount = 2+3*LOOP_COUNT;
+    }
+    infoArray[j].physicalType = LowOpndRegType_gp;
+    j++;
+    if(count >= 1 && count <= 10) {
+        infoArray[j].regNum = 22;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 2 && count <= 10) {
+        infoArray[j].regNum = 23;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 3 && count <= 10) {
+        infoArray[j].regNum = 24;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 4 && count <= 10) {
+        infoArray[j].regNum = 25;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 5 && count <= 10) {
+        infoArray[j].regNum = 26;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 6 && count <= 10) {
+        infoArray[j].regNum = 27;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 7 && count <= 10) {
+        infoArray[j].regNum = 28;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 8 && count <= 10) {
+        infoArray[j].regNum = 29;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 9 && count <= 10) {
+        infoArray[j].regNum = 30;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count == 10) {
+        infoArray[j].regNum = 31;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count > 10) {
+        //NOTE: inside a loop, LOOP_COUNT can't be 1
+        //      if LOOP_COUNT is 1, it is likely that a logical register is freed inside the loop
+        //         and the next iteration will have incorrect result
+        infoArray[j].regNum = 12;
+        infoArray[j].refCount = 1+3*LOOP_COUNT; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+        infoArray[j].regNum = 13;
+        infoArray[j].refCount = 1+LOOP_COUNT; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+        infoArray[j].regNum = 14;
+        //MUST be 2, otherwise, transferToState will think its state was in memory
+        infoArray[j].refCount = 2; //DU local
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    return j;
+}
+
+/* update temporaries used by RETURN bytecodes
+   a temporary is represented by <number, type of the temporary>
+   */
+int updateReturnCommon(TempRegInfo* infoArray) {
+    int numTmps;
+    infoArray[0].regNum = 1;
+    infoArray[0].refCount = 4; //DU
+    infoArray[0].physicalType = LowOpndRegType_scratch;
+    infoArray[1].regNum = 2;
+    infoArray[1].refCount = 2; //DU
+    infoArray[1].physicalType = LowOpndRegType_scratch;
+    infoArray[2].regNum = PhysicalReg_EAX;
+    infoArray[2].refCount = 5; //DU
+    infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+    infoArray[3].regNum = 1;
+#if defined(ENABLE_TRACING)//WITH_DEBUGGER is true WITH_PROFILER can be false
+    infoArray[3].refCount = 6+4;
+#else
+    infoArray[3].refCount = 6; //DU
+#endif
+    infoArray[3].physicalType = LowOpndRegType_gp;
+    infoArray[4].regNum = 2;
+    infoArray[4].refCount = 4; //DU
+    infoArray[4].physicalType = LowOpndRegType_gp;
+    infoArray[5].regNum = 5;
+    infoArray[5].refCount = 2; //DU
+    infoArray[5].physicalType = LowOpndRegType_gp;
+    infoArray[6].regNum = 10;
+    infoArray[6].refCount = 3;
+    infoArray[6].physicalType = LowOpndRegType_gp;
+    infoArray[7].regNum = 6;
+    infoArray[7].refCount = 4; //DU
+    infoArray[7].physicalType = LowOpndRegType_gp;
+    infoArray[8].regNum = 3;
+    infoArray[8].refCount = 3;
+    infoArray[8].physicalType = LowOpndRegType_gp;
+    infoArray[9].regNum = 7;
+    infoArray[9].refCount = 2; //DU
+    infoArray[9].physicalType = LowOpndRegType_gp;
+    numTmps = 12;
+#if defined(ENABLE_TRACING)
+    infoArray[12].regNum = 4;
+    infoArray[12].refCount = 3; //DU
+    infoArray[12].physicalType = LowOpndRegType_gp;
+    infoArray[13].regNum = 3;
+    infoArray[13].refCount = 2; //DU
+    infoArray[13].physicalType = LowOpndRegType_scratch;
+    infoArray[14].regNum = 15;
+    infoArray[14].refCount = 2; //DU
+    infoArray[14].physicalType = LowOpndRegType_gp;
+    infoArray[15].regNum = 16;
+    infoArray[15].refCount = 2; //DU
+    infoArray[15].physicalType = LowOpndRegType_gp;
+    infoArray[16].regNum = PhysicalReg_EDX;
+    infoArray[16].refCount = 2; //DU
+    infoArray[16].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+    infoArray[17].regNum = 6;
+    infoArray[17].refCount = 2; //DU
+    infoArray[17].physicalType = LowOpndRegType_scratch;
+    numTmps = 18;
+#endif
+    infoArray[10].regNum = 14;
+    infoArray[10].refCount = 2; //DU
+    infoArray[10].physicalType = LowOpndRegType_gp;
+    infoArray[11].regNum = 4;
+    infoArray[11].refCount = 2; //DU
+    infoArray[11].physicalType = LowOpndRegType_scratch;
+#ifdef DEBUG_CALL_STACK
+    infoArray[numTmps].regNum = 5;
+    infoArray[numTmps].refCount = 2;
+    infoArray[numTmps].physicalType = LowOpndRegType_scratch;
+    numTmps++;
+#endif
+    infoArray[numTmps].regNum = PhysicalReg_EBX;
+    /* used to hold chaining cell
+       updated to be returnAddr
+       then conditionally updated to zero
+       used to update inJitCodeCache
+       compare against zero to determine whether to jump to native code
+       jump to native code (%ebx)
+    */
+    infoArray[numTmps].refCount = 3+1+1;
+    infoArray[numTmps].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+    numTmps++;
+    infoArray[numTmps].regNum = 17;
+    infoArray[numTmps].refCount = 2; //DU
+    infoArray[numTmps].physicalType = LowOpndRegType_gp;
+    numTmps++;
+    infoArray[numTmps].regNum = 7;
+    infoArray[numTmps].refCount = 4; //DU
+    infoArray[numTmps].physicalType = LowOpndRegType_scratch;
+    numTmps++;
+    return numTmps;
+}
+
+/* update temporaries used by predicted INVOKE_VIRTUAL & INVOKE_INTERFACE */
+int updateGenPrediction(TempRegInfo* infoArray, bool isInterface) {
+    infoArray[0].regNum = 40;
+    infoArray[0].physicalType = LowOpndRegType_gp;
+    infoArray[1].regNum = 41;
+    infoArray[1].physicalType = LowOpndRegType_gp;
+    infoArray[2].regNum = 32;
+    infoArray[2].refCount = 2;
+    infoArray[2].physicalType = LowOpndRegType_gp;
+
+    if(isInterface) {
+        infoArray[0].refCount = 2+2;
+        infoArray[1].refCount = 3+2-1; //for temp41, -1 for gingerbread
+        infoArray[3].regNum = 33;
+        infoArray[3].refCount = 4+1;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = PhysicalReg_EAX;
+        infoArray[4].refCount = 5;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[5].regNum = PhysicalReg_ECX;
+        infoArray[5].refCount = 1+1+2; //used in ArgsDone (twice)
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = 10;
+        infoArray[6].refCount = 2;
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 9;
+        infoArray[7].refCount = 2;
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = 8;
+        infoArray[8].refCount = 2;
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = PhysicalReg_EDX; //space holder
+        infoArray[9].refCount = 1;
+        infoArray[9].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[10].regNum = 43;
+        infoArray[10].refCount = 3;
+        infoArray[10].physicalType = LowOpndRegType_gp;
+        infoArray[11].regNum = 44;
+        infoArray[11].refCount = 3;
+        infoArray[11].physicalType = LowOpndRegType_gp;
+        infoArray[12].regNum = 45;
+        infoArray[12].refCount = 2;
+        infoArray[12].physicalType = LowOpndRegType_gp;
+        infoArray[13].regNum = 7;
+        infoArray[13].refCount = 4;
+        infoArray[13].physicalType = LowOpndRegType_scratch;
+        return 14;
+    } else { //virtual or virtual_quick
+        infoArray[0].refCount = 2+2;
+        infoArray[1].refCount = 3+2-2; //for temp41, -2 for gingerbread
+        infoArray[2].refCount++; //for temp32 gingerbread
+        infoArray[3].regNum = 33;
+        infoArray[3].refCount = 4+1;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 34;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EAX;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_ECX;
+        infoArray[6].refCount = 1+3+2;
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = 10;
+        infoArray[7].refCount = 2;
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = PhysicalReg_EDX; //space holder
+        infoArray[8].refCount = 1;
+        infoArray[8].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[9].regNum = 43;
+        infoArray[9].refCount = 3;
+        infoArray[9].physicalType = LowOpndRegType_gp;
+        infoArray[10].regNum = 44;
+        infoArray[10].refCount = 3;
+        infoArray[10].physicalType = LowOpndRegType_gp;
+        infoArray[11].regNum = 7;
+        infoArray[11].refCount = 4;
+        infoArray[11].physicalType = LowOpndRegType_scratch;
+        return 12;
+    }
+}
+
+int updateMarkCard(TempRegInfo* infoArray, int j1/*valReg*/,
+                    int j2/*tgtAddrReg*/, int j3/*scratchReg*/) {
+    infoArray[j3].regNum = 11;
+    infoArray[j3].physicalType = LowOpndRegType_gp;
+    infoArray[j3].refCount = 3;
+    infoArray[j3].is8Bit = true;
+    infoArray[j1].refCount++;
+    infoArray[j2].refCount += 2;
+    infoArray[j3+1].regNum = 6;
+    infoArray[j3+1].physicalType = LowOpndRegType_scratch;
+    infoArray[j3+1].refCount = 2;
+    return j3+2;
+}
+
+int updateMarkCard_notNull(TempRegInfo* infoArray,
+                           int j2/*tgtAddrReg*/, int j3/*scratchReg*/) {
+    infoArray[j3].regNum = 11;
+    infoArray[j3].physicalType = LowOpndRegType_gp;
+    infoArray[j3].refCount = 3;
+    infoArray[j3].is8Bit = true;
+    infoArray[j2].refCount += 2;
+    infoArray[j3+1].regNum = 2;
+    infoArray[j3+1].refCount = 2; //DU
+    infoArray[j3+1].physicalType = LowOpndRegType_scratch;
+    return j3+2;
+}
+
+int iget_obj_inst = -1;
+//! This function updates infoArray with temporaries accessed when lowering the bytecode
+
+//! returns the number of temporaries
+int getTempRegInfo(TempRegInfo* infoArray) { //returns an array of TempRegInfo
+    int k;
+    int numTmps;
+    for(k = 0; k < MAX_TEMP_REG_PER_BYTECODE; k++) {
+        infoArray[k].linkageToVR = -1;
+        infoArray[k].versionNum = 0;
+        infoArray[k].shareWithVR = true;
+        infoArray[k].is8Bit = false;
+    }
+    u2 vA, v1, length, num, tmp;
+    u2 inst_op = INST_INST(inst);
+    s2 tmp_s2;
+    s4 tmp_s4;
+    switch(inst_op) {
+    case OP_APUT_BYTE:
+        for(k = 0; k < MAX_TEMP_REG_PER_BYTECODE; k++)
+            infoArray[k].shareWithVR = true; //false;
+        break;
+    }
+    switch (INST_INST(inst)) {
+    case OP_NOP:
+        return 0;
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        return 1;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        return 1;
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        return 2;
+    case OP_MOVE_RESULT_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        return 2;
+    case OP_MOVE_EXCEPTION:
+        infoArray[0].regNum = 2;
+        infoArray[0].refCount = 3; //DUU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        return 3;
+
+    case OP_CONST_4:
+    case OP_CONST_16:
+    case OP_CONST:
+    case OP_CONST_HIGH16:
+    case OP_CONST_WIDE_16:
+    case OP_CONST_WIDE_32:
+    case OP_CONST_WIDE:
+    case OP_CONST_WIDE_HIGH16:
+        return 0;
+    case OP_CONST_STRING: //hardcode %eax
+    case OP_CONST_STRING_JUMBO:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 4;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 4;
+    case OP_CONST_CLASS:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 4;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 4;
+
+    case OP_MONITOR_ENTER:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = 2;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_MONITOR_EXIT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EAX;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = PhysicalReg_EDX;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 3;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        return 6;
+    case OP_CHECK_CAST:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 4;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 6;
+        infoArray[2].refCount = 3; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+
+        infoArray[5].regNum = PhysicalReg_EAX;
+        /* %eax has 3 live ranges
+           1> 5 accesses: to resolve the class object
+           2> call dvmInstanceofNonTrivial to define %eax, then use it once
+           3> move exception object to %eax, then jump to throw_exception
+           if WITH_JIT is true, the first live range has 6 accesses
+        */
+        infoArray[5].refCount = 6;
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_EDX;
+        infoArray[6].refCount = 2; //export_pc
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = PhysicalReg_ECX;
+        infoArray[7].refCount = 1;
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[8].regNum = 3;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        return 9;
+    case OP_INSTANCE_OF:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 4; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 4;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 2;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+
+        infoArray[6].regNum = PhysicalReg_EAX;
+        infoArray[6].refCount = 6;
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = 3;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = PhysicalReg_EDX;
+        infoArray[8].refCount = 2; //export_pc for class_resolve
+        infoArray[8].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 9;
+
+    case OP_ARRAY_LENGTH:
+        vA = INST_A(inst);
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].linkageToVR = vA;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+    case OP_NEW_INSTANCE:
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //6: class object
+        //3: defined by C function, used twice
+        infoArray[0].refCount = 6; //next version has 3 references
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_ECX; //before common_throw_message
+        infoArray[1].refCount = 1;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[3].is8Bit = true;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 3;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+
+        infoArray[8].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[8].refCount = 2;
+        infoArray[8].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[9].regNum = 4;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        return 10;
+
+    case OP_NEW_ARRAY:
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //4: class object
+        //3: defined by C function, used twice
+        infoArray[0].refCount = 4; //next version has 3 references
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 2;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 3;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 4;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        return 8;
+
+    case OP_FILLED_NEW_ARRAY:
+        length = INST_B(inst);
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //4: class object
+        //3: defined by C function, used twice (array object)
+        //length: access array object to update the content
+        infoArray[0].refCount = 4; //next version has 5+length references
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 8; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[4].is8Bit = true;
+
+        if(length >= 1) {
+            infoArray[5].regNum = 7;
+            infoArray[5].refCount = 2; //DU
+            infoArray[5].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 2) {
+            infoArray[6].regNum = 8;
+            infoArray[6].refCount = 2; //DU
+            infoArray[6].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 3) {
+            infoArray[7].regNum = 9;
+            infoArray[7].refCount = 2; //DU
+            infoArray[7].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 4) {
+            infoArray[8].regNum = 10;
+            infoArray[8].refCount = 2; //DU
+            infoArray[8].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 5) {
+            infoArray[9].regNum = 11;
+            infoArray[9].refCount = 2; //DU
+            infoArray[9].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[5+length].regNum = 1;
+        infoArray[5+length].refCount = 2; //DU
+        infoArray[5+length].physicalType = LowOpndRegType_scratch;
+        infoArray[6+length].regNum = 2;
+        infoArray[6+length].refCount = 4; //DU
+        infoArray[6+length].physicalType = LowOpndRegType_scratch;
+        infoArray[7+length].regNum = 3;
+        infoArray[7+length].refCount = 2; //DU
+        infoArray[7+length].physicalType = LowOpndRegType_scratch;
+        infoArray[8+length].regNum = 4;
+        infoArray[8+length].refCount = 5; //DU
+        infoArray[8+length].physicalType = LowOpndRegType_scratch;
+        return 9+length;
+
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        length = INST_AA(inst);
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //4: class object
+        //3: defined by C function, used twice (array object)
+        //if length is 0, no access to array object
+        //else, used inside a loop
+        infoArray[0].refCount = 4; //next version: 5+(length >= 1 ? LOOP_COUNT : 0)
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 8; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[4].is8Bit = true;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 4; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 3;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+
+        infoArray[8].regNum = 7;
+        infoArray[8].refCount = 3*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[8].physicalType = LowOpndRegType_gp;
+        infoArray[9].regNum = 8;
+        infoArray[9].refCount = 3*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[9].physicalType = LowOpndRegType_gp;
+        infoArray[10].regNum = 9;
+        infoArray[10].refCount = 2*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[10].physicalType = LowOpndRegType_gp;
+        infoArray[11].regNum = 10;
+        infoArray[11].refCount = 2*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[11].physicalType = LowOpndRegType_gp;
+        infoArray[12].regNum = 4;
+        infoArray[12].refCount = 5; //DU
+        infoArray[12].physicalType = LowOpndRegType_scratch;
+        return 13;
+
+    case OP_FILL_ARRAY_DATA:
+        infoArray[0].regNum = PhysicalReg_EAX;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+#if 0//def HARDREG_OPT
+        infoArray[1].refCount = 3; //next version has refCount of 2
+#else
+        infoArray[1].refCount = 5;
+#endif
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum =1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        return 5;
+
+    case OP_THROW:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = 2;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        return 4;
+    case OP_THROW_VERIFICATION_ERROR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX; //export_pc
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = 2;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        return 4;
+
+    case OP_GOTO: //called function common_periodicChecks4
+#if defined(ENABLE_TRACING)
+        tt = INST_AA(inst);
+        tmp_s2 = (s2)((s2)tt << 8) >> 8;
+        if(tmp_s2 < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_GOTO_16:
+#if defined(ENABLE_TRACING)
+        tmp_s2 = (s2)FETCH(1);
+        if(tmp_s2 < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_GOTO_32:
+#if defined(ENABLE_TRACING)
+        tmp_u4 = (u4)FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        if(((s4)tmp_u4) < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_IF_EQ:
+    case OP_IF_NE:
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+#if defined(ENABLE_TRACING)
+        tmp_s2 = (s2)FETCH(1);
+        if(tmp_s2 < 0) {
+            infoArray[1].regNum = PhysicalReg_EDX;
+            infoArray[1].refCount = 2;
+            infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 2;
+        }
+#endif
+        return 1;
+    case OP_IF_EQZ: //called function common_periodicChecks4
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+#if defined(ENABLE_TRACING)
+        tmp_s2 = (s2)FETCH(1);
+        if(tmp_s2 < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_PACKED_SWITCH: //jump common_backwardBranch, which calls common_periodicChecks_entry, then jump_reg %eax
+    case OP_SPARSE_SWITCH: //%edx, %eax
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 6;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[2].regNum = PhysicalReg_EAX; //return by dvm helper
+        infoArray[2].refCount = 2+1; //2 uses
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2;
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        return 5;
+
+    case OP_AGET:
+    case OP_AGET_OBJECT:
+    case OP_AGET_BOOLEAN:
+    case OP_AGET_BYTE:
+    case OP_AGET_CHAR:
+    case OP_AGET_SHORT:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[3].linkageToVR = vA;
+        if(inst_op == OP_AGET_BYTE || inst_op == OP_AGET_BOOLEAN)
+            infoArray[3].is8Bit = true;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_AGET_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+
+    case OP_APUT:
+    case OP_APUT_BOOLEAN:
+    case OP_APUT_BYTE:
+    case OP_APUT_CHAR:
+    case OP_APUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        if(inst_op == OP_APUT_BYTE || inst_op == OP_APUT_BOOLEAN)
+            infoArray[3].is8Bit = true;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_APUT_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_APUT_OBJECT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 5+1; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2; //live through function call dvmCanPut
+        infoArray[1].refCount = 3+1; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 4+1; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 6;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp;
+
+        infoArray[6].regNum = PhysicalReg_EDX;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = PhysicalReg_EAX;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[8].regNum = 1;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[0].shareWithVR = false;
+        return updateMarkCard_notNull(infoArray,
+                                      0/*index for tgtAddrReg*/, 9);
+
+    case OP_IGET:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 3;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 7;
+#ifdef DEBUG_IGET_OBJ
+        //add hack for a specific instance (iget_obj_inst) of IGET_OBJECT within a method
+        if(inst_op == OP_IGET_OBJECT && !strncmp(currentMethod->clazz->descriptor, "Lspec/benchmarks/_228_jack/Parse", 32) &&
+           !strncmp(currentMethod->name, "buildPhase3", 11))
+        {
+#if 0
+          if(iget_obj_inst == 12) {
+            LOGD("increase count for instance %d of %s %s", iget_obj_inst, currentMethod->clazz->descriptor, currentMethod->name);
+            infoArray[5].refCount = 4; //DU
+          }
+          else
+#endif
+            infoArray[5].refCount = 3;
+          iget_obj_inst++;
+        }
+        else
+          infoArray[5].refCount = 3;
+#else
+        infoArray[5].refCount = 3; //DU
+#endif
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        infoArray[6].regNum = 8;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp;
+        infoArray[7].regNum = 9;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp;
+        return 8;
+    case OP_IPUT:
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 3;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 7;
+        infoArray[5].refCount = 3; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        infoArray[6].regNum = 8;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp;
+        infoArray[7].regNum = 9;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp;
+        if(inst_op == OP_IPUT_OBJECT || inst_op == OP_IPUT_OBJECT_VOLATILE) {
+            infoArray[5].shareWithVR = false;
+            return updateMarkCard(infoArray, 7/*index for valReg*/,
+                                  5/*index for tgtAddrReg*/, 8);
+        }
+        return 8;
+    case OP_IGET_WIDE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_WIDE_VOLATILE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 3;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 7;
+        infoArray[5].refCount = 3; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        infoArray[6].regNum = 8;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp;
+        infoArray[7].regNum = 1;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_xmm;
+
+        if(inst_op == OP_IPUT_WIDE_VOLATILE || inst_op == OP_IGET_WIDE_VOLATILE) {
+            infoArray[8].regNum = 3;
+            infoArray[8].refCount = 2; //DU
+            infoArray[8].physicalType = LowOpndRegType_scratch;
+            infoArray[9].regNum = 9;
+            infoArray[9].refCount = 2; //DU
+            infoArray[9].physicalType = LowOpndRegType_gp;
+            return 10;
+        }
+        return 8;
+
+    case OP_SGET:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EAX;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 7;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 6;
+    case OP_SPUT:
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EAX;
+        infoArray[2].refCount = 2+1; //access clazz of the field
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 7;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        if(inst_op == OP_SPUT_OBJECT || inst_op == OP_SPUT_OBJECT_VOLATILE) {
+            infoArray[2].shareWithVR = false;
+            infoArray[6].regNum = 12;
+            infoArray[6].refCount = 1; //1 def, 2 uses in updateMarkCard
+            infoArray[6].physicalType = LowOpndRegType_gp;
+            return updateMarkCard(infoArray, 4/*index for valReg*/,
+                                  6/*index for tgtAddrReg */, 7);
+        }
+        return 6;
+    case OP_SGET_WIDE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_WIDE_VOLATILE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EAX;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_xmm;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        if(inst_op == OP_SPUT_WIDE_VOLATILE || inst_op == OP_SGET_WIDE_VOLATILE) {
+            infoArray[6].regNum = 3;
+            infoArray[6].refCount = 2; //DU
+            infoArray[6].physicalType = LowOpndRegType_scratch;
+            infoArray[7].regNum = 9;
+            infoArray[7].refCount = 2; //DU
+            infoArray[7].physicalType = LowOpndRegType_gp;
+            return 8;
+        }
+        return 6;
+
+    case OP_IGET_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+    case OP_IPUT_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        if(inst_op == OP_IPUT_OBJECT_QUICK) {
+            infoArray[0].shareWithVR = false;
+            return updateMarkCard(infoArray, 1/*index for valReg*/,
+                                  0/*index for tgtAddrReg*/, 3);
+        }
+        return 3;
+    case OP_IGET_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+    case OP_IPUT_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        return updateReturnCommon(infoArray);
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        numTmps = updateReturnCommon(infoArray);
+
+        infoArray[numTmps].regNum = 21;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        infoArray[numTmps].regNum = 22;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        return numTmps;
+    case OP_RETURN_WIDE:
+        numTmps = updateReturnCommon(infoArray);
+
+        infoArray[numTmps].regNum = 10;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_scratch;
+        numTmps++;
+        infoArray[numTmps].regNum = 1;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_xmm;
+        numTmps++;
+        return numTmps;
+
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_VIRTUAL_RANGE:
+#ifdef PREDICTED_CHAINING
+        numTmps = updateGenPrediction(infoArray, false /*not interface*/);
+        infoArray[numTmps].regNum = 5;
+        infoArray[numTmps].refCount = 3; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        if(inst_op == OP_INVOKE_VIRTUAL)
+            k = updateInvokeNoRange(infoArray, numTmps);
+        else
+            k = updateInvokeRange(infoArray, numTmps);
+        return k;
+#else
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 7;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 8;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //2 versions, first version DU is for exception, 2nd version: eip right before jumping to invokeArgsDone
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_ECX; //ecx is ued in invokeArgsDone
+        infoArray[6].refCount = 1+1; //used in .invokeArgsDone
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        //when WITH_JIT is true and PREDICTED_CHAINING is false
+        //  temp 8 and EAX are not used; but it is okay to keep it here
+        infoArray[7].regNum = PhysicalReg_EAX;
+        infoArray[7].refCount = 4; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[8].regNum = 1;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = 2;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_VIRTUAL)
+            k = updateInvokeNoRange(infoArray, 10);
+        else
+            k = updateInvokeRange(infoArray, 10);
+        return k;
+#endif
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_SUPER_RANGE:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 7;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 8;
+        infoArray[2].refCount = 3; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 9;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_ECX;
+        infoArray[6].refCount = 1+1; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = PhysicalReg_EAX;
+        infoArray[7].refCount = 4; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[8].regNum = 1;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = 2;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        infoArray[10].regNum = 3;
+        infoArray[10].refCount = 2; //DU
+        infoArray[10].physicalType = LowOpndRegType_scratch;
+        infoArray[11].regNum = 4;
+        infoArray[11].refCount = 2; //DU
+        infoArray[11].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_SUPER)
+            k = updateInvokeNoRange(infoArray, 12);
+        else
+            k = updateInvokeRange(infoArray, 12);
+        return k;
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_DIRECT_RANGE:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 5;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_ECX;
+        infoArray[3].refCount = 2;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EAX;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_DIRECT)
+            k = updateInvokeNoRange(infoArray, 7);
+        else
+            k = updateInvokeRange(infoArray, 7);
+        return k;
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_STATIC_RANGE:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[2].regNum = PhysicalReg_ECX;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 2;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_STATIC)
+            k = updateInvokeNoRange(infoArray, 6);
+        else
+            k = updateInvokeRange(infoArray, 6);
+        return k;
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_INTERFACE_RANGE:
+#ifdef PREDICTED_CHAINING
+        numTmps = updateGenPrediction(infoArray, true /*interface*/);
+        infoArray[numTmps].regNum = 1;
+        infoArray[numTmps].refCount = 3; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        if(inst_op == OP_INVOKE_INTERFACE)
+            k = updateInvokeNoRange(infoArray, numTmps);
+        else
+            k = updateInvokeRange(infoArray, numTmps);
+        return k;
+#else
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 4;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[5].regNum = PhysicalReg_ECX;
+        infoArray[5].refCount = 1+1; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_EAX;
+        infoArray[6].refCount = 2+1; //2 uses
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[7].regNum = 1;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = 2;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = 3;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_INTERFACE)
+            k = updateInvokeNoRange(infoArray, 10);
+        else
+            k = updateInvokeRange(infoArray, 10);
+        return k;
+#endif
+        ////////////////////////////////////////////// ALU
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        return 1;
+    case OP_NEG_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 4; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_NOT_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_NEG_FLOAT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        return 1;
+    case OP_NEG_DOUBLE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_INT_TO_LONG: //hard-code eax & edx
+        infoArray[0].regNum = PhysicalReg_EAX;
+        infoArray[0].refCount = 2+1;
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 1+1; //cdq accesses edx & eax
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 2;
+    case OP_INT_TO_FLOAT:
+    case OP_INT_TO_DOUBLE:
+    case OP_LONG_TO_FLOAT:
+    case OP_LONG_TO_DOUBLE:
+    case OP_FLOAT_TO_DOUBLE:
+    case OP_DOUBLE_TO_FLOAT:
+        return 0; //fp stack
+    case OP_LONG_TO_INT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        return 1;
+    case OP_FLOAT_TO_INT:
+    case OP_DOUBLE_TO_INT: //fp stack
+        return 0;
+    case OP_FLOAT_TO_LONG:
+    case OP_DOUBLE_TO_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //define, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        return 3;
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //define, update, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        return 1;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+        if(inst_op == OP_ADD_INT || inst_op == OP_SUB_INT || inst_op == OP_MUL_INT ||
+           inst_op == OP_AND_INT || inst_op == OP_OR_INT || inst_op == OP_XOR_INT) {
+            vA = INST_AA(inst);
+            v1 = *((u1*)rPC + 2);
+        } else {
+            vA = INST_A(inst);
+            v1 = vA;
+        }
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        return 1; //common_alu_int
+
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR: //use %cl or %ecx?
+        if(inst_op == OP_SHL_INT || inst_op == OP_SHR_INT || inst_op == OP_USHR_INT) {
+            vA = INST_AA(inst);
+            v1 = *((u1*)rPC + 2);
+        } else {
+            vA = INST_A(inst);
+            v1 = vA;
+        }
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = PhysicalReg_ECX;
+        infoArray[1].refCount = 2; //define, use
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 2;//common_shift_int
+
+    case OP_DIV_INT:
+    case OP_REM_INT:
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR: //hard-code %eax, %edx (dividend in edx:eax; quotient in eax; remainder in edx)
+        infoArray[0].regNum = 2;
+        infoArray[0].refCount = 4; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EAX; //dividend, quotient
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = PhysicalReg_EDX; //export_pc, output for REM
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //define, use
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_DIV_INT || inst_op == OP_DIV_INT_2ADDR) {
+            infoArray[1].refCount = 5;
+            infoArray[2].refCount = 4;
+        } else {
+            infoArray[1].refCount = 4;
+            infoArray[2].refCount = 5;
+        }
+        return 4;
+
+    case OP_ADD_INT_LIT16:
+    case OP_MUL_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+    case OP_ADD_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        if(inst_op == OP_ADD_INT_LIT16 || inst_op == OP_MUL_INT_LIT16 ||
+           inst_op == OP_AND_INT_LIT16 || inst_op == OP_OR_INT_LIT16 || inst_op == OP_XOR_INT_LIT16) {
+            vA = INST_A(inst);
+            v1 = INST_B(inst);
+        } else {
+            vA = INST_AA(inst);
+            v1 = (u2)FETCH(1) & 0xff;
+        }
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        return 1;
+
+    case OP_RSUB_INT_LIT8:
+    case OP_RSUB_INT:
+        vA = INST_AA(inst);
+        v1 = (inst_op == OP_RSUB_INT) ? INST_B(inst) : ((u2)FETCH(1) & 0xff);
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[1].shareWithVR = false;
+        return 2;
+
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+        if(inst_op == OP_DIV_INT_LIT8 || inst_op == OP_REM_INT_LIT8) {
+            tmp_s2 = (s2)FETCH(1) >> 8;
+        }
+        else {
+            tmp_s4 = (s2)FETCH(1);
+            tmp_s2 = tmp_s4;
+        }
+        if((inst_op == OP_DIV_INT_LIT8 || inst_op == OP_DIV_INT_LIT16)) {
+            int power = isPowerOfTwo(tmp_s2);
+            if(power >= 1) { /* divide by a power of 2 constant */
+                infoArray[0].regNum = 2;
+                infoArray[0].refCount = 3; //define, use, use
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 1;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                if(power == 1) infoArray[1].refCount = 5;
+                else infoArray[1].refCount = 6;
+                return 2;
+            }
+        }
+        if(tmp_s2 == 0) {
+            //export_pc
+            infoArray[0].regNum = PhysicalReg_EDX; //export_pc, output for REM
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+        if(inst_op == OP_DIV_INT_LIT16 || inst_op == OP_DIV_INT_LIT8) {
+            if(tmp_s2 == -1)
+                infoArray[1].refCount = 4+1;
+            else
+                infoArray[1].refCount = 4;
+            infoArray[2].refCount = 2; //edx
+        } else {
+            if(tmp_s2 == -1)
+                infoArray[1].refCount = 3+1;
+            else
+                infoArray[1].refCount = 3;
+            infoArray[2].refCount = 3; //edx
+        }
+        infoArray[0].regNum = 2;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EAX; //dividend, quotient
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = PhysicalReg_EDX; //export_pc, output for REM
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //define, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+
+    case OP_SHL_LONG:
+    case OP_SHL_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        return 3;
+
+    case OP_SHR_LONG:
+    case OP_SHR_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 4; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 3;
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 3;
+        infoArray[4].physicalType = LowOpndRegType_xmm;
+        return 5;
+
+    case OP_USHR_LONG:
+    case OP_USHR_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        return 3;
+
+    case OP_MUL_LONG: //general purpose register
+    case OP_MUL_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 6;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 2+1; //for mul_opc
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2; //for mul_opc
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 2; //defined by function call
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2; //next version has 2 references
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        return 6;
+
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_ADD_DOUBLE: //PhysicalReg_FP TODO
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_FLOAT:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_DIV_DOUBLE:
+    case OP_DIV_DOUBLE_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        //for ALU ops with 2ADDR, the temp variable can share the same physical
+        //reg as the virtual register, since the content of VR is updated by
+        //the content of the temp variable
+        if(inst_op == OP_ADD_FLOAT || inst_op == OP_SUB_FLOAT ||
+           inst_op == OP_MUL_FLOAT || inst_op == OP_ADD_DOUBLE ||
+           inst_op == OP_SUB_DOUBLE || inst_op == OP_MUL_DOUBLE ||
+           inst_op == OP_DIV_FLOAT || inst_op == OP_DIV_DOUBLE)
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_REM_FLOAT:
+    case OP_REM_FLOAT_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        return 3;
+
+    case OP_REM_DOUBLE:
+    case OP_REM_DOUBLE_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        return 3;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPL_DOUBLE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 4; //return
+        infoArray[4].refCount = 5;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        return 5;
+
+    case OP_CMPG_FLOAT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 5;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        return 4;
+        break;
+    case OP_CMPG_DOUBLE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 5;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        return 4;
+
+    case OP_CMP_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 3;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 6;
+        infoArray[5].refCount = 7;
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        return 6;
+
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+        if(inst_op == OP_EXECUTE_INLINE)
+            num = INST_B(inst);
+        else
+            num = INST_AA(inst);
+        tmp = FETCH(1);
+        switch (tmp) {
+            case INLINE_STRING_LENGTH:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 3;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                infoArray[3].regNum = 1;
+                infoArray[3].refCount = 2;
+                infoArray[3].physicalType = LowOpndRegType_scratch;
+                return 4;
+            case INLINE_STRING_IS_EMPTY:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 3;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 4;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 1;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_scratch;
+                return 3;
+            case INLINE_STRING_FASTINDEXOF_II:
+#if defined(USE_GLOBAL_STRING_DEFS)
+                break;
+#else
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 14 * LOOP_COUNT;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 3 * LOOP_COUNT;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 11 * LOOP_COUNT;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                infoArray[3].regNum = 4;
+                infoArray[3].refCount = 3 * LOOP_COUNT;
+                infoArray[3].physicalType = LowOpndRegType_gp;
+                infoArray[4].regNum = 5;
+                infoArray[4].refCount = 9 * LOOP_COUNT;
+                infoArray[4].physicalType = LowOpndRegType_gp;
+                infoArray[5].regNum = 6;
+                infoArray[5].refCount = 4 * LOOP_COUNT;
+                infoArray[5].physicalType = LowOpndRegType_gp;
+                infoArray[6].regNum = 7;
+                infoArray[6].refCount = 2;
+                infoArray[6].physicalType = LowOpndRegType_gp;
+                infoArray[7].regNum = 1;
+                infoArray[7].refCount = 2;
+                infoArray[7].physicalType = LowOpndRegType_scratch;
+                return 8;
+#endif
+            case INLINE_MATH_ABS_LONG:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 7;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                infoArray[3].regNum = 4;
+                infoArray[3].refCount = 3;
+                infoArray[3].physicalType = LowOpndRegType_gp;
+                infoArray[4].regNum = 5;
+                infoArray[4].refCount = 2;
+                infoArray[4].physicalType = LowOpndRegType_gp;
+                infoArray[5].regNum = 6;
+                infoArray[5].refCount = 5;
+                infoArray[5].physicalType = LowOpndRegType_gp;
+                return 6;
+            case INLINE_MATH_ABS_INT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 5;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 4;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_MATH_MAX_INT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 4;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 3;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_MATH_ABS_FLOAT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 3;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                return 2;
+            case INLINE_MATH_ABS_DOUBLE:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 3;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_FLOAT_TO_RAW_INT_BITS:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                return 2;
+            case INLINE_INT_BITS_TO_FLOAT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                return 2;
+            case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_LONG_BITS_TO_DOUBLE:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            default:
+                break;
+        }
+
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(num >= 1) {
+            infoArray[1].regNum = 2;
+            infoArray[1].refCount = 2;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 2) {
+            infoArray[2].regNum = 3;
+            infoArray[2].refCount = 2;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 3) {
+            infoArray[3].regNum = 4;
+            infoArray[3].refCount = 2;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 4) {
+            infoArray[4].regNum = 5;
+            infoArray[4].refCount = 2;
+            infoArray[4].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[num+1].regNum = 6;
+        infoArray[num+1].refCount = 2;
+        infoArray[num+1].physicalType = LowOpndRegType_gp;
+        infoArray[num+2].regNum = PhysicalReg_EAX;
+        infoArray[num+2].refCount = 2;
+        infoArray[num+2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[num+3].regNum = PhysicalReg_EDX;
+        infoArray[num+3].refCount = 2;
+        infoArray[num+3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[num+4].regNum = 1;
+        infoArray[num+4].refCount = 4;
+        infoArray[num+4].physicalType = LowOpndRegType_scratch;
+        return num+5;
+#if FIXME
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        return 0;
+#endif
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+#ifdef PREDICTED_CHAINING
+        numTmps = updateGenPrediction(infoArray, false /*not interface*/);
+        infoArray[numTmps].regNum = 1;
+        infoArray[numTmps].refCount = 3; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        if(inst_op == OP_INVOKE_VIRTUAL_QUICK)
+            k = updateInvokeNoRange(infoArray, numTmps);
+        else
+            k = updateInvokeRange(infoArray, numTmps);
+        return k;
+#else
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = PhysicalReg_ECX;
+        infoArray[3].refCount = 1+1;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        if(inst_op == OP_INVOKE_VIRTUAL_QUICK_RANGE)
+            k = updateInvokeRange(infoArray, 5);
+        else
+            k = updateInvokeNoRange(infoArray, 5);
+        return k;
+#endif
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 4;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 5;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = PhysicalReg_ECX;
+        infoArray[3].refCount = 1+1;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 2;
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_SUPER_QUICK_RANGE)
+            k = updateInvokeRange(infoArray, 7);
+        else
+            k = updateInvokeNoRange(infoArray, 7);
+        return k;
+#ifdef SUPPORT_HLO
+    case kExtInstruction:
+        switch(inst) {
+    case OP_X_AGET_QUICK:
+    case OP_X_AGET_OBJECT_QUICK:
+    case OP_X_AGET_BOOLEAN_QUICK:
+    case OP_X_AGET_BYTE_QUICK:
+    case OP_X_AGET_CHAR_QUICK:
+    case OP_X_AGET_SHORT_QUICK:
+        vA = FETCH(1) & 0xff;
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[3].linkageToVR = vA;
+        if(inst == OP_X_AGET_BYTE_QUICK || inst == OP_X_AGET_BOOLEAN_QUICK)
+            infoArray[3].is8Bit = true;
+        return 4;
+    case OP_X_AGET_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        return 4;
+    case OP_X_APUT_QUICK:
+    case OP_X_APUT_OBJECT_QUICK:
+    case OP_X_APUT_BOOLEAN_QUICK:
+    case OP_X_APUT_BYTE_QUICK:
+    case OP_X_APUT_CHAR_QUICK:
+    case OP_X_APUT_SHORT_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        if(inst == OP_X_APUT_BYTE_QUICK || inst == OP_X_APUT_BOOLEAN_QUICK)
+            infoArray[3].is8Bit = true;
+        return 4;
+    case OP_X_APUT_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        return 4;
+    case OP_X_DEREF_GET:
+    case OP_X_DEREF_GET_OBJECT:
+    case OP_X_DEREF_GET_BOOLEAN:
+    case OP_X_DEREF_GET_BYTE:
+    case OP_X_DEREF_GET_CHAR:
+    case OP_X_DEREF_GET_SHORT:
+        vA = FETCH(1) & 0xff;
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].linkageToVR = vA;
+        if(inst == OP_X_DEREF_GET_BYTE || inst == OP_X_DEREF_GET_BOOLEAN)
+            infoArray[1].is8Bit = true;
+        return 2;
+    case OP_X_DEREF_GET_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_X_DEREF_PUT:
+    case OP_X_DEREF_PUT_OBJECT:
+    case OP_X_DEREF_PUT_BOOLEAN:
+    case OP_X_DEREF_PUT_BYTE:
+    case OP_X_DEREF_PUT_CHAR:
+    case OP_X_DEREF_PUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        if(inst == OP_X_DEREF_PUT_BYTE || inst == OP_X_DEREF_PUT_BOOLEAN)
+            infoArray[1].is8Bit = true;
+        return 2;
+    case OP_X_DEREF_PUT_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_X_ARRAY_CHECKS:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        return 2;
+    case OP_X_CHECK_BOUNDS:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        return 2;
+    case OP_X_CHECK_NULL:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 2;
+    case OP_X_CHECK_TYPE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 5;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = PhysicalReg_EAX;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 6;
+    case OP_X_ARRAY_OBJECT_CHECKS:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 4; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = PhysicalReg_EAX;
+        infoArray[6].refCount = 2;
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 7;
+    }
+#endif
+    }
+    return -1;
+}
diff --git a/vm/compiler/codegen/x86/CodegenInterface.cpp b/vm/compiler/codegen/x86/CodegenInterface.cpp
new file mode 100644
index 0000000..6670bda
--- /dev/null
+++ b/vm/compiler/codegen/x86/CodegenInterface.cpp
@@ -0,0 +1,1532 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <sys/mman.h>
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/Compiler.h"
+#include "compiler/CompilerIR.h"
+#include "interp/Jit.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "compiler/codegen/CompilerCodegen.h"
+
+/* Init values when a predicted chain is initially assembled */
+/* E7FE is branch to self */
+#define PREDICTED_CHAIN_BX_PAIR_INIT     0xe7fe
+
+/* Target-specific save/restore */
+extern "C" void dvmJitCalleeSave(double *saveArea);
+extern "C" void dvmJitCalleeRestore(double *saveArea);
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+//JitInstructionSetType dvmCompilerInstructionSet(CompilationUnit *cUnit)
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_IA32;
+}
+
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet()
+{
+    return DALVIK_JIT_IA32;
+}
+
+/* we don't use template for IA32 */
+void *dvmCompilerGetInterpretTemplate()
+{
+      return NULL;
+}
+
+/* Track the number of times that the code cache is patched */
+#if defined(WITH_JIT_TUNING)
+#define UPDATE_CODE_CACHE_PATCHES()    (gDvmJit.codeCachePatches++)
+#else
+#define UPDATE_CODE_CACHE_PATCHES()
+#endif
+
+bool dvmCompilerArchInit() {
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 12;
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold != 0) {
+        gDvmJit.threshold = 255;
+    }
+    gDvmJit.codeCacheSize = 512*1024;
+    gDvmJit.optLevel = kJitOptLevelO1;
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking mode */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+void dvmCompilerPatchInlineCache(void)
+{
+    int i;
+    PredictedChainingCell *minAddr, *maxAddr;
+
+    /* Nothing to be done */
+    if (gDvmJit.compilerICPatchIndex == 0) return;
+
+    /*
+     * Since all threads are already stopped we don't really need to acquire
+     * the lock. But race condition can be easily introduced in the future w/o
+     * paying attention so we still acquire the lock here.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    //ALOGD("Number of IC patch work orders: %d", gDvmJit.compilerICPatchIndex);
+
+    /* Initialize the min/max address range */
+    minAddr = (PredictedChainingCell *)
+        ((char *) gDvmJit.codeCache + gDvmJit.codeCacheSize);
+    maxAddr = (PredictedChainingCell *) gDvmJit.codeCache;
+
+    for (i = 0; i < gDvmJit.compilerICPatchIndex; i++) {
+        ICPatchWorkOrder *workOrder = &gDvmJit.compilerICPatchQueue[i];
+        PredictedChainingCell *cellAddr = workOrder->cellAddr;
+        PredictedChainingCell *cellContent = &workOrder->cellContent;
+        ClassObject *clazz = dvmFindClassNoInit(workOrder->classDescriptor,
+                                                workOrder->classLoader);
+
+        assert(clazz->serialNumber == workOrder->serialNumber);
+
+        /* Use the newly resolved clazz pointer */
+        cellContent->clazz = clazz;
+
+        if (cellAddr->clazz == NULL) {
+            COMPILER_TRACE_CHAINING(
+                ALOGI("Jit Runtime: predicted chain %p to %s (%s) initialized",
+                      cellAddr,
+                      cellContent->clazz->descriptor,
+                      cellContent->method->name));
+        } else {
+            COMPILER_TRACE_CHAINING(
+                ALOGI("Jit Runtime: predicted chain %p from %s to %s (%s) "
+                      "patched",
+                      cellAddr,
+                      cellAddr->clazz->descriptor,
+                      cellContent->clazz->descriptor,
+                      cellContent->method->name));
+        }
+
+        /* Patch the chaining cell */
+        *cellAddr = *cellContent;
+        minAddr = (cellAddr < minAddr) ? cellAddr : minAddr;
+        maxAddr = (cellAddr > maxAddr) ? cellAddr : maxAddr;
+    }
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    gDvmJit.compilerICPatchIndex = 0;
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+
+/* Target-specific cache clearing */
+void dvmCompilerCacheClear(char *start, size_t size)
+{
+    /* "0xFF 0xFF" is an invalid opcode for x86. */
+    memset(start, 0xFF, size);
+}
+
+/* for JIT debugging, to be implemented */
+void dvmJitCalleeSave(double *saveArea) {
+}
+
+void dvmJitCalleeRestore(double *saveArea) {
+}
+
+void dvmJitToInterpSingleStep() {
+}
+
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const JitEntry *knownEntry) {
+    return NULL;
+}
+
+void dvmCompilerCodegenDump(CompilationUnit *cUnit) //in ArchUtility.c
+{
+}
+
+void dvmCompilerArchDump(void)
+{
+}
+
+char *getTraceBase(const JitEntry *p)
+{
+    return NULL;
+}
+
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo* info)
+{
+}
+
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit, char *codeAddress)
+{
+}
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    // Method-based JIT not supported for x86.
+}
+
+void dvmJitScanAllClassPointers(void (*callback)(void *))
+{
+}
+
+/* Handy function to retrieve the profile count */
+static inline int getProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0)
+        return 0;
+    u4 *pExecutionCount = (u4 *) getTraceBase(entry);
+
+    return pExecutionCount ? *pExecutionCount : 0;
+}
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+    const JitEntry *jitEntry1 = (const JitEntry *)entry1;
+    const JitEntry *jitEntry2 = (const JitEntry *)entry2;
+
+    JitTraceCounter_t count1 = getProfileCount(jitEntry1);
+    JitTraceCounter_t count2 = getProfileCount(jitEntry2);
+    return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles() //in Assemble.c
+{
+    JitEntry *sortedEntries;
+    int numTraces = 0;
+    unsigned long counts = 0;
+    unsigned int i;
+
+    /* Make sure that the table is not changing */
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Sort the entries by descending order */
+    sortedEntries = (JitEntry *)malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+    if (sortedEntries == NULL)
+        goto done;
+    memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+           sizeof(JitEntry) * gDvmJit.jitTableSize);
+    qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+          sortTraceProfileCount);
+
+    /* Dump the sorted entries */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            numTraces++;
+        }
+    }
+    if (numTraces == 0)
+        numTraces = 1;
+    ALOGI("JIT: Average execution count -> %d",(int)(counts / numTraces));
+
+    free(sortedEntries);
+done:
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    return;
+}
+
+void jumpWithRelOffset(char* instAddr, int relOffset) {
+    stream = instAddr;
+    OpndSize immSize = estOpndSizeFromImm(relOffset);
+    relOffset -= getJmpCallInstSize(immSize, JmpCall_uncond);
+    dump_imm(Mnemonic_JMP, immSize, relOffset);
+}
+
+// works whether instructions for target basic block are generated or not
+LowOp* jumpToBasicBlock(char* instAddr, int targetId) {
+    stream = instAddr;
+    bool unknown;
+    OpndSize size;
+    int relativeNCG = targetId;
+    relativeNCG = getRelativeNCG(targetId, JmpCall_uncond, &unknown, &size);
+    unconditional_jump_int(relativeNCG, size);
+    return NULL;
+}
+
+LowOp* condJumpToBasicBlock(char* instAddr, ConditionCode cc, int targetId) {
+    stream = instAddr;
+    bool unknown;
+    OpndSize size;
+    int relativeNCG = targetId;
+    relativeNCG = getRelativeNCG(targetId, JmpCall_cond, &unknown, &size);
+    conditional_jump_int(cc, relativeNCG, size);
+    return NULL;
+}
+
+/*
+ * Attempt to enqueue a work order to patch an inline cache for a predicted
+ * chaining cell for virtual/interface calls.
+ */
+static bool inlineCachePatchEnqueue(PredictedChainingCell *cellAddr,
+                                    PredictedChainingCell *newContent)
+{
+    bool result = true;
+
+    /*
+     * Make sure only one thread gets here since updating the cell (ie fast
+     * path and queueing the request (ie the queued path) have to be done
+     * in an atomic fashion.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    /* Fast path for uninitialized chaining cell */
+    if (cellAddr->clazz == NULL &&
+        cellAddr->branch == PREDICTED_CHAIN_BX_PAIR_INIT) {
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->method = newContent->method;
+        cellAddr->branch = newContent->branch;
+        cellAddr->branch2 = newContent->branch2;
+
+        /*
+         * The update order matters - make sure clazz is updated last since it
+         * will bring the uninitialized chaining cell to life.
+         */
+        android_atomic_release_store((int32_t)newContent->clazz,
+            (volatile int32_t *)(void*) &cellAddr->clazz);
+        //cacheflush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if 0
+        MEM_BARRIER();
+        cellAddr->clazz = newContent->clazz;
+        //cacheflush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
+#endif
+#if defined(IA_JIT_TUNING)
+        gDvmJit.icPatchInit++;
+#endif
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: FAST predicted chain %p to method %s%s %p",
+                  cellAddr, newContent->clazz->descriptor, newContent->method->name, newContent->method));
+    /* Check if this is a frequently missed clazz */
+    } else if (cellAddr->stagedClazz != newContent->clazz) {
+        /* Not proven to be frequent yet - build up the filter cache */
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->stagedClazz = newContent->clazz;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchRejected++;
+#endif
+    /*
+     * Different classes but same method implementation - it is safe to just
+     * patch the class value without the need to stop the world.
+     */
+    } else if (cellAddr->method == newContent->method) {
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->clazz = newContent->clazz;
+        /* No need to flush the cache here since the branch is not patched */
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchLockFree++;
+#endif
+    /*
+     * Cannot patch the chaining cell inline - queue it until the next safe
+     * point.
+     */
+    } else if (gDvmJit.compilerICPatchIndex < COMPILER_IC_PATCH_QUEUE_SIZE)  {
+        int index = gDvmJit.compilerICPatchIndex++;
+        const ClassObject *clazz = newContent->clazz;
+
+        gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+        gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+        gDvmJit.compilerICPatchQueue[index].classDescriptor = clazz->descriptor;
+        gDvmJit.compilerICPatchQueue[index].classLoader = clazz->classLoader;
+        /* For verification purpose only */
+        gDvmJit.compilerICPatchQueue[index].serialNumber = clazz->serialNumber;
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchQueued++;
+#endif
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: QUEUE predicted chain %p to method %s%s",
+                  cellAddr, newContent->clazz->descriptor, newContent->method->name));
+    } else {
+    /* Queue is full - just drop this patch request */
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchDropped++;
+#endif
+
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: DROP predicted chain %p to method %s%s",
+                  cellAddr, newContent->clazz->descriptor, newContent->method->name));
+    }
+
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+    return result;
+}
+
+/*
+ * This method is called from the invoke templates for virtual and interface
+ * methods to speculatively setup a chain to the callee. The templates are
+ * written in assembly and have setup method, cell, and clazz at r0, r2, and
+ * r3 respectively, so there is a unused argument in the list. Upon return one
+ * of the following three results may happen:
+ *   1) Chain is not setup because the callee is native. Reset the rechain
+ *      count to a big number so that it will take a long time before the next
+ *      rechain attempt to happen.
+ *   2) Chain is not setup because the callee has not been created yet. Reset
+ *      the rechain count to a small number and retry in the near future.
+ *   3) Ask all other threads to stop before patching this chaining cell.
+ *      This is required because another thread may have passed the class check
+ *      but hasn't reached the chaining cell yet to follow the chain. If we
+ *      patch the content before halting the other thread, there could be a
+ *      small window for race conditions to happen that it may follow the new
+ *      but wrong chain to invoke a different method.
+ */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz)
+{
+    int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+    /* Don't come back here for a long time if the method is native */
+    if (dvmIsNativeMethod(method)) {
+        UNPROTECT_CODE_CACHE(cell, sizeof(*cell));
+
+        /*
+         * Put a non-zero/bogus value in the clazz field so that it won't
+         * trigger immediate patching and will continue to fail to match with
+         * a real clazz pointer.
+         */
+        cell->clazz = (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cell, sizeof(*cell));
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: predicted chain %p to native method %s ignored",
+                  cell, method->name));
+        goto done;
+    }
+    {
+    int tgtAddr = (int) dvmJitGetTraceAddr(method->insns);
+
+    /*
+     * Compilation not made yet for the callee. Reset the counter to a small
+     * value and come back to check soon.
+     */
+    if ((tgtAddr == 0) ||
+        ((void*)tgtAddr == dvmCompilerGetInterpretTemplate())) {
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: predicted chain %p to method %s%s delayed",
+                  cell, method->clazz->descriptor, method->name));
+        goto done;
+    }
+
+    PredictedChainingCell newCell;
+
+    if (cell->clazz == NULL) {
+        newRechainCount = self->icRechainCount;
+    }
+
+    int relOffset = (int) tgtAddr - (int)cell;
+    OpndSize immSize = estOpndSizeFromImm(relOffset);
+    int jumpSize = getJmpCallInstSize(immSize, JmpCall_uncond);
+    relOffset -= jumpSize;
+    COMPILER_TRACE_CHAINING(
+            ALOGI("inlineCachePatchEnqueue chain %p to method %s%s inst size %d",
+                  cell, method->clazz->descriptor, method->name, jumpSize));
+    //can't use stream here since it is used by the compilation thread
+    dump_imm_with_codeaddr(Mnemonic_JMP, immSize, relOffset, (char*) (&newCell)); //update newCell.branch
+
+    newCell.clazz = clazz;
+    newCell.method = method;
+
+    /*
+     * Enter the work order to the queue and the chaining cell will be patched
+     * the next time a safe point is entered.
+     *
+     * If the enqueuing fails reset the rechain count to a normal value so that
+     * it won't get indefinitely delayed.
+     */
+    inlineCachePatchEnqueue(cell, &newCell);
+    }
+done:
+    self->icRechainCount = newRechainCount;
+    return method;
+}
+
+/*
+ * Unchain a trace given the starting address of the translation
+ * in the code cache.  Refer to the diagram in dvmCompilerAssembleLIR.
+ * For ARM, it returns the address following the last cell unchained.
+ * For IA, it returns NULL since cacheflush is not required for IA.
+ */
+u4* dvmJitUnchain(void* codeAddr)
+{
+    /* codeAddr is 4-byte aligned, so is chain cell count offset */
+    u2* pChainCellCountOffset = (u2*)((char*)codeAddr - 4);
+    u2 chainCellCountOffset = *pChainCellCountOffset;
+    /* chain cell counts information is 4-byte aligned */
+    ChainCellCounts *pChainCellCounts =
+          (ChainCellCounts*)((char*)codeAddr + chainCellCountOffset);
+    u2* pChainCellOffset = (u2*)((char*)codeAddr - 2);
+    u2 chainCellOffset = *pChainCellOffset;
+    u1* pChainCells;
+    int i,j;
+    PredictedChainingCell *predChainCell;
+    int padding;
+
+    /* Locate the beginning of the chain cell region */
+    pChainCells = (u1 *)((char*)codeAddr + chainCellOffset);
+
+    /* The cells are sorted in order - walk through them and reset */
+    for (i = 0; i < kChainingCellGap; i++) {
+        /* for hot, normal, singleton chaining:
+               nop  //padding.
+               jmp 0
+               mov imm32, reg1
+               mov imm32, reg2
+               call reg2
+           after chaining:
+               nop
+               jmp imm
+               mov imm32, reg1
+               mov imm32, reg2
+               call reg2
+           after unchaining:
+               nop
+               jmp 0
+               mov imm32, reg1
+               mov imm32, reg2
+               call reg2
+           Space occupied by the chaining cell in bytes: nop is for padding,
+                jump 0, the target 0 is 4 bytes aligned.
+           Space for predicted chaining: 5 words = 20 bytes
+        */
+        int elemSize = 0;
+        if (i == kChainingCellInvokePredicted) {
+            elemSize = 20;
+        }
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: unchaining type %d count %d", i, pChainCellCounts->u.count[i]));
+
+        for (j = 0; j < pChainCellCounts->u.count[i]; j++) {
+            switch(i) {
+                case kChainingCellNormal:
+                case kChainingCellHot:
+                case kChainingCellInvokeSingleton:
+                case kChainingCellBackwardBranch:
+                    COMPILER_TRACE_CHAINING(
+                        ALOGI("Jit Runtime: unchaining of normal, hot, or singleton"));
+                    pChainCells = (u1*) (((uint)pChainCells + 4)&(~0x03));
+                    elemSize = 4+5+5+2;
+                    memset(pChainCells, 0, 4);
+                    break;
+                case kChainingCellInvokePredicted:
+                    COMPILER_TRACE_CHAINING(
+                        ALOGI("Jit Runtime: unchaining of predicted"));
+                    /* 4-byte aligned */
+                    padding = (4 - ((u4)pChainCells & 3)) & 3;
+                    pChainCells += padding;
+                    predChainCell = (PredictedChainingCell *) pChainCells;
+                    /*
+                     * There could be a race on another mutator thread to use
+                     * this particular predicted cell and the check has passed
+                     * the clazz comparison. So we cannot safely wipe the
+                     * method and branch but it is safe to clear the clazz,
+                     * which serves as the key.
+                     */
+                    predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT;
+                    break;
+                default:
+                    ALOGE("Unexpected chaining type: %d", i);
+                    dvmAbort();  // dvmAbort OK here - can't safely recover
+            }
+            COMPILER_TRACE_CHAINING(
+                ALOGI("Jit Runtime: unchaining 0x%x", (int)pChainCells));
+            pChainCells += elemSize;  /* Advance by a fixed number of bytes */
+        }
+    }
+    return NULL;
+}
+
+/* Unchain all translation in the cache. */
+void dvmJitUnchainAll()
+{
+    ALOGV("Jit Runtime: unchaining all");
+    if (gDvmJit.pJitEntryTable != NULL) {
+        COMPILER_TRACE_CHAINING(ALOGI("Jit Runtime: unchaining all"));
+        dvmLockMutex(&gDvmJit.tableLock);
+
+        UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        for (size_t i = 0; i < gDvmJit.jitTableSize; i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC &&
+                !gDvmJit.pJitEntryTable[i].u.info.isMethodEntry &&
+                gDvmJit.pJitEntryTable[i].codeAddress) {
+                      dvmJitUnchain(gDvmJit.pJitEntryTable[i].codeAddress);
+            }
+        }
+
+        PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        gDvmJit.translationChains = 0;
+    }
+    gDvmJit.hasNewChain = false;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+/* Add an additional jump instruction, keep jump target 4 bytes aligned.*/
+static void insertJumpHelp()
+{
+    int rem = (uint)stream % 4;
+    int nop_size = 3 - rem;
+    dump_nop(nop_size);
+    unconditional_jump_int(0, OpndSize_32);
+    return;
+}
+
+/* Chaining cell for code that may need warmup. */
+/* ARM assembly: ldr r0, [r6, #76] (why a single instruction to access member of glue structure?)
+                 blx r0
+                 data 0xb23a //bytecode address: 0x5115b23a
+                 data 0x5115
+   IA32 assembly:
+                  jmp  0 //5 bytes
+                  movl address, %ebx
+                  movl dvmJitToInterpNormal, %eax
+                  call %eax
+                  <-- return address
+*/
+static void handleNormalChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleNormalChainingCell for method %s block %d BC offset %x NCG offset %x",
+          cUnit->method->name, blockId, offset, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER NormalChainingCell at offsetPC %x offsetNCG %x @%p",
+              offset, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpNormal();
+    //move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true); /* used when unchaining */
+}
+
+/*
+ * Chaining cell for instructions that immediately following already translated
+ * code.
+ */
+static void handleHotChainingCell(CompilationUnit *cUnit,
+                                  unsigned int offset, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleHotChainingCell for method %s block %d BC offset %x NCG offset %x",
+          cUnit->method->name, blockId, offset, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER HotChainingCell at offsetPC %x offsetNCG %x @%p",
+              offset, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpTraceSelect();
+    //move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true); /* used when unchaining */
+}
+
+/* Chaining cell for branches that branch back into the same basic block */
+static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleBackwardBranchChainingCell for method %s block %d BC offset %x NCG offset %x",
+          cUnit->method->name, blockId, offset, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER BackwardBranchChainingCell at offsetPC %x offsetNCG %x @%p",
+              offset, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpNormal();
+    //move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true); /* used when unchaining */
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
+                                              const Method *callee, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleInvokeSingletonChainingCell for method %s block %d callee %s NCG offset %x",
+          cUnit->method->name, blockId, callee->name, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER InvokeSingletonChainingCell at block %d offsetNCG %x @%p",
+              blockId, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (callee->insns), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpTraceSelect();
+    //move_imm_to_reg(OpndSize_32, (int) (callee->insns), P_GPR_1, true); /* used when unchaining */
+}
+#undef P_GPR_1
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit, int blockId)
+{
+    if(dump_x86_inst)
+        ALOGI("LOWER InvokePredictedChainingCell at block %d offsetNCG %x @%p",
+              blockId, stream - streamMethodStart, stream);
+#ifndef PREDICTED_CHAINING
+    //assume rPC for callee->insns in %ebx
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpTraceSelectNoChain();
+#else
+    /* make sure section for predicited chaining cell is 4-byte aligned */
+    //int padding = (4 - ((u4)stream & 3)) & 3;
+    //stream += padding;
+    int* streamData = (int*)stream;
+    /* Should not be executed in the initial state */
+    streamData[0] = PREDICTED_CHAIN_BX_PAIR_INIT;
+    streamData[1] = 0;
+    /* To be filled: class */
+    streamData[2] = PREDICTED_CHAIN_CLAZZ_INIT;
+    /* To be filled: method */
+    streamData[3] = PREDICTED_CHAIN_METHOD_INIT;
+    /*
+     * Rechain count. The initial value of 0 here will trigger chaining upon
+     * the first invocation of this callsite.
+     */
+    streamData[4] = PREDICTED_CHAIN_COUNTER_INIT;
+#if 0
+    ALOGI("--- DATA @ %p: %x %x %x %x", stream, *((int*)stream), *((int*)(stream+4)),
+          *((int*)(stream+8)), *((int*)(stream+12)));
+#endif
+    stream += 20; //5 *4
+#endif
+}
+
+/* Load the Dalvik PC into r0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+                                   LowOpBlockLabel *targetLabel)
+{
+#if 0
+    LowOp **pcrLabel =
+        (LowOp **) cUnit->pcReconstructionList.elemList;
+    int numElems = cUnit->pcReconstructionList.numUsed;
+    int i;
+    for (i = 0; i < numElems; i++) {
+        dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+        /* r0 = dalvik PC */
+        loadConstant(cUnit, r0, pcrLabel[i]->operands[0]);
+        genUnconditionalBranch(cUnit, targetLabel);
+    }
+#endif
+}
+
+//use O0 code generator for hoisted checks outside of the loop
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    /*
+     * NOTE: these synthesized blocks don't have ssa names assigned
+     * for Dalvik registers.  However, because they dominate the following
+     * blocks we can simply use the Dalvik name w/ subscript 0 as the
+     * ssa name.
+     */
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int maxC = dInsn->arg[0];
+
+    /* assign array in virtual register to P_GPR_1 */
+    get_virtual_reg(mir->dalvikInsn.vA, OpndSize_32, P_GPR_1, true);
+    /* assign index in virtual register to P_GPR_2 */
+    get_virtual_reg(mir->dalvikInsn.vC, OpndSize_32, P_GPR_2, true);
+    export_pc();
+    compare_imm_reg(OpndSize_32, 0, P_GPR_1, true);
+    condJumpToBasicBlock(stream, Condition_E, cUnit->exceptionBlockId);
+    int delta = maxC;
+    /*
+     * If the loop end condition is ">=" instead of ">", then the largest value
+     * of the index is "endCondition - 1".
+     */
+    if (dInsn->arg[2] == OP_IF_GE) {
+        delta--;
+    }
+
+    if (delta < 0) { //+delta
+        //if P_GPR_2 is mapped to a VR, we can't do this
+        alu_binary_imm_reg(OpndSize_32, sub_opc, -delta, P_GPR_2, true);
+    } else if(delta > 0) {
+        alu_binary_imm_reg(OpndSize_32, add_opc, delta, P_GPR_2, true);
+    }
+    compare_mem_reg(OpndSize_32, offArrayObject_length, P_GPR_1, true, P_GPR_2, true);
+    condJumpToBasicBlock(stream, Condition_NC, cUnit->exceptionBlockId);
+}
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int maxC = dInsn->arg[0];
+
+    /* assign array in virtual register to P_GPR_1 */
+    get_virtual_reg(mir->dalvikInsn.vA, OpndSize_32, P_GPR_1, true);
+    /* assign index in virtual register to P_GPR_2 */
+    get_virtual_reg(mir->dalvikInsn.vB, OpndSize_32, P_GPR_2, true);
+    export_pc();
+    compare_imm_reg(OpndSize_32, 0, P_GPR_1, true);
+    condJumpToBasicBlock(stream, Condition_E, cUnit->exceptionBlockId);
+
+    if (maxC < 0) {
+        //if P_GPR_2 is mapped to a VR, we can't do this
+        alu_binary_imm_reg(OpndSize_32, sub_opc, -maxC, P_GPR_2, true);
+    } else if(maxC > 0) {
+        alu_binary_imm_reg(OpndSize_32, add_opc, maxC, P_GPR_2, true);
+    }
+    compare_mem_reg(OpndSize_32, offArrayObject_length, P_GPR_1, true, P_GPR_2, true);
+    condJumpToBasicBlock(stream, Condition_NC, cUnit->exceptionBlockId);
+
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+/*
+ * vA = idxReg;
+ * vB = minC;
+ */
+#define P_GPR_1 PhysicalReg_ECX
+static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int minC = dInsn->vB;
+    get_virtual_reg(mir->dalvikInsn.vA, OpndSize_32, P_GPR_1, true); //array
+    export_pc();
+    compare_imm_reg(OpndSize_32, -minC, P_GPR_1, true);
+    condJumpToBasicBlock(stream, Condition_C, cUnit->exceptionBlockId);
+}
+#undef P_GPR_1
+
+#ifdef WITH_JIT_INLINING
+static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    if(gDvm.executionMode == kExecutionModeNcgO0) {
+        get_virtual_reg(mir->dalvikInsn.vC, OpndSize_32, PhysicalReg_EBX, true);
+        move_imm_to_reg(OpndSize_32, (int) callsiteInfo->clazz, PhysicalReg_ECX, true);
+        compare_imm_reg(OpndSize_32, 0, PhysicalReg_EBX, true);
+        export_pc(); //use %edx
+        conditional_jump_global_API(, Condition_E, "common_errNullObject", false);
+        move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true, PhysicalReg_EAX, true);
+        compare_reg_reg(PhysicalReg_ECX, true, PhysicalReg_EAX, true);
+    } else {
+        get_virtual_reg(mir->dalvikInsn.vC, OpndSize_32, 5, false);
+        move_imm_to_reg(OpndSize_32, (int) callsiteInfo->clazz, 4, false);
+        nullCheck(5, false, 1, mir->dalvikInsn.vC);
+        move_mem_to_reg(OpndSize_32, offObject_clazz, 5, false, 6, false);
+        compare_reg_reg(4, false, 6, false);
+    }
+
+    //immdiate will be updated later in genLandingPadForMispredictedCallee
+    streamMisPred = stream;
+    callsiteInfo->misPredBranchOver = (LIR*)conditional_jump_int(Condition_NE, 0, OpndSize_8);
+}
+#endif
+
+/* Extended MIR instructions like PHI */
+void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
+{
+    ExecutionMode origMode = gDvm.executionMode;
+    gDvm.executionMode = kExecutionModeNcgO0;
+    switch ((ExtendedMIROpcode)mir->dalvikInsn.opcode) {
+        case kMirOpPhi: {
+            break;
+        }
+        case kMirOpNullNRangeUpCheck: {
+            genHoistedChecksForCountUpLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpNullNRangeDownCheck: {
+            genHoistedChecksForCountDownLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpLowerBound: {
+            genHoistedLowerBoundCheck(cUnit, mir);
+            break;
+        }
+        case kMirOpPunt: {
+            break;
+        }
+#ifdef WITH_JIT_INLINING
+        case kMirOpCheckInlinePrediction: { //handled in ncg_o1_data.c
+            genValidationForPredictedInline(cUnit, mir);
+            break;
+        }
+#endif
+        default:
+            break;
+    }
+    gDvm.executionMode = origMode;
+}
+
+static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry,
+                                int bodyId)
+{
+    /*
+     * Next, create two branches - one branch over to the loop body and the
+     * other branch to the PCR cell to punt.
+     */
+    //LowOp* branchToBody = jumpToBasicBlock(stream, bodyId);
+    //setupResourceMasks(branchToBody);
+    //cUnit->loopAnalysis->branchToBody = ((LIR*)branchToBody);
+
+#if 0
+    LowOp *branchToPCR = dvmCompilerNew(sizeof(ArmLIR), true);
+    branchToPCR->opCode = kThumbBUncond;
+    branchToPCR->generic.target = (LIR *) pcrLabel;
+    setupResourceMasks(branchToPCR);
+    cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR;
+#endif
+}
+
+/* check whether we can merge the block at index i with its target block */
+bool mergeBlock(BasicBlock *bb) {
+    if(bb->blockType == kDalvikByteCode &&
+       bb->firstMIRInsn != NULL &&
+       (bb->lastMIRInsn->dalvikInsn.opcode == OP_GOTO_16 ||
+        bb->lastMIRInsn->dalvikInsn.opcode == OP_GOTO ||
+        bb->lastMIRInsn->dalvikInsn.opcode == OP_GOTO_32) &&
+       bb->fallThrough == NULL) {// &&
+       //cUnit->hasLoop) {
+        //ALOGI("merge blocks ending with goto at index %d", i);
+        MIR* prevInsn = bb->lastMIRInsn->prev;
+        if(bb->taken == NULL) return false;
+        MIR* mergeInsn = bb->taken->firstMIRInsn;
+        if(mergeInsn == NULL) return false;
+        if(prevInsn == NULL) {//the block has a single instruction
+            bb->firstMIRInsn = mergeInsn;
+        } else {
+            prevInsn->next = mergeInsn; //remove goto from the chain
+        }
+        mergeInsn->prev = prevInsn;
+        bb->lastMIRInsn = bb->taken->lastMIRInsn;
+        bb->taken->firstMIRInsn = NULL; //block being merged in
+        bb->fallThrough = bb->taken->fallThrough;
+        bb->taken = bb->taken->taken;
+        return true;
+    }
+    return false;
+}
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    cUnit->headerSize = 6;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        return 12;
+    } else {
+        return 4;
+    }
+
+}
+
+#define PRINT_BUFFER_LEN 1024
+/* Print the code block in code cache in the range of [startAddr, endAddr)
+ * in readable format.
+ */
+void printEmittedCodeBlock(unsigned char *startAddr, unsigned char *endAddr)
+{
+    char strbuf[PRINT_BUFFER_LEN];
+    unsigned char *addr;
+    unsigned char *next_addr;
+    int n;
+
+    if (gDvmJit.printBinary) {
+        // print binary in bytes
+        n = 0;
+        for (addr = startAddr; addr < endAddr; addr++) {
+            n += snprintf(&strbuf[n], PRINT_BUFFER_LEN-n, "0x%x, ", *addr);
+            if (n > PRINT_BUFFER_LEN - 10) {
+                ALOGD("## %s", strbuf);
+                n = 0;
+            }
+        }
+        if (n > 0)
+            ALOGD("## %s", strbuf);
+    }
+
+    // print disassembled instructions
+    addr = startAddr;
+    while (addr < endAddr) {
+        next_addr = reinterpret_cast<unsigned char*>
+            (decoder_disassemble_instr(reinterpret_cast<char*>(addr),
+                                       strbuf, PRINT_BUFFER_LEN));
+        if (addr != next_addr) {
+            ALOGD("**  %p: %s", addr, strbuf);
+        } else {                // check whether this is nop padding
+            if (addr[0] == 0x90) {
+                ALOGD("**  %p: NOP (1 byte)", addr);
+                next_addr += 1;
+            } else if (addr[0] == 0x66 && addr[1] == 0x90) {
+                ALOGD("**  %p: NOP (2 bytes)", addr);
+                next_addr += 2;
+            } else if (addr[0] == 0x0f && addr[1] == 0x1f && addr[2] == 0x00) {
+                ALOGD("**  %p: NOP (3 bytes)", addr);
+                next_addr += 3;
+            } else {
+                ALOGD("** unable to decode binary at %p", addr);
+                break;
+            }
+        }
+        addr = next_addr;
+    }
+}
+
+/* 4 is the number of additional bytes needed for chaining information for trace:
+ * 2 bytes for chaining cell count offset and 2 bytes for chaining cell offset */
+#define EXTRA_BYTES_FOR_CHAINING 4
+
+/* Entry function to invoke the backend of the JIT compiler */
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    dump_x86_inst = cUnit->printMe;
+    /* Used to hold the labels of each block */
+    LowOpBlockLabel *labelList =
+        (LowOpBlockLabel *)dvmCompilerNew(sizeof(LowOpBlockLabel) * cUnit->numBlocks, true); //Utility.c
+    LowOp *headLIR = NULL;
+    GrowableList chainingListByType[kChainingCellLast];
+    unsigned int i, padding;
+
+    /*
+     * Initialize various types chaining lists.
+     */
+    for (i = 0; i < kChainingCellLast; i++) {
+        dvmInitGrowableList(&chainingListByType[i], 2);
+    }
+
+    /* Clear the visited flag for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes, false /* isIterative */);
+
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    /* Traces start with a profiling entry point.  Generate it here */
+    cUnit->profileCodeSize = genTraceProfileEntry(cUnit);
+
+    //BasicBlock **blockList = cUnit->blockList;
+    GrowableList *blockList = &cUnit->blockList;
+    BasicBlock *bb;
+
+    info->codeAddress = NULL;
+    stream = (char*)gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+
+    // TODO: compile into a temporary buffer and then copy into the code cache.
+    // That would let us leave the code cache unprotected for a shorter time.
+    size_t unprotected_code_cache_bytes =
+            gDvmJit.codeCacheSize - gDvmJit.codeCacheByteUsed - CODE_CACHE_PADDING;
+    UNPROTECT_CODE_CACHE(stream, unprotected_code_cache_bytes);
+
+    streamStart = stream; /* trace start before alignment */
+    stream += EXTRA_BYTES_FOR_CHAINING; /* This is needed for chaining. Add the bytes before the alignment */
+    stream = (char*)(((unsigned int)stream + 0xF) & ~0xF); /* Align trace to 16-bytes */
+    streamMethodStart = stream; /* code start */
+    for (i = 0; i < ((unsigned int) cUnit->numBlocks); i++) {
+        labelList[i].lop.generic.offset = -1;
+    }
+    cUnit->exceptionBlockId = -1;
+    for (i = 0; i < blockList->numUsed; i++) {
+        bb = (BasicBlock *) blockList->elemList[i];
+        if(bb->blockType == kExceptionHandling)
+            cUnit->exceptionBlockId = i;
+    }
+    startOfTrace(cUnit->method, labelList, cUnit->exceptionBlockId, cUnit);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //merge blocks ending with "goto" with the fall through block
+        if (cUnit->jitMode != kJitLoop)
+            for (i = 0; i < blockList->numUsed; i++) {
+                bb = (BasicBlock *) blockList->elemList[i];
+                bool merged = mergeBlock(bb);
+                while(merged) merged = mergeBlock(bb);
+            }
+        for (i = 0; i < blockList->numUsed; i++) {
+            bb = (BasicBlock *) blockList->elemList[i];
+            if(bb->blockType == kDalvikByteCode &&
+               bb->firstMIRInsn != NULL) {
+                preprocessingBB(bb);
+            }
+        }
+        preprocessingTrace();
+    }
+
+    /* Handle the content in each basic block */
+    for (i = 0; ; i++) {
+        MIR *mir;
+        bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->visited == true) continue;
+
+        labelList[i].immOpnd.value = bb->startOffset;
+
+        if (bb->blockType >= kChainingCellLast) {
+            /*
+             * Append the label pseudo LIR first. Chaining cells will be handled
+             * separately afterwards.
+             */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            labelList[i].lop.opCode2 = ATOM_PSEUDO_ENTRY_BLOCK;
+            if (bb->firstMIRInsn == NULL) {
+                continue;
+            } else {
+              setupLoopEntryBlock(cUnit, bb, bb->fallThrough->id);
+                                  //&labelList[blockList[i]->fallThrough->id]);
+            }
+        } else if (bb->blockType == kExitBlock) {
+            labelList[i].lop.opCode2 = ATOM_PSEUDO_EXIT_BLOCK;
+            labelList[i].lop.generic.offset = (stream - streamMethodStart);
+            goto gen_fallthrough;
+        } else if (bb->blockType == kDalvikByteCode) {
+            if (bb->hidden == true) continue;
+            labelList[i].lop.opCode2 = ATOM_PSEUDO_NORMAL_BLOCK_LABEL;
+            /* Reset the register state */
+#if 0
+            resetRegisterScoreboard(cUnit);
+#endif
+        } else {
+            switch (bb->blockType) {
+                case kChainingCellNormal:
+                    labelList[i].lop.opCode2 = ATOM_PSEUDO_CHAINING_CELL_NORMAL;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellNormal], i);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON;
+                    labelList[i].immOpnd.value =
+                        (int) bb->containingMethod;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokeSingleton], i);
+                    break;
+                case kChainingCellInvokePredicted:
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED;
+                   /*
+                     * Move the cached method pointer from operand 1 to 0.
+                     * Operand 0 was clobbered earlier in this routine to store
+                     * the block starting offset, which is not applicable to
+                     * predicted chaining cell.
+                     */
+                    //TODO
+                    //labelList[i].operands[0] = labelList[i].operands[1];
+
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokePredicted], i);
+                    break;
+                case kChainingCellHot:
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_CHAINING_CELL_HOT;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellHot], i);
+                    break;
+                case kPCReconstruction:
+                    /* Make sure exception handling block is next */
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL;
+                    //assert (i == cUnit->numBlocks - 2);
+                    labelList[i].lop.generic.offset = (stream - streamMethodStart);
+                    handlePCReconstruction(cUnit,
+                                           &labelList[cUnit->puntBlock->id]);
+                    break;
+                case kExceptionHandling:
+                    labelList[i].lop.opCode2 = ATOM_PSEUDO_EH_BLOCK_LABEL;
+                    labelList[i].lop.generic.offset = (stream - streamMethodStart);
+                    //if (cUnit->pcReconstructionList.numUsed) {
+                        scratchRegs[0] = PhysicalReg_EAX;
+                        jumpToInterpPunt();
+                        //call_dvmJitToInterpPunt();
+                    //}
+                    break;
+                case kChainingCellBackwardBranch:
+                    labelList[i].lop.opCode2 = ATOM_PSEUDO_CHAINING_CELL_BACKWARD_BRANCH;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellBackwardBranch],
+                        i);
+                    break;
+                default:
+                    break;
+            }
+            continue;
+        }
+        {
+        //LowOp *headLIR = NULL;
+        const DexCode *dexCode = dvmGetMethodCode(cUnit->method);
+        const u2 *startCodePtr = dexCode->insns;
+        const u2 *codePtr;
+        labelList[i].lop.generic.offset = (stream - streamMethodStart);
+        ALOGV("get ready to handle JIT bb %d type %d hidden %d",
+              bb->id, bb->blockType, bb->hidden);
+        for (BasicBlock *nextBB = bb; nextBB != NULL; nextBB = cUnit->nextCodegenBlock) {
+            bb = nextBB;
+            bb->visited = true;
+            cUnit->nextCodegenBlock = NULL;
+
+        if(gDvm.executionMode == kExecutionModeNcgO1 &&
+           bb->blockType != kEntryBlock &&
+           bb->firstMIRInsn != NULL) {
+            startOfBasicBlock(bb);
+            int cg_ret = codeGenBasicBlockJit(cUnit->method, bb);
+            endOfBasicBlock(bb);
+            if(cg_ret < 0) {
+                endOfTrace(true/*freeOnly*/);
+                cUnit->baseAddr = NULL;
+                PROTECT_CODE_CACHE(stream, unprotected_code_cache_bytes);
+                return;
+            }
+        } else {
+        for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+            startOfBasicBlock(bb); //why here for O0
+            Opcode dalvikOpCode = mir->dalvikInsn.opcode;
+            if((int)dalvikOpCode >= (int)kMirOpFirst) {
+                handleExtendedMIR(cUnit, mir);
+                continue;
+            }
+            InstructionFormat dalvikFormat =
+                dexGetFormatFromOpcode(dalvikOpCode);
+            ALOGV("ready to handle bytecode at offset %x: opcode %d format %d",
+                  mir->offset, dalvikOpCode, dalvikFormat);
+            LowOpImm *boundaryLIR = dump_special(ATOM_PSEUDO_DALVIK_BYTECODE_BOUNDARY, mir->offset);
+            /* Remember the first LIR for this block */
+            if (headLIR == NULL) {
+                headLIR = (LowOp*)boundaryLIR;
+            }
+            bool notHandled = true;
+            /*
+             * Debugging: screen the opcode first to see if it is in the
+             * do[-not]-compile list
+             */
+            bool singleStepMe =
+                gDvmJit.includeSelectedOp !=
+                ((gDvmJit.opList[dalvikOpCode >> 3] &
+                  (1 << (dalvikOpCode & 0x7))) !=
+                 0);
+            if (singleStepMe || cUnit->allSingleStep) {
+            } else {
+                codePtr = startCodePtr + mir->offset;
+                //lower each byte code, update LIR
+                notHandled = lowerByteCodeJit(cUnit->method, cUnit->method->insns+mir->offset, mir);
+                if(gDvmJit.codeCacheByteUsed + (stream - streamStart) +
+                   CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+                    ALOGI("JIT code cache full after lowerByteCodeJit (trace uses %uB)", (stream - streamStart));
+                    gDvmJit.codeCacheFull = true;
+                    cUnit->baseAddr = NULL;
+                    endOfTrace(true/*freeOnly*/);
+                    PROTECT_CODE_CACHE(stream, unprotected_code_cache_bytes);
+                    return;
+                }
+            }
+            if (notHandled) {
+                ALOGE("%#06x: Opcode 0x%x (%s) / Fmt %d not handled",
+                     mir->offset,
+                     dalvikOpCode, dexGetOpcodeName(dalvikOpCode),
+                     dalvikFormat);
+                dvmAbort();
+                break;
+            }
+        } // end for
+        } // end else //JIT + O0 code generator
+        }
+        } // end for
+        /* Eliminate redundant loads/stores and delay stores into later slots */
+#if 0
+        dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                           cUnit->lastLIRInsn);
+#endif
+        if (headLIR) headLIR = NULL;
+gen_fallthrough:
+        /*
+         * Check if the block is terminated due to trace length constraint -
+         * insert an unconditional branch to the chaining cell.
+         */
+        if (bb->needFallThroughBranch) {
+            jumpToBasicBlock(stream, bb->fallThrough->id);
+        }
+
+    }
+
+    char* streamChainingStart = (char*)stream;
+    /* Handle the chaining cells in predefined order */
+    for (i = 0; i < kChainingCellGap; i++) {
+        size_t j;
+        int *blockIdList = (int *) chainingListByType[i].elemList;
+
+        cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+        /* No chaining cells of this type */
+        if (cUnit->numChainingCells[i] == 0)
+            continue;
+
+        /* Record the first LIR for a new type of chaining cell */
+        cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+        for (j = 0; j < chainingListByType[i].numUsed; j++) {
+            int blockId = blockIdList[j];
+            BasicBlock *chainingBlock =
+                (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                         blockId);
+
+            labelList[blockId].lop.generic.offset = (stream - streamMethodStart);
+
+            /* Align this chaining cell first */
+#if 0
+            newLIR0(cUnit, ATOM_PSEUDO_ALIGN4);
+#endif
+            /* Insert the pseudo chaining instruction */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+            switch (chainingBlock->blockType) {
+                case kChainingCellNormal:
+                    handleNormalChainingCell(cUnit,
+                     chainingBlock->startOffset, blockId, labelList);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    handleInvokeSingletonChainingCell(cUnit,
+                        chainingBlock->containingMethod, blockId, labelList);
+                    break;
+                case kChainingCellInvokePredicted:
+                    handleInvokePredictedChainingCell(cUnit, blockId);
+                    break;
+                case kChainingCellHot:
+                    handleHotChainingCell(cUnit,
+                        chainingBlock->startOffset, blockId, labelList);
+                    break;
+                case kChainingCellBackwardBranch:
+                    handleBackwardBranchChainingCell(cUnit,
+                        chainingBlock->startOffset, blockId, labelList);
+                    break;
+                default:
+                    ALOGE("Bad blocktype %d", chainingBlock->blockType);
+                    dvmAbort();
+                    break;
+            }
+
+            if (gDvmJit.codeCacheByteUsed + (stream - streamStart) + CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+                ALOGI("JIT code cache full after ChainingCell (trace uses %uB)", (stream - streamStart));
+                gDvmJit.codeCacheFull = true;
+                cUnit->baseAddr = NULL;
+                endOfTrace(true); /* need to free structures */
+                PROTECT_CODE_CACHE(stream, unprotected_code_cache_bytes);
+                return;
+            }
+        }
+    }
+#if 0
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+#endif
+    endOfTrace(false);
+
+    if (gDvmJit.codeCacheFull) {
+        /* We hit code cache size limit inside endofTrace(false).
+         * Bail out for this trace!
+         */
+        ALOGI("JIT code cache full after endOfTrace (trace uses %uB)", (stream - streamStart));
+        cUnit->baseAddr = NULL;
+        PROTECT_CODE_CACHE(stream, unprotected_code_cache_bytes);
+        return;
+    }
+
+    /* dump section for chaining cell counts, make sure it is 4-byte aligned */
+    padding = (4 - ((u4)stream & 3)) & 3;
+    stream += padding;
+    ChainCellCounts chainCellCounts;
+    /* Install the chaining cell counts */
+    for (i=0; i< kChainingCellGap; i++) {
+        chainCellCounts.u.count[i] = cUnit->numChainingCells[i];
+    }
+    char* streamCountStart = (char*)stream;
+    memcpy((char*)stream, &chainCellCounts, sizeof(chainCellCounts));
+    stream += sizeof(chainCellCounts);
+
+    cUnit->baseAddr = streamMethodStart;
+    cUnit->totalSize = (stream - streamStart);
+    if(gDvmJit.codeCacheByteUsed + cUnit->totalSize + CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+        ALOGI("JIT code cache full after ChainingCellCounts (trace uses %uB)", (stream - streamStart));
+        gDvmJit.codeCacheFull = true;
+        cUnit->baseAddr = NULL;
+        PROTECT_CODE_CACHE(stream, unprotected_code_cache_bytes);
+        return;
+    }
+
+    /* write chaining cell count offset & chaining cell offset */
+    u2* pOffset = (u2*)(streamMethodStart - EXTRA_BYTES_FOR_CHAINING); /* space was already allocated for this purpose */
+    *pOffset = streamCountStart - streamMethodStart; /* from codeAddr */
+    pOffset[1] = streamChainingStart - streamMethodStart;
+
+    PROTECT_CODE_CACHE(stream, unprotected_code_cache_bytes);
+
+    gDvmJit.codeCacheByteUsed += (stream - streamStart);
+    if (cUnit->printMe) {
+        unsigned char* codeBaseAddr = (unsigned char *) cUnit->baseAddr;
+        unsigned char* codeBaseAddrNext = ((unsigned char *) gDvmJit.codeCache) + gDvmJit.codeCacheByteUsed;
+        ALOGD("-------- Built trace for %s%s, JIT code [%p, %p) cache start %p",
+              cUnit->method->clazz->descriptor, cUnit->method->name,
+              codeBaseAddr, codeBaseAddrNext, gDvmJit.codeCache);
+        ALOGD("** %s%s@0x%x:", cUnit->method->clazz->descriptor,
+              cUnit->method->name, cUnit->traceDesc->trace[0].info.frag.startOffset);
+        printEmittedCodeBlock(codeBaseAddr, codeBaseAddrNext);
+    }
+    ALOGV("JIT CODE after trace %p to %p size %x START %p", cUnit->baseAddr,
+          (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed,
+          cUnit->totalSize, gDvmJit.codeCache);
+
+    gDvmJit.numCompilations++;
+
+    info->codeAddress = (char*)cUnit->baseAddr;// + cUnit->headerSize;
+}
+
+/*
+ * Perform translation chain operation.
+ */
+void* dvmJitChain(void* tgtAddr, u4* branchAddr)
+{
+#ifdef JIT_CHAIN
+    int relOffset = (int) tgtAddr - (int)branchAddr;
+
+    if ((gDvmJit.pProfTable != NULL) && (gDvm.sumThreadSuspendCount == 0) &&
+        (gDvmJit.codeCacheFull == false)) {
+
+        gDvmJit.translationChains++;
+
+        //OpndSize immSize = estOpndSizeFromImm(relOffset);
+        //relOffset -= getJmpCallInstSize(immSize, JmpCall_uncond);
+        /* Hard coded the jump opnd size to 32 bits, This instruction will replace the "jump 0" in
+         * the original code sequence.
+         */
+        OpndSize immSize = OpndSize_32;
+        relOffset -= 5;
+        //can't use stream here since it is used by the compilation thread
+        UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+        dump_imm_with_codeaddr(Mnemonic_JMP, immSize, relOffset, (char*)branchAddr); //dump to branchAddr
+        PROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        gDvmJit.hasNewChain = true;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: chaining 0x%x to %p with relOffset %x",
+                  (int) branchAddr, tgtAddr, relOffset));
+    }
+#endif
+    return tgtAddr;
+}
+
+/*
+ * Accept the work and start compiling.  Returns true if compilation
+ * is attempted.
+ */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+    JitTraceDescription *desc;
+    bool isCompile;
+    bool success = true;
+
+    if (gDvmJit.codeCacheFull) {
+        return false;
+    }
+
+    switch (work->kind) {
+        case kWorkOrderTrace:
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            break;
+        case kWorkOrderTraceDebug: {
+            bool oldPrintMe = gDvmJit.printMe;
+            gDvmJit.printMe = true;
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            gDvmJit.printMe = oldPrintMe;
+            break;
+        }
+        case kWorkOrderProfileMode:
+            dvmJitChangeProfileMode((TraceProfilingModes)(int)work->info);
+            isCompile = false;
+            break;
+        default:
+            isCompile = false;
+            ALOGE("Jit: unknown work order type");
+            assert(0);  // Bail if debug build, discard otherwise
+    }
+    if (!success)
+        work->result.codeAddress = NULL;
+    return isCompile;
+}
+
+void dvmCompilerCacheFlush(long start, long end, long flags) {
+  /* cacheflush is needed for ARM, but not for IA32 (coherent icache) */
+}
+
+//#endif
diff --git a/vm/compiler/codegen/x86/Lower.cpp b/vm/compiler/codegen/x86/Lower.cpp
new file mode 100644
index 0000000..d0e7f92
--- /dev/null
+++ b/vm/compiler/codegen/x86/Lower.cpp
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file Lower.cpp
+    \brief This file implements the high-level wrapper for lowering
+
+*/
+
+//#include "uthash.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include <math.h>
+#include <sys/mman.h>
+#include "Translator.h"
+#include "Lower.h"
+#include "enc_wrapper.h"
+#include "vm/mterp/Mterp.h"
+#include "NcgHelper.h"
+#include "libdex/DexCatch.h"
+#include "compiler/CompilerIR.h"
+
+//statistics for optimization
+int num_removed_nullCheck;
+
+PhysicalReg scratchRegs[4];
+
+LowOp* ops[BUFFER_SIZE];
+LowOp* op;
+u2* rPC; //PC pointer to bytecode
+u2 inst; //current bytecode
+int offsetPC/*offset in bytecode*/, offsetNCG/*byte offset in native code*/;
+int ncg_rPC;
+//! map from PC in bytecode to PC in native code
+int mapFromBCtoNCG[BYTECODE_SIZE_PER_METHOD]; //initially mapped to -1
+char* streamStart = NULL; //start of the Pure CodeItem?, not include the global symbols
+char* streamCode = NULL; //start of the Pure CodeItem?, not include the global symbols
+char* streamMethodStart; //start of the method
+char* stream; //current stream pointer
+int lowOpTimeStamp = 0;
+Method* currentMethod = NULL;
+int currentExceptionBlockIdx = -1;
+LowOpBlockLabel* traceLabelList = NULL;
+BasicBlock* traceCurrentBB = NULL;
+MIR* traceCurrentMIR = NULL;
+bool scheduling_is_on = false;
+
+int common_invokeMethodNoRange();
+int common_invokeMethodRange();
+int common_invokeArgsDone(ArgsDoneType, bool);
+
+//data section of .ia32:
+char globalData[128];
+
+char strClassCastException[] = "Ljava/lang/ClassCastException;";
+char strInstantiationError[] = "Ljava/lang/InstantiationError;";
+char strInternalError[] = "Ljava/lang/InternalError;";
+char strFilledNewArrayNotImpl[] = "filled-new-array only implemented for 'int'";
+char strArithmeticException[] = "Ljava/lang/ArithmeticException;";
+char strArrayIndexException[] = "Ljava/lang/ArrayIndexOutOfBoundsException;";
+char strArrayStoreException[] = "Ljava/lang/ArrayStoreException;";
+char strDivideByZero[] = "divide by zero";
+char strNegativeArraySizeException[] = "Ljava/lang/NegativeArraySizeException;";
+char strNoSuchMethodError[] = "Ljava/lang/NoSuchMethodError;";
+char strNullPointerException[] = "Ljava/lang/NullPointerException;";
+char strStringIndexOutOfBoundsException[] = "Ljava/lang/StringIndexOutOfBoundsException;";
+
+int LstrClassCastExceptionPtr, LstrInstantiationErrorPtr, LstrInternalError, LstrFilledNewArrayNotImpl;
+int LstrArithmeticException, LstrArrayIndexException, LstrArrayStoreException, LstrStringIndexOutOfBoundsException;
+int LstrDivideByZero, LstrNegativeArraySizeException, LstrNoSuchMethodError, LstrNullPointerException;
+int LdoubNeg, LvaluePosInfLong, LvalueNegInfLong, LvalueNanLong, LshiftMask, Lvalue64, L64bits, LintMax, LintMin;
+
+void initConstDataSec() {
+    char* tmpPtr = globalData;
+
+    LdoubNeg = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x00000000;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0x80000000;
+    tmpPtr += sizeof(u4);
+
+    LvaluePosInfLong = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0xFFFFFFFF;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0x7FFFFFFF;
+    tmpPtr += sizeof(u4);
+
+    LvalueNegInfLong = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x00000000;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0x80000000;
+    tmpPtr += sizeof(u4);
+
+    LvalueNanLong = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+
+    LshiftMask = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x3f;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+
+    Lvalue64 = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x40;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+
+    L64bits = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0xFFFFFFFF;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0xFFFFFFFF;
+    tmpPtr += sizeof(u4);
+
+    LintMin = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x80000000;
+    tmpPtr += sizeof(u4);
+
+    LintMax = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x7FFFFFFF;
+    tmpPtr += sizeof(u4);
+
+    LstrClassCastExceptionPtr = (int)strClassCastException;
+    LstrInstantiationErrorPtr = (int)strInstantiationError;
+    LstrInternalError = (int)strInternalError;
+    LstrFilledNewArrayNotImpl = (int)strFilledNewArrayNotImpl;
+    LstrArithmeticException = (int)strArithmeticException;
+    LstrArrayIndexException = (int)strArrayIndexException;
+    LstrArrayStoreException = (int)strArrayStoreException;
+    LstrDivideByZero = (int)strDivideByZero;
+    LstrNegativeArraySizeException = (int)strNegativeArraySizeException;
+    LstrNoSuchMethodError = (int)strNoSuchMethodError;
+    LstrNullPointerException = (int)strNullPointerException;
+    LstrStringIndexOutOfBoundsException = (int)strStringIndexOutOfBoundsException;
+}
+
+//declarations of functions used in this file
+int spill_reg(int reg, bool isPhysical);
+int unspill_reg(int reg, bool isPhysical);
+
+int const_string_resolve();
+int sget_sput_resolve();
+int new_instance_needinit();
+int new_instance_abstract();
+int invoke_virtual_resolve();
+int invoke_direct_resolve();
+int invoke_static_resolve();
+int filled_new_array_notimpl();
+int resolve_class2(
+                   int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+                   bool indexPhysical,
+                   int thirdArg);
+int resolve_method2(
+                    int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+                    bool indexPhysical,
+                    int thirdArg/*VIRTUAL*/);
+int resolve_inst_field2(
+                        int startLR/*logical register index*/, bool isPhysical,
+                        int indexReg/*const pool index*/,
+                        bool indexPhysical);
+int resolve_static_field2(
+                          int startLR/*logical register index*/, bool isPhysical,
+                          int indexReg/*const pool index*/,
+                          bool indexPhysical);
+
+int invokeMethodNoRange_1_helper();
+int invokeMethodNoRange_2_helper();
+int invokeMethodNoRange_3_helper();
+int invokeMethodNoRange_4_helper();
+int invokeMethodNoRange_5_helper();
+int invokeMethodRange_helper();
+
+int invoke_virtual_helper();
+int invoke_virtual_quick_helper();
+int invoke_static_helper();
+int invoke_direct_helper();
+int new_instance_helper();
+int sget_sput_helper(int flag);
+int aput_obj_helper();
+int aget_helper(int flag);
+int aput_helper(int flag);
+int monitor_enter_helper();
+int monitor_exit_helper();
+int throw_helper();
+int const_string_helper();
+int array_length_helper();
+int invoke_super_helper();
+int invoke_interface_helper();
+int iget_iput_helper(int flag);
+int check_cast_helper(bool instance);
+int new_array_helper();
+
+int common_returnFromMethod();
+
+/*!
+\brief dump helper functions
+
+*/
+int performCGWorklist() {
+    filled_new_array_notimpl();
+    freeShortMap();
+    const_string_resolve();
+    freeShortMap();
+
+    resolve_class2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, 0);
+    freeShortMap();
+    resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_VIRTUAL);
+    freeShortMap();
+    resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_DIRECT);
+    freeShortMap();
+    resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_STATIC);
+    freeShortMap();
+    resolve_inst_field2(PhysicalReg_EAX, true, PhysicalReg_EAX, true);
+    freeShortMap();
+    resolve_static_field2(PhysicalReg_EAX, true, PhysicalReg_EAX, true);
+    freeShortMap();
+    throw_exception_message(PhysicalReg_ECX, PhysicalReg_EAX, true, PhysicalReg_Null, true);
+    freeShortMap();
+    throw_exception(PhysicalReg_ECX, PhysicalReg_EAX, PhysicalReg_Null, true);
+    freeShortMap();
+    new_instance_needinit();
+    freeShortMap();
+    return 0;
+}
+
+int aput_object_count;
+int common_periodicChecks_entry();
+int common_periodicChecks4();
+/*!
+\brief for debugging purpose, dump the sequence of native code for each bytecode
+
+*/
+int ncgMethodFake(Method* method) {
+    //to measure code size expansion, no need to patch up labels
+    methodDataWorklist = NULL;
+    globalShortWorklist = NULL;
+    globalNCGWorklist = NULL;
+    streamMethodStart = stream;
+
+    //initialize mapFromBCtoNCG
+    memset(&mapFromBCtoNCG[0], -1, BYTECODE_SIZE_PER_METHOD * sizeof(mapFromBCtoNCG[0]));
+    unsigned int i;
+    u2* rStart = (u2*)malloc(5*sizeof(u2));
+    if(rStart == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    rPC = rStart;
+    method->insns = rStart;
+    for(i = 0; i < 5; i++) *rPC++ = 0;
+    for(i = 0; i < 256; i++) {
+        rPC = rStart;
+        //modify the opcode
+        char* tmp = (char*)rStart;
+        *tmp++ = i;
+        *tmp = i;
+        inst = FETCH(0);
+        char* tmpStart = stream;
+        lowerByteCode(method); //use inst, rPC, method, modify rPC
+        int size_in_u2 = rPC - rStart;
+        if(stream - tmpStart  > 0)
+            ALOGI("LOWER bytecode %x size in u2: %d ncg size in byte: %d", i, size_in_u2, stream - tmpStart);
+    }
+    exit(0);
+}
+
+bool existATryBlock(Method* method, int startPC, int endPC) {
+    const DexCode* pCode = dvmGetMethodCode(method);
+    u4 triesSize = pCode->triesSize;
+    const DexTry* pTries = dexGetTries(pCode);
+    unsigned int i;
+    for (i = 0; i < triesSize; i++) {
+        const DexTry* pTry = &pTries[i];
+        u4 start = pTry->startAddr; //offsetPC
+        u4 end = start + pTry->insnCount;
+        //if [start, end] overlaps with [startPC, endPC] returns true
+        if((int)end < startPC || (int)start > endPC) { //no overlap
+        } else {
+            return true;
+        }
+    }
+    return false;
+}
+
+int mm_bytecode_size = 0;
+int mm_ncg_size = 0;
+int mm_relocation_size = 0;
+int mm_map_size = 0;
+void resetCodeSize() {
+    mm_bytecode_size = 0;
+    mm_ncg_size = 0;
+    mm_relocation_size = 0;
+    mm_map_size = 0;
+}
+
+bool bytecodeIsRemoved(const Method* method, u4 bytecodeOffset) {
+    if(gDvm.executionMode == kExecutionModeNcgO0) return false;
+    u4 ncgOff = mapFromBCtoNCG[bytecodeOffset];
+    int k = bytecodeOffset+1;
+    u2 insnsSize = dvmGetMethodInsnsSize(method);
+    while(k < insnsSize) {
+        if(mapFromBCtoNCG[k] < 0) {
+            k++;
+            continue;
+        }
+        if(mapFromBCtoNCG[k] == (int)ncgOff) return true;
+        return false;
+    }
+    return false;
+}
+
+int invoke_super_nsm();
+void init_common(const char* curFileName, DvmDex *pDvmDex, bool forNCG); //forward declaration
+void initGlobalMethods(); //forward declaration
+
+//called once when compiler thread starts up
+void initJIT(const char* curFileName, DvmDex *pDvmDex) {
+    init_common(curFileName, pDvmDex, false);
+}
+
+void init_common(const char* curFileName, DvmDex *pDvmDex, bool forNCG) {
+    if(!gDvm.constInit) {
+        globalMapNum = 0;
+        globalMap = NULL;
+        initConstDataSec();
+        gDvm.constInit = true;
+    }
+
+    //for initJIT: stream is already set
+    if(!gDvm.commonInit) {
+        initGlobalMethods();
+        gDvm.commonInit = true;
+    }
+}
+
+void initGlobalMethods() {
+    dump_x86_inst = false; /* DEBUG */
+    // generate native code for function ncgGetEIP
+    insertLabel("ncgGetEIP", false);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EDX, true);
+    x86_return();
+
+    //generate code for common labels
+    //jumps within a helper function is treated as short labels
+    globalShortMap = NULL;
+    common_periodicChecks_entry();
+    freeShortMap();
+    common_periodicChecks4();
+    freeShortMap();
+    //common_invokeMethodNoRange();
+    //common_invokeMethodRange();
+
+    if(dump_x86_inst) ALOGI("ArgsDone_Normal start");
+    common_invokeArgsDone(ArgsDone_Normal, false);
+    freeShortMap();
+    if(dump_x86_inst) ALOGI("ArgsDone_Native start");
+    common_invokeArgsDone(ArgsDone_Native, false);
+    freeShortMap();
+    if(dump_x86_inst) ALOGI("ArgsDone_Full start");
+    common_invokeArgsDone(ArgsDone_Full, true/*isJitFull*/);
+    if(dump_x86_inst) ALOGI("ArgsDone_Full end");
+    freeShortMap();
+
+    common_backwardBranch();
+    freeShortMap();
+    common_exceptionThrown();
+    freeShortMap();
+    common_errNullObject();
+    freeShortMap();
+    common_errArrayIndex();
+    freeShortMap();
+    common_errArrayStore();
+    freeShortMap();
+    common_errNegArraySize();
+    freeShortMap();
+    common_errNoSuchMethod();
+    freeShortMap();
+    common_errDivideByZero();
+    freeShortMap();
+    common_gotoBail();
+    freeShortMap();
+    common_gotoBail_0();
+    freeShortMap();
+    invoke_super_nsm();
+    freeShortMap();
+
+    performCGWorklist(); //generate code for helper functions
+    performLabelWorklist(); //it is likely that the common labels will jump to other common labels
+
+    dump_x86_inst = false;
+}
+
+ExecutionMode origMode;
+//when to update streamMethodStart
+bool lowerByteCodeJit(const Method* method, const u2* codePtr, MIR* mir) {
+    rPC = (u2*)codePtr;
+    inst = FETCH(0);
+    traceCurrentMIR = mir;
+    int retCode = lowerByteCode(method);
+    traceCurrentMIR = NULL;
+    freeShortMap();
+    if(retCode >= 0) return false; //handled
+    return true; //not handled
+}
+
+void startOfBasicBlock(BasicBlock* bb) {
+    traceCurrentBB = bb;
+    if(gDvm.executionMode == kExecutionModeNcgO0) {
+        isScratchPhysical = true;
+    } else {
+        isScratchPhysical = false;
+    }
+}
+
+void startOfTrace(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId,
+                  CompilationUnit *cUnit) {
+    origMode = gDvm.executionMode;
+    gDvm.executionMode = kExecutionModeNcgO1;
+    if(gDvm.executionMode == kExecutionModeNcgO0) {
+        isScratchPhysical = true;
+    } else {
+        isScratchPhysical = false;
+    }
+    currentMethod = (Method*)method;
+    currentExceptionBlockIdx = exceptionBlockId;
+    methodDataWorklist = NULL;
+    globalShortWorklist = NULL;
+    globalNCGWorklist = NULL;
+
+    streamMethodStart = stream;
+    //initialize mapFromBCtoNCG
+    memset(&mapFromBCtoNCG[0], -1, BYTECODE_SIZE_PER_METHOD * sizeof(mapFromBCtoNCG[0]));
+    traceLabelList = labelList;
+    if(gDvm.executionMode == kExecutionModeNcgO1)
+        startOfTraceO1(method, labelList, exceptionBlockId, cUnit);
+}
+
+void endOfTrace(bool freeOnly) {
+    if(freeOnly) {
+        freeLabelWorklist();
+        freeNCGWorklist();
+        freeDataWorklist();
+        freeChainingWorklist();
+    }
+    else {
+        performLabelWorklist();
+        performNCGWorklist(); //handle forward jump (GOTO, IF)
+        performDataWorklist(); //handle SWITCH & FILL_ARRAY_DATA
+        performChainingWorklist();
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        endOfTraceO1();
+    }
+    gDvm.executionMode = origMode;
+}
+
+///////////////////////////////////////////////////////////////////
+//!
+//! each bytecode is translated to a sequence of machine codes
+int lowerByteCode(const Method* method) { //inputs: rPC & inst & stream & streamMethodStart
+    /* offsetPC is used in O1 code generator, where it is defined as the sequence number
+       use a local version to avoid overwriting */
+    int offsetPC = rPC - (u2*)method->insns;
+
+    if(dump_x86_inst)
+        ALOGI("LOWER bytecode %x at offsetPC %x offsetNCG %x @%p",
+              INST_INST(inst), offsetPC, stream - streamMethodStart, stream);
+
+    //update mapFromBCtoNCG
+    offsetNCG = stream - streamMethodStart;
+    if(offsetPC >= BYTECODE_SIZE_PER_METHOD) ALOGE("offsetPC %d exceeds BYTECODE_SIZE_PER_METHOD", offsetPC);
+    mapFromBCtoNCG[offsetPC] = offsetNCG;
+#if defined(ENABLE_TRACING) && defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC, mapFromBCtoNCG[offsetPC], 1);
+#endif
+    //return number of LowOps generated
+    switch (INST_INST(inst)) {
+    case OP_NOP:
+        return op_nop();
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+        return op_move();
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+        return op_move_from16();
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        return op_move_16();
+    case OP_MOVE_WIDE:
+        return op_move_wide();
+    case OP_MOVE_WIDE_FROM16:
+        return op_move_wide_from16();
+    case OP_MOVE_WIDE_16:
+        return op_move_wide_16();
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+        return op_move_result();
+    case OP_MOVE_RESULT_WIDE:
+        return op_move_result_wide();
+    case OP_MOVE_EXCEPTION:
+        return op_move_exception();
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        return op_return_void();
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        return op_return();
+    case OP_RETURN_WIDE:
+        return op_return_wide();
+    case OP_CONST_4:
+        return op_const_4();
+    case OP_CONST_16:
+        return op_const_16();
+    case OP_CONST:
+        return op_const();
+    case OP_CONST_HIGH16:
+        return op_const_high16();
+    case OP_CONST_WIDE_16:
+        return op_const_wide_16();
+    case OP_CONST_WIDE_32:
+        return op_const_wide_32();
+    case OP_CONST_WIDE:
+        return op_const_wide();
+    case OP_CONST_WIDE_HIGH16:
+        return op_const_wide_high16();
+    case OP_CONST_STRING:
+        return op_const_string();
+    case OP_CONST_STRING_JUMBO:
+        return op_const_string_jumbo();
+    case OP_CONST_CLASS:
+        return op_const_class();
+    case OP_MONITOR_ENTER:
+        return op_monitor_enter();
+    case OP_MONITOR_EXIT:
+        return op_monitor_exit();
+    case OP_CHECK_CAST:
+        return op_check_cast();
+    case OP_INSTANCE_OF:
+        return op_instance_of();
+    case OP_ARRAY_LENGTH:
+        return op_array_length();
+    case OP_NEW_INSTANCE:
+        return op_new_instance();
+    case OP_NEW_ARRAY:
+        return op_new_array();
+    case OP_FILLED_NEW_ARRAY:
+        return op_filled_new_array();
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        return op_filled_new_array_range();
+    case OP_FILL_ARRAY_DATA:
+        return op_fill_array_data();
+    case OP_THROW:
+        return op_throw();
+    case OP_THROW_VERIFICATION_ERROR:
+        return op_throw_verification_error();
+    case OP_GOTO:
+        return op_goto();
+    case OP_GOTO_16:
+        return op_goto_16();
+    case OP_GOTO_32:
+        return op_goto_32();
+    case OP_PACKED_SWITCH:
+        return op_packed_switch();
+    case OP_SPARSE_SWITCH:
+        return op_sparse_switch();
+    case OP_CMPL_FLOAT:
+        return op_cmpl_float();
+    case OP_CMPG_FLOAT:
+        return op_cmpg_float();
+    case OP_CMPL_DOUBLE:
+        return op_cmpl_double();
+    case OP_CMPG_DOUBLE:
+        return op_cmpg_double();
+    case OP_CMP_LONG:
+        return op_cmp_long();
+    case OP_IF_EQ:
+        return op_if_eq();
+    case OP_IF_NE:
+        return op_if_ne();
+    case OP_IF_LT:
+        return op_if_lt();
+    case OP_IF_GE:
+        return op_if_ge();
+    case OP_IF_GT:
+        return op_if_gt();
+    case OP_IF_LE:
+        return op_if_le();
+    case OP_IF_EQZ:
+        return op_if_eqz();
+    case OP_IF_NEZ:
+        return op_if_nez();
+    case OP_IF_LTZ:
+        return op_if_ltz();
+    case OP_IF_GEZ:
+        return op_if_gez();
+    case OP_IF_GTZ:
+        return op_if_gtz();
+    case OP_IF_LEZ:
+        return op_if_lez();
+    case OP_AGET:
+        return op_aget();
+    case OP_AGET_WIDE:
+        return op_aget_wide();
+    case OP_AGET_OBJECT:
+        return op_aget_object();
+    case OP_AGET_BOOLEAN:
+        return op_aget_boolean();
+    case OP_AGET_BYTE:
+        return op_aget_byte();
+    case OP_AGET_CHAR:
+        return op_aget_char();
+    case OP_AGET_SHORT:
+        return op_aget_short();
+    case OP_APUT:
+        return op_aput();
+    case OP_APUT_WIDE:
+        return op_aput_wide();
+    case OP_APUT_OBJECT:
+        return op_aput_object();
+    case OP_APUT_BOOLEAN:
+        return op_aput_boolean();
+    case OP_APUT_BYTE:
+        return op_aput_byte();
+    case OP_APUT_CHAR:
+        return op_aput_char();
+    case OP_APUT_SHORT:
+        return op_aput_short();
+    case OP_IGET:
+    case OP_IGET_VOLATILE:
+        return op_iget();
+    case OP_IGET_WIDE:
+        return op_iget_wide(false); // isVolatile==false
+    case OP_IGET_WIDE_VOLATILE:
+        return op_iget_wide(true);  // isVolatile==true
+    case OP_IGET_OBJECT:
+    case OP_IGET_OBJECT_VOLATILE:
+        return op_iget_object();
+    case OP_IGET_BOOLEAN:
+        return op_iget_boolean();
+    case OP_IGET_BYTE:
+        return op_iget_byte();
+    case OP_IGET_CHAR:
+        return op_iget_char();
+    case OP_IGET_SHORT:
+        return op_iget_short();
+    case OP_IPUT:
+    case OP_IPUT_VOLATILE:
+        return op_iput();
+    case OP_IPUT_WIDE:
+        return op_iput_wide(false); // isVolatile==false
+    case OP_IPUT_WIDE_VOLATILE:
+        return op_iput_wide(true);  // isVolatile==true
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_OBJECT_VOLATILE:
+        return op_iput_object();
+    case OP_IPUT_BOOLEAN:
+        return op_iput_boolean();
+    case OP_IPUT_BYTE:
+        return op_iput_byte();
+    case OP_IPUT_CHAR:
+        return op_iput_char();
+    case OP_IPUT_SHORT:
+        return op_iput_short();
+    case OP_SGET:
+    case OP_SGET_VOLATILE:
+        return op_sget();
+    case OP_SGET_WIDE:
+        return op_sget_wide(false); // isVolatile==false
+    case OP_SGET_WIDE_VOLATILE:
+        return op_sget_wide(true);  // isVolatile==true
+    case OP_SGET_OBJECT:
+    case OP_SGET_OBJECT_VOLATILE:
+        return op_sget_object();
+    case OP_SGET_BOOLEAN:
+        return op_sget_boolean();
+    case OP_SGET_BYTE:
+        return op_sget_byte();
+    case OP_SGET_CHAR:
+        return op_sget_char();
+    case OP_SGET_SHORT:
+        return op_sget_short();
+    case OP_SPUT:
+    case OP_SPUT_VOLATILE:
+        return op_sput(false);
+    case OP_SPUT_WIDE:
+        return op_sput_wide(false); // isVolatile==false
+    case OP_SPUT_WIDE_VOLATILE:
+        return op_sput_wide(true);  // isVolatile==true
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_OBJECT_VOLATILE:
+        return op_sput_object();
+    case OP_SPUT_BOOLEAN:
+        return op_sput_boolean();
+    case OP_SPUT_BYTE:
+        return op_sput_byte();
+    case OP_SPUT_CHAR:
+        return op_sput_char();
+    case OP_SPUT_SHORT:
+        return op_sput_short();
+    case OP_INVOKE_VIRTUAL:
+        return op_invoke_virtual();
+    case OP_INVOKE_SUPER:
+        return op_invoke_super();
+    case OP_INVOKE_DIRECT:
+        return op_invoke_direct();
+    case OP_INVOKE_STATIC:
+        return op_invoke_static();
+    case OP_INVOKE_INTERFACE:
+        return op_invoke_interface();
+    case OP_INVOKE_VIRTUAL_RANGE:
+        return op_invoke_virtual_range();
+    case OP_INVOKE_SUPER_RANGE:
+        return op_invoke_super_range();
+    case OP_INVOKE_DIRECT_RANGE:
+        return op_invoke_direct_range();
+    case OP_INVOKE_STATIC_RANGE:
+        return op_invoke_static_range();
+    case OP_INVOKE_INTERFACE_RANGE:
+        return op_invoke_interface_range();
+    case OP_NEG_INT:
+        return op_neg_int();
+    case OP_NOT_INT:
+        return op_not_int();
+    case OP_NEG_LONG:
+        return op_neg_long();
+    case OP_NOT_LONG:
+        return op_not_long();
+    case OP_NEG_FLOAT:
+        return op_neg_float();
+    case OP_NEG_DOUBLE:
+        return op_neg_double();
+    case OP_INT_TO_LONG:
+        return op_int_to_long();
+    case OP_INT_TO_FLOAT:
+        return op_int_to_float();
+    case OP_INT_TO_DOUBLE:
+        return op_int_to_double();
+    case OP_LONG_TO_INT:
+        return op_long_to_int();
+    case OP_LONG_TO_FLOAT:
+        return op_long_to_float();
+    case OP_LONG_TO_DOUBLE:
+        return op_long_to_double();
+    case OP_FLOAT_TO_INT:
+        return op_float_to_int();
+    case OP_FLOAT_TO_LONG:
+        return op_float_to_long();
+    case OP_FLOAT_TO_DOUBLE:
+        return op_float_to_double();
+    case OP_DOUBLE_TO_INT:
+        return op_double_to_int();
+    case OP_DOUBLE_TO_LONG:
+        return op_double_to_long();
+    case OP_DOUBLE_TO_FLOAT:
+        return op_double_to_float();
+    case OP_INT_TO_BYTE:
+        return op_int_to_byte();
+    case OP_INT_TO_CHAR:
+        return op_int_to_char();
+    case OP_INT_TO_SHORT:
+        return op_int_to_short();
+    case OP_ADD_INT:
+        return op_add_int();
+    case OP_SUB_INT:
+        return op_sub_int();
+    case OP_MUL_INT:
+        return op_mul_int();
+    case OP_DIV_INT:
+        return op_div_int();
+    case OP_REM_INT:
+        return op_rem_int();
+    case OP_AND_INT:
+        return op_and_int();
+    case OP_OR_INT:
+        return op_or_int();
+    case OP_XOR_INT:
+        return op_xor_int();
+    case OP_SHL_INT:
+        return op_shl_int();
+    case OP_SHR_INT:
+        return op_shr_int();
+    case OP_USHR_INT:
+        return op_ushr_int();
+    case OP_ADD_LONG:
+        return op_add_long();
+    case OP_SUB_LONG:
+        return op_sub_long();
+    case OP_MUL_LONG:
+        return op_mul_long();
+    case OP_DIV_LONG:
+        return op_div_long();
+    case OP_REM_LONG:
+        return op_rem_long();
+    case OP_AND_LONG:
+        return op_and_long();
+    case OP_OR_LONG:
+        return op_or_long();
+    case OP_XOR_LONG:
+        return op_xor_long();
+    case OP_SHL_LONG:
+        return op_shl_long();
+    case OP_SHR_LONG:
+        return op_shr_long();
+    case OP_USHR_LONG:
+        return op_ushr_long();
+    case OP_ADD_FLOAT:
+        return op_add_float();
+    case OP_SUB_FLOAT:
+        return op_sub_float();
+    case OP_MUL_FLOAT:
+        return op_mul_float();
+    case OP_DIV_FLOAT:
+        return op_div_float();
+    case OP_REM_FLOAT:
+        return op_rem_float();
+    case OP_ADD_DOUBLE:
+        return op_add_double();
+    case OP_SUB_DOUBLE:
+        return op_sub_double();
+    case OP_MUL_DOUBLE:
+        return op_mul_double();
+    case OP_DIV_DOUBLE:
+        return op_div_double();
+    case OP_REM_DOUBLE:
+        return op_rem_double();
+    case OP_ADD_INT_2ADDR:
+        return op_add_int_2addr();
+    case OP_SUB_INT_2ADDR:
+        return op_sub_int_2addr();
+    case OP_MUL_INT_2ADDR:
+        return op_mul_int_2addr();
+    case OP_DIV_INT_2ADDR:
+        return op_div_int_2addr();
+    case OP_REM_INT_2ADDR:
+        return op_rem_int_2addr();
+    case OP_AND_INT_2ADDR:
+        return op_and_int_2addr();
+    case OP_OR_INT_2ADDR:
+        return op_or_int_2addr();
+    case OP_XOR_INT_2ADDR:
+        return op_xor_int_2addr();
+    case OP_SHL_INT_2ADDR:
+        return op_shl_int_2addr();
+    case OP_SHR_INT_2ADDR:
+        return op_shr_int_2addr();
+    case OP_USHR_INT_2ADDR:
+        return op_ushr_int_2addr();
+    case OP_ADD_LONG_2ADDR:
+        return op_add_long_2addr();
+    case OP_SUB_LONG_2ADDR:
+        return op_sub_long_2addr();
+    case OP_MUL_LONG_2ADDR:
+        return op_mul_long_2addr();
+    case OP_DIV_LONG_2ADDR:
+        return op_div_long_2addr();
+    case OP_REM_LONG_2ADDR:
+        return op_rem_long_2addr();
+    case OP_AND_LONG_2ADDR:
+        return op_and_long_2addr();
+    case OP_OR_LONG_2ADDR:
+        return op_or_long_2addr();
+    case OP_XOR_LONG_2ADDR:
+        return op_xor_long_2addr();
+    case OP_SHL_LONG_2ADDR:
+        return op_shl_long_2addr();
+    case OP_SHR_LONG_2ADDR:
+        return op_shr_long_2addr();
+    case OP_USHR_LONG_2ADDR:
+        return op_ushr_long_2addr();
+    case OP_ADD_FLOAT_2ADDR:
+        return op_add_float_2addr();
+    case OP_SUB_FLOAT_2ADDR:
+        return op_sub_float_2addr();
+    case OP_MUL_FLOAT_2ADDR:
+        return op_mul_float_2addr();
+    case OP_DIV_FLOAT_2ADDR:
+        return op_div_float_2addr();
+    case OP_REM_FLOAT_2ADDR:
+        return op_rem_float_2addr();
+    case OP_ADD_DOUBLE_2ADDR:
+        return op_add_double_2addr();
+    case OP_SUB_DOUBLE_2ADDR:
+        return op_sub_double_2addr();
+    case OP_MUL_DOUBLE_2ADDR:
+        return op_mul_double_2addr();
+    case OP_DIV_DOUBLE_2ADDR:
+        return op_div_double_2addr();
+    case OP_REM_DOUBLE_2ADDR:
+        return op_rem_double_2addr();
+    case OP_ADD_INT_LIT16:
+        return op_add_int_lit16();
+    case OP_RSUB_INT:
+        return op_rsub_int();
+    case OP_MUL_INT_LIT16:
+        return op_mul_int_lit16();
+    case OP_DIV_INT_LIT16:
+        return op_div_int_lit16();
+    case OP_REM_INT_LIT16:
+        return op_rem_int_lit16();
+    case OP_AND_INT_LIT16:
+        return op_and_int_lit16();
+    case OP_OR_INT_LIT16:
+        return op_or_int_lit16();
+    case OP_XOR_INT_LIT16:
+        return op_xor_int_lit16();
+    case OP_ADD_INT_LIT8:
+        return op_add_int_lit8();
+    case OP_RSUB_INT_LIT8:
+        return op_rsub_int_lit8();
+    case OP_MUL_INT_LIT8:
+        return op_mul_int_lit8();
+    case OP_DIV_INT_LIT8:
+        return op_div_int_lit8();
+    case OP_REM_INT_LIT8:
+        return op_rem_int_lit8();
+    case OP_AND_INT_LIT8:
+        return op_and_int_lit8();
+    case OP_OR_INT_LIT8:
+        return op_or_int_lit8();
+    case OP_XOR_INT_LIT8:
+        return op_xor_int_lit8();
+    case OP_SHL_INT_LIT8:
+        return op_shl_int_lit8();
+    case OP_SHR_INT_LIT8:
+        return op_shr_int_lit8();
+    case OP_USHR_INT_LIT8:
+        return op_ushr_int_lit8();
+    case OP_EXECUTE_INLINE:
+        return op_execute_inline(false);
+    case OP_EXECUTE_INLINE_RANGE:
+        return op_execute_inline(true);
+    case OP_BREAKPOINT:
+        ALOGE("found bytecode OP_BREAKPOINT");
+        dvmAbort();
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        return op_invoke_object_init_range();
+    case OP_IGET_QUICK:
+        return op_iget_quick();
+    case OP_IGET_WIDE_QUICK:
+        return op_iget_wide_quick();
+    case OP_IGET_OBJECT_QUICK:
+        return op_iget_object_quick();
+    case OP_IPUT_QUICK:
+        return op_iput_quick();
+    case OP_IPUT_WIDE_QUICK:
+        return op_iput_wide_quick();
+    case OP_IPUT_OBJECT_QUICK:
+        return op_iput_object_quick();
+    case OP_INVOKE_VIRTUAL_QUICK:
+        return op_invoke_virtual_quick();
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        return op_invoke_virtual_quick_range();
+    case OP_INVOKE_SUPER_QUICK:
+        return op_invoke_super_quick();
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        return op_invoke_super_quick_range();
+    }
+
+    ALOGE("No JIT support for bytecode %x at offsetPC %x",
+          INST_INST(inst), offsetPC);
+    return -1;
+}
+int op_nop() {
+    rPC++;
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/Lower.h b/vm/compiler/codegen/x86/Lower.h
new file mode 100644
index 0000000..630ccb9
--- /dev/null
+++ b/vm/compiler/codegen/x86/Lower.h
@@ -0,0 +1,1240 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file lower.h
+    \brief A header file to define interface between lowering and register allocator
+*/
+
+#ifndef _DALVIK_LOWER
+#define _DALVIK_LOWER
+
+#define CODE_CACHE_PADDING 1024 //code space for a single bytecode
+// comment out for phase 1 porting
+#define PREDICTED_CHAINING
+#define JIT_CHAIN
+
+#define NUM_DEPENDENCIES 24 /* max number of dependencies from a LowOp */
+//compilaton flags used by NCG O1
+#define DUMP_EXCEPTION //to measure performance, required to have correct exception handling
+/*! multiple versions for hardcoded registers */
+#define HARDREG_OPT
+#define CFG_OPT
+/*! remove redundant move ops when accessing virtual registers */
+#define MOVE_OPT
+/*! remove redundant spill of virtual registers */
+#define SPILL_OPT
+#define XFER_OPT
+//#define DSE_OPT //no perf improvement for cme
+/*! use live range analysis to allocate registers */
+#define LIVERANGE_OPT
+/*! remove redundant null check */
+#define NULLCHECK_OPT
+//#define BOUNDCHECK_OPT
+/*! optimize the access to glue structure */
+#define GLUE_OPT
+#define CALL_FIX
+#define NATIVE_FIX
+#define INVOKE_FIX //optimization
+#define GETVR_FIX //optimization
+
+#include "Dalvik.h"
+#include "enc_wrapper.h"
+#include "AnalysisO1.h"
+#include "compiler/CompilerIR.h"
+
+//compilation flags for debugging
+//#define DEBUG_INFO
+//#define DEBUG_CALL_STACK
+//#define DEBUG_IGET_OBJ
+//#define DEBUG_NCG_CODE_SIZE
+//#define DEBUG_NCG
+//#define DEBUG_NCG_1
+//#define DEBUG_LOADING
+//#define USE_INTERPRETER
+//#define DEBUG_EACH_BYTECODE
+
+/*! registers for functions are hardcoded */
+#define HARDCODE_REG_CALL
+#define HARDCODE_REG_SHARE
+#define HARDCODE_REG_HELPER
+
+#define PhysicalReg_FP PhysicalReg_EDI
+#define PhysicalReg_Glue PhysicalReg_EBP
+
+//COPIED from interp/InterpDefs.h
+#define FETCH(_offset) (rPC[(_offset)])
+#define INST_INST(_inst) ((_inst) & 0xff)
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+//#include "vm/mterp/common/asm-constants.h"
+#define offEBP_self 8
+#define offEBP_spill -56
+#define offThread_exception 68
+#define offClassObject_descriptor 24
+#define offArrayObject_length 8
+#ifdef PROFILE_FIELD_ACCESS
+#define offStaticField_value 24
+#define offInstField_byteOffset 24
+#else
+#define offStaticField_value 16
+#define offInstField_byteOffset 16
+#endif
+
+#ifdef EASY_GDB
+#define offStackSaveArea_prevFrame 4
+#define offStackSaveArea_savedPc 8
+#define offStackSaveArea_method 12
+#define offStackSaveArea_localRefTop 16 // -> StackSaveArea.xtra.locakRefCookie
+#define offStackSaveArea_returnAddr 20
+#define offStackSaveArea_isDebugInterpreted 24
+#define sizeofStackSaveArea 24
+#else
+#define offStackSaveArea_prevFrame 0
+#define offStackSaveArea_savedPc 4
+#define offStackSaveArea_method 8
+#define offStackSaveArea_localRefTop 12 // -> StackSaveArea.xtra.locakRefCookie
+#define offStackSaveArea_returnAddr 16
+#define offStackSaveArea_isDebugInterpreted 20
+#define sizeofStackSaveArea 20
+#endif
+
+#define offClassObject_status 44
+#define offClassObject_accessFlags 32
+#ifdef MTERP_NO_UNALIGN_64
+#define offArrayObject_contents 16
+#else
+#define offArrayObject_contents 12
+#endif
+
+#define offField_clazz 0
+#define offObject_clazz 0
+#define offClassObject_vtable 116
+#define offClassObject_pDvmDex 40
+#define offClassObject_super 72
+#define offClassObject_vtableCount 112
+#define offMethod_name 16
+#define offMethod_accessFlags 4
+#define offMethod_methodIndex 8
+#define offMethod_registersSize 10
+#define offMethod_outsSize 12
+#define offGlue_interpStackEnd 32
+#define offThread_inJitCodeCache 124
+#define offThread_jniLocal_nextEntry 168
+#define offMethod_insns 32
+#ifdef ENABLE_TRACING
+#define offMethod_insns_bytecode 44
+#define offMethod_insns_ncg 48
+#endif
+
+#define offGlue_pc     0
+#define offGlue_fp     4
+#define offGlue_retval 8
+
+#define offThread_curFrame 4
+#define offGlue_method 16
+#define offGlue_methodClassDex 20
+#define offGlue_self 24
+#define offGlue_pSelfSuspendCount 36
+#define offGlue_cardTable 40
+#define offGlue_pDebuggerActive 44
+#define offGlue_pActiveProfilers 48
+#define offGlue_entryPoint 52
+#define offGlue_icRechainCount 84
+#define offGlue_espEntry 88
+#define offGlue_spillRegion 92
+#define offDvmDex_pResStrings 8
+#define offDvmDex_pResClasses 12
+#define offDvmDex_pResMethods 16
+#define offDvmDex_pResFields  20
+#define offMethod_clazz       0
+
+// Definitions must be consistent with vm/mterp/x86/header.S
+#define FRAME_SIZE     124
+
+typedef enum ArgsDoneType {
+    ArgsDone_Normal = 0,
+    ArgsDone_Native,
+    ArgsDone_Full
+} ArgsDoneType;
+
+/*! An enum type
+    to list bytecodes for AGET, APUT
+*/
+typedef enum ArrayAccess {
+    AGET, AGET_WIDE, AGET_CHAR, AGET_SHORT, AGET_BOOLEAN, AGET_BYTE,
+    APUT, APUT_WIDE, APUT_CHAR, APUT_SHORT, APUT_BOOLEAN, APUT_BYTE
+} ArrayAccess;
+/*! An enum type
+    to list bytecodes for IGET, IPUT
+*/
+typedef enum InstanceAccess {
+    IGET, IGET_WIDE, IPUT, IPUT_WIDE
+} InstanceAccess;
+/*! An enum type
+    to list bytecodes for SGET, SPUT
+*/
+typedef enum StaticAccess {
+    SGET, SGET_WIDE, SPUT, SPUT_WIDE
+} StaticAccess;
+
+typedef enum JmpCall_type {
+    JmpCall_uncond = 1,
+    JmpCall_cond,
+    JmpCall_reg, //jump reg32
+    JmpCall_call
+} JmpCall_type;
+
+////////////////////////////////////////////////////////////////
+/* data structure for native codes */
+/* Due to space considation, a lowered op (LowOp) has two operands (LowOpnd), depending on
+   the type of the operand, LowOpndReg or LowOpndImm or LowOpndMem will follow */
+/*! type of an operand can be immediate, register or memory */
+typedef enum LowOpndType {
+  LowOpndType_Imm = 0,
+  LowOpndType_Reg,
+  LowOpndType_Mem,
+  LowOpndType_Label,
+  LowOpndType_NCG,
+  LowOpndType_Chain
+} LowOpndType;
+typedef enum LowOpndDefUse {
+  LowOpndDefUse_Def = 0,
+  LowOpndDefUse_Use,
+  LowOpndDefUse_UseDef
+} LowOpndDefUse;
+
+/*!
+\brief base data structure for an operand */
+typedef struct LowOpnd {
+  LowOpndType type;
+  OpndSize size;
+  LowOpndDefUse defuse;
+} LowOpnd;
+/*!
+\brief data structure for a register operand */
+typedef struct LowOpndReg {
+  LowOpndRegType regType;
+  int logicalReg;
+  int physicalReg;
+} LowOpndReg;
+/*!
+\brief data structure for an immediate operand */
+typedef struct LowOpndImm {
+  union {
+    s4 value;
+    unsigned char bytes[4];
+  };
+} LowOpndImm;
+
+typedef struct LowOpndNCG {
+  union {
+    s4 value;
+    unsigned char bytes[4];
+  };
+} LowOpndNCG;
+
+#define LABEL_SIZE 256
+typedef struct LowOpndLabel {
+  char label[LABEL_SIZE];
+  bool isLocal;
+} LowOpndLabel;
+
+/* get ready for optimizations at LIR
+   add MemoryAccessType & virtualRegNum to memory operands */
+typedef enum MemoryAccessType {
+  MemoryAccess_GLUE,
+  MemoryAccess_VR,
+  MemoryAccess_SPILL,
+  MemoryAccess_Unknown
+} MemoryAccessType;
+typedef enum UseDefEntryType {
+  UseDefType_Ctrl = 0,
+  UseDefType_Float,
+  UseDefType_MemVR,
+  UseDefType_MemSpill,
+  UseDefType_MemUnknown,
+  UseDefType_Reg
+} UseDefEntryType;
+typedef struct UseDefProducerEntry {
+  UseDefEntryType type;
+  int index; //enum PhysicalReg for "Reg" type
+  int producerSlot;
+} UseDefProducerEntry;
+#define MAX_USE_PER_ENTRY 50 /* at most 10 uses for each entry */
+typedef struct UseDefUserEntry {
+  UseDefEntryType type;
+  int index;
+  int useSlots[MAX_USE_PER_ENTRY];
+  int num_uses_per_entry;
+} UseDefUserEntry;
+
+/*!
+\brief data structure for a memory operand */
+typedef struct LowOpndMem {
+  LowOpndImm m_disp;
+  LowOpndImm m_scale;
+  LowOpndReg m_index;
+  LowOpndReg m_base;
+  bool hasScale;
+  MemoryAccessType mType;
+  int index;
+} LowOpndMem;
+
+typedef enum AtomOpCode {
+    ATOM_PSEUDO_CHAINING_CELL_BACKWARD_BRANCH = -15,
+    ATOM_NORMAL_ALU = -14,
+    ATOM_PSEUDO_ENTRY_BLOCK = -13,
+    ATOM_PSEUDO_EXIT_BLOCK = -12,
+    ATOM_PSEUDO_TARGET_LABEL = -11,
+    ATOM_PSEUDO_CHAINING_CELL_HOT = -10,
+    ATOM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED = -9,
+    ATOM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON = -8,
+    ATOM_PSEUDO_CHAINING_CELL_NORMAL = -7,
+    ATOM_PSEUDO_DALVIK_BYTECODE_BOUNDARY = -6,
+    ATOM_PSEUDO_ALIGN4 = -5,
+    ATOM_PSEUDO_PC_RECONSTRUCTION_CELL = -4,
+    ATOM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL = -3,
+    ATOM_PSEUDO_EH_BLOCK_LABEL = -2,
+    ATOM_PSEUDO_NORMAL_BLOCK_LABEL = -1,
+    ATOM_NORMAL,
+} AtomOpCode;
+
+typedef enum DependencyType {
+  Dependency_RAW,
+  Dependency_WAW,
+  Dependency_WAR,
+  Dependency_FLAG
+} DependencyType;
+typedef struct DependencyStruct {
+  DependencyType dType;
+  int nodeId;
+  int latency;
+} DependencyStruct;
+
+typedef struct LowOpBlock {
+  LIR generic;
+  Mnemonic opCode;
+  AtomOpCode opCode2;
+} LowOpBlock;
+
+/*!
+\brief data structure for a lowered operation */
+typedef struct LowOp {
+  LIR generic;
+  Mnemonic opCode;
+  AtomOpCode opCode2;
+  LowOpnd opnd1;
+  LowOpnd opnd2;
+  int numOperands;
+} LowOp;
+
+typedef struct LowOpLabel {
+  LowOp lop;
+  LowOpndLabel labelOpnd;
+}LowOpLabel;
+
+typedef struct LowOpNCG {
+  LowOp lop;
+  LowOpndNCG ncgOpnd;
+}LowOpNCG;
+
+typedef struct LowOpBlockLabel {
+  LowOpBlock lop;
+  LowOpndImm immOpnd;
+} LowOpBlockLabel;
+
+typedef struct LowOpImm {
+  LowOp lop;
+  LowOpndImm immOpnd;
+} LowOpImm;
+
+typedef struct LowOpMem {
+  LowOp lop;
+  LowOpndMem memOpnd;
+} LowOpMem;
+
+typedef struct LowOpReg {
+  LowOp lop;
+  LowOpndReg regOpnd;
+} LowOpReg;
+
+typedef struct LowOpImmImm {
+  LowOp lop;
+  LowOpndImm immOpnd1;
+  LowOpndImm immOpnd2;
+} LowOpImmImm;
+
+typedef struct LowOpImmReg {
+  LowOp lop;
+  LowOpndImm immOpnd1;
+  LowOpndReg regOpnd2;
+} LowOpImmReg;
+
+typedef struct LowOpImmMem {
+  LowOp lop;
+  LowOpndImm immOpnd1;
+  LowOpndMem memOpnd2;
+} LowOpImmMem;
+
+typedef struct LowOpRegImm {
+  LowOp lop;
+  LowOpndReg regOpnd1;
+  LowOpndImm immOpnd2;
+} LowOpRegImm;
+
+typedef struct LowOpRegReg {
+  LowOp lop;
+  LowOpndReg regOpnd1;
+  LowOpndReg regOpnd2;
+} LowOpRegReg;
+
+typedef struct LowOpRegMem {
+  LowOp lop;
+  LowOpndReg regOpnd1;
+  LowOpndMem memOpnd2;
+} LowOpRegMem;
+
+typedef struct LowOpMemImm {
+  LowOp lop;
+  LowOpndMem memOpnd1;
+  LowOpndImm immOpnd2;
+} LowOpMemImm;
+
+typedef struct LowOpMemReg {
+  LowOp lop;
+  LowOpndMem memOpnd1;
+  LowOpndReg regOpnd2;
+} LowOpMemReg;
+
+typedef struct LowOpMemMem {
+  LowOp lop;
+  LowOpndMem memOpnd1;
+  LowOpndMem memOpnd2;
+} LowOpMemMem;
+
+/*!
+\brief data structure for labels used when lowering a method
+
+four label maps are defined: globalMap globalShortMap globalWorklist globalShortWorklist
+globalMap: global labels where codePtr points to the label
+           freeLabelMap called in clearNCG
+globalWorklist: global labels where codePtr points to an instruciton using the label
+  standalone NCG -------
+                accessed by insertLabelWorklist & performLabelWorklist
+  code cache ------
+                inserted by performLabelWorklist(false),
+                handled & cleared by generateRelocation in NcgFile.c
+globalShortMap: local labels where codePtr points to the label
+                freeShortMap called after generation of one bytecode
+globalShortWorklist: local labels where codePtr points to an instruction using the label
+                accessed by insertShortWorklist & insertLabel
+definition of local label: life time of the label is within a bytecode or within a helper function
+extra label maps are used by code cache:
+  globalDataWorklist VMAPIWorklist
+*/
+typedef struct LabelMap {
+  char label[LABEL_SIZE];
+  char* codePtr; //code corresponding to the label or code that uses the label
+  struct LabelMap* nextItem;
+  OpndSize size;
+  uint  addend;
+} LabelMap;
+/*!
+\brief data structure to handle forward jump (GOTO, IF)
+
+accessed by insertNCGWorklist & performNCGWorklist
+*/
+typedef struct NCGWorklist {
+  //when WITH_JIT, relativePC stores the target basic block id
+  s4 relativePC; //relative offset in bytecode
+  int offsetPC;  //PC in bytecode
+  int offsetNCG; //PC in native code
+  char* codePtr; //code for native jump instruction
+  struct NCGWorklist* nextItem;
+  OpndSize size;
+}NCGWorklist;
+/*!
+\brief data structure to handle SWITCH & FILL_ARRAY_DATA
+
+two data worklist are defined: globalDataWorklist (used by code cache) & methodDataWorklist
+methodDataWorklist is accessed by insertDataWorklist & performDataWorklist
+*/
+typedef struct DataWorklist {
+  s4 relativePC; //relative offset in bytecode to access the data
+  int offsetPC;  //PC in bytecode
+  int offsetNCG; //PC in native code
+  char* codePtr; //code for native instruction add_imm_reg imm, %edx
+  char* codePtr2;//code for native instruction add_reg_reg %eax, %edx for SWITCH
+                 //                            add_imm_reg imm, %edx for FILL_ARRAY_DATA
+  struct DataWorklist* nextItem;
+}DataWorklist;
+#ifdef ENABLE_TRACING
+typedef struct MapWorklist {
+  u4 offsetPC;
+  u4 offsetNCG;
+  int isStartOfPC; //1 --> true 0 --> false
+  struct MapWorklist* nextItem;
+} MapWorklist;
+#endif
+
+#define BUFFER_SIZE 1024 //# of Low Ops buffered
+//the following three numbers are hardcoded, please CHECK
+#define BYTECODE_SIZE_PER_METHOD 81920
+#define NATIVE_SIZE_PER_DEX 19000000 //FIXME for core.jar: 16M --> 18M for O1
+#define NATIVE_SIZE_FOR_VM_STUBS 100000
+#define MAX_HANDLER_OFFSET 1024 //maximal number of handler offsets
+
+extern int LstrClassCastExceptionPtr, LstrInstantiationErrorPtr, LstrInternalError, LstrFilledNewArrayNotImpl;
+extern int LstrArithmeticException, LstrArrayIndexException, LstrArrayStoreException, LstrStringIndexOutOfBoundsException;
+extern int LstrDivideByZero, LstrNegativeArraySizeException, LstrNoSuchMethodError, LstrNullPointerException;
+extern int LdoubNeg, LvaluePosInfLong, LvalueNegInfLong, LvalueNanLong, LshiftMask, Lvalue64, L64bits, LintMax, LintMin;
+
+extern LabelMap* globalMap;
+extern LabelMap* globalShortMap;
+extern LabelMap* globalWorklist;
+extern LabelMap* globalShortWorklist;
+extern NCGWorklist* globalNCGWorklist;
+extern DataWorklist* methodDataWorklist;
+#ifdef ENABLE_TRACING
+extern MapWorklist* methodMapWorklist;
+#endif
+extern PhysicalReg scratchRegs[4];
+
+#define C_SCRATCH_1 scratchRegs[0]
+#define C_SCRATCH_2 scratchRegs[1]
+#define C_SCRATCH_3 scratchRegs[2] //scratch reg inside callee
+
+extern LowOp* ops[BUFFER_SIZE];
+extern bool isScratchPhysical;
+extern u2* rPC;
+extern u2 inst;
+extern int offsetPC;
+extern int offsetNCG;
+extern int mapFromBCtoNCG[BYTECODE_SIZE_PER_METHOD];
+extern char* streamStart;
+
+extern char* streamCode;
+
+extern char* streamMethodStart; //start of the method
+extern char* stream; //current stream pointer
+extern char* streamMisPred;
+extern int lowOpTimeStamp;
+extern Method* currentMethod;
+extern int currentExceptionBlockIdx;
+
+extern int globalMapNum;
+extern int globalWorklistNum;
+extern int globalDataWorklistNum;
+extern int globalPCWorklistNum;
+extern int chainingWorklistNum;
+extern int VMAPIWorklistNum;
+
+extern LabelMap* globalDataWorklist;
+extern LabelMap* globalPCWorklist;
+extern LabelMap* chainingWorklist;
+extern LabelMap* VMAPIWorklist;
+
+extern int ncgClassNum;
+extern int ncgMethodNum;
+
+extern LowOp* lirTable[200]; //Number of LIRs for all bytecodes do not exceed 200
+extern int num_lirs_in_table;
+
+bool existATryBlock(Method* method, int startPC, int endPC);
+// interface between register allocator & lowering
+extern int num_removed_nullCheck;
+
+int registerAlloc(int type, int reg, bool isPhysical, bool updateRef);
+int registerAllocMove(int reg, int type, bool isPhysical, int srcReg);
+int checkVirtualReg(int reg, LowOpndRegType type, int updateRef); //returns the physical register
+int updateRefCount(int reg, LowOpndRegType type);
+int updateRefCount2(int reg, int type, bool isPhysical);
+int spillVirtualReg(int vrNum, LowOpndRegType type, bool updateTable);
+int isVirtualRegConstant(int regNum, LowOpndRegType type, int* valuePtr, bool updateRef);
+int checkTempReg(int reg, int type, bool isPhysical, int vA);
+bool checkTempReg2(int reg, int type, bool isPhysical, int physicalRegForVR);
+int freeReg(bool spillGL);
+int nextVersionOfHardReg(PhysicalReg pReg, int refCount);
+int updateVirtualReg(int reg, LowOpndRegType type);
+void setVRNullCheck(int regNum, OpndSize size);
+bool isVRNullCheck(int regNum, OpndSize size);
+void setVRBoundCheck(int vr_array, int vr_index);
+bool isVRBoundCheck(int vr_array, int vr_index);
+int requestVRFreeDelay(int regNum, u4 reason);
+void cancelVRFreeDelayRequest(int regNum, u4 reason);
+bool getVRFreeDelayRequested(int regNum);
+bool isGlueHandled(int glue_reg);
+void resetGlue(int glue_reg);
+void updateGlue(int reg, bool isPhysical, int glue_reg);
+int updateVRAtUse(int reg, LowOpndRegType pType, int regAll);
+int touchEcx();
+int touchEax();
+int touchEdx();
+int beforeCall(const char* target);
+int afterCall(const char* target);
+void startBranch();
+void endBranch();
+void rememberState(int);
+void goToState(int);
+void transferToState(int);
+void globalVREndOfBB(const Method*);
+void constVREndOfBB();
+void startNativeCode(int num, int type);
+void endNativeCode();
+void donotSpillReg(int physicalReg);
+void doSpillReg(int physicalReg);
+
+#define XMM_1 PhysicalReg_XMM0
+#define XMM_2 PhysicalReg_XMM1
+#define XMM_3 PhysicalReg_XMM2
+#define XMM_4 PhysicalReg_XMM3
+
+/////////////////////////////////////////////////////////////////////////////////
+//LR[reg] = disp + PR[base_reg] or disp + LR[base_reg]
+void load_effective_addr(int disp, int base_reg, bool isBasePhysical,
+                          int reg, bool isPhysical);
+void load_effective_addr_scale(int base_reg, bool isBasePhysical,
+                                int index_reg, bool isIndexPhysical, int scale,
+                                int reg, bool isPhysical);
+void load_fpu_cw(int disp, int base_reg, bool isBasePhysical);
+void store_fpu_cw(bool checkException, int disp, int base_reg, bool isBasePhysical);
+void convert_integer(OpndSize srcSize, OpndSize dstSize);
+void load_fp_stack(LowOp* op, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void load_int_fp_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void load_int_fp_stack_imm(OpndSize size, int imm);
+void store_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void store_int_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+
+void load_fp_stack_VR(OpndSize size, int vA);
+void load_int_fp_stack_VR(OpndSize size, int vA);
+void store_fp_stack_VR(bool pop, OpndSize size, int vA);
+void store_int_fp_stack_VR(bool pop, OpndSize size, int vA);
+void compare_VR_ss_reg(int vA, int reg, bool isPhysical);
+void compare_VR_sd_reg(int vA, int reg, bool isPhysical);
+void fpu_VR(ALU_Opcode opc, OpndSize size, int vA);
+void compare_reg_mem(LowOp* op, OpndSize size, int reg, bool isPhysical,
+                           int disp, int base_reg, bool isBasePhysical);
+void compare_mem_reg(OpndSize size,
+                           int disp, int base_reg, bool isBasePhysical,
+                           int reg, bool isPhysical);
+void compare_VR_reg(OpndSize size,
+                           int vA,
+                           int reg, bool isPhysical);
+void compare_imm_reg(OpndSize size, int imm,
+                           int reg, bool isPhysical);
+void compare_imm_mem(OpndSize size, int imm,
+                           int disp, int base_reg, bool isBasePhysical);
+void compare_imm_VR(OpndSize size, int imm,
+                           int vA);
+void compare_reg_reg(int reg1, bool isPhysical1,
+                           int reg2, bool isPhysical2);
+void compare_reg_reg_16(int reg1, bool isPhysical1,
+                         int reg2, bool isPhysical2);
+void compare_ss_mem_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                              int reg, bool isPhysical);
+void compare_ss_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                              int reg2, bool isPhysical2);
+void compare_sd_mem_with_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                              int reg, bool isPhysical);
+void compare_sd_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                              int reg2, bool isPhysical2);
+void compare_fp_stack(bool pop, int reg, bool isDouble);
+void test_imm_reg(OpndSize size, int imm, int reg, bool isPhysical);
+void test_imm_mem(OpndSize size, int imm, int disp, int reg, bool isPhysical);
+
+void conditional_move_reg_to_reg(OpndSize size, ConditionCode cc, int reg1, bool isPhysical1, int reg, bool isPhysical);
+void move_ss_mem_to_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                        int reg, bool isPhysical);
+void move_ss_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical);
+LowOpRegMem* move_ss_mem_to_reg_noalloc(int disp, int base_reg, bool isBasePhysical,
+                         MemoryAccessType mType, int mIndex,
+                         int reg, bool isPhysical);
+LowOpMemReg* move_ss_reg_to_mem_noalloc(int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical,
+                         MemoryAccessType mType, int mIndex);
+void move_sd_mem_to_reg(int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical);
+void move_sd_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical);
+
+void conditional_jump(ConditionCode cc, const char* target, bool isShortTerm);
+void unconditional_jump(const char* target, bool isShortTerm);
+void conditional_jump_int(ConditionCode cc, int target, OpndSize size);
+void unconditional_jump_int(int target, OpndSize size);
+void unconditional_jump_reg(int reg, bool isPhysical);
+void call(const char* target);
+void call_reg(int reg, bool isPhysical);
+void call_reg_noalloc(int reg, bool isPhysical);
+void call_mem(int disp, int reg, bool isPhysical);
+void x86_return();
+
+void alu_unary_reg(OpndSize size, ALU_Opcode opc, int reg, bool isPhysical);
+void alu_unary_mem(LowOp* op, OpndSize size, ALU_Opcode opc, int disp, int base_reg, bool isBasePhysical);
+
+void alu_binary_imm_mem(OpndSize size, ALU_Opcode opc,
+                         int imm, int disp, int base_reg, bool isBasePhysical);
+void alu_binary_imm_reg(OpndSize size, ALU_Opcode opc, int imm, int reg, bool isPhysical);
+void alu_binary_mem_reg(OpndSize size, ALU_Opcode opc,
+                         int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical);
+void alu_binary_VR_reg(OpndSize size, ALU_Opcode opc, int vA, int reg, bool isPhysical);
+void alu_sd_binary_VR_reg(ALU_Opcode opc, int vA, int reg, bool isPhysical, bool isSD);
+void alu_binary_reg_reg(OpndSize size, ALU_Opcode opc,
+                         int reg1, bool isPhysical1,
+                         int reg2, bool isPhysical2);
+void alu_binary_reg_mem(OpndSize size, ALU_Opcode opc,
+                         int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical);
+
+void fpu_mem(LowOp* op, ALU_Opcode opc, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void alu_ss_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                            int reg2, bool isPhysical2);
+void alu_sd_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                            int reg2, bool isPhysical2);
+
+void push_mem_to_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void push_reg_to_stack(OpndSize size, int reg, bool isPhysical);
+
+//returns the pointer to end of the native code
+void move_reg_to_mem(OpndSize size,
+                      int reg, bool isPhysical,
+                      int disp, int base_reg, bool isBasePhysical);
+LowOpRegMem* move_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical);
+void movez_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical);
+void movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2);
+void moves_mem_to_reg(LowOp* op, OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical);
+void movez_mem_disp_scale_to_reg(OpndSize size,
+                      int base_reg, bool isBasePhysical,
+                      int disp, int index_reg, bool isIndexPhysical, int scale,
+                      int reg, bool isPhysical);
+void moves_mem_disp_scale_to_reg(OpndSize size,
+                      int base_reg, bool isBasePhysical,
+                      int disp, int index_reg, bool isIndexPhysical, int scale,
+                      int reg, bool isPhysical);
+void move_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2);
+void move_reg_to_reg_noalloc(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2);
+void move_mem_scale_to_reg(OpndSize size,
+                            int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                            int reg, bool isPhysical);
+void move_mem_disp_scale_to_reg(OpndSize size,
+                int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical);
+void move_reg_to_mem_scale(OpndSize size,
+                            int reg, bool isPhysical,
+                            int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale);
+void move_reg_to_mem_disp_scale(OpndSize size,
+                            int reg, bool isPhysical,
+                            int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale);
+void move_imm_to_mem(OpndSize size, int imm,
+                      int disp, int base_reg, bool isBasePhysical);
+void set_VR_to_imm(u2 vA, OpndSize size, int imm);
+void set_VR_to_imm_noalloc(u2 vA, OpndSize size, int imm);
+void set_VR_to_imm_noupdateref(LowOp* op, u2 vA, OpndSize size, int imm);
+void move_imm_to_reg(OpndSize size, int imm, int reg, bool isPhysical);
+void move_imm_to_reg_noalloc(OpndSize size, int imm, int reg, bool isPhysical);
+
+//LR[reg] = VR[vB]
+//or
+//PR[reg] = VR[vB]
+void get_virtual_reg(u2 vB, OpndSize size, int reg, bool isPhysical);
+void get_virtual_reg_noalloc(u2 vB, OpndSize size, int reg, bool isPhysical);
+//VR[v] = LR[reg]
+//or
+//VR[v] = PR[reg]
+void set_virtual_reg(u2 vA, OpndSize size, int reg, bool isPhysical);
+void set_virtual_reg_noalloc(u2 vA, OpndSize size, int reg, bool isPhysical);
+void get_VR_ss(int vB, int reg, bool isPhysical);
+void set_VR_ss(int vA, int reg, bool isPhysical);
+void get_VR_sd(int vB, int reg, bool isPhysical);
+void set_VR_sd(int vA, int reg, bool isPhysical);
+
+int spill_reg(int reg, bool isPhysical);
+int unspill_reg(int reg, bool isPhysical);
+
+void move_reg_to_mem_noalloc(OpndSize size,
+                      int reg, bool isPhysical,
+                      int disp, int base_reg, bool isBasePhysical,
+                      MemoryAccessType mType, int mIndex);
+LowOpRegMem* move_mem_to_reg_noalloc(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      MemoryAccessType mType, int mIndex,
+                      int reg, bool isPhysical);
+
+//////////////////////////////////////////////////////////////
+int insertLabel(const char* label, bool checkDup);
+int export_pc();
+int simpleNullCheck(int reg, bool isPhysical, int vr);
+int nullCheck(int reg, bool isPhysical, int exceptionNum, int vr);
+int handlePotentialException(
+                             ConditionCode code_excep, ConditionCode code_okay,
+                             int exceptionNum, const char* errName);
+int get_currentpc(int reg, bool isPhysical);
+int get_self_pointer(int reg, bool isPhysical);
+int get_res_strings(int reg, bool isPhysical);
+int get_res_classes(int reg, bool isPhysical);
+int get_res_fields(int reg, bool isPhysical);
+int get_res_methods(int reg, bool isPhysical);
+int get_glue_method_class(int reg, bool isPhysical);
+int get_glue_method(int reg, bool isPhysical);
+int set_glue_method(int reg, bool isPhysical);
+int get_glue_dvmdex(int reg, bool isPhysical);
+int set_glue_dvmdex(int reg, bool isPhysical);
+int get_suspendCount(int reg, bool isPhysical);
+int get_return_value(OpndSize size, int reg, bool isPhysical);
+int set_return_value(OpndSize size, int reg, bool isPhysical);
+int clear_exception();
+int get_exception(int reg, bool isPhysical);
+int set_exception(int reg, bool isPhysical);
+int save_pc_fp_to_glue();
+int savearea_from_fp(int reg, bool isPhysical);
+
+int call_moddi3();
+int call_divdi3();
+int call_fmod();
+int call_fmodf();
+int call_dvmFindCatchBlock();
+int call_dvmThrowVerificationError();
+int call_dvmAllocObject();
+int call_dvmAllocArrayByClass();
+int call_dvmResolveMethod();
+int call_dvmResolveClass();
+int call_dvmInstanceofNonTrivial();
+int call_dvmThrow();
+int call_dvmThrowWithMessage();
+int call_dvmCheckSuspendPending();
+int call_dvmLockObject();
+int call_dvmUnlockObject();
+int call_dvmInitClass();
+int call_dvmAllocPrimitiveArray();
+int call_dvmInterpHandleFillArrayData();
+int call_dvmNcgHandlePackedSwitch();
+int call_dvmNcgHandleSparseSwitch();
+int call_dvmJitHandlePackedSwitch();
+int call_dvmJitHandleSparseSwitch();
+int call_dvmJitToInterpTraceSelectNoChain();
+int call_dvmJitToPatchPredictedChain();
+int call_dvmJitToInterpNormal();
+int call_dvmJitToInterpTraceSelect();
+int call_dvmQuasiAtomicSwap64();
+int call_dvmQuasiAtomicRead64();
+int call_dvmCanPutArrayElement();
+int call_dvmFindInterfaceMethodInCache();
+int call_dvmHandleStackOverflow();
+int call_dvmResolveString();
+int call_dvmResolveInstField();
+int call_dvmResolveStaticField();
+
+//labels and branches
+//shared branch to resolve class: 2 specialized versions
+//OPTION 1: call & ret
+//OPTION 2: store jump back label in a fixed register or memory
+//jump to .class_resolve, then jump back
+//OPTION 3: share translator code
+/* global variables: ncg_rPC */
+int resolve_class(
+                  int startLR/*logical register index*/, bool isPhysical, int tmp/*const pool index*/,
+                  int thirdArg);
+/* EXPORT_PC; movl exceptionPtr, -8(%esp); movl descriptor, -4(%esp); lea; call; lea; jmp */
+int throw_exception_message(int exceptionPtr, int obj_reg, bool isPhysical,
+                            int startLR/*logical register index*/, bool startPhysical);
+/* EXPORT_PC; movl exceptionPtr, -8(%esp); movl imm, -4(%esp); lea; call; lea; jmp */
+int throw_exception(int exceptionPtr, int imm,
+                    int startLR/*logical register index*/, bool startPhysical);
+
+void freeShortMap();
+int insertDataWorklist(s4 relativePC, char* codePtr1);
+#ifdef ENABLE_TRACING
+int insertMapWorklist(s4 BCOffset, s4 NCGOffset, int isStartOfPC);
+#endif
+int performNCGWorklist();
+int performDataWorklist();
+void performLabelWorklist();
+void performMethodLabelWorklist();
+void freeLabelMap();
+void performSharedWorklist();
+void performChainingWorklist();
+void freeNCGWorklist();
+void freeDataWorklist();
+void freeLabelWorklist();
+void freeChainingWorklist();
+
+int common_invokeArgsDone(ArgsDoneType form, bool isJitFull);
+int common_backwardBranch();
+int common_exceptionThrown();
+int common_errNullObject();
+int common_errArrayIndex();
+int common_errArrayStore();
+int common_errNegArraySize();
+int common_errNoSuchMethod();
+int common_errDivideByZero();
+int common_periodicChecks_entry();
+int common_periodicChecks4();
+int common_gotoBail();
+int common_gotoBail_0();
+int common_StringIndexOutOfBounds();
+void goto_invokeArgsDone();
+
+//lower a bytecode
+int lowerByteCode(const Method* method);
+
+int op_nop();
+int op_move();
+int op_move_from16();
+int op_move_16();
+int op_move_wide();
+int op_move_wide_from16();
+int op_move_wide_16();
+int op_move_result();
+int op_move_result_wide();
+int op_move_exception();
+
+int op_return_void();
+int op_return();
+int op_return_wide();
+int op_const_4();
+int op_const_16();
+int op_const();
+int op_const_high16();
+int op_const_wide_16();
+int op_const_wide_32();
+int op_const_wide();
+int op_const_wide_high16();
+int op_const_string();
+int op_const_string_jumbo();
+int op_const_class();
+int op_monitor_enter();
+int op_monitor_exit();
+int op_check_cast();
+int op_instance_of();
+
+int op_array_length();
+int op_new_instance();
+int op_new_array();
+int op_filled_new_array();
+int op_filled_new_array_range();
+int op_fill_array_data();
+int op_throw();
+int op_throw_verification_error();
+int op_goto();
+int op_goto_16();
+int op_goto_32();
+int op_packed_switch();
+int op_sparse_switch();
+int op_if_ge();
+int op_aget();
+int op_aget_wide();
+int op_aget_object();
+int op_aget_boolean();
+int op_aget_byte();
+int op_aget_char();
+int op_aget_short();
+int op_aput();
+int op_aput_wide();
+int op_aput_object();
+int op_aput_boolean();
+int op_aput_byte();
+int op_aput_char();
+int op_aput_short();
+int op_iget();
+int op_iget_wide(bool isVolatile);
+int op_iget_object();
+int op_iget_boolean();
+int op_iget_byte();
+int op_iget_char();
+int op_iget_short();
+int op_iput();
+int op_iput_wide(bool isVolatile);
+int op_iput_object();
+int op_iput_boolean();
+int op_iput_byte();
+int op_iput_char();
+int op_iput_short();
+int op_sget();
+int op_sget_wide(bool isVolatile);
+int op_sget_object();
+int op_sget_boolean();
+int op_sget_byte();
+int op_sget_char();
+int op_sget_short();
+int op_sput(bool isObj);
+int op_sput_wide(bool isVolatile);
+int op_sput_object();
+int op_sput_boolean();
+int op_sput_byte();
+int op_sput_char();
+int op_sput_short();
+int op_invoke_virtual();
+int op_invoke_super();
+int op_invoke_direct();
+int op_invoke_static();
+int op_invoke_interface();
+int op_invoke_virtual_range();
+int op_invoke_super_range();
+int op_invoke_direct_range();
+int op_invoke_static_range();
+int op_invoke_interface_range();
+int op_int_to_long();
+int op_add_long_2addr();
+int op_add_int_lit8();
+int op_cmpl_float();
+int op_cmpg_float();
+int op_cmpl_double();
+int op_cmpg_double();
+int op_cmp_long();
+int op_if_eq();
+int op_if_ne();
+int op_if_lt();
+int op_if_gt();
+int op_if_le();
+int op_if_eqz();
+int op_if_nez();
+int op_if_ltz();
+int op_if_gez();
+int op_if_gtz();
+int op_if_lez();
+int op_neg_int();
+int op_not_int();
+int op_neg_long();
+int op_not_long();
+int op_neg_float();
+int op_neg_double();
+int op_int_to_float();
+int op_int_to_double();
+int op_long_to_int();
+int op_long_to_float();
+int op_long_to_double();
+int op_float_to_int();
+int op_float_to_long();
+int op_float_to_double();
+int op_double_to_int();
+int op_double_to_long();
+int op_double_to_float();
+int op_int_to_byte();
+int op_int_to_char();
+int op_int_to_short();
+int op_add_int();
+int op_sub_int();
+int op_mul_int();
+int op_div_int();
+int op_rem_int();
+int op_and_int();
+int op_or_int();
+int op_xor_int();
+int op_shl_int();
+int op_shr_int();
+int op_ushr_int();
+int op_add_long();
+int op_sub_long();
+int op_mul_long();
+int op_div_long();
+int op_rem_long();
+int op_and_long();
+int op_or_long();
+int op_xor_long();
+int op_shl_long();
+int op_shr_long();
+int op_ushr_long();
+int op_add_float();
+int op_sub_float();
+int op_mul_float();
+int op_div_float();
+int op_rem_float();
+int op_add_double();
+int op_sub_double();
+int op_mul_double();
+int op_div_double();
+int op_rem_double();
+int op_add_int_2addr();
+int op_sub_int_2addr();
+int op_mul_int_2addr();
+int op_div_int_2addr();
+int op_rem_int_2addr();
+int op_and_int_2addr();
+int op_or_int_2addr();
+int op_xor_int_2addr();
+int op_shl_int_2addr();
+int op_shr_int_2addr();
+int op_ushr_int_2addr();
+int op_sub_long_2addr();
+int op_mul_long_2addr();
+int op_div_long_2addr();
+int op_rem_long_2addr();
+int op_and_long_2addr();
+int op_or_long_2addr();
+int op_xor_long_2addr();
+int op_shl_long_2addr();
+int op_shr_long_2addr();
+int op_ushr_long_2addr();
+int op_add_float_2addr();
+int op_sub_float_2addr();
+int op_mul_float_2addr();
+int op_div_float_2addr();
+int op_rem_float_2addr();
+int op_add_double_2addr();
+int op_sub_double_2addr();
+int op_mul_double_2addr();
+int op_div_double_2addr();
+int op_rem_double_2addr();
+int op_add_int_lit16();
+int op_rsub_int();
+int op_mul_int_lit16();
+int op_div_int_lit16();
+int op_rem_int_lit16();
+int op_and_int_lit16();
+int op_or_int_lit16();
+int op_xor_int_lit16();
+int op_rsub_int_lit8();
+int op_mul_int_lit8();
+int op_div_int_lit8();
+int op_rem_int_lit8();
+int op_and_int_lit8();
+int op_or_int_lit8();
+int op_xor_int_lit8();
+int op_shl_int_lit8();
+int op_shr_int_lit8();
+int op_ushr_int_lit8();
+int op_execute_inline(bool isRange);
+int op_invoke_object_init_range();
+int op_iget_quick();
+int op_iget_wide_quick();
+int op_iget_object_quick();
+int op_iput_quick();
+int op_iput_wide_quick();
+int op_iput_object_quick();
+int op_invoke_virtual_quick();
+int op_invoke_virtual_quick_range();
+int op_invoke_super_quick();
+int op_invoke_super_quick_range();
+
+///////////////////////////////////////////////
+void set_reg_opnd(LowOpndReg* op_reg, int reg, bool isPhysical, LowOpndRegType type);
+void set_mem_opnd(LowOpndMem* mem, int disp, int base, bool isPhysical);
+void set_mem_opnd_scale(LowOpndMem* mem, int base, bool isPhysical, int disp, int index, bool indexPhysical, int scale);
+LowOpImm* dump_imm(Mnemonic m, OpndSize size,
+               int imm);
+LowOpNCG* dump_ncg(Mnemonic m, OpndSize size, int imm);
+LowOpImm* dump_imm_with_codeaddr(Mnemonic m, OpndSize size,
+               int imm, char* codePtr);
+LowOpImm* dump_special(AtomOpCode cc, int imm);
+LowOpMem* dump_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical);
+LowOpReg* dump_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type);
+LowOpReg* dump_reg_noalloc(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type);
+LowOpMemImm* dump_imm_mem_noalloc(Mnemonic m, OpndSize size,
+                           int imm,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex);
+LowOpRegReg* dump_reg_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type);
+LowOpRegReg* dump_movez_reg_reg(Mnemonic m, OpndSize size,
+                        int reg, bool isPhysical,
+                        int reg2, bool isPhysical2);
+LowOpRegMem* dump_mem_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex,
+                   int reg, bool isPhysical, LowOpndRegType type);
+LowOpRegMem* dump_mem_reg_noalloc(Mnemonic m, OpndSize size,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex,
+                           int reg, bool isPhysical, LowOpndRegType type);
+LowOpRegMem* dump_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type);
+LowOpMemReg* dump_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type);
+LowOpMemReg* dump_reg_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, LowOpndRegType type);
+LowOpMemReg* dump_reg_mem_noalloc(Mnemonic m, OpndSize size,
+                           int reg, bool isPhysical,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex, LowOpndRegType type);
+LowOpRegImm* dump_imm_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, bool chaining);
+LowOpMemImm* dump_imm_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, bool chaining);
+LowOpMemReg* dump_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex);
+LowOpRegMem* dump_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex,
+                  int reg);
+LowOpLabel* dump_label(Mnemonic m, OpndSize size, int imm,
+               const char* label, bool isLocal);
+
+unsigned getJmpCallInstSize(OpndSize size, JmpCall_type type);
+bool lowerByteCodeJit(const Method* method, const u2* codePtr, MIR* mir);
+void startOfBasicBlock(struct BasicBlock* bb);
+extern LowOpBlockLabel* traceLabelList;
+extern struct BasicBlock* traceCurrentBB;
+extern struct MIR* traceCurrentMIR;
+void startOfTrace(const Method* method, LowOpBlockLabel* labelList, int, CompilationUnit*);
+void endOfTrace(bool freeOnly);
+LowOp* jumpToBasicBlock(char* instAddr, int targetId);
+LowOp* condJumpToBasicBlock(char* instAddr, ConditionCode cc, int targetId);
+bool jumpToException(const char* target);
+int codeGenBasicBlockJit(const Method* method, BasicBlock* bb);
+void endOfBasicBlock(struct BasicBlock* bb);
+void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir);
+int insertChainingWorklist(int bbId, char * codeStart);
+void startOfTraceO1(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId, CompilationUnit *cUnit);
+void endOfTraceO1();
+int isPowerOfTwo(int imm);
+void move_chain_to_mem(OpndSize size, int imm,
+                        int disp, int base_reg, bool isBasePhysical);
+void move_chain_to_reg(OpndSize size, int imm, int reg, bool isPhysical);
+
+void dumpImmToMem(int vrNum, OpndSize size, int value);
+bool isInMemory(int regNum, OpndSize size);
+int touchEbx();
+int boundCheck(int vr_array, int reg_array, bool isPhysical_array,
+               int vr_index, int reg_index, bool isPhysical_index,
+               int exceptionNum);
+int getRelativeOffset(const char* target, bool isShortTerm, JmpCall_type type, bool* unknown,
+                      OpndSize* immSize);
+int getRelativeNCG(s4 tmp, JmpCall_type type, bool* unknown, OpndSize* size);
+void freeAtomMem();
+OpndSize estOpndSizeFromImm(int target);
+
+void preprocessingBB(BasicBlock* bb);
+void preprocessingTrace();
+void dump_nop(int size);
+#endif
diff --git a/vm/compiler/codegen/x86/LowerAlu.cpp b/vm/compiler/codegen/x86/LowerAlu.cpp
new file mode 100644
index 0000000..2231bac
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerAlu.cpp
@@ -0,0 +1,1962 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerAlu.cpp
+    \brief This file lowers ALU bytecodes.
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+/////////////////////////////////////////////
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode NEG_INT
+
+//!
+int op_neg_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_unary_reg(OpndSize_32, neg_opc, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode NOT_INT
+
+//!
+int op_not_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_unary_reg(OpndSize_32, not_opc, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode NEG_LONG
+
+//! This implementation uses XMM registers
+int op_neg_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    alu_binary_reg_reg(OpndSize_64, xor_opc, 2, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, sub_opc, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_64, 2, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode NOT_LONG
+
+//! This implementation uses XMM registers
+int op_not_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    load_global_data_API("64bits", OpndSize_64, 2, false);
+    alu_binary_reg_reg(OpndSize_64, andn_opc, 2, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 1;
+    return 0;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode NEG_FLOAT
+
+//! This implementation uses GPR
+int op_neg_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, add_opc, 0x80000000, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+
+//! lower bytecode NEG_DOUBLE
+
+//! This implementation uses XMM registers
+int op_neg_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    load_global_data_API("doubNeg", OpndSize_64, 2, false);
+    alu_binary_reg_reg(OpndSize_64, xor_opc, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_64, 2, false);
+    rPC += 1;
+    return 0;
+}
+
+//! lower bytecode INT_TO_LONG
+
+//! It uses native instruction cdq
+int op_int_to_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, PhysicalReg_EAX, true);
+    convert_integer(OpndSize_32, OpndSize_64);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_FLOAT
+
+//! This implementation uses FP stack
+int op_int_to_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_32, vB); //fildl
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_DOUBLE
+
+//! This implementation uses FP stack
+int op_int_to_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_32, vB); //fildl
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode LONG_TO_FLOAT
+
+//! This implementation uses FP stack
+int op_long_to_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_64, vB); //fildll
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode LONG_TO_DOUBLE
+
+//! This implementation uses FP stack
+int op_long_to_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_64, vB); //fildll
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode FLOAT_TO_DOUBLE
+
+//! This implementation uses FP stack
+int op_float_to_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_fp_stack_VR(OpndSize_32, vB); //flds
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode DOUBLE_TO_FLOAT
+
+//! This implementation uses FP stack
+int op_double_to_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_fp_stack_VR(OpndSize_64, vB); //fldl
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    rPC += 1;
+    return 0;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode LONG_TO_INT
+
+//! This implementation uses GPR
+int op_long_to_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+
+//! common code to convert a float or double to integer
+
+//! It uses FP stack
+int common_fp_to_int(bool isDouble, u2 vA, u2 vB) {
+    if(isDouble) {
+        load_fp_stack_VR(OpndSize_64, vB); //fldl
+    }
+    else {
+        load_fp_stack_VR(OpndSize_32, vB); //flds
+    }
+
+    load_fp_stack_global_data_API("intMax", OpndSize_32);
+    load_fp_stack_global_data_API("intMin", OpndSize_32);
+
+    //ST(0) ST(1) ST(2) --> LintMin LintMax value
+    compare_fp_stack(true, 2, false/*isDouble*/); //ST(2)
+    //ST(0) ST(1) --> LintMax value
+    conditional_jump(Condition_AE, ".float_to_int_negInf", true);
+    rememberState(1);
+    compare_fp_stack(true, 1, false/*isDouble*/); //ST(1)
+    //ST(0) --> value
+    rememberState(2);
+    conditional_jump(Condition_C, ".float_to_int_nanInf", true);
+    //fnstcw, orw, fldcw, xorw
+    load_effective_addr(-2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fpu_cw(false/*checkException*/, 0, PhysicalReg_ESP, true);
+    alu_binary_imm_mem(OpndSize_16, or_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    alu_binary_imm_mem(OpndSize_16, xor_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    store_int_fp_stack_VR(true/*pop*/, OpndSize_32, vA); //fistpl
+    //fldcw
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    load_effective_addr(2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    rememberState(3);
+    unconditional_jump(".float_to_int_okay", true);
+    insertLabel(".float_to_int_nanInf", true);
+    conditional_jump(Condition_NP, ".float_to_int_posInf", true);
+    //fstps CHECK
+    goToState(2);
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    set_VR_to_imm(vA, OpndSize_32, 0);
+    transferToState(3);
+    unconditional_jump(".float_to_int_okay", true);
+    insertLabel(".float_to_int_posInf", true);
+    //fstps CHECK
+    goToState(2);
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    set_VR_to_imm(vA, OpndSize_32, 0x7fffffff);
+    transferToState(3);
+    unconditional_jump(".float_to_int_okay", true);
+    insertLabel(".float_to_int_negInf", true);
+    goToState(1);
+    //fstps CHECK
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    set_VR_to_imm(vA, OpndSize_32, 0x80000000);
+    transferToState(3);
+    insertLabel(".float_to_int_okay", true);
+    return 0;
+}
+//! lower bytecode FLOAT_TO_INT by calling common_fp_to_int
+
+//!
+int op_float_to_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_int(false, vA, vB);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode DOUBLE_TO_INT by calling common_fp_to_int
+
+//!
+int op_double_to_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_int(true, vA, vB);
+    rPC += 1;
+    return retval;
+}
+
+//! common code to convert float or double to long
+
+//! It uses FP stack
+int common_fp_to_long(bool isDouble, u2 vA, u2 vB) {
+    if(isDouble) {
+        load_fp_stack_VR(OpndSize_64, vB); //fldl
+    }
+    else {
+        load_fp_stack_VR(OpndSize_32, vB); //flds
+    }
+
+    load_fp_stack_global_data_API("valuePosInfLong", OpndSize_64);
+    load_fp_stack_global_data_API("valueNegInfLong", OpndSize_64);
+
+    //ST(0) ST(1) ST(2) --> LintMin LintMax value
+    compare_fp_stack(true, 2, false/*isDouble*/); //ST(2)
+    //ST(0) ST(1) --> LintMax value
+    conditional_jump(Condition_AE, ".float_to_long_negInf", true);
+    rememberState(1);
+    compare_fp_stack(true, 1, false/*isDouble*/); //ST(1)
+    rememberState(2);
+    //ST(0) --> value
+    conditional_jump(Condition_C, ".float_to_long_nanInf", true);
+    //fnstcw, orw, fldcw, xorw
+    load_effective_addr(-2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fpu_cw(false/*checkException*/, 0, PhysicalReg_ESP, true);
+    alu_binary_imm_mem(OpndSize_16, or_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    alu_binary_imm_mem(OpndSize_16, xor_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    store_int_fp_stack_VR(true/*pop*/, OpndSize_64, vA); //fistpll
+    //fldcw
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    load_effective_addr(2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    rememberState(3);
+    unconditional_jump(".float_to_long_okay", true);
+    insertLabel(".float_to_long_nanInf", true);
+    conditional_jump(Condition_NP, ".float_to_long_posInf", true);
+    //fstpl??
+    goToState(2);
+
+    load_global_data_API("valueNanLong", OpndSize_64, 1, false);
+
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    transferToState(3);
+    unconditional_jump(".float_to_long_okay", true);
+    insertLabel(".float_to_long_posInf", true);
+    //fstpl
+    goToState(2);
+
+    load_global_data_API("valuePosInfLong", OpndSize_64, 2, false);
+    set_virtual_reg(vA, OpndSize_64, 2, false);
+    transferToState(3);
+    unconditional_jump(".float_to_long_okay", true);
+    insertLabel(".float_to_long_negInf", true);
+    //fstpl
+    //fstpl
+    goToState(1);
+
+    load_global_data_API("valueNegInfLong", OpndSize_64, 3, false);
+    set_virtual_reg(vA, OpndSize_64, 3, false);
+    transferToState(3);
+    insertLabel(".float_to_long_okay", true);
+    return 0;
+}
+//! lower bytecode FLOAT_TO_LONG by calling common_fp_to_long
+
+//!
+int op_float_to_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_long(false, vA, vB);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode DOUBLE_TO_LONG by calling common_fp_to_long
+
+//!
+int op_double_to_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_long(true, vA, vB);
+    rPC += 1;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode INT_TO_BYTE
+
+//! It uses GPR
+int op_int_to_byte() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sal_opc, 24, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sar_opc, 24, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_CHAR
+
+//! It uses GPR
+int op_int_to_char() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sal_opc, 16, 1, false);
+    alu_binary_imm_reg(OpndSize_32, shr_opc, 16, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_SHORT
+
+//! It uses GPR
+int op_int_to_short() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sal_opc, 16, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sar_opc, 16, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! common code to handle integer ALU ops
+
+//! It uses GPR
+int common_alu_int(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) { //except div and rem
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    //in encoder, reg is first operand, which is the destination
+    //gpr_1 op v2(rFP) --> gpr_1
+    //shift only works with reg cl, v2 should be in %ecx
+    alu_binary_VR_reg(OpndSize_32, opc, v2, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    return 0;
+}
+#undef P_GPR_1
+#define P_GPR_1 PhysicalReg_EBX
+//! common code to handle integer shift ops
+
+//! It uses GPR
+int common_shift_int(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v2, OpndSize_32, PhysicalReg_ECX, true);
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    //in encoder, reg2 is first operand, which is the destination
+    //gpr_1 op v2(rFP) --> gpr_1
+    //shift only works with reg cl, v2 should be in %ecx
+    alu_binary_reg_reg(OpndSize_32, opc, PhysicalReg_ECX, true, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    return 0;
+}
+#undef p_GPR_1
+//! lower bytecode ADD_INT by calling common_alu_int
+
+//!
+int op_add_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_INT by calling common_alu_int
+
+//!
+int op_sub_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_INT by calling common_alu_int
+
+//!
+int op_mul_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(imul_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_INT by calling common_alu_int
+
+//!
+int op_and_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(and_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_INT by calling common_alu_int
+
+//!
+int op_or_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(or_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_INT by calling common_alu_int
+
+//!
+int op_xor_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(xor_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_INT by calling common_shift_int
+
+//!
+int op_shl_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_shift_int(shl_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_INT by calling common_shift_int
+
+//!
+int op_shr_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_shift_int(sar_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_INT by calling common_shift_int
+
+//!
+int op_ushr_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_shift_int(shr_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_INT_2ADDR by calling common_alu_int
+
+//!
+int op_add_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_INT_2ADDR by calling common_alu_int
+
+//!
+int op_sub_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode MUL_INT_2ADDR by calling common_alu_int
+
+//!
+int op_mul_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(imul_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode AND_INT_2ADDR by calling common_alu_int
+
+//!
+int op_and_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(and_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode OR_INT_2ADDR by calling common_alu_int
+
+//!
+int op_or_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(or_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode XOR_INT_2ADDR by calling common_alu_int
+
+//!
+int op_xor_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(xor_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SHL_INT_2ADDR by calling common_shift_int
+
+//!
+int op_shl_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_shift_int(shl_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SHR_INT_2ADDR by calling common_shift_int
+
+//!
+int op_shr_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_shift_int(sar_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode USHR_INT_2ADDR by calling common_shift_int
+
+//!
+int op_ushr_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_shift_int(shr_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//!common code to handle integer DIV & REM, it used GPR
+
+//!The special case: when op0 == minint && op1 == -1, return 0 for isRem, return 0x80000000 for isDiv
+//!There are two merge points in the control flow for this bytecode
+//!make sure the reg. alloc. state is the same at merge points by calling transferToState
+int common_div_rem_int(bool isRem, u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v1, OpndSize_32, PhysicalReg_EAX, true);
+    get_virtual_reg(v2, OpndSize_32, 2, false);
+    compare_imm_reg(OpndSize_32, 0, 2, false);
+    handlePotentialException(
+                                       Condition_E, Condition_NE,
+                                       1, "common_errDivideByZero");
+    /////////////////// handle special cases
+    //conditional move 0 to $edx for rem for the two special cases
+    //conditional move 0x80000000 to $eax for div
+    //handle -1 special case divide error
+    compare_imm_reg(OpndSize_32, -1, 2, false);
+    conditional_jump(Condition_NE, ".common_div_rem_int_normal", true);
+    //handle min int special case divide error
+    rememberState(1);
+    compare_imm_reg(OpndSize_32, 0x80000000, PhysicalReg_EAX, true);
+    transferToState(1);
+    conditional_jump(Condition_E, ".common_div_rem_int_special", true);
+
+    insertLabel(".common_div_rem_int_normal", true); //merge point
+    convert_integer(OpndSize_32, OpndSize_64); //cdq
+    //idiv: dividend in edx:eax; quotient in eax; remainder in edx
+    alu_unary_reg(OpndSize_32, idiv_opc, 2, false);
+    if(isRem)
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EDX, true);
+    else //divide: quotient in %eax
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    rememberState(2);
+    unconditional_jump(".common_div_rem_int_okay", true);
+
+    insertLabel(".common_div_rem_int_special", true);
+    goToState(1);
+    if(isRem)
+        set_VR_to_imm(vA, OpndSize_32, 0);
+    else
+        set_VR_to_imm(vA, OpndSize_32, 0x80000000);
+    transferToState(2);
+    insertLabel(".common_div_rem_int_okay", true); //merge point 2
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode DIV_INT by calling common_div_rem_int
+
+//!
+int op_div_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_int(false, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_INT by calling common_div_rem_int
+
+//!
+int op_rem_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_int(true, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_INT_2ADDR by calling common_div_rem_int
+
+//!
+int op_div_int_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_int(false, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode REM_INT_2ADDR by calling common_div_rem_int
+
+//!
+int op_rem_int_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_int(true, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+//! common code to handle integer ALU ops with literal
+
+//! It uses GPR
+int common_alu_int_lit(ALU_Opcode opc, u2 vA, u2 vB, s2 imm) { //except div and rem
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, opc, imm, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    return 0;
+}
+//! calls common_alu_int_lit
+int common_shift_int_lit(ALU_Opcode opc, u2 vA, u2 vB, s2 imm) {
+    return common_alu_int_lit(opc, vA, vB, imm);
+}
+#undef p_GPR_1
+//! lower bytecode ADD_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_add_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(add_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+
+int alu_rsub_int(ALU_Opcode opc, u2 vA, s2 imm, u2 vB) {
+    move_imm_to_reg(OpndSize_32, imm, 2, false);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_reg_reg(OpndSize_32, opc, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 2, false);
+    return 0;
+}
+
+
+//! lower bytecode RSUB_INT by calling common_alu_int_lit
+
+//!
+int op_rsub_int() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = alu_rsub_int(sub_opc, vA, tmp, vB);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_mul_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(imul_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_and_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(and_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_or_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(or_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_xor_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(xor_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_INT_LIT16 by calling common_shift_int_lit
+
+//!
+int op_shl_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_shift_int_lit(shl_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_INT_LIT16 by calling common_shift_int_lit
+
+//!
+int op_shr_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_shift_int_lit(sar_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_INT_LIT16 by calling common_shift_int_lit
+
+//!
+int op_ushr_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_shift_int_lit(shr_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_add_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(add_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode RSUB_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_rsub_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = alu_rsub_int(sub_opc, vA, tmp, vB);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_mul_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(imul_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_and_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(and_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_or_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(or_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_xor_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(xor_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_INT_LIT8 by calling common_shift_int_lit
+
+//!
+int op_shl_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_shift_int_lit(shl_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_INT_LIT8 by calling common_shift_int_lit
+
+//!
+int op_shr_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_shift_int_lit(sar_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_INT_LIT8 by calling common_shift_int_lit
+
+//!
+int op_ushr_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_shift_int_lit(shr_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+
+int isPowerOfTwo(int imm) {
+    int i;
+    for(i = 1; i < 17; i++) {
+        if(imm == (1 << i)) return i;
+    }
+    return -1;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+int div_lit_strength_reduction(u2 vA, u2 vB, s2 imm) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //strength reduction for div by 2,4,8,...
+        int power = isPowerOfTwo(imm);
+        if(power < 1) return 0;
+        //tmp2 is not updated, so it can share with vB
+        get_virtual_reg(vB, OpndSize_32, 2, false);
+        //if imm is 2, power will be 1
+        if(power == 1) {
+            /* mov tmp1, tmp2
+               shrl $31, tmp1
+               addl tmp2, tmp1
+               sarl $1, tmp1 */
+            move_reg_to_reg(OpndSize_32, 2, false, 1, false);
+            alu_binary_imm_reg(OpndSize_32, shr_opc, 31, 1, false);
+            alu_binary_reg_reg(OpndSize_32, add_opc, 2, false, 1, false);
+            alu_binary_imm_reg(OpndSize_32, sar_opc, 1, 1, false);
+            set_virtual_reg(vA, OpndSize_32, 1, false);
+            return 1;
+        }
+        //power > 1
+        /* mov tmp1, tmp2
+           sarl $power-1, tmp1
+           shrl 32-$power, tmp1
+           addl tmp2, tmp1
+           sarl $power, tmp1 */
+        move_reg_to_reg(OpndSize_32, 2, false, 1, false);
+        alu_binary_imm_reg(OpndSize_32, sar_opc, power-1, 1, false);
+        alu_binary_imm_reg(OpndSize_32, shr_opc, 32-power, 1, false);
+        alu_binary_reg_reg(OpndSize_32, add_opc, 2, false, 1, false);
+        alu_binary_imm_reg(OpndSize_32, sar_opc, power, 1, false);
+        set_virtual_reg(vA, OpndSize_32, 1, false);
+        return 1;
+    }
+    return 0;
+}
+
+////////// throws exception!!!
+//! common code to handle integer DIV & REM with literal
+
+//! It uses GPR
+int common_div_rem_int_lit(bool isRem, u2 vA, u2 vB, s2 imm) {
+    if(!isRem) {
+        int retCode = div_lit_strength_reduction(vA, vB, imm);
+        if(retCode > 0) return 0;
+    }
+    if(imm == 0) {
+        export_pc(); //use %edx
+#ifdef DEBUG_EXCEPTION
+        LOGI("EXTRA code to handle exception");
+#endif
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+        unconditional_jump_global_API(
+                          "common_errDivideByZero", false);
+
+        return 0;
+    }
+    get_virtual_reg(vB, OpndSize_32, PhysicalReg_EAX, true);
+    //check against -1 for DIV_INT??
+    if(imm == -1) {
+        compare_imm_reg(OpndSize_32, 0x80000000, PhysicalReg_EAX, true);
+        conditional_jump(Condition_E, ".div_rem_int_lit_special", true);
+        rememberState(1);
+    }
+    move_imm_to_reg(OpndSize_32, imm, 2, false);
+    convert_integer(OpndSize_32, OpndSize_64); //cdq
+    //idiv: dividend in edx:eax; quotient in eax; remainder in edx
+    alu_unary_reg(OpndSize_32, idiv_opc, 2, false);
+    if(isRem)
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EDX, true);
+    else
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+
+    if(imm == -1) {
+        unconditional_jump(".div_rem_int_lit_okay", true);
+        rememberState(2);
+        insertLabel(".div_rem_int_lit_special", true);
+        goToState(1);
+        if(isRem)
+            set_VR_to_imm(vA, OpndSize_32, 0);
+        else
+            set_VR_to_imm(vA, OpndSize_32, 0x80000000);
+        transferToState(2);
+    }
+
+    insertLabel(".div_rem_int_lit_okay", true); //merge point 2
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode DIV_INT_LIT16 by calling common_div_rem_int_lit
+
+//!
+int op_div_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_div_rem_int_lit(false, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_INT_LIT16 by calling common_div_rem_int_lit
+
+//!
+int op_rem_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_div_rem_int_lit(true, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_INT_LIT8 by calling common_div_rem_int_lit
+
+//!
+int op_div_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_div_rem_int_lit(false, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_INT_LIT8 by calling common_div_rem_int_lit
+
+//!
+int op_rem_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_div_rem_int_lit(true, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! common code to hanle long ALU ops
+
+//! It uses XMM
+int common_alu_long(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) { //except div and rem
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    get_virtual_reg(v2, OpndSize_64, 2, false);
+    alu_binary_reg_reg(OpndSize_64, opc, 2, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    return 0;
+}
+//! lower bytecode ADD_LONG by calling common_alu_long
+
+//!
+int op_add_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_LONG by calling common_alu_long
+
+//!
+int op_sub_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_LONG by calling common_alu_long
+
+//!
+int op_and_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(and_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_LONG by calling common_alu_long
+
+//!
+int op_or_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(or_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_LONG by calling common_alu_long
+
+//!
+int op_xor_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(xor_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_add_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_sub_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode AND_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_and_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(and_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode OR_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_or_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(or_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode XOR_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_xor_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(xor_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+//signed vs unsigned imul and mul?
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! common code to handle multiplication of long
+
+//! It uses GPR
+int common_mul_long(u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v2, OpndSize_32, 1, false);
+    move_reg_to_reg(OpndSize_32, 1, false, PhysicalReg_EAX, true);
+    //imul: 2L * 1H update temporary 1
+    alu_binary_VR_reg(OpndSize_32, imul_opc, (v1+1), 1, false);
+    get_virtual_reg(v1, OpndSize_32, 3, false);
+    move_reg_to_reg(OpndSize_32, 3, false, 2, false);
+    //imul: 1L * 2H
+    alu_binary_VR_reg(OpndSize_32, imul_opc, (v2+1), 2, false);
+    alu_binary_reg_reg(OpndSize_32, add_opc, 2, false, 1, false);
+    alu_unary_reg(OpndSize_32, mul_opc, 3, false);
+    alu_binary_reg_reg(OpndSize_32, add_opc, PhysicalReg_EDX, true, 1, false);
+    set_virtual_reg(vA+1, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+//! lower bytecode MUL_LONG by calling common_mul_long
+
+//!
+int op_mul_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_mul_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_LONG_2ADDR by calling common_mul_long
+
+//!
+int op_mul_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_mul_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! common code to handle DIV & REM of long
+
+//! It uses GPR & XMM; and calls call_moddi3 & call_divdi3
+int common_div_rem_long(bool isRem, u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v2, OpndSize_32, 1, false);
+    get_virtual_reg(v2+1, OpndSize_32, 2, false);
+    //save to native stack before changing register P_GPR_1
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 8, PhysicalReg_ESP, true);
+    alu_binary_reg_reg(OpndSize_32, or_opc, 2, false, 1, false);
+
+    handlePotentialException(
+                                       Condition_E, Condition_NE,
+                                       1, "common_errDivideByZero");
+    move_reg_to_mem(OpndSize_32, 2, false, 12, PhysicalReg_ESP, true);
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    move_reg_to_mem(OpndSize_64, 1, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 refs
+    if(isRem)
+        call_moddi3();
+    else
+        call_divdi3();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    set_virtual_reg(vA+1, OpndSize_32,PhysicalReg_EDX, true);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+//! lower bytecode DIV_LONG by calling common_div_rem_long
+
+//!
+int op_div_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_long(false, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_LONG by calling common_div_rem_long
+
+//!
+int op_rem_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_long(true, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_LONG_2ADDR by calling common_div_rem_long
+
+//!
+int op_div_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_long(false, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode REM_LONG_2ADDR by calling common_div_rem_long
+
+//!
+int op_rem_long_2addr() { //call __moddi3 instead of __divdi3
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_long(true, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+//! common code to handle SHL long
+
+//! It uses XMM
+int common_shl_long(u2 vA, u2 v1, u2 v2) {
+    get_VR_ss(v2, 2, false);
+
+    load_global_data_API("shiftMask", OpndSize_64, 3, false);
+
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    alu_binary_reg_reg(OpndSize_64, and_opc, 3, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, sll_opc, 2, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    return 0;
+}
+
+//! common code to handle SHR long
+
+//! It uses XMM
+int common_shr_long(u2 vA, u2 v1, u2 v2) {
+    get_VR_ss(v2, 2, false);
+
+    load_global_data_API("shiftMask", OpndSize_64, 3, false);
+
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    alu_binary_reg_reg(OpndSize_64, and_opc, 3, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, srl_opc, 2, false, 1, false);
+    compare_imm_VR(OpndSize_32, 0, (v1+1));
+    conditional_jump(Condition_GE, ".common_shr_long_special", true);
+    rememberState(1);
+
+    load_global_data_API("value64", OpndSize_64, 4, false);
+
+    alu_binary_reg_reg(OpndSize_64, sub_opc, 2, false, 4, false);
+
+    load_global_data_API("64bits", OpndSize_64, 5, false);
+
+    alu_binary_reg_reg(OpndSize_64, sll_opc, 4, false, 5, false);
+    alu_binary_reg_reg(OpndSize_64, or_opc, 5, false, 1, false);
+    rememberState(2);
+    //check whether the target is next instruction TODO
+    unconditional_jump(".common_shr_long_done", true);
+
+    insertLabel(".common_shr_long_special", true);
+    goToState(1);
+    transferToState(2);
+    insertLabel(".common_shr_long_done", true);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    return 0;
+}
+
+//! common code to handle USHR long
+
+//! It uses XMM
+int common_ushr_long(u2 vA, u2 v1, u2 v2) {
+    get_VR_sd(v1, 1, false);
+    get_VR_ss(v2, 2, false);
+
+    load_sd_global_data_API("shiftMask", 3, false);
+
+    alu_binary_reg_reg(OpndSize_64, and_opc, 3, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, srl_opc, 2, false, 1, false);
+    set_VR_sd(vA, 1, false);
+    return 0;
+}
+//! lower bytecode SHL_LONG by calling common_shl_long
+
+//!
+int op_shl_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_shl_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_LONG_2ADDR by calling common_shl_long
+
+//!
+int op_shl_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_shl_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SHR_LONG by calling common_shr_long
+
+//!
+int op_shr_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_shr_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_LONG_2ADDR by calling common_shr_long
+
+//!
+int op_shr_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_shr_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode USHR_LONG by calling common_ushr_long
+
+//!
+int op_ushr_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_ushr_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_LONG_2ADDR by calling common_ushr_long
+
+//!
+int op_ushr_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_ushr_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+#define USE_MEM_OPERAND
+///////////////////////////////////////////
+//! common code to handle ALU of floats
+
+//! It uses XMM
+int common_alu_float(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) {//add, sub, mul
+    get_VR_ss(v1, 1, false);
+#ifdef USE_MEM_OPERAND
+    alu_sd_binary_VR_reg(opc, v2, 1, false, false/*isSD*/);
+#else
+    get_VR_ss(v2, 2, false);
+    alu_ss_binary_reg_reg(opc, 2, false, 1, false);
+#endif
+    set_VR_ss(vA, 1, false);
+    return 0;
+}
+//! lower bytecode ADD_FLOAT by calling common_alu_float
+
+//!
+int op_add_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_FLOAT by calling common_alu_float
+
+//!
+int op_sub_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_FLOAT by calling common_alu_float
+
+//!
+int op_mul_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(mul_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_FLOAT_2ADDR by calling common_alu_float
+
+//!
+int op_add_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_FLOAT_2ADDR by calling common_alu_float
+
+//!
+int op_sub_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode MUL_FLOAT_2ADDR by calling common_alu_float
+
+//!
+int op_mul_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(mul_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle DIV of float
+
+//! It uses FP stack
+int common_div_float(u2 vA, u2 v1, u2 v2) {
+    load_fp_stack_VR(OpndSize_32, v1); //flds
+    fpu_VR(div_opc, OpndSize_32, v2);
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    return 0;
+}
+//! lower bytecode DIV_FLOAT by calling common_div_float
+
+//!
+int op_div_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(div_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_FLOAT_2ADDR by calling common_div_float
+
+//!
+int op_div_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(div_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle DIV of double
+
+//! It uses XMM
+int common_alu_double(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) {//add, sub, mul
+    get_VR_sd(v1, 1, false);
+#ifdef USE_MEM_OPERAND
+    alu_sd_binary_VR_reg(opc, v2, 1, false, true /*isSD*/);
+#else
+    get_VR_sd(v2, 2, false);
+    alu_sd_binary_reg_reg(opc, 2, false, 1, false);
+#endif
+    set_VR_sd(vA, 1, false);
+    return 0;
+}
+//! lower bytecode ADD_DOUBLE by calling common_alu_double
+
+//!
+int op_add_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_DOUBLE by calling common_alu_double
+
+//!
+int op_sub_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_DOUBLE by calling common_alu_double
+
+//!
+int op_mul_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(mul_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_DOUBLE_2ADDR by calling common_alu_double
+
+//!
+int op_add_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_DOUBLE_2ADDR by calling common_alu_double
+
+//!
+int op_sub_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode MUL_DOUBLE_2ADDR by calling common_alu_double
+
+//!
+int op_mul_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(mul_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle DIV of double
+
+//! It uses FP stack
+int common_div_double(u2 vA, u2 v1, u2 v2) {
+    load_fp_stack_VR(OpndSize_64, v1); //fldl
+    fpu_VR(div_opc, OpndSize_64, v2); //fdivl
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    return 0;
+}
+//! lower bytecode DIV_DOUBLE by calling common_div_double
+
+//!
+int op_div_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(div_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_DOUBLE_2ADDR by calling common_div_double
+
+//!
+int op_div_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(div_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! common code to handle REM of float
+
+//! It uses GPR & calls call_fmodf
+int common_rem_float(u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    get_virtual_reg(v2, OpndSize_32, 2, false);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 2, false, 4, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_fmodf(); //(float x, float y) return float
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+//! lower bytecode REM_FLOAT by calling common_rem_float
+
+//!
+int op_rem_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_rem_float(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_FLOAT_2ADDR by calling common_rem_float
+
+//!
+int op_rem_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_rem_float(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle REM of double
+
+//! It uses XMM & calls call_fmod
+int common_rem_double(u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    get_virtual_reg(v2, OpndSize_64, 2, false);
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_64, 1, false, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_64, 2, false, 8, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_fmod(); //(long double x, long double y) return double
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    return 0;
+}
+//! lower bytecode REM_DOUBLE by calling common_rem_double
+
+//!
+int op_rem_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_rem_double(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_DOUBLE_2ADDR by calling common_rem_double
+
+//!
+int op_rem_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_rem_double(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode CMPL_FLOAT
+
+//!
+int op_cmpl_float() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_ss(v1, 1, false); //xmm
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+    compare_VR_ss_reg(v2, 1, false);
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32,
+                                 0xffffffff, 4, false);
+    //ORDER of cmov matters !!! (Z,P,A)
+    //finalNaN: unordered 0xffffffff
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                             1, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                             3, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                             2, false, 4, false);
+    set_virtual_reg(vA, OpndSize_32, 4, false);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode CMPG_FLOAT
+
+//!
+int op_cmpg_float() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_ss(v1, 1, false);
+    compare_VR_ss_reg(v2, 1, false);
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                1, false, 3, false);
+    //finalNaN: unordered
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                2, false, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                2, false, 3, false);
+    set_virtual_reg(vA, OpndSize_32, 3, false);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode CMPL_DOUBLE
+
+//!
+int op_cmpl_double() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_sd(v1, 1, false);
+    compare_VR_sd_reg(v2, 1, false);
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                             1, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                             3, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                             2, false, 4, false);
+    set_virtual_reg(vA, OpndSize_32, 4, false);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode CMPG_DOUBLE
+
+//!
+int op_cmpg_double() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_sd(v1, 1, false);
+    compare_VR_sd_reg(v2, 1, false);
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32,
+                                 0xffffffff, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                             1, false, 3, false);
+    //finalNaN: unordered
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                             2, false, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                             2, false, 3, false);
+   set_virtual_reg(vA, OpndSize_32, 3, false);
+    rPC += 2;
+    return 0;
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define P_SCRATCH_2 PhysicalReg_EAX
+#define OPTION_OLD //for simpler cfg
+//! lower bytecode CMP_LONG
+
+//!
+int op_cmp_long() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_virtual_reg(v1+1, OpndSize_32, 2, false);
+#ifdef OPTION_OLD
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+    move_imm_to_reg(OpndSize_32, 1, 4, false);
+    move_imm_to_reg(OpndSize_32, 0, 5, false);
+#endif
+    compare_VR_reg(OpndSize_32,
+                                v2+1, 2, false);
+#ifndef OPTION_OLD
+    conditional_jump(Condition_L, ".cmp_long_less", true);
+    conditional_jump(Condition_G, ".cmp_long_greater", true);
+#else
+    conditional_jump(Condition_E, ".cmp_long_equal", true);
+    rememberState(1);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_L, //below vs less
+                                             3, false, 6, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_G, //above vs greater
+                                             4, false, 6, false);
+    set_virtual_reg(vA, OpndSize_32, 6, false);
+    rememberState(2);
+    unconditional_jump(".cmp_long_okay", true);
+    insertLabel(".cmp_long_equal", true);
+    goToState(1);
+#endif
+
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32,
+                                v2, 1, false);
+#ifdef OPTION_OLD
+    conditional_move_reg_to_reg(OpndSize_32, Condition_E,
+                                             5, false, 6, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_B, //below vs less
+                                             3, false, 6, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A, //above vs greater
+                                             4, false, 6, false);
+    set_virtual_reg(vA, OpndSize_32, 6, false);
+    transferToState(2);
+#else
+    conditional_jump(Condition_A, ".cmp_long_greater", true);
+    conditional_jump(Condition_NE, ".cmp_long_less", true);
+    set_VR_to_imm(vA, OpndSize_32, 0);
+    unconditional_jump(".cmp_long_okay", true);
+
+    insertLabel(".cmp_long_less", true);
+    set_VR_to_imm(vA, OpndSize_32, 0xffffffff);
+    unconditional_jump(".cmp_long_okay", true);
+
+    insertLabel(".cmp_long_greater", true);
+    set_VR_to_imm(vA, OpndSize_32, 1);
+#endif
+    insertLabel(".cmp_long_okay", true);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
diff --git a/vm/compiler/codegen/x86/LowerConst.cpp b/vm/compiler/codegen/x86/LowerConst.cpp
new file mode 100644
index 0000000..62e0ed1
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerConst.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerConst.cpp
+    \brief This file lowers the following bytecodes: CONST_XXX
+
+    Functions are called from the lowered native sequence:
+    1> const_string_resolve
+       INPUT: const pool index in %eax
+       OUTPUT: resolved string in %eax
+       The only register that is still live after this function is ebx
+    2> class_resolve
+       INPUT: const pool index in %eax
+       OUTPUT: resolved class in %eax
+       The only register that is still live after this function is ebx
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+
+//! LOWER bytecode CONST_STRING without usage of helper function
+
+//! It calls const_string_resolve (%ebx is live across the call)
+//! Since the register allocator does not handle control flow within the lowered native sequence,
+//!   we define an interface between the lowering module and register allocator:
+//!     rememberState, gotoState, transferToState
+//!   to make sure at the control flow merge point the state of registers is the same
+int const_string_common_nohelper(u4 tmp, u2 vA) {
+    /* for trace-based JIT, the string is already resolved since this code has been executed */
+    void *strPtr = (void*)
+              (currentMethod->clazz->pDvmDex->pResStrings[tmp]);
+    assert(strPtr != NULL);
+    set_VR_to_imm(vA, OpndSize_32, (int) strPtr );
+    return 0;
+}
+//! dispatcher to select either const_string_common_helper or const_string_common_nohelper
+
+//!
+int const_string_common(u4 tmp, u2 vA) {
+    return const_string_common_nohelper(tmp, vA);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+//! lower bytecode CONST_4
+
+//!
+int op_const_4() {
+    u2 vA = INST_A(inst);
+    s4 tmp = (s4) (INST_B(inst) << 28) >> 28;
+    set_VR_to_imm(vA, OpndSize_32, tmp);
+    rPC += 1;
+    return 1;
+}
+//! lower bytecode CONST_16
+
+//!
+int op_const_16() {
+    u2 BBBB = FETCH(1);
+    u2 vA = INST_AA(inst);
+    set_VR_to_imm(vA, OpndSize_32, (s2)BBBB);
+    rPC += 2;
+    return 1;
+}
+//! lower bytecode CONST
+
+//!
+int op_const() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp);
+    rPC += 3;
+    return 1;
+}
+//! lower bytecode CONST_HIGH16
+
+//!
+int op_const_high16() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp<<16); //??
+    rPC += 2;
+    return 1;
+}
+//! lower bytecode CONST_WIDE_16
+
+//!
+int op_const_wide_16() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    set_VR_to_imm(vA, OpndSize_32, (s2)tmp);
+    set_VR_to_imm(vA+1, OpndSize_32, (s2)tmp>>31);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode CONST_WIDE_32
+
+//!
+int op_const_wide_32() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp);
+    set_VR_to_imm(vA+1, OpndSize_32, (s4)tmp>>31);
+    rPC += 3;
+    return 2;
+}
+//! lower bytecode CONST_WIDE
+
+//!
+int op_const_wide() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u8)FETCH(2) << 16;
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp);
+    tmp = (u8)FETCH(3);
+    tmp |= (u8)FETCH(4) << 16;
+    set_VR_to_imm(vA+1, OpndSize_32, (s4)tmp);
+    rPC += 5;
+    return 2;
+}
+//! lower bytecode CONST_WIDE_HIGH16
+
+//!
+int op_const_wide_high16() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    set_VR_to_imm(vA, OpndSize_32, 0);
+    set_VR_to_imm(vA+1, OpndSize_32, (s4)tmp<<16);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode CONST_STRING
+
+//!
+int op_const_string() {
+    u2 vB = FETCH(1);
+    u2 vA = INST_AA(inst);
+    u4 tmp = vB;
+    int retval = const_string_common(tmp, vA);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode CONST_STRING_JUMBO
+
+//!
+int op_const_string_jumbo() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    int retval = const_string_common(tmp, vA);
+    rPC += 3;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode CONST_CLASS
+
+//! It calls class_resolve (%ebx is live across the call)
+//! Since the register allocator does not handle control flow within the lowered native sequence,
+//!   we define an interface between the lowering module and register allocator:
+//!     rememberState, gotoState, transferToState
+//!   to make sure at the control flow merge point the state of registers is the same
+int op_const_class() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    /* for trace-based JIT, the class is already resolved since this code has been executed */
+    void *classPtr = (void*)
+       (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    assert(classPtr != NULL);
+    set_VR_to_imm(vA, OpndSize_32, (int) classPtr );
+    rPC += 2;
+    return 0;
+}
+
+#undef P_GPR_1
+
diff --git a/vm/compiler/codegen/x86/LowerGetPut.cpp b/vm/compiler/codegen/x86/LowerGetPut.cpp
new file mode 100644
index 0000000..c87b174
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerGetPut.cpp
@@ -0,0 +1,933 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerGetPut.cpp
+    \brief This file lowers the following bytecodes: XGET|PUT_XXX
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_GPR_4 PhysicalReg_EDX
+//! LOWER bytecode AGET without usage of helper function
+
+//! It has null check and length check
+int aget_common_nohelper(int flag, u2 vA, u2 vref, u2 vindex) {
+    ////////////////////////////
+    // Request VR free delays before register allocation for the temporaries
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK))
+        requestVRFreeDelay(vref,VRDELAY_NULLCHECK);
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK);
+        requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK);
+    }
+
+    get_virtual_reg(vref, OpndSize_32, 1, false); //array
+    get_virtual_reg(vindex, OpndSize_32, 2, false); //index
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        //last argument is the exception number for this bytecode
+        nullCheck(1, false, 1, vref); //maybe optimized away, if not, call
+        cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+    }
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        boundCheck(vref, 1, false,
+                             vindex, 2, false,
+                             2);
+        cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK);
+        cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+        updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2
+    }
+
+    if(flag == AGET) {
+        move_mem_disp_scale_to_reg(OpndSize_32, 1, false, offArrayObject_contents, 2, false, 4, 4, false);
+    }
+    else if(flag == AGET_WIDE) {
+        move_mem_disp_scale_to_reg(OpndSize_64, 1, false, offArrayObject_contents, 2, false, 8, 1, false);
+    }
+    else if(flag == AGET_CHAR) {
+        movez_mem_disp_scale_to_reg(OpndSize_16, 1, false, offArrayObject_contents, 2, false, 2, 4, false);
+    }
+    else if(flag == AGET_SHORT) {
+        moves_mem_disp_scale_to_reg(OpndSize_16, 1, false, offArrayObject_contents, 2, false, 2, 4, false);
+    }
+    else if(flag == AGET_BOOLEAN) {
+        movez_mem_disp_scale_to_reg(OpndSize_8, 1, false, offArrayObject_contents, 2, false, 1, 4, false);
+    }
+    else if(flag == AGET_BYTE) {
+        moves_mem_disp_scale_to_reg(OpndSize_8, 1, false, offArrayObject_contents, 2, false, 1, 4, false);
+    }
+    if(flag == AGET_WIDE) {
+        set_virtual_reg(vA, OpndSize_64, 1, false);
+    }
+    else {
+        set_virtual_reg(vA, OpndSize_32, 4, false);
+    }
+    //////////////////////////////////
+    return 0;
+}
+//! wrapper to call either aget_common_helper or aget_common_nohelper
+
+//!
+int aget_common(int flag, u2 vA, u2 vref, u2 vindex) {
+    return aget_common_nohelper(flag, vA, vref, vindex);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_GPR_4
+//! lower bytecode AGET by calling aget_common
+
+//!
+int op_aget() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_WIDE by calling aget_common
+
+//!
+int op_aget_wide() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_WIDE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_OBJECT by calling aget_common
+
+//!
+int op_aget_object() {
+    return op_aget();
+}
+//! lower bytecode BOOLEAN by calling aget_common
+
+//!
+int op_aget_boolean() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_BOOLEAN, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_BYTE by calling aget_common
+
+//!
+int op_aget_byte() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_BYTE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_CHAR by calling aget_common
+
+//!
+int op_aget_char() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_CHAR, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_SHORT by calling aget_common
+
+//!
+int op_aget_short() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_SHORT, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_GPR_4 PhysicalReg_EDX
+//! LOWER bytecode APUT without usage of helper function
+
+//! It has null check and length check
+int aput_common_nohelper(int flag, u2 vA, u2 vref, u2 vindex) {
+    //////////////////////////////////////
+    // Request VR free delays before register allocation for the temporaries.
+    // No need to request delay for vA since it will be transferred to temporary
+    // after the null check and bound check.
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK))
+        requestVRFreeDelay(vref,VRDELAY_NULLCHECK);
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK);
+        requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK);
+    }
+
+    get_virtual_reg(vref, OpndSize_32, 1, false); //array
+    get_virtual_reg(vindex, OpndSize_32, 2, false); //index
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        //last argument is the exception number for this bytecode
+        nullCheck(1, false, 1, vref); //maybe optimized away, if not, call
+        cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+    }
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        boundCheck(vref, 1, false,
+                             vindex, 2, false,
+                             2);
+        cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK);
+        cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+        updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2
+    }
+
+    if(flag == APUT_WIDE) {
+        get_virtual_reg(vA, OpndSize_64, 1, false);
+    }
+    else {
+        get_virtual_reg(vA, OpndSize_32, 4, false);
+    }
+    if(flag == APUT)
+        move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4);
+    else if(flag == APUT_WIDE)
+        move_reg_to_mem_disp_scale(OpndSize_64, 1, false, 1, false, offArrayObject_contents, 2, false, 8);
+    else if(flag == APUT_CHAR || flag == APUT_SHORT)
+        move_reg_to_mem_disp_scale(OpndSize_16, 4, false, 1, false, offArrayObject_contents, 2, false, 2);
+    else if(flag == APUT_BOOLEAN || flag == APUT_BYTE)
+        move_reg_to_mem_disp_scale(OpndSize_8, 4, false, 1, false, offArrayObject_contents, 2, false, 1);
+    //////////////////////////////////
+    return 0;
+}
+//! wrapper to call either aput_common_helper or aput_common_nohelper
+
+//!
+int aput_common(int flag, u2 vA, u2 vref, u2 vindex) {
+    return aput_common_nohelper(flag, vA, vref, vindex);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_GPR_4
+//! lower bytecode APUT by calling aput_common
+
+//!
+int op_aput() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_WIDE by calling aput_common
+
+//!
+int op_aput_wide() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_WIDE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_BOOLEAN by calling aput_common
+
+//!
+int op_aput_boolean() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_BOOLEAN, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_BYTE by calling aput_common
+
+//!
+int op_aput_byte() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_BYTE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_CHAR by calling aput_common
+
+//!
+int op_aput_char() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_CHAR, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_SHORT by calling aput_common
+
+//!
+int op_aput_short() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_SHORT, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX //callee-saved valid after CanPutArray
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI //callee-saved
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define P_SCRATCH_2 PhysicalReg_EAX
+#define P_SCRATCH_3 PhysicalReg_EDX
+
+void markCard_notNull(int tgtAddrReg, int scratchReg, bool isPhysical);
+
+//! lower bytecode APUT_OBJECT
+
+//! Lower the bytecode using helper function ".aput_obj_helper" if helper switch is on
+int op_aput_object() { //type checking
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+
+    ///////////////////////////
+    // Request VR free delays before register allocation for the temporaries
+    // No need to request delay for vA since it will be transferred to temporary
+    // after the null check and bound check.
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK))
+        requestVRFreeDelay(vref,VRDELAY_NULLCHECK);
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK);
+        requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK);
+    }
+
+    get_virtual_reg(vref, OpndSize_32, 1, false); //array
+    export_pc(); //use %edx
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        compare_imm_reg(OpndSize_32, 0, 1, false);
+        conditional_jump_global_API(Condition_E, "common_errNullObject", false);
+        cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+    }
+
+    get_virtual_reg(vindex, OpndSize_32, 2, false); //index
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        compare_mem_reg(OpndSize_32, offArrayObject_length, 1, false, 2, false);
+        conditional_jump_global_API(Condition_NC, "common_errArrayIndex", false);
+        cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK);
+        cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+        updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2
+    }
+
+    get_virtual_reg(vA, OpndSize_32, 4, false);
+    compare_imm_reg(OpndSize_32, 0, 4, false);
+    conditional_jump(Condition_E, ".aput_object_skip_check", true);
+    rememberState(1);
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 4, false, 5, false);
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 6, false);
+    move_reg_to_mem(OpndSize_32, 6, false, 4, PhysicalReg_ESP, true);
+
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_dvmCanPutArrayElement(); //scratch??
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump_global_API(Condition_E, "common_errArrayStore", false);
+
+    //NOTE: "2, false" is live through function call
+    move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4);
+    markCard_notNull(1, 11, false);
+    rememberState(2);
+    ////TODO NCG O1 + code cache
+    unconditional_jump(".aput_object_after_check", true);
+
+    insertLabel(".aput_object_skip_check", true);
+    goToState(1);
+    //NOTE: "2, false" is live through function call
+    move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4);
+
+    transferToState(2);
+    insertLabel(".aput_object_after_check", true);
+    ///////////////////////////////
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+
+//////////////////////////////////////////
+#define P_GPR_1 PhysicalReg_ECX
+#define P_GPR_2 PhysicalReg_EBX //should be callee-saved to avoid overwritten by inst_field_resolve
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EDX
+
+/*
+   movl offThread_cardTable(self), scratchReg
+   compare_imm_reg 0, valReg (testl valReg, valReg)
+   je .markCard_skip
+   shrl $GC_CARD_SHIFT, tgtAddrReg
+   movb %, (scratchReg, tgtAddrReg)
+   NOTE: scratchReg can be accessed with the corresponding byte
+         tgtAddrReg will be updated
+   for O1, update the corresponding reference count
+*/
+void markCard(int valReg, int tgtAddrReg, bool targetPhysical, int scratchReg, bool isPhysical) {
+   get_self_pointer(PhysicalReg_SCRATCH_6, isScratchPhysical);
+   move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_6, isScratchPhysical, scratchReg, isPhysical);
+   compare_imm_reg(OpndSize_32, 0, valReg, isPhysical);
+   conditional_jump(Condition_E, ".markCard_skip", true);
+   alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, targetPhysical);
+   move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isPhysical, scratchReg, isPhysical, 0, tgtAddrReg, targetPhysical, 1);
+   insertLabel(".markCard_skip", true);
+}
+
+void markCard_notNull(int tgtAddrReg, int scratchReg, bool isPhysical) {
+   get_self_pointer(PhysicalReg_SCRATCH_2, isScratchPhysical);
+   move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_2, isScratchPhysical, scratchReg, isPhysical);
+   alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, isPhysical);
+   move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isPhysical, scratchReg, isPhysical, 0, tgtAddrReg, isPhysical, 1);
+}
+
+void markCard_filled(int tgtAddrReg, bool isTgtPhysical, int scratchReg, bool isScratchPhysical) {
+   get_self_pointer(PhysicalReg_SCRATCH_2, false/*isPhysical*/);
+   move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_2, isScratchPhysical, scratchReg, isScratchPhysical);
+   alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, isTgtPhysical);
+   move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isScratchPhysical, scratchReg, isScratchPhysical, 0, tgtAddrReg, isTgtPhysical, 1);
+}
+//! LOWER bytecode IGET,IPUT without usage of helper function
+
+//! It has null check and calls assembly function inst_field_resolve
+int iget_iput_common_nohelper(int tmp, int flag, u2 vA, u2 vB, int isObj, bool isVolatile) {
+#ifdef WITH_JIT_INLINING
+    const Method *method = (traceCurrentMIR->OptimizationFlags & MIR_CALLEE) ?
+        traceCurrentMIR->meta.calleeMethod : currentMethod;
+    InstField *pInstField = (InstField *)
+            method->clazz->pDvmDex->pResFields[tmp];
+#else
+    InstField *pInstField = (InstField *)
+            currentMethod->clazz->pDvmDex->pResFields[tmp];
+#endif
+    int fieldOffset;
+
+    assert(pInstField != NULL);
+    fieldOffset = pInstField->byteOffset;
+    move_imm_to_reg(OpndSize_32, fieldOffset, 8, false);
+    // Request VR delay before transfer to temporary. Only vB needs delay.
+    // vA will have non-zero reference count since transfer to temporary for
+    // it happens after null check, thus no delay is needed.
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK);
+    get_virtual_reg(vB, OpndSize_32, 7, false);
+    nullCheck(7, false, 2, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+    if(flag == IGET) {
+        move_mem_scale_to_reg(OpndSize_32, 7, false, 8, false, 1, 9, false);
+        set_virtual_reg(vA, OpndSize_32, 9, false);
+#ifdef DEBUG_IGET_OBJ
+        if(isObj > 0) {
+            pushAllRegs();
+            load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            move_reg_to_mem(OpndSize_32, 9, false, 12, PhysicalReg_ESP, true); //field
+            move_reg_to_mem(OpndSize_32, 7, false, 8, PhysicalReg_ESP, true); //object
+            move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true); //field
+            move_imm_to_mem(OpndSize_32, 0, 0, PhysicalReg_ESP, true); //iget
+            call_dvmDebugIgetIput();
+            load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            popAllRegs();
+        }
+#endif
+    } else if(flag == IGET_WIDE) {
+        if(isVolatile) {
+            /* call dvmQuasiAtomicRead64(addr) */
+            load_effective_addr(fieldOffset, 7, false, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            nextVersionOfHardReg(PhysicalReg_EAX, 2);
+            nextVersionOfHardReg(PhysicalReg_EDX, 2);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicRead64();
+            load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            //memory content in %edx, %eax
+            set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+            set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true);
+        } else {
+            move_mem_scale_to_reg(OpndSize_64, 7, false, 8, false, 1, 1, false); //access field
+            set_virtual_reg(vA, OpndSize_64, 1, false);
+        }
+    } else if(flag == IPUT) {
+        get_virtual_reg(vA, OpndSize_32, 9, false);
+        move_reg_to_mem_scale(OpndSize_32, 9, false, 7, false, 8, false, 1); //access field
+        if(isObj) {
+            markCard(9, 7, false, 11, false);
+        }
+    } else if(flag == IPUT_WIDE) {
+        get_virtual_reg(vA, OpndSize_64, 1, false);
+        if(isVolatile) {
+            /* call dvmQuasiAtomicSwap64(val, addr) */
+            load_effective_addr(fieldOffset, 7, false, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //2nd argument
+            move_reg_to_mem(OpndSize_64, 1, false, -12, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicSwap64();
+            load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+        }
+        else {
+            move_reg_to_mem_scale(OpndSize_64, 1, false, 7, false, 8, false, 1);
+        }
+    }
+    ///////////////////////////
+    return 0;
+}
+//! wrapper to call either iget_iput_common_helper or iget_iput_common_nohelper
+
+//!
+int iget_iput_common(int tmp, int flag, u2 vA, u2 vB, int isObj, bool isVolatile) {
+    return iget_iput_common_nohelper(tmp, flag, vA, vB, isObj, isVolatile);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+//! lower bytecode IGET by calling iget_iput_common
+
+//!
+int op_iget() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IGET, vA, vB, 0, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IGET_WIDE by calling iget_iput_common
+
+//!
+int op_iget_wide(bool isVolatile) {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IGET_WIDE, vA, vB, 0, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IGET_OBJECT by calling iget_iput_common
+
+//!
+int op_iget_object() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IGET, vA, vB, 1, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IGET_BOOLEAN by calling iget_iput_common
+
+//!
+int op_iget_boolean() {
+    return op_iget();
+}
+//! lower bytecode IGET_BYTE by calling iget_iput_common
+
+//!
+int op_iget_byte() {
+    return op_iget();
+}
+//! lower bytecode IGET_CHAR by calling iget_iput_common
+
+//!
+int op_iget_char() {
+    return op_iget();
+}
+//! lower bytecode IGET_SHORT by calling iget_iput_common
+
+//!
+int op_iget_short() {
+    return op_iget();
+}
+//! lower bytecode IPUT by calling iget_iput_common
+
+//!
+int op_iput() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IPUT, vA, vB, 0, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IPUT_WIDE by calling iget_iput_common
+
+//!
+int op_iput_wide(bool isVolatile) {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IPUT_WIDE, vA, vB, 0, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IPUT_OBJECT by calling iget_iput_common
+
+//!
+int op_iput_object() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IPUT, vA, vB, 1, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IPUT_BOOLEAN by calling iget_iput_common
+
+//!
+int op_iput_boolean() {
+    return op_iput();
+}
+//! lower bytecode IPUT_BYTE by calling iget_iput_common
+
+//!
+int op_iput_byte() {
+    return op_iput();
+}
+//! lower bytecode IPUT_CHAR by calling iget_iput_common
+
+//!
+int op_iput_char() {
+    return op_iput();
+}
+//! lower bytecode IPUT_SHORT by calling iget_iput_common
+
+//!
+int op_iput_short() {
+    return op_iput();
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_EDX //used by helper only
+
+//! common section to lower IGET & IPUT
+
+//! It will use helper function sget_helper if the switch is on
+int sget_sput_common(int flag, u2 vA, u2 tmp, bool isObj, bool isVolatile) {
+    //call assembly static_field_resolve
+    //no exception
+    //glue: get_res_fields
+    //hard-coded: eax (one version?)
+    //////////////////////////////////////////
+#ifdef WITH_JIT_INLINING
+    const Method *method = (traceCurrentMIR->OptimizationFlags & MIR_CALLEE) ? traceCurrentMIR->meta.calleeMethod : currentMethod;
+    void *fieldPtr = (void*)
+        (method->clazz->pDvmDex->pResFields[tmp]);
+#else
+    void *fieldPtr = (void*)
+        (currentMethod->clazz->pDvmDex->pResFields[tmp]);
+#endif
+    assert(fieldPtr != NULL);
+    move_imm_to_reg(OpndSize_32, (int)fieldPtr, PhysicalReg_EAX, true);
+    if(flag == SGET) {
+        move_mem_to_reg(OpndSize_32, offStaticField_value, PhysicalReg_EAX, true, 7, false); //access field
+        set_virtual_reg(vA, OpndSize_32, 7, false);
+    } else if(flag == SGET_WIDE) {
+        if(isVolatile) {
+            /* call dvmQuasiAtomicRead64(addr) */
+            load_effective_addr(offStaticField_value, PhysicalReg_EAX, true, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            nextVersionOfHardReg(PhysicalReg_EAX, 2);
+            nextVersionOfHardReg(PhysicalReg_EDX, 2);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicRead64();
+            load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            //memory content in %edx, %eax
+            set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+            set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true);
+        }
+        else {
+            move_mem_to_reg(OpndSize_64, offStaticField_value, PhysicalReg_EAX, true, 1, false); //access field
+            set_virtual_reg(vA, OpndSize_64, 1, false);
+        }
+    } else if(flag == SPUT) {
+        get_virtual_reg(vA, OpndSize_32, 7, false);
+        move_reg_to_mem(OpndSize_32, 7, false, offStaticField_value, PhysicalReg_EAX, true); //access field
+        if(isObj) {
+            /* get clazz object, then use clazz object to mark card */
+            move_mem_to_reg(OpndSize_32, offField_clazz, PhysicalReg_EAX, true, 12, false);
+            markCard(7/*valReg*/, 12, false, 11, false);
+        }
+    } else if(flag == SPUT_WIDE) {
+        get_virtual_reg(vA, OpndSize_64, 1, false);
+        if(isVolatile) {
+            /* call dvmQuasiAtomicSwap64(val, addr) */
+            load_effective_addr(offStaticField_value, PhysicalReg_EAX, true, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //2nd argument
+            move_reg_to_mem(OpndSize_64, 1, false, -12, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicSwap64();
+            load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+        }
+        else {
+            move_reg_to_mem(OpndSize_64, 1, false, offStaticField_value, PhysicalReg_EAX, true); //access field
+        }
+    }
+    //////////////////////////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+//! lower bytecode SGET by calling sget_sput_common
+
+//!
+int op_sget() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SGET, vA, tmp, false, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SGET_WIDE by calling sget_sput_common
+
+//!
+int op_sget_wide(bool isVolatile) {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SGET_WIDE, vA, tmp, false, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SGET_OBJECT by calling sget_sput_common
+
+//!
+int op_sget_object() {
+    return op_sget();
+}
+//! lower bytecode SGET_BOOLEAN by calling sget_sput_common
+
+//!
+int op_sget_boolean() {
+    return op_sget();
+}
+//! lower bytecode SGET_BYTE by calling sget_sput_common
+
+//!
+int op_sget_byte() {
+    return op_sget();
+}
+//! lower bytecode SGET_CHAR by calling sget_sput_common
+
+//!
+int op_sget_char() {
+    return op_sget();
+}
+//! lower bytecode SGET_SHORT by calling sget_sput_common
+
+//!
+int op_sget_short() {
+    return op_sget();
+}
+//! lower bytecode SPUT by calling sget_sput_common
+
+//!
+int op_sput(bool isObj) {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SPUT, vA, tmp, isObj, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SPUT_WIDE by calling sget_sput_common
+
+//!
+int op_sput_wide(bool isVolatile) {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SPUT_WIDE, vA, tmp, false, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SPUT_OBJECT by calling sget_sput_common
+
+//!
+int op_sput_object() {
+    return op_sput(true);
+}
+//! lower bytecode SPUT_OBJECT by calling sget_sput_common
+
+//!
+int op_sput_boolean() {
+    return op_sput(false);
+}
+//! lower bytecode SPUT_BOOLEAN by calling sget_sput_common
+
+//!
+int op_sput_byte() {
+    return op_sput(false);
+}
+//! lower bytecode SPUT_BYTE by calling sget_sput_common
+
+//!
+int op_sput_char() {
+    return op_sput(false);
+}
+//! lower bytecode SPUT_SHORT by calling sget_sput_common
+
+//!
+int op_sput_short() {
+    return op_sput(false);
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! lower bytecode IGET_QUICK
+
+//!
+int op_iget_quick() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1);
+
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    move_mem_to_reg(OpndSize_32, tmp, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 2, false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode IGET_WIDE_QUICK
+
+//!
+int op_iget_wide_quick() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1);
+
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    move_mem_to_reg(OpndSize_64, tmp, 1, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode IGET_OBJECT_QUICK
+
+//!
+int op_iget_object_quick() {
+    return op_iget_quick();
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! lower bytecode IPUT_QUICK
+
+//!
+int iput_quick_common(bool isObj) {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1);
+
+    // Request VR delay before transfer to temporary. Only vB needs delay.
+    // vA will have non-zero reference count since transfer to temporary for
+    // it happens after null check, thus no delay is needed.
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    get_virtual_reg(vA, OpndSize_32, 2, false);
+    move_reg_to_mem(OpndSize_32, 2, false, tmp, 1, false);
+    if(isObj) {
+        markCard(2/*valReg*/, 1, false, 11, false);
+    }
+    rPC += 2;
+    return 0;
+}
+int op_iput_quick() {
+    return iput_quick_common(false);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode IPUT_WIDE_QUICK
+
+//!
+int op_iput_wide_quick() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1); //byte offset
+
+    // Request VR delay before transfer to temporary. Only vB needs delay.
+    // vA will have non-zero reference count since transfer to temporary for
+    // it happens after null check, thus no delay is needed.
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    get_virtual_reg(vA, OpndSize_64, 1, false);
+    move_reg_to_mem(OpndSize_64, 1, false, tmp, 1, false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode IPUT_OBJECT_QUICK
+
+//!
+int op_iput_object_quick() {
+    return iput_quick_common(true);
+}
+
diff --git a/vm/compiler/codegen/x86/LowerHelper.cpp b/vm/compiler/codegen/x86/LowerHelper.cpp
new file mode 100644
index 0000000..3fec038
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerHelper.cpp
@@ -0,0 +1,2993 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerHelper.cpp
+    \brief This file implements helper functions for lowering
+
+With NCG O0: all registers are hard-coded ;
+With NCG O1: the lowering module will use variables that will be allocated to a physical register by the register allocator.
+
+register types: FS 32-bit or 64-bit;
+                XMM: SS(32-bit) SD (64-bit);
+                GPR: 8-bit, 16-bit, 32-bit;
+LowOpndRegType tells whether it is gpr, xmm or fs;
+OpndSize can be OpndSize_8, OpndSize_16, OpndSize_32, OpndSize_64
+
+A single native instruction can use multiple physical registers.
+  we can't call freeReg in the middle of emitting a native instruction,
+  since it may free the physical register used by an operand and cause two operands being allocated to the same physical register.
+
+When allocating a physical register for an operand, we can't spill the operands that are already allocated. To avoid that, we call startNativeCode before each native instruction, here flag "canSpill" is set to true for each physical register;
+  when a physical register is allocated, we set its flag "canSpill" to false;
+  at end of each native instruction, call endNativeCode to set flag "canSpill" to true.
+*/
+
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+#include "vm/mterp/Mterp.h"
+#include "vm/mterp/common/FindInterface.h"
+#include "NcgHelper.h"
+#include <math.h>
+#include "interp/InterpState.h"
+
+extern "C" int64_t __divdi3(int64_t, int64_t);
+extern "C" int64_t __moddi3(int64_t, int64_t);
+bool isScratchPhysical;
+LowOp* lirTable[200];
+int num_lirs_in_table = 0;
+
+//4 tables are defined: GPR integer ALU ops, ALU ops in FPU, SSE 32-bit, SSE 64-bit
+//the index to the table is the opcode
+//add_opc,    or_opc,     adc_opc,    sbb_opc,
+//and_opc,    sub_opc,    xor_opc,    cmp_opc,
+//mul_opc,    imul_opc,   div_opc,    idiv_opc,
+//sll_opc,    srl_opc,    sra, (SSE)
+//shl_opc,    shr_opc,    sal_opc,    sar_opc, //integer shift
+//neg_opc,    not_opc,    andn_opc, (SSE)
+//n_alu
+//!mnemonic for integer ALU operations
+const  Mnemonic map_of_alu_opcode_2_mnemonic[] = {
+    Mnemonic_ADD,  Mnemonic_OR,   Mnemonic_ADC,  Mnemonic_SBB,
+    Mnemonic_AND,  Mnemonic_SUB,  Mnemonic_XOR,  Mnemonic_CMP,
+    Mnemonic_MUL,  Mnemonic_IMUL, Mnemonic_DIV,  Mnemonic_IDIV,
+    Mnemonic_Null, Mnemonic_Null, Mnemonic_Null,
+    Mnemonic_SHL,  Mnemonic_SHR,  Mnemonic_SAL,  Mnemonic_SAR,
+    Mnemonic_NEG,  Mnemonic_NOT,  Mnemonic_Null,
+    Mnemonic_Null
+};
+//!mnemonic for ALU operations in FPU
+const  Mnemonic map_of_fpu_opcode_2_mnemonic[] = {
+    Mnemonic_FADD,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_FSUB,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_FMUL,  Mnemonic_Null,  Mnemonic_FDIV,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null
+};
+//!mnemonic for SSE 32-bit
+const  Mnemonic map_of_sse_opcode_2_mnemonic[] = {
+    Mnemonic_ADDSD,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_SUBSD, Mnemonic_XORPD, Mnemonic_Null,
+    Mnemonic_MULSD,  Mnemonic_Null,  Mnemonic_DIVSD,  Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null
+};
+//!mnemonic for SSE 64-bit integer
+const  Mnemonic map_of_64_opcode_2_mnemonic[] = {
+    Mnemonic_PADDQ, Mnemonic_POR,   Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_PAND,  Mnemonic_PSUBQ, Mnemonic_PXOR,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_PSLLQ, Mnemonic_PSRLQ, Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_PANDN,
+    Mnemonic_Null
+};
+
+////////////////////////////////////////////////
+//!update fields of LowOpndReg
+
+//!
+void set_reg_opnd(LowOpndReg* op_reg, int reg, bool isPhysical, LowOpndRegType type) {
+    op_reg->regType = type;
+    if(isPhysical) {
+        op_reg->logicalReg = -1;
+        op_reg->physicalReg = reg;
+    }
+    else
+        op_reg->logicalReg = reg;
+    return;
+}
+//!update fields of LowOpndMem
+
+//!
+void set_mem_opnd(LowOpndMem* mem, int disp, int base, bool isPhysical) {
+    mem->m_disp.value = disp;
+    mem->hasScale = false;
+    mem->m_base.regType = LowOpndRegType_gp;
+    if(isPhysical) {
+        mem->m_base.logicalReg = -1;
+        mem->m_base.physicalReg = base;
+    } else {
+        mem->m_base.logicalReg = base;
+    }
+    return;
+}
+//!update fields of LowOpndMem
+
+//!
+void set_mem_opnd_scale(LowOpndMem* mem, int base, bool isPhysical, int disp, int index, bool indexPhysical, int scale) {
+    mem->hasScale = true;
+    mem->m_base.regType = LowOpndRegType_gp;
+    if(isPhysical) {
+        mem->m_base.logicalReg = -1;
+        mem->m_base.physicalReg = base;
+    } else {
+        mem->m_base.logicalReg = base;
+    }
+    if(indexPhysical) {
+        mem->m_index.logicalReg = -1;
+        mem->m_index.physicalReg = index;
+    } else {
+        mem->m_index.logicalReg = index;
+    }
+    mem->m_disp.value = disp;
+    mem->m_scale.value = scale;
+    return;
+}
+//!return either LowOpndRegType_xmm or LowOpndRegType_gp
+
+//!
+inline LowOpndRegType getTypeFromIntSize(OpndSize size) {
+    return size == OpndSize_64 ? LowOpndRegType_xmm : LowOpndRegType_gp;
+}
+
+// copied from JIT compiler
+typedef struct AtomMemBlock {
+    size_t bytesAllocated;
+    struct AtomMemBlock *next;
+    char ptr[0];
+} AtomMemBlock;
+
+#define ATOMBLOCK_DEFAULT_SIZE 4096
+AtomMemBlock *atomMemHead = NULL;
+AtomMemBlock *currentAtomMem = NULL;
+void * atomNew(size_t size) {
+    lowOpTimeStamp++; //one LowOp constructed
+    if(atomMemHead == NULL) {
+        atomMemHead = (AtomMemBlock*)malloc(sizeof(AtomMemBlock) + ATOMBLOCK_DEFAULT_SIZE);
+        if(atomMemHead == NULL) {
+            ALOGE("Memory allocation failed");
+            return NULL;
+        }
+        currentAtomMem = atomMemHead;
+        currentAtomMem->bytesAllocated = 0;
+        currentAtomMem->next = NULL;
+    }
+    size = (size + 3) & ~3;
+    if (size > ATOMBLOCK_DEFAULT_SIZE) {
+        ALOGE("Requesting %d bytes which exceed the maximal size allowed", size);
+        return NULL;
+    }
+retry:
+    if (size + currentAtomMem->bytesAllocated <= ATOMBLOCK_DEFAULT_SIZE) {
+        void *ptr;
+        ptr = &currentAtomMem->ptr[currentAtomMem->bytesAllocated];
+        return ptr;
+    }
+    if (currentAtomMem->next) {
+        currentAtomMem = currentAtomMem->next;
+        goto retry;
+    }
+    /* Time to allocate a new arena */
+    AtomMemBlock *newAtomMem = (AtomMemBlock*)malloc(sizeof(AtomMemBlock) + ATOMBLOCK_DEFAULT_SIZE);
+    if(newAtomMem == NULL) {
+        ALOGE("Memory allocation failed");
+        return NULL;
+    }
+    newAtomMem->bytesAllocated = 0;
+    newAtomMem->next = NULL;
+    currentAtomMem->next = newAtomMem;
+    currentAtomMem = newAtomMem;
+    goto retry;
+    ALOGE("atomNew requesting %d bytes", size);
+    return NULL;
+}
+
+void freeAtomMem() {
+    //LOGI("free all atom memory");
+    AtomMemBlock * tmpMem = atomMemHead;
+    while(tmpMem != NULL) {
+        tmpMem->bytesAllocated = 0;
+        tmpMem = tmpMem->next;
+    }
+    currentAtomMem = atomMemHead;
+}
+
+LowOpImm* dump_special(AtomOpCode cc, int imm) {
+    LowOpImm* op = (LowOpImm*)atomNew(sizeof(LowOpImm));
+    op->lop.opCode = Mnemonic_NULL;
+    op->lop.opCode2 = cc;
+    op->lop.opnd1.type = LowOpndType_Imm;
+    op->lop.numOperands = 1;
+    op->immOpnd.value = imm;
+    //stream = encoder_imm(m, size, imm, stream);
+    return op;
+}
+
+LowOpLabel* lower_label(Mnemonic m, OpndSize size, int imm, const char* label, bool isLocal) {
+    stream = encoder_imm(m, size, imm, stream);
+    return NULL;
+}
+
+LowOpLabel* dump_label(Mnemonic m, OpndSize size, int imm,
+               const char* label, bool isLocal) {
+    return lower_label(m, size, imm, label, isLocal);
+}
+
+LowOpNCG* dump_ncg(Mnemonic m, OpndSize size, int imm) {
+    stream = encoder_imm(m, size, imm, stream);
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction with a single immediate operand
+
+//!
+LowOpImm* lower_imm(Mnemonic m, OpndSize size, int imm, bool updateTable) {
+    stream = encoder_imm(m, size, imm, stream);
+    return NULL;
+}
+
+LowOpImm* dump_imm(Mnemonic m, OpndSize size, int imm) {
+    return lower_imm(m, size, imm, true);
+}
+
+LowOpImm* dump_imm_with_codeaddr(Mnemonic m, OpndSize size,
+               int imm, char* codePtr) {
+    encoder_imm(m, size, imm, codePtr);
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes a single memory operand
+
+//!With NCG O1, we call freeReg to free up physical registers, then call registerAlloc to allocate a physical register for memory base
+LowOpMem* lower_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int disp, int base_reg) {
+    stream = encoder_mem(m, size, disp, base_reg, true, stream);
+    return NULL;
+}
+
+LowOpMem* dump_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        //type of the base is gpr
+        int regAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_mem(m, m2, size, disp, regAll);
+    } else {
+        stream = encoder_mem(m, size, disp, base_reg, isBasePhysical, stream);
+        return NULL;
+    }
+}
+//!update fields of LowOp and generate a x86 instruction that takes a single reg operand
+
+//!With NCG O1, wecall freeReg to free up physical registers, then call registerAlloc to allocate a physical register for the single operand
+LowOpReg* lower_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int reg, LowOpndRegType type) {
+    stream = encoder_reg(m, size, reg, true, type, stream);
+    return NULL;
+}
+
+LowOpReg* dump_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        if(m == Mnemonic_MUL || m == Mnemonic_IDIV) {
+            //these two instructions use eax & edx implicitly
+            touchEax();
+            touchEdx();
+        }
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        return lower_reg(m, m2, size, regAll, type);
+    } else {
+        stream = encoder_reg(m, size, reg, isPhysical, type, stream);
+        return NULL;
+    }
+}
+LowOpReg* dump_reg_noalloc(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type) {
+    return lower_reg(m, ATOM_NORMAL, size, reg, type);
+}
+
+LowOpRegReg* lower_reg_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                 int reg, int reg2, LowOpndRegType type) {
+    if(m == Mnemonic_FUCOMP || m == Mnemonic_FUCOM) {
+        stream = encoder_compare_fp_stack(m == Mnemonic_FUCOMP,
+                                          reg-reg2, size==OpndSize_64, stream);
+    }
+    else {
+        stream = encoder_reg_reg(m, size, reg, true, reg2, true, type, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//Here, both registers are physical
+LowOpRegReg* dump_reg_reg_noalloc(Mnemonic m, OpndSize size,
+                           int reg, bool isPhysical,
+                           int reg2, bool isPhysical2, LowOpndRegType type) {
+    return lower_reg_reg(m, ATOM_NORMAL, size, reg, reg2, type);
+}
+
+inline bool isMnemonicMove(Mnemonic m) {
+    return (m == Mnemonic_MOV || m == Mnemonic_MOVQ ||
+            m == Mnemonic_MOVSS || m == Mnemonic_MOVSD);
+}
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//!here dst reg is already allocated to a physical reg
+//! we should not spill the physical register for dst when allocating for src
+LowOpRegReg* dump_reg_reg_noalloc_dst(Mnemonic m, OpndSize size,
+                               int reg, bool isPhysical,
+                               int reg2, bool isPhysical2, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        /* remove move from one register to the same register */
+        if(isMnemonicMove(m) && regAll == reg2) return NULL;
+        return lower_reg_reg(m, ATOM_NORMAL, size, regAll, reg2, type);
+    } else {
+        stream = encoder_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2, type, stream);
+        return NULL;
+    }
+}
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//!here src reg is already allocated to a physical reg
+LowOpRegReg* dump_reg_reg_noalloc_src(Mnemonic m, AtomOpCode m2, OpndSize size,
+                               int reg, bool isPhysical,
+                               int reg2, bool isPhysical2, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll2;
+        if(isMnemonicMove(m) && checkTempReg2(reg2, type, isPhysical2, reg)) { //dst reg is logical
+            //only from get_virtual_reg_all
+            regAll2 = registerAllocMove(reg2, type, isPhysical2, reg);
+        } else {
+            regAll2 = registerAlloc(type, reg2, isPhysical2, true);
+            return lower_reg_reg(m, m2, size, reg, regAll2, type);
+        }
+    } else {
+        stream = encoder_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2, type, stream);
+        return NULL;
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//!
+LowOpRegReg* dump_reg_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        //reg is source if m is MOV
+        freeReg(true);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        int regAll2;
+        LowOpRegReg* op = NULL;
+#ifdef MOVE_OPT2
+        if(isMnemonicMove(m) &&
+           ((reg != PhysicalReg_EDI && reg != PhysicalReg_ESP && reg != PhysicalReg_EBP) || (!isPhysical)) &&
+           isPhysical2 == false) { //dst reg is logical
+            //called from move_reg_to_reg
+            regAll2 = registerAllocMove(reg2, type, isPhysical2, regAll);
+        } else {
+#endif
+            donotSpillReg(regAll);
+            regAll2 = registerAlloc(type, reg2, isPhysical2, true);
+            op = lower_reg_reg(m, m2, size, regAll, regAll2, type);
+#ifdef MOVE_OPT2
+        }
+#endif
+        endNativeCode();
+        return op;
+    }
+    else {
+        stream = encoder_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2, type, stream);
+    }
+    return NULL;
+}
+
+LowOpRegMem* lower_mem_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                 int disp, int base_reg,
+                 MemoryAccessType mType, int mIndex,
+                 int reg, LowOpndRegType type, bool isMoves) {
+    if(m == Mnemonic_MOVSX) {
+        stream = encoder_moves_mem_to_reg(size, disp, base_reg, true,
+                                          reg, true, stream);
+    }
+    else if(m == Mnemonic_MOVZX) {
+        stream = encoder_movez_mem_to_reg(size, disp, base_reg, true,
+                                          reg, true, stream);
+    }
+    else {
+        stream = encoder_mem_reg(m, size, disp, base_reg, true,
+                                 reg, true, type, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!Here, operands are already allocated to physical registers
+LowOpRegMem* dump_mem_reg_noalloc(Mnemonic m, OpndSize size,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex,
+                           int reg, bool isPhysical, LowOpndRegType type) {
+    return lower_mem_reg(m, ATOM_NORMAL, size, disp, base_reg, mType, mIndex, reg, type, false);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!Here, memory operand is already allocated to physical register
+LowOpRegMem* dump_mem_reg_noalloc_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                               int disp, int base_reg, bool isBasePhysical,
+                               MemoryAccessType mType, int mIndex,
+                               int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        return lower_mem_reg(m, m2, size, disp, base_reg, mType, mIndex, regAll, type, false);
+    } else {
+        stream = encoder_mem_reg(m, size, disp, base_reg, isBasePhysical,
+                                 reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* dump_mem_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex,
+                   int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        //it is okay to use the same physical register
+        if(isMnemonicMove(m)) {
+            freeReg(true);
+        } else {
+            donotSpillReg(baseAll);
+        }
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_reg(m, m2, size, disp, baseAll, mType, mIndex, regAll, type, false);
+    } else {
+        stream = encoder_mem_reg(m, size, disp, base_reg, isBasePhysical,
+                                 reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* dump_moves_mem_reg(Mnemonic m, OpndSize size,
+                         int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int regAll = registerAlloc(LowOpndRegType_gp, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_reg(m, ATOM_NORMAL, size, disp, baseAll, MemoryAccess_Unknown, -1,
+            regAll, LowOpndRegType_gp, true/*moves*/);
+    } else {
+        stream = encoder_moves_mem_to_reg(size, disp, base_reg, isBasePhysical, reg, isPhysical, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* dump_movez_mem_reg(Mnemonic m, OpndSize size,
+             int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int regAll = registerAlloc(LowOpndRegType_gp, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_reg(m, ATOM_NORMAL, size, disp, baseAll, MemoryAccess_Unknown, -1,
+            regAll, LowOpndRegType_gp, true/*moves*/);
+    } else {
+        stream = encoder_movez_mem_to_reg(size, disp, base_reg, isBasePhysical, reg, isPhysical, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one reg operand
+
+//!
+LowOpRegReg* dump_movez_reg_reg(Mnemonic m, OpndSize size,
+             int reg, bool isPhysical,
+             int reg2, bool isPhysical2) {
+    LowOpRegReg* op = (LowOpRegReg*)atomNew(sizeof(LowOpRegReg));
+    op->lop.opCode = m;
+    op->lop.opnd1.size = OpndSize_32;
+    op->lop.opnd1.type = LowOpndType_Reg;
+    op->lop.opnd2.size = size;
+    op->lop.opnd2.type = LowOpndType_Reg;
+    set_reg_opnd(&(op->regOpnd1), reg2, isPhysical2, LowOpndRegType_gp);
+    set_reg_opnd(&(op->regOpnd2), reg, isPhysical, LowOpndRegType_gp);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        //reg is source if m is MOV
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, reg, isPhysical, true);
+        donotSpillReg(regAll);
+        int regAll2 = registerAlloc(LowOpndRegType_gp, reg2, isPhysical2, true);
+        stream = encoder_movez_reg_to_reg(size, regAll, true, regAll2, true,
+                                          LowOpndRegType_gp, stream);
+        endNativeCode();
+    }
+    else {
+        stream = encoder_movez_reg_to_reg(size, reg, isPhysical, reg2,
+                                        isPhysical2, LowOpndRegType_gp, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* lower_mem_scale_reg(Mnemonic m, OpndSize size, int base_reg, int disp, int index_reg,
+                 int scale, int reg, LowOpndRegType type) {
+    bool isMovzs = (m == Mnemonic_MOVZX || m == Mnemonic_MOVSX);
+    if(isMovzs)
+        stream = encoder_movzs_mem_disp_scale_reg(m, size, base_reg, true, disp, index_reg, true,
+                                                  scale, reg, true, type, stream);
+    else {
+        if(disp == 0)
+            stream = encoder_mem_scale_reg(m, size, base_reg, true, index_reg, true,
+                                           scale, reg, true, type, stream);
+        else
+            stream = encoder_mem_disp_scale_reg(m, size, base_reg, true, disp, index_reg, true,
+                                                scale, reg, true, type, stream);
+    }
+    return NULL;
+}
+
+LowOpRegMem* dump_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll); //make sure index will not use the same physical reg
+        int indexAll = registerAlloc(LowOpndRegType_gp, index_reg, isIndexPhysical, true);
+        if(isMnemonicMove(m)) {
+            freeReg(true);
+            doSpillReg(baseAll); //base can be used now
+        } else {
+            donotSpillReg(indexAll);
+        }
+        bool isMovzs = (m == Mnemonic_MOVZX || m == Mnemonic_MOVSX);
+        int regAll = registerAlloc(isMovzs ? LowOpndRegType_gp : type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_scale_reg(m, size, baseAll, disp, indexAll, scale, regAll, type);
+    } else {
+        stream = encoder_mem_scale_reg(m, size, base_reg, isBasePhysical, index_reg,
+                                       isIndexPhysical, scale, reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpMemReg* lower_reg_mem_scale(Mnemonic m, OpndSize size, int reg,
+                 int base_reg, int disp, int index_reg, int scale, LowOpndRegType type) {
+    if(disp == 0)
+        stream = encoder_reg_mem_scale(m, size, reg, true, base_reg, true,
+                                       index_reg, true, scale, type, stream);
+    else
+        stream = encoder_reg_mem_disp_scale(m, size, reg, true, base_reg, true,
+                                            disp, index_reg, true, scale, type, stream);
+    return NULL;
+}
+
+LowOpMemReg* dump_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int indexAll = registerAlloc(LowOpndRegType_gp, index_reg, isIndexPhysical, true);
+        donotSpillReg(indexAll);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_reg_mem_scale(m, size, regAll, baseAll, disp, indexAll, scale, type);
+    } else {
+        stream = encoder_reg_mem_scale(m, size, reg, isPhysical, base_reg, isBasePhysical,
+                                       index_reg, isIndexPhysical, scale, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!Here operands are already allocated
+LowOpMemReg* lower_reg_mem(Mnemonic m, AtomOpCode m2, OpndSize size, int reg,
+                 int disp, int base_reg, MemoryAccessType mType, int mIndex,
+                 LowOpndRegType type) {
+    stream = encoder_reg_mem(m, size, reg, true, disp, base_reg, true, type, stream);
+    return NULL;
+}
+
+LowOpMemReg* dump_reg_mem_noalloc(Mnemonic m, OpndSize size,
+                           int reg, bool isPhysical,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex, LowOpndRegType type) {
+    return lower_reg_mem(m, ATOM_NORMAL, size, reg, disp, base_reg, mType, mIndex, type);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpMemReg* dump_reg_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_reg_mem(m, m2, size, regAll, disp, baseAll, mType, mIndex, type);
+    } else {
+        stream = encoder_reg_mem(m, size, reg, isPhysical, disp, base_reg, isBasePhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one reg operand
+
+//!The reg operand is allocated already
+LowOpRegImm* lower_imm_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                 int imm, int reg, LowOpndRegType type, bool chaining) {
+    stream = encoder_imm_reg(m, size, imm, reg, true, type, stream);
+    return NULL;
+}
+
+LowOpRegImm* dump_imm_reg_noalloc(Mnemonic m, OpndSize size,
+                           int imm, int reg, bool isPhysical, LowOpndRegType type) {
+    return lower_imm_reg(m, ATOM_NORMAL, size, imm, reg, type, false);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one reg operand
+
+//!
+LowOpRegImm* dump_imm_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, bool chaining) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        return lower_imm_reg(m, m2, size, imm, regAll, type, chaining);
+    } else {
+        stream = encoder_imm_reg(m, size, imm, reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one mem operand
+
+//!The mem operand is already allocated
+LowOpMemImm* lower_imm_mem(Mnemonic m, AtomOpCode m2, OpndSize size, int imm,
+                 int disp, int base_reg, MemoryAccessType mType, int mIndex,
+                 bool chaining) {
+    stream = encoder_imm_mem(m, size, imm, disp, base_reg, true, stream);
+    return NULL;
+}
+
+LowOpMemImm* dump_imm_mem_noalloc(Mnemonic m, OpndSize size,
+                           int imm,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex) {
+    return lower_imm_mem(m, ATOM_NORMAL, size, imm, disp, base_reg, mType, mIndex, false);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one mem operand
+
+//!
+LowOpMemImm* dump_imm_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, bool chaining) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        /* do not free register if the base is %edi, %esp, or %ebp
+           make sure dump_imm_mem will only generate a single instruction */
+        if(!isBasePhysical || (base_reg != PhysicalReg_EDI &&
+                               base_reg != PhysicalReg_ESP &&
+                               base_reg != PhysicalReg_EBP)) {
+            freeReg(true);
+        }
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_imm_mem(m, m2, size, imm, disp, baseAll, mType, mIndex, chaining);
+    } else {
+        stream = encoder_imm_mem(m, size, imm, disp, base_reg, isBasePhysical, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that uses the FP stack and takes one mem operand
+
+//!
+LowOpMemReg* lower_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, MemoryAccessType mType, int mIndex) {
+    stream = encoder_fp_mem(m, size, reg, disp, base_reg, true, stream);
+    return NULL;
+}
+
+LowOpMemReg* dump_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_fp_mem(m, size, reg, disp, baseAll, mType, mIndex);
+    } else {
+        stream = encoder_fp_mem(m, size, reg, disp, base_reg, isBasePhysical, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that uses the FP stack and takes one mem operand
+
+//!
+LowOpRegMem* lower_mem_fp(Mnemonic m, OpndSize size, int disp, int base_reg,
+                 MemoryAccessType mType, int mIndex, int reg) {
+    stream = encoder_mem_fp(m, size, disp, base_reg, true, reg, stream);
+    return NULL;
+}
+
+LowOpRegMem* dump_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex,
+                  int reg) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_mem_fp(m, size, disp, baseAll, mType, mIndex, reg);
+    } else {
+        stream = encoder_mem_fp(m, size, disp, base_reg, isBasePhysical, reg, stream);
+    }
+    return NULL;
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+//OPERAND ORDER:
+//LowOp same as EncoderBase destination first
+//parameter order of function: src first
+
+////////////////////////////////// IA32 native instructions //////////////
+//! generate a native instruction lea
+
+//!
+void load_effective_addr(int disp, int base_reg, bool isBasePhysical,
+                          int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_LEA;
+    dump_mem_reg(m, ATOM_NORMAL, OpndSize_32, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+}
+//! generate a native instruction lea
+
+//!
+void load_effective_addr_scale(int base_reg, bool isBasePhysical,
+                int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_LEA;
+    dump_mem_scale_reg(m, OpndSize_32,
+                              base_reg, isBasePhysical, 0/*disp*/, index_reg, isIndexPhysical, scale,
+                              reg, isPhysical, LowOpndRegType_gp);
+}
+//!fldcw
+
+//!
+void load_fpu_cw(int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = Mnemonic_FLDCW;
+    dump_mem(m, ATOM_NORMAL, OpndSize_16, disp, base_reg, isBasePhysical);
+}
+//!fnstcw
+
+//!
+void store_fpu_cw(bool checkException, int disp, int base_reg, bool isBasePhysical) {
+    assert(!checkException);
+    Mnemonic m = Mnemonic_FNSTCW;
+    dump_mem(m, ATOM_NORMAL, OpndSize_16, disp, base_reg, isBasePhysical);
+}
+//!cdq
+
+//!
+void convert_integer(OpndSize srcSize, OpndSize dstSize) { //cbw, cwd, cdq
+    assert(srcSize == OpndSize_32 && dstSize == OpndSize_64);
+    Mnemonic m = Mnemonic_CDQ;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_EDX, true, LowOpndRegType_gp);
+}
+//!fld: load from memory (float or double) to stack
+
+//!
+void load_fp_stack(LowOp* op, OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fld(s|l)
+    Mnemonic m = Mnemonic_FLD;
+    dump_mem_fp(m, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, 0); //ST0
+}
+//! fild: load from memory (int or long) to stack
+
+//!
+void load_int_fp_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fild(ll|l)
+    Mnemonic m = Mnemonic_FILD;
+    dump_mem_fp(m, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, 0); //ST0
+}
+//!fild: load from memory (absolute addr)
+
+//!
+void load_int_fp_stack_imm(OpndSize size, int imm) {//fild(ll|l)
+    return load_int_fp_stack(size, imm, PhysicalReg_Null, true);
+}
+//!fst: store from stack to memory (float or double)
+
+//!
+void store_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fst(p)(s|l)
+    Mnemonic m = pop ? Mnemonic_FSTP : Mnemonic_FST;
+    dump_fp_mem(m, size, 0, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1);
+}
+//!fist: store from stack to memory (int or long)
+
+//!
+void store_int_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fist(p)(l)
+    Mnemonic m = pop ? Mnemonic_FISTP : Mnemonic_FIST;
+    dump_fp_mem(m, size, 0, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1);
+}
+//!cmp reg, mem
+
+//!
+void compare_reg_mem(LowOp* op, OpndSize size, int reg, bool isPhysical,
+              int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_reg_mem(m, ATOM_NORMAL, size, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, getTypeFromIntSize(size));
+}
+//!cmp mem, reg
+
+//!
+void compare_mem_reg(OpndSize size,
+              int disp, int base_reg, bool isBasePhysical,
+              int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_mem_reg(m, ATOM_NORMAL, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, getTypeFromIntSize(size));
+}
+//! compare a VR with a temporary variable
+
+//!
+void compare_VR_reg_all(OpndSize size,
+             int vA,
+             int reg, bool isPhysical, Mnemonic m) {
+    LowOpndRegType type = getTypeFromIntSize(size);
+    LowOpndRegType pType = type;
+    if(m == Mnemonic_COMISS) {
+        size = OpndSize_32;
+        type = LowOpndRegType_ss;
+        pType = LowOpndRegType_xmm;
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, type, tmpValue, true/*updateRefCount*/);
+        if(isConst == 3) {
+            if(m == Mnemonic_COMISS) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and SS in compare_VR_reg");
+#endif
+                dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+                //dumpImmToMem(vA+1, OpndSize_32, 0); //CHECK necessary? will overwrite vA+1!!!
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, reg, isPhysical, pType);
+                return;
+            }
+            else if(size != OpndSize_64) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 32 bits in compare_VR_reg");
+#endif
+                dump_imm_reg(m, ATOM_NORMAL, size, tmpValue[0], reg, isPhysical, pType, false);
+                return;
+            }
+            else if(size == OpndSize_64) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 64 bits in compare_VR_reg");
+#endif
+                dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+                dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vA, reg, isPhysical, pType);
+                return;
+            }
+        }
+        if(isConst == 1) dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+        freeReg(true);
+        int regAll = checkVirtualReg(vA, type, 0/*do not update*/);
+        if(regAll != PhysicalReg_Null) { //do not spill regAll when allocating register for dst
+            startNativeCode(-1, -1);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m, ATOM_NORMAL, size, regAll, true, reg, isPhysical, pType);
+            endNativeCode();
+        }
+        else {
+            //virtual register is not allocated to a physical register
+            dump_mem_reg_noalloc_mem(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, reg, isPhysical, pType);
+        }
+        updateRefCount(vA, type);
+        return;
+    } else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+            MemoryAccess_VR, vA, reg, isPhysical, pType);
+        return;
+    }
+}
+void compare_VR_reg(OpndSize size,
+             int vA,
+             int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    return compare_VR_reg_all(size, vA, reg, isPhysical, m);
+}
+void compare_VR_ss_reg(int vA, int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISS;
+    return compare_VR_reg_all(OpndSize_32, vA, reg, isPhysical, m);
+}
+void compare_VR_sd_reg(int vA, int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISD;
+    return compare_VR_reg_all(OpndSize_64, vA, reg, isPhysical, m);
+}
+//!load VR to stack
+
+//!
+void load_fp_stack_VR_all(OpndSize size, int vB, Mnemonic m) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //can't load from immediate to fp stack
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vB, getTypeFromIntSize(size), tmpValue, false/*updateRefCount*/);
+        if(isConst > 0) {
+            if(size != OpndSize_64) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 32 bits in load_fp_stack");
+#endif
+                dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+            }
+            else {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 64 bits in load_fp_stack_VR");
+#endif
+                if(isConst == 1 || isConst == 3) dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+                if(isConst == 2 || isConst == 3) dumpImmToMem(vB+1, OpndSize_32, tmpValue[1]);
+            }
+        }
+        else { //if VR was updated by a def of gp, a xfer point was inserted
+            //if VR was updated by a def of xmm, a xfer point was inserted
+#if 0
+            int regAll = checkVirtualReg(vB, size, 1);
+            if(regAll != PhysicalReg_Null) //dump from register to memory
+                dump_reg_mem_noalloc(m, size, regAll, true, 4*vB, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vB, getTypeFromIntSize(size));
+#endif
+        }
+        dump_mem_fp(m, size, 4*vB, PhysicalReg_FP, true, MemoryAccess_VR, vB, 0);
+    } else {
+        dump_mem_fp(m, size, 4*vB, PhysicalReg_FP, true, MemoryAccess_VR, vB, 0);
+    }
+}
+//!load VR(float or double) to stack
+
+//!
+void load_fp_stack_VR(OpndSize size, int vA) {//fld(s|l)
+    Mnemonic m = Mnemonic_FLD;
+    return load_fp_stack_VR_all(size, vA, m);
+}
+//!load VR(int or long) to stack
+
+//!
+void load_int_fp_stack_VR(OpndSize size, int vA) {//fild(ll|l)
+    Mnemonic m = Mnemonic_FILD;
+    return load_fp_stack_VR_all(size, vA, m);
+}
+//!store from stack to VR (float or double)
+
+//!
+void store_fp_stack_VR(bool pop, OpndSize size, int vA) {//fst(p)(s|l)
+    Mnemonic m = pop ? Mnemonic_FSTP : Mnemonic_FST;
+    dump_fp_mem(m, size, 0, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(size == OpndSize_32)
+            updateVirtualReg(vA, LowOpndRegType_fs_s);
+        else
+            updateVirtualReg(vA, LowOpndRegType_fs);
+    }
+}
+//!store from stack to VR (int or long)
+
+//!
+void store_int_fp_stack_VR(bool pop, OpndSize size, int vA) {//fist(p)(l)
+    Mnemonic m = pop ? Mnemonic_FISTP : Mnemonic_FIST;
+    dump_fp_mem(m, size, 0, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(size == OpndSize_32)
+            updateVirtualReg(vA, LowOpndRegType_fs_s);
+        else
+            updateVirtualReg(vA, LowOpndRegType_fs);
+    }
+}
+//! ALU ops in FPU, one operand is a VR
+
+//!
+void fpu_VR(ALU_Opcode opc, OpndSize size, int vA) {
+    Mnemonic m = map_of_fpu_opcode_2_mnemonic[opc];
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, getTypeFromIntSize(size), tmpValue, false/*updateRefCount*/);
+        if(isConst > 0) {
+            if(size != OpndSize_64) {
+                //allocate a register for dst
+                dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            }
+            else {
+                if((isConst == 1 || isConst == 3) && size == OpndSize_64) {
+                    dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+                }
+                if((isConst == 2 || isConst == 3) && size == OpndSize_64) {
+                    dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+                }
+            }
+        }
+        if(!isInMemory(vA, size)) {
+            ALOGE("fpu_VR");
+        }
+        dump_mem_fp(m, size, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, 0);
+    } else {
+        dump_mem_fp(m, size, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, 0);
+    }
+}
+//! cmp imm reg
+
+//!
+void compare_imm_reg(OpndSize size, int imm,
+              int reg, bool isPhysical) {
+    if(imm == 0) {
+        LowOpndRegType type = getTypeFromIntSize(size);
+        Mnemonic m = Mnemonic_TEST;
+        if(gDvm.executionMode == kExecutionModeNcgO1) {
+            freeReg(true);
+            int regAll = registerAlloc(type, reg, isPhysical, true);
+            lower_reg_reg(m, ATOM_NORMAL, size, regAll, regAll, type);
+        } else {
+            stream = encoder_reg_reg(m, size, reg, isPhysical, reg, isPhysical, type, stream);
+        }
+        return;
+    }
+    Mnemonic m = Mnemonic_CMP;
+    dump_imm_reg(m, ATOM_NORMAL, size, imm, reg, isPhysical, getTypeFromIntSize(size), false);
+}
+//! cmp imm mem
+
+//!
+void compare_imm_mem(OpndSize size, int imm,
+              int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_imm_mem(m, ATOM_NORMAL, size, imm, disp,
+                        base_reg, isBasePhysical, MemoryAccess_Unknown, -1, false);
+}
+//! cmp imm VR
+
+//!
+void compare_imm_VR(OpndSize size, int imm,
+             int vA) {
+    Mnemonic m = Mnemonic_CMP;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(size != OpndSize_32) ALOGE("only 32 bits supported in compare_imm_VR");
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, getTypeFromIntSize(size), tmpValue, false/*updateRefCount*/);
+        if(isConst > 0) {
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        }
+        int regAll = checkVirtualReg(vA, getTypeFromIntSize(size), 0);
+        if(regAll != PhysicalReg_Null)
+            dump_imm_reg_noalloc(m, size, imm, regAll, true, LowOpndRegType_gp);
+        else
+            dump_imm_mem_noalloc(m, size, imm, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA);
+        updateRefCount(vA, getTypeFromIntSize(size));
+    } else {
+        dump_imm_mem(m, ATOM_NORMAL, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, false);
+    }
+}
+//! cmp reg reg
+
+//!
+void compare_reg_reg(int reg1, bool isPhysical1,
+              int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_32, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_gp);
+}
+void compare_reg_reg_16(int reg1, bool isPhysical1,
+              int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_16, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_gp);
+}
+
+//! comiss mem reg
+
+//!SSE, XMM: comparison of floating point numbers
+void compare_ss_mem_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISS;
+    dump_mem_reg(m, ATOM_NORMAL, OpndSize_32, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//! comiss reg reg
+
+//!
+void compare_ss_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                  int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_COMISS;
+    dump_reg_reg(m,  ATOM_NORMAL, OpndSize_32, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//! comisd mem reg
+
+//!
+void compare_sd_mem_with_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                  int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISD;
+    dump_mem_reg(m, ATOM_NORMAL, OpndSize_64, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//! comisd reg reg
+
+//!
+void compare_sd_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                  int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_COMISD;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_64, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//! fucom[p]
+
+//!
+void compare_fp_stack(bool pop, int reg, bool isDouble) { //compare ST(0) with ST(reg)
+    Mnemonic m = pop ? Mnemonic_FUCOMP : Mnemonic_FUCOM;
+    lower_reg_reg(m, ATOM_NORMAL, isDouble ? OpndSize_64 : OpndSize_32,
+                  PhysicalReg_ST0+reg, PhysicalReg_ST0, LowOpndRegType_fs);
+}
+/*!
+\brief generate a single return instruction
+
+*/
+LowOp* lower_return() {
+    stream = encoder_return(stream);
+    return NULL;
+}
+
+void x86_return() {
+    lower_return();
+}
+
+//!test imm reg
+
+//!
+void test_imm_reg(OpndSize size, int imm, int reg, bool isPhysical) {
+    dump_imm_reg(Mnemonic_TEST, ATOM_NORMAL, size, imm, reg, isPhysical, getTypeFromIntSize(size), false);
+}
+//!test imm mem
+
+//!
+void test_imm_mem(OpndSize size, int imm, int disp, int reg, bool isPhysical) {
+    dump_imm_mem(Mnemonic_TEST, ATOM_NORMAL, size, imm, disp, reg, isPhysical, MemoryAccess_Unknown, -1, false);
+}
+//!alu unary op with one reg operand
+
+//!
+void alu_unary_reg(OpndSize size, ALU_Opcode opc, int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_reg(m, ATOM_NORMAL_ALU, size, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!alu unary op with one mem operand
+
+//!
+void alu_unary_mem(LowOp* op, OpndSize size, ALU_Opcode opc, int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_mem(m, ATOM_NORMAL_ALU, size, disp, base_reg, isBasePhysical);
+}
+//!alu binary op with immediate and one mem operand
+
+//!
+void alu_binary_imm_mem(OpndSize size, ALU_Opcode opc, int imm, int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_imm_mem(m, ATOM_NORMAL_ALU, size, imm, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, false);
+}
+//!alu binary op with immediate and one reg operand
+
+//!
+void alu_binary_imm_reg(OpndSize size, ALU_Opcode opc, int imm, int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_imm_reg(m, ATOM_NORMAL_ALU, size, imm, reg, isPhysical, getTypeFromIntSize(size), false);
+}
+//!alu binary op with one mem operand and one reg operand
+
+//!
+void alu_binary_mem_reg(OpndSize size, ALU_Opcode opc,
+             int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_mem_reg(m, ATOM_NORMAL_ALU, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, getTypeFromIntSize(size));
+}
+
+void alu_sd_binary_VR_reg(ALU_Opcode opc, int vA, int reg, bool isPhysical, bool isSD) {
+    Mnemonic m;
+    if(isSD) m = map_of_sse_opcode_2_mnemonic[opc];
+    else m = (Mnemonic)(map_of_sse_opcode_2_mnemonic[opc]+1); //from SD to SS
+    OpndSize size = isSD ? OpndSize_64 : OpndSize_32;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        LowOpndRegType type = isSD ? LowOpndRegType_xmm : LowOpndRegType_ss; //type of the mem operand
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, type, tmpValue,
+                          true/*updateRefCount*/);
+        if(isConst == 3 && !isSD) {
+            //isConst can be 0 or 3, mem32, use xmm
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            dump_mem_reg(m, ATOM_NORMAL_ALU, OpndSize_32, 4*vA, PhysicalReg_FP, true,
+                       MemoryAccess_VR, vA, reg, isPhysical,
+                       LowOpndRegType_xmm);
+            return;
+        }
+        if(isConst == 3 && isSD) {
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+            dump_mem_reg(m, ATOM_NORMAL_ALU, OpndSize_64, 4*vA, PhysicalReg_FP, true,
+                       MemoryAccess_VR, vA, reg, isPhysical, LowOpndRegType_xmm);
+            return;
+        }
+        if(isConst == 1) dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+        freeReg(true);
+
+        int regAll = checkVirtualReg(vA, type, 0/*do not update refCount*/);
+        if(regAll != PhysicalReg_Null) {
+            startNativeCode(-1, -1); //should we use vA, type
+            //CHECK: callupdateVRAtUse
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m, ATOM_NORMAL_ALU, size, regAll, true, reg,
+                         isPhysical, LowOpndRegType_xmm);
+            endNativeCode();
+        }
+        else {
+            dump_mem_reg_noalloc_mem(m, ATOM_NORMAL_ALU, size, 4*vA, PhysicalReg_FP, true,
+                         MemoryAccess_VR, vA, reg, isPhysical, LowOpndRegType_xmm);
+        }
+        updateRefCount(vA, type);
+    }
+    else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vA, reg, isPhysical, LowOpndRegType_xmm);
+    }
+}
+
+//!alu binary op with a VR and one reg operand
+
+//!
+void alu_binary_VR_reg(OpndSize size, ALU_Opcode opc, int vA, int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, getTypeFromIntSize(size), tmpValue,
+                          true/*updateRefCount*/);
+        if(isConst == 3 && size != OpndSize_64) {
+            //allocate a register for dst
+            dump_imm_reg(m, ATOM_NORMAL_ALU, size, tmpValue[0], reg, isPhysical,
+                       getTypeFromIntSize(size), false);
+            return;
+        }
+        if(isConst == 3 && size == OpndSize_64) {
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+            dump_mem_reg(m, ATOM_NORMAL_ALU, size, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, reg, isPhysical, getTypeFromIntSize(size));
+            return;
+        }
+        if(isConst == 1) dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+
+        freeReg(true);
+        int regAll = checkVirtualReg(vA, getTypeFromIntSize(size), 0);
+        if(regAll != PhysicalReg_Null) {
+            startNativeCode(-1, -1);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m, ATOM_NORMAL_ALU, size, regAll, true, reg,
+                         isPhysical, getTypeFromIntSize(size));
+            endNativeCode();
+        }
+        else {
+            dump_mem_reg_noalloc_mem(m, ATOM_NORMAL_ALU, size, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, reg, isPhysical, getTypeFromIntSize(size));
+        }
+        updateRefCount(vA, getTypeFromIntSize(size));
+    }
+    else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+            MemoryAccess_VR, vA, reg, isPhysical, getTypeFromIntSize(size));
+    }
+}
+//!alu binary op with two reg operands
+
+//!
+void alu_binary_reg_reg(OpndSize size, ALU_Opcode opc,
+                         int reg1, bool isPhysical1,
+                         int reg2, bool isPhysical2) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_reg_reg(m, ATOM_NORMAL_ALU, size, reg1, isPhysical1, reg2, isPhysical2, getTypeFromIntSize(size));
+}
+//!alu binary op with one reg operand and one mem operand
+
+//!
+void alu_binary_reg_mem(OpndSize size, ALU_Opcode opc,
+             int reg, bool isPhysical,
+             int disp, int base_reg, bool isBasePhysical) { //destination is mem!!
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_reg_mem(m, ATOM_NORMAL_ALU, size, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, getTypeFromIntSize(size));
+}
+//!FPU ops with one mem operand
+
+//!
+void fpu_mem(LowOp* op, ALU_Opcode opc, OpndSize size, int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = map_of_fpu_opcode_2_mnemonic[opc];
+    dump_mem_fp(m, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, 0);
+}
+//!SSE 32-bit ALU
+
+//!
+void alu_ss_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                int reg2, bool isPhysical2) {
+    Mnemonic m = (Mnemonic)(map_of_sse_opcode_2_mnemonic[opc]+1); //from SD to SS
+    dump_reg_reg(m, ATOM_NORMAL_ALU, OpndSize_32, reg, isPhysical, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//!SSE 64-bit ALU
+
+//!
+void alu_sd_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                int reg2, bool isPhysical2) {
+    Mnemonic m = map_of_sse_opcode_2_mnemonic[opc];
+    dump_reg_reg(m, ATOM_NORMAL_ALU, OpndSize_64, reg, isPhysical, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//!push reg to native stack
+
+//!
+void push_reg_to_stack(OpndSize size, int reg, bool isPhysical) {
+    dump_reg(Mnemonic_PUSH, ATOM_NORMAL, size, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!push mem to native stack
+
+//!
+void push_mem_to_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical) {
+    dump_mem(Mnemonic_PUSH, ATOM_NORMAL, size, disp, base_reg, isBasePhysical);
+}
+//!move from reg to memory
+
+//!
+void move_reg_to_mem(OpndSize size,
+                      int reg, bool isPhysical,
+                      int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem(m, ATOM_NORMAL, size, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, getTypeFromIntSize(size));
+}
+//!move from reg to memory
+
+//!Operands are already allocated
+void move_reg_to_mem_noalloc(OpndSize size,
+                  int reg, bool isPhysical,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_noalloc(m, size, reg, isPhysical, disp, base_reg, isBasePhysical, mType, mIndex, getTypeFromIntSize(size));
+}
+//!move from memory to reg
+
+//!
+LowOpRegMem* move_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return dump_mem_reg(m, ATOM_NORMAL, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!move from memory to reg
+
+//!Operands are already allocated
+LowOpRegMem* move_mem_to_reg_noalloc(OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex,
+                  int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return dump_mem_reg_noalloc(m, size, disp, base_reg, isBasePhysical, mType, mIndex, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!movss from memory to reg
+
+//!Operands are already allocated
+LowOpRegMem* move_ss_mem_to_reg_noalloc(int disp, int base_reg, bool isBasePhysical,
+                 MemoryAccessType mType, int mIndex,
+                 int reg, bool isPhysical) {
+    return dump_mem_reg_noalloc(Mnemonic_MOVSS, OpndSize_32, disp, base_reg, isBasePhysical, mType, mIndex, reg, isPhysical, LowOpndRegType_xmm);
+}
+//!movss from reg to memory
+
+//!Operands are already allocated
+LowOpMemReg* move_ss_reg_to_mem_noalloc(int reg, bool isPhysical,
+                 int disp, int base_reg, bool isBasePhysical,
+                 MemoryAccessType mType, int mIndex) {
+    return dump_reg_mem_noalloc(Mnemonic_MOVSS, OpndSize_32, reg, isPhysical, disp, base_reg, isBasePhysical, mType, mIndex, LowOpndRegType_xmm);
+}
+//!movzx from memory to reg
+
+//!
+void movez_mem_to_reg(OpndSize size,
+               int disp, int base_reg, bool isBasePhysical,
+               int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_MOVZX;
+    dump_movez_mem_reg(m, size, disp, base_reg, isBasePhysical, reg, isPhysical);
+}
+
+//!movzx from one reg to another reg
+
+//!
+void movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_MOVZX;
+    dump_movez_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2);
+}
+
+void movez_mem_disp_scale_to_reg(OpndSize size,
+                 int base_reg, bool isBasePhysical,
+                 int disp, int index_reg, bool isIndexPhysical, int scale,
+                 int reg, bool isPhysical) {
+    dump_mem_scale_reg(Mnemonic_MOVZX, size, base_reg, isBasePhysical,
+                 disp, index_reg, isIndexPhysical, scale,
+                 reg, isPhysical, LowOpndRegType_gp);
+}
+void moves_mem_disp_scale_to_reg(OpndSize size,
+                  int base_reg, bool isBasePhysical,
+                  int disp, int index_reg, bool isIndexPhysical, int scale,
+                  int reg, bool isPhysical) {
+    dump_mem_scale_reg(Mnemonic_MOVSX, size, base_reg, isBasePhysical,
+                  disp, index_reg, isIndexPhysical, scale,
+                  reg, isPhysical, LowOpndRegType_gp);
+}
+
+//!movsx from memory to reg
+
+//!
+void moves_mem_to_reg(LowOp* op, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical,
+               int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_MOVSX;
+    dump_moves_mem_reg(m, size, disp, base_reg, isBasePhysical, reg, isPhysical);
+}
+//!mov from one reg to another reg
+
+//!
+void move_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_reg(m, ATOM_NORMAL, size, reg, isPhysical, reg2, isPhysical2, getTypeFromIntSize(size));
+}
+//!mov from one reg to another reg
+
+//!Operands are already allocated
+void move_reg_to_reg_noalloc(OpndSize size,
+                  int reg, bool isPhysical,
+                  int reg2, bool isPhysical2) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_reg_noalloc(m, size, reg, isPhysical, reg2, isPhysical2, getTypeFromIntSize(size));
+}
+//!move from memory to reg
+
+//!
+void move_mem_scale_to_reg(OpndSize size,
+                int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_mem_scale_reg(m, size, base_reg, isBasePhysical, 0/*disp*/, index_reg, isIndexPhysical, scale,
+                              reg, isPhysical, getTypeFromIntSize(size));
+}
+void move_mem_disp_scale_to_reg(OpndSize size,
+                int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_mem_scale_reg(m, size, base_reg, isBasePhysical, disp, index_reg, isIndexPhysical, scale,
+                              reg, isPhysical, getTypeFromIntSize(size));
+}
+//!move from reg to memory
+
+//!
+void move_reg_to_mem_scale(OpndSize size,
+                int reg, bool isPhysical,
+                int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_scale(m, size, reg, isPhysical,
+                              base_reg, isBasePhysical, 0/*disp*/, index_reg, isIndexPhysical, scale,
+                              getTypeFromIntSize(size));
+}
+void move_reg_to_mem_disp_scale(OpndSize size,
+                int reg, bool isPhysical,
+                int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_scale(m, size, reg, isPhysical,
+                              base_reg, isBasePhysical, disp, index_reg, isIndexPhysical, scale,
+                              getTypeFromIntSize(size));
+}
+
+void move_chain_to_mem(OpndSize size, int imm,
+                        int disp, int base_reg, bool isBasePhysical) {
+    dump_imm_mem(Mnemonic_MOV, ATOM_NORMAL, size, imm, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, true);
+}
+
+//!move an immediate to memory
+
+//!
+void move_imm_to_mem(OpndSize size, int imm,
+                      int disp, int base_reg, bool isBasePhysical) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_mem with 64 bits");
+    dump_imm_mem(Mnemonic_MOV, ATOM_NORMAL, size, imm, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, false);
+}
+//! set a VR to an immediate
+
+//!
+void set_VR_to_imm(u2 vA, OpndSize size, int imm) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_mem with 64 bits");
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll = checkVirtualReg(vA, getTypeFromIntSize(size), 0);
+        if(regAll != PhysicalReg_Null) {
+            dump_imm_reg_noalloc(m, size, imm, regAll, true, LowOpndRegType_gp);
+            updateRefCount(vA, getTypeFromIntSize(size));
+            updateVirtualReg(vA, getTypeFromIntSize(size));
+            return;
+        }
+        //will call freeReg
+        freeReg(true);
+        regAll = registerAlloc(LowOpndRegType_virtual | getTypeFromIntSize(size), vA, false/*dummy*/, true);
+        if(regAll == PhysicalReg_Null) {
+            dump_imm_mem_noalloc(m, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+            return;
+        }
+        dump_imm_reg_noalloc(m, size, imm, regAll, true, LowOpndRegType_gp);
+        updateVirtualReg(vA, getTypeFromIntSize(size));
+    }
+    else {
+        dump_imm_mem(m, ATOM_NORMAL, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, false);
+    }
+}
+void set_VR_to_imm_noupdateref(LowOp* op, u2 vA, OpndSize size, int imm) {
+    return;
+}
+//! set a VR to an immediate
+
+//! Do not allocate a physical register for the VR
+void set_VR_to_imm_noalloc(u2 vA, OpndSize size, int imm) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_mem with 64 bits");
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_imm_mem_noalloc(m, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+}
+
+void move_chain_to_reg(OpndSize size, int imm, int reg, bool isPhysical) {
+    dump_imm_reg(Mnemonic_MOV, ATOM_NORMAL, size, imm, reg, isPhysical, LowOpndRegType_gp, true);
+}
+
+//! move an immediate to reg
+
+//!
+void move_imm_to_reg(OpndSize size, int imm, int reg, bool isPhysical) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_reg with 64 bits");
+    Mnemonic m = Mnemonic_MOV;
+    dump_imm_reg(m, ATOM_NORMAL, size, imm, reg, isPhysical, LowOpndRegType_gp, false);
+}
+//! move an immediate to reg
+
+//! The operand is already allocated
+void move_imm_to_reg_noalloc(OpndSize size, int imm, int reg, bool isPhysical) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_reg with 64 bits");
+    Mnemonic m = Mnemonic_MOV;
+    dump_imm_reg_noalloc(m, size, imm, reg, isPhysical, LowOpndRegType_gp);
+}
+//!cmov from reg to reg
+
+//!
+void conditional_move_reg_to_reg(OpndSize size, ConditionCode cc, int reg1, bool isPhysical1, int reg, bool isPhysical) {
+    Mnemonic m = (Mnemonic)(Mnemonic_CMOVcc+cc);
+    dump_reg_reg(m, ATOM_NORMAL, size, reg1, isPhysical1, reg, isPhysical, LowOpndRegType_gp);
+}
+//!movss from memory to reg
+
+//!
+void move_ss_mem_to_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical) {
+    dump_mem_reg(Mnemonic_MOVSS, ATOM_NORMAL, OpndSize_32, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//!movss from reg to memory
+
+//!
+void move_ss_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical) {
+    dump_reg_mem(Mnemonic_MOVSS, ATOM_NORMAL, OpndSize_32, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, LowOpndRegType_xmm);
+}
+//!movsd from memory to reg
+
+//!
+void move_sd_mem_to_reg(int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical) {
+    dump_mem_reg(Mnemonic_MOVSD, ATOM_NORMAL, OpndSize_64, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//!movsd from reg to memory
+
+//!
+void move_sd_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical) {
+    dump_reg_mem(Mnemonic_MOVSD, ATOM_NORMAL, OpndSize_64, reg, isPhysical,
+                        disp, base_reg, isBasePhysical,
+                        MemoryAccess_Unknown, -1, LowOpndRegType_xmm);
+}
+//!load from VR to a temporary
+
+//!
+void get_virtual_reg_all(u2 vB, OpndSize size, int reg, bool isPhysical, Mnemonic m) {
+    LowOpndRegType type = getTypeFromIntSize(size);
+    LowOpndRegType pType = type;//gp or xmm
+    OpndSize size2 = size;
+    Mnemonic m2 = m;
+    if(m == Mnemonic_MOVSS) {
+        size = OpndSize_32;
+        size2 = OpndSize_64;
+        type = LowOpndRegType_ss;
+        pType = LowOpndRegType_xmm;
+        m2 = Mnemonic_MOVQ; //to move from one xmm register to another
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst;
+        isConst = isVirtualRegConstant(vB, type, tmpValue, true/*updateRefCount*/);
+        if(isConst == 3) {
+            if(m == Mnemonic_MOVSS) { //load 32 bits from VR
+                //VR is not mapped to a register but in memory
+                dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+                //temporary reg has "pType" (which is xmm)
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vB, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vB, reg, isPhysical, pType);
+                return;
+            }
+            else if(m == Mnemonic_MOVSD || size == OpndSize_64) {
+                //VR is not mapped to a register but in memory
+                dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+                dumpImmToMem(vB+1, OpndSize_32, tmpValue[1]);
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vB, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vB, reg, isPhysical, pType);
+                return;
+            }
+            else if(size != OpndSize_64) {
+                //VR is not mapped to a register
+                dump_imm_reg(m, ATOM_NORMAL, size, tmpValue[0], reg, isPhysical, pType, false);
+                return;
+            }
+        }
+        if(isConst == 1) dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vB+1, OpndSize_32, tmpValue[1]);
+        freeReg(true);
+        int regAll = checkVirtualReg(vB, type, 0);
+        if(regAll != PhysicalReg_Null) {
+            startNativeCode(vB, type);
+            donotSpillReg(regAll);
+            //check XFER_MEM_TO_XMM
+            updateVRAtUse(vB, type, regAll);
+            //temporary reg has "pType"
+            dump_reg_reg_noalloc_src(m2, ATOM_NORMAL, size2, regAll, true, reg, isPhysical, pType); //register allocator handles assembly move
+            endNativeCode();
+            updateRefCount(vB, type);
+            return;
+        }
+        //not allocated to a register yet, no need to check XFER_MEM_TO_XMM
+        regAll = registerAlloc(LowOpndRegType_virtual | type, vB, false/*dummy*/, false);
+        if(regAll == PhysicalReg_Null) {
+            dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+                MemoryAccess_VR, vB, reg, isPhysical, pType);
+            return;
+        }
+
+        //temporary reg has pType
+        if(checkTempReg2(reg, pType, isPhysical, regAll)) {
+            registerAllocMove(reg, pType, isPhysical, regAll);
+            dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+                MemoryAccess_VR, vB, regAll, true, pType);
+            updateRefCount(vB, type);
+            return;
+        }
+        else {
+            dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+                MemoryAccess_VR, vB, regAll, true, pType);
+            //xmm with 32 bits
+            startNativeCode(vB, type);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m2, ATOM_NORMAL, size2, regAll, true, reg, isPhysical, pType);
+            endNativeCode();
+            updateRefCount(vB, type);
+            return;
+        }
+    }
+    else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vB, PhysicalReg_FP, true,
+            MemoryAccess_VR, vB, reg, isPhysical, pType);
+    }
+}
+void get_virtual_reg(u2 vB, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return get_virtual_reg_all(vB, size, reg, isPhysical, m);
+}
+void get_virtual_reg_noalloc(u2 vB, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+        MemoryAccess_VR, vB, reg, isPhysical, getTypeFromIntSize(size));
+}
+//3 cases: gp, xmm, ss
+//ss: the temporary register is xmm
+//!load from a temporary to a VR
+
+//!
+void set_virtual_reg_all(u2 vA, OpndSize size, int reg, bool isPhysical, Mnemonic m) {
+    LowOpndRegType type = getTypeFromIntSize(size);
+    LowOpndRegType pType = type;//gp or xmm
+    OpndSize size2 = size;
+    Mnemonic m2 = m;
+    if(m == Mnemonic_MOVSS) {
+        size = OpndSize_32;
+        size2 = OpndSize_64;
+        type = LowOpndRegType_ss;
+        pType = LowOpndRegType_xmm;
+        m2 = Mnemonic_MOVQ;
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //3 cases
+        //1: virtual register is already allocated to a physical register
+        //   call dump_reg_reg_noalloc_dst
+        //2: src reg is already allocated, VR is not yet allocated
+        //   allocate VR to the same physical register used by src reg
+        //   [call registerAllocMove]
+        //3: both not yet allocated
+        //   allocate a physical register for the VR
+        //   then call dump_reg_reg_noalloc_dst
+        //may need to convert from gp to xmm or the other way
+        freeReg(true);
+        int regAll = checkVirtualReg(vA, type, 0);
+        if(regAll != PhysicalReg_Null)  { //case 1
+            startNativeCode(-1, -1);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_dst(m2, size2, reg, isPhysical, regAll, true, pType); //temporary reg is "pType"
+            endNativeCode();
+            updateRefCount(vA, type);
+            updateVirtualReg(vA, type); //will dump VR to memory, should happen afterwards
+            return;
+        }
+        regAll = checkTempReg(reg, pType, isPhysical, vA); //vA is not used inside
+        if(regAll != PhysicalReg_Null) { //case 2
+            registerAllocMove(vA, LowOpndRegType_virtual | type, false, regAll);
+            updateVirtualReg(vA, type); //will dump VR to memory, should happen afterwards
+            return; //next native instruction starts at op
+        }
+        //case 3
+        regAll = registerAlloc(LowOpndRegType_virtual | type, vA, false/*dummy*/, false);
+        if(regAll == PhysicalReg_Null) {
+            dump_reg_mem_noalloc(m, size, reg, isPhysical, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, pType);
+            return;
+        }
+        startNativeCode(-1, -1);
+        donotSpillReg(regAll);
+        dump_reg_reg_noalloc_dst(m2, size2, reg, isPhysical, regAll, true, pType);
+        endNativeCode();
+        updateRefCount(vA, type);
+        updateVirtualReg(vA, type);
+    }
+    else {
+        dump_reg_mem(m, ATOM_NORMAL, size, reg, isPhysical, 4*vA, PhysicalReg_FP, true,
+            MemoryAccess_VR, vA, pType);
+    }
+}
+void set_virtual_reg(u2 vA, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return set_virtual_reg_all(vA, size, reg, isPhysical, m);
+}
+void set_virtual_reg_noalloc(u2 vA, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_noalloc(m, size, reg, isPhysical, 4*vA, PhysicalReg_FP, true,
+        MemoryAccess_VR, vA, getTypeFromIntSize(size));
+}
+void get_VR_ss(int vB, int reg, bool isPhysical) {
+    return get_virtual_reg_all(vB, OpndSize_64, reg, isPhysical, Mnemonic_MOVSS);
+}
+void set_VR_ss(int vA, int reg, bool isPhysical) {
+    return set_virtual_reg_all(vA, OpndSize_64, reg, isPhysical, Mnemonic_MOVSS);
+}
+void get_VR_sd(int vB, int reg, bool isPhysical) {
+    return get_virtual_reg_all(vB, OpndSize_64, reg, isPhysical, Mnemonic_MOVSD);
+}
+void set_VR_sd(int vA, int reg, bool isPhysical) {
+    return set_virtual_reg_all(vA, OpndSize_64, reg, isPhysical, Mnemonic_MOVSD);
+}
+////////////////////////////////// END: IA32 native instructions //////////////
+//! generate native instructions to get current PC in the stack frame
+
+//!
+int get_currentpc(int reg, bool isPhysical) {
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_localRefTop, PhysicalReg_FP, true, reg, isPhysical);
+    return 1;
+}
+//!generate native code to perform null check
+
+//!This function does not export PC
+int simpleNullCheck(int reg, bool isPhysical, int vr) {
+    if(isVRNullCheck(vr, OpndSize_32)) {
+        updateRefCount2(reg, LowOpndRegType_gp, isPhysical);
+        num_removed_nullCheck++;
+        return 0;
+    }
+    compare_imm_reg(OpndSize_32, 0, reg, isPhysical);
+    conditional_jump_global_API(Condition_E, "common_errNullObject", false);
+    setVRNullCheck(vr, OpndSize_32);
+    return 0;
+}
+
+/* only for O1 code generator */
+int boundCheck(int vr_array, int reg_array, bool isPhysical_array,
+               int vr_index, int reg_index, bool isPhysical_index,
+               int exceptionNum) {
+#ifdef BOUNDCHECK_OPT
+    if(isVRBoundCheck(vr_array, vr_index)) {
+        updateRefCount2(reg_array, LowOpndRegType_gp, isPhysical_array);
+        updateRefCount2(reg_index, LowOpndRegType_gp, isPhysical_index);
+        return 0;
+    }
+#endif
+    compare_mem_reg(OpndSize_32, offArrayObject_length,
+                    reg_array, isPhysical_array,
+                    reg_index, isPhysical_index);
+
+    char errName[256];
+    sprintf(errName, "common_errArrayIndex");
+    handlePotentialException(
+                                       Condition_NC, Condition_C,
+                                       exceptionNum, errName);
+#ifdef BOUNDCHECK_OPT
+    setVRBoundCheck(vr_array, vr_index);
+#endif
+    return 0;
+}
+
+//!generate native code to perform null check
+
+//!
+int nullCheck(int reg, bool isPhysical, int exceptionNum, int vr) {
+    char label[LABEL_SIZE];
+
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //nullCheck optimization is available in O1 mode only
+        if(isVRNullCheck(vr, OpndSize_32)) {
+            updateRefCount2(reg, LowOpndRegType_gp, isPhysical);
+            if(exceptionNum <= 1) {
+                updateRefCount2(PhysicalReg_EDX, LowOpndRegType_gp, true);
+                updateRefCount2(PhysicalReg_EDX, LowOpndRegType_gp, true);
+            }
+            num_removed_nullCheck++;
+            return 0;
+        }
+        compare_imm_reg(OpndSize_32, 0, reg, isPhysical);
+        rememberState(exceptionNum);
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(Condition_NE, label, true);
+        if(exceptionNum > 1)
+            nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 ref count
+        export_pc(); //use %edx
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+        unconditional_jump_global_API("common_errNullObject", false);
+        insertLabel(label, true);
+        goToState(exceptionNum);
+        setVRNullCheck(vr, OpndSize_32);
+    } else {
+        compare_imm_reg(OpndSize_32, 0, reg, isPhysical);
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(Condition_NE, label, true);
+        export_pc(); //use %edx
+        unconditional_jump_global_API("common_errNullObject", false);
+        insertLabel(label, true);
+    }
+    return 0;
+}
+//!generate native code to handle potential exception
+
+//!
+int handlePotentialException(
+                             ConditionCode code_excep, ConditionCode code_okay,
+                             int exceptionNum, const char* errName) {
+    char label[LABEL_SIZE];
+
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        rememberState(exceptionNum);
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(code_okay, label, true);
+        if(exceptionNum > 1)
+            nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 ref count
+        export_pc(); //use %edx
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+        if(!strcmp(errName, "common_throw_message")) {
+            move_imm_to_reg(OpndSize_32, LstrInstantiationErrorPtr, PhysicalReg_ECX, true);
+        }
+        unconditional_jump_global_API(errName, false);
+        insertLabel(label, true);
+        goToState(exceptionNum);
+    } else {
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(code_okay, label, true);
+        export_pc(); //use %edx
+        if(!strcmp(errName, "common_throw_message")) {
+            move_imm_to_reg(OpndSize_32, LstrInstantiationErrorPtr, PhysicalReg_ECX, true);
+        }
+        unconditional_jump_global_API(errName, false);
+        insertLabel(label, true);
+    }
+    return 0;
+}
+//!generate native code to get the self pointer from glue
+
+//!It uses one scratch register
+int get_self_pointer(int reg, bool isPhysical) {
+    move_mem_to_reg(OpndSize_32, offEBP_self, PhysicalReg_EBP, true, reg, isPhysical);
+    return 0;
+}
+//!generate native code to get ResStrings from glue
+
+//!It uses two scratch registers
+int get_res_strings(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, update spill_loc_index & physicalReg
+#if 0
+        updateRefCount2(C_SCRATCH_1, LowOpndRegType_gp, isScratchPhysical);
+        updateRefCount2(C_SCRATCH_1, LowOpndRegType_gp, isScratchPhysical);
+        updateRefCount2(C_SCRATCH_2, LowOpndRegType_gp, isScratchPhysical);
+        updateRefCount2(C_SCRATCH_2, LowOpndRegType_gp, isScratchPhysical);
+#endif
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResStrings, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResStrings, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+int get_res_classes(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResClasses, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResClasses, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+//!generate native code to get ResFields from glue
+
+//!It uses two scratch registers
+int get_res_fields(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResFields, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResFields, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+//!generate native code to get ResMethods from glue
+
+//!It uses two scratch registers
+int get_res_methods(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResMethods, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResMethods, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+//!generate native code to get the current class object from glue
+
+//!It uses two scratch registers
+int get_glue_method_class(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.method), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offMethod_clazz, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to get the current method from glue
+
+//!It uses one scratch register
+int get_glue_method(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.method), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to set the current method in glue
+
+//!It uses one scratch register
+int set_glue_method(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, offsetof(Thread, interpSave.method), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+
+//!generate native code to get DvmDex from glue
+
+//!It uses one scratch register
+int get_glue_dvmdex(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_reg_reg_noalloc_src(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, regAll, true,
+                                          reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(reg, isPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+        }
+    return 0;
+}
+//!generate native code to set DvmDex in glue
+
+//!It uses one scratch register
+int set_glue_dvmdex(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+//!generate native code to get SuspendCount from glue
+
+//!It uses one scratch register
+int get_suspendCount(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, suspendCount), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+
+//!generate native code to get retval from glue
+
+//!It uses one scratch register
+int get_return_value(OpndSize size, int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(size, offsetof(Thread, interpSave.retval), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to set retval in glue
+
+//!It uses one scratch register
+int set_return_value(OpndSize size, int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(size, reg, isPhysical, offsetof(Thread, interpSave.retval), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+//!generate native code to clear exception object in glue
+
+//!It uses two scratch registers
+int clear_exception() {
+    get_self_pointer(C_SCRATCH_2, isScratchPhysical);
+    move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, exception), C_SCRATCH_2, isScratchPhysical);
+    return 0;
+}
+//!generate native code to get exception object in glue
+
+//!It uses two scratch registers
+int get_exception(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_2, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, exception), C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to set exception object in glue
+
+//!It uses two scratch registers
+int set_exception(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_2, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, offsetof(Thread, exception), C_SCRATCH_2, isScratchPhysical);
+    return 0;
+}
+//!generate native code to save frame pointer and current PC in stack frame to glue
+
+//!It uses two scratch registers
+int save_pc_fp_to_glue() {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), C_SCRATCH_1, isScratchPhysical);
+
+    //from stack-save currentPc
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_localRefTop, PhysicalReg_FP, true, C_SCRATCH_2, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, C_SCRATCH_2, isScratchPhysical, offsetof(Thread, interpSave.pc), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+//! get SaveArea pointer
+
+//!
+int savearea_from_fp(int reg, bool isPhysical) {
+    load_effective_addr(-sizeofStackSaveArea, PhysicalReg_FP, true, reg, isPhysical);
+    return 0;
+}
+
+#ifdef DEBUG_CALL_STACK3
+int call_debug_dumpSwitch() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = debug_dumpSwitch;
+    callFuncPtr((int)funcPtr, "debug_dumpSwitch");
+    return 0;
+}
+#endif
+
+int call_dvmQuasiAtomicSwap64() {
+    typedef int64_t (*vmHelper)(int64_t, volatile int64_t*);
+    vmHelper funcPtr = dvmQuasiAtomicSwap64;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmQuasiAtomicSwap64");
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicSwap64");
+        afterCall("dvmQuasiAtomicSwap64");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicSwap64");
+    }
+    return 0;
+}
+
+int call_dvmQuasiAtomicRead64() {
+    typedef int64_t (*vmHelper)(volatile const int64_t*);
+    vmHelper funcPtr = dvmQuasiAtomicRead64;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmQuasiAtomiRead64");
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicRead64");
+        afterCall("dvmQuasiAtomicRead64");
+        touchEax(); //for return value
+        touchEdx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicRead64");
+    }
+    return 0;
+}
+
+int call_dvmJitToInterpPunt() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpPunt;
+    callFuncPtr((int)funcPtr, "dvmJitToInterpPunt");
+    return 0;
+}
+
+int call_dvmJitToInterpNormal() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpNormal;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToInterpNormal");
+        callFuncPtr((int)funcPtr, "dvmJitToInterpNormal");
+        afterCall("dvmJitToInterpNormal");
+        touchEbx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToInterpNormal");
+    }
+    return 0;
+}
+
+int call_dvmJitToInterpTraceSelectNoChain() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpTraceSelectNoChain;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToInterpTraceSelectNoChain");
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelectNoChain");
+        afterCall("dvmJitToInterpTraceSelectNoChain");
+        touchEbx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelectNoChain");
+    }
+    return 0;
+}
+
+int call_dvmJitToInterpTraceSelect() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpTraceSelect;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToInterpTraceSelect");
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelect");
+        afterCall("dvmJitToInterpTraceSelect");
+        touchEbx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelect");
+    }
+    return 0;
+}
+
+int call_dvmJitToPatchPredictedChain() {
+    typedef const Method * (*vmHelper)(const Method *method,
+                                       Thread *self,
+                                       PredictedChainingCell *cell,
+                                       const ClassObject *clazz);
+    vmHelper funcPtr = dvmJitToPatchPredictedChain;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToPatchPredictedChain");
+        callFuncPtr((int)funcPtr, "dvmJitToPatchPredictedChain");
+        afterCall("dvmJitToPatchPredictedChain");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToPatchPredictedChain");
+    }
+    return 0;
+}
+
+//!generate native code to call __moddi3
+
+//!
+int call_moddi3() {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("moddi3");
+        callFuncPtr((intptr_t)__moddi3, "__moddi3");
+        afterCall("moddi3");
+    } else {
+        callFuncPtr((intptr_t)__moddi3, "__moddi3");
+    }
+    return 0;
+}
+//!generate native code to call __divdi3
+
+//!
+int call_divdi3() {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("divdi3");
+        callFuncPtr((intptr_t)__divdi3, "__divdi3");
+        afterCall("divdi3");
+    } else {
+        callFuncPtr((intptr_t)__divdi3, "__divdi3");
+    }
+    return 0;
+}
+
+//!generate native code to call fmod
+
+//!
+int call_fmod() {
+    typedef double (*libHelper)(double, double);
+    libHelper funcPtr = fmod;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("fmod");
+        callFuncPtr((int)funcPtr, "fmod");
+        afterCall("fmod");
+    } else {
+        callFuncPtr((int)funcPtr, "fmod");
+    }
+    return 0;
+}
+//!generate native code to call fmodf
+
+//!
+int call_fmodf() {
+    typedef float (*libHelper)(float, float);
+    libHelper funcPtr = fmodf;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("fmodf");
+        callFuncPtr((int)funcPtr, "fmodf");
+        afterCall("fmodf");
+    } else {
+        callFuncPtr((int)funcPtr, "fmodf");
+    }
+    return 0;
+}
+//!generate native code to call dvmFindCatchBlock
+
+//!
+int call_dvmFindCatchBlock() {
+    //int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+    //bool doUnroll, void** newFrame)
+    typedef int (*vmHelper)(Thread*, int, Object*, bool, void**);
+    vmHelper funcPtr = dvmFindCatchBlock;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmFindCatchBlock");
+        callFuncPtr((int)funcPtr, "dvmFindCatchBlock");
+        afterCall("dvmFindCatchBlock");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmFindCatchBlock");
+    }
+    return 0;
+}
+//!generate native code to call dvmThrowVerificationError
+
+//!
+int call_dvmThrowVerificationError() {
+    typedef void (*vmHelper)(const Method*, int, int);
+    vmHelper funcPtr = dvmThrowVerificationError;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmThrowVerificationError");
+        callFuncPtr((int)funcPtr, "dvmThrowVerificationError");
+        afterCall("dvmThrowVerificationError");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmThrowVerificationError");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmResolveMethod
+
+//!
+int call_dvmResolveMethod() {
+    //Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx, MethodType methodType);
+    typedef Method* (*vmHelper)(const ClassObject*, u4, MethodType);
+    vmHelper funcPtr = dvmResolveMethod;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveMethod");
+        callFuncPtr((int)funcPtr, "dvmResolveMethod");
+        afterCall("dvmResolveMethod");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveMethod");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveClass
+
+//!
+int call_dvmResolveClass() {
+    //ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx, bool fromUnverifiedConstant)
+    typedef ClassObject* (*vmHelper)(const ClassObject*, u4, bool);
+    vmHelper funcPtr = dvmResolveClass;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveClass");
+        callFuncPtr((int)funcPtr, "dvmResolveClass");
+        afterCall("dvmResolveClass");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveClass");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmInstanceofNonTrivial
+
+//!
+int call_dvmInstanceofNonTrivial() {
+    typedef int (*vmHelper)(const ClassObject*, const ClassObject*);
+    vmHelper funcPtr = dvmInstanceofNonTrivial;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmInstanceofNonTrivial");
+        callFuncPtr((int)funcPtr, "dvmInstanceofNonTrivial");
+        afterCall("dvmInstanceofNonTrivial");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmInstanceofNonTrivial");
+    }
+    return 0;
+}
+//!generate native code to call dvmThrowException
+
+//!
+int call_dvmThrow() {
+    typedef void (*vmHelper)(ClassObject* exceptionClass, const char*);
+    vmHelper funcPtr = dvmThrowException;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmThrowException");
+        callFuncPtr((int)funcPtr, "dvmThrowException");
+        afterCall("dvmThrowException");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmThrowException");
+    }
+    return 0;
+}
+//!generate native code to call dvmThrowExceptionWithClassMessage
+
+//!
+int call_dvmThrowWithMessage() {
+    typedef void (*vmHelper)(ClassObject* exceptionClass, const char*);
+    vmHelper funcPtr = dvmThrowExceptionWithClassMessage;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmThrowExceptionWithClassMessage");
+        callFuncPtr((int)funcPtr, "dvmThrowExceptionWithClassMessage");
+        afterCall("dvmThrowExceptionWithClassMessage");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmThrowExceptionWithClassMessage");
+    }
+    return 0;
+}
+//!generate native code to call dvmCheckSuspendPending
+
+//!
+int call_dvmCheckSuspendPending() {
+    typedef bool (*vmHelper)(Thread*);
+    vmHelper funcPtr = dvmCheckSuspendPending;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmCheckSuspendPending");
+        callFuncPtr((int)funcPtr, "dvmCheckSuspendPending");
+        afterCall("dvmCheckSuspendPending");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmCheckSuspendPending");
+    }
+    return 0;
+}
+//!generate native code to call dvmLockObject
+
+//!
+int call_dvmLockObject() {
+    typedef void (*vmHelper)(struct Thread*, struct Object*);
+    vmHelper funcPtr = dvmLockObject;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmLockObject");
+        callFuncPtr((int)funcPtr, "dvmLockObject");
+        afterCall("dvmLockObject");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmLockObject");
+    }
+    return 0;
+}
+//!generate native code to call dvmUnlockObject
+
+//!
+int call_dvmUnlockObject() {
+    typedef bool (*vmHelper)(Thread*, Object*);
+    vmHelper funcPtr = dvmUnlockObject;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmUnlockObject");
+        callFuncPtr((int)funcPtr, "dvmUnlockObject");
+        afterCall("dvmUnlockObject");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmUnlockObject");
+    }
+    return 0;
+}
+//!generate native code to call dvmInitClass
+
+//!
+int call_dvmInitClass() {
+    typedef bool (*vmHelper)(ClassObject*);
+    vmHelper funcPtr = dvmInitClass;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmInitClass");
+        callFuncPtr((int)funcPtr, "dvmInitClass");
+        afterCall("dvmInitClass");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmInitClass");
+    }
+    return 0;
+}
+//!generate native code to call dvmAllocObject
+
+//!
+int call_dvmAllocObject() {
+    typedef Object* (*vmHelper)(ClassObject*, int);
+    vmHelper funcPtr = dvmAllocObject;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmAllocObject");
+        callFuncPtr((int)funcPtr, "dvmAllocObject");
+        afterCall("dvmAllocObject");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmAllocObject");
+    }
+    return 0;
+}
+//!generate native code to call dvmAllocArrayByClass
+
+//!
+int call_dvmAllocArrayByClass() {
+    typedef ArrayObject* (*vmHelper)(ClassObject*, size_t, int);
+    vmHelper funcPtr = dvmAllocArrayByClass;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmAllocArrayByClass");
+        callFuncPtr((int)funcPtr, "dvmAllocArrayByClass");
+        afterCall("dvmAllocArrayByClass");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmAllocArrayByClass");
+    }
+    return 0;
+}
+//!generate native code to call dvmAllocPrimitiveArray
+
+//!
+int call_dvmAllocPrimitiveArray() {
+    typedef ArrayObject* (*vmHelper)(char, size_t, int);
+    vmHelper funcPtr = dvmAllocPrimitiveArray;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmAllocPrimitiveArray");
+        callFuncPtr((int)funcPtr, "dvmAllocPrimitiveArray");
+        afterCall("dvmAllocPrimitiveArray");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmAllocPrimitiveArray");
+    }
+    return 0;
+}
+//!generate native code to call dvmInterpHandleFillArrayData
+
+//!
+int call_dvmInterpHandleFillArrayData() {
+    typedef bool (*vmHelper)(ArrayObject*, const u2*);
+    vmHelper funcPtr = dvmInterpHandleFillArrayData;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmInterpHandleFillArrayData"); //before move_imm_to_reg to avoid spilling C_SCRATCH_1
+        callFuncPtr((int)funcPtr, "dvmInterpHandleFillArrayData");
+        afterCall("dvmInterpHandleFillArrayData");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmInterpHandleFillArrayData");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmNcgHandlePackedSwitch
+
+//!
+int call_dvmNcgHandlePackedSwitch() {
+    typedef s4 (*vmHelper)(const s4*, s4, u2, s4);
+    vmHelper funcPtr = dvmNcgHandlePackedSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmNcgHandlePackedSwitch");
+        callFuncPtr((int)funcPtr, "dvmNcgHandlePackedSwitch");
+        afterCall("dvmNcgHandlePackedSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmNcgHandlePackedSwitch");
+    }
+    return 0;
+}
+
+int call_dvmJitHandlePackedSwitch() {
+    typedef s4 (*vmHelper)(const s4*, s4, u2, s4);
+    vmHelper funcPtr = dvmJitHandlePackedSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitHandlePackedSwitch");
+        callFuncPtr((int)funcPtr, "dvmJitHandlePackedSwitch");
+        afterCall("dvmJitHandlePackedSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitHandlePackedSwitch");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmNcgHandleSparseSwitch
+
+//!
+int call_dvmNcgHandleSparseSwitch() {
+    typedef s4 (*vmHelper)(const s4*, u2, s4);
+    vmHelper funcPtr = dvmNcgHandleSparseSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmNcgHandleSparseSwitch");
+        callFuncPtr((int)funcPtr, "dvmNcgHandleSparseSwitch");
+        afterCall("dvmNcgHandleSparseSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmNcgHandleSparseSwitch");
+    }
+    return 0;
+}
+
+int call_dvmJitHandleSparseSwitch() {
+    typedef s4 (*vmHelper)(const s4*, u2, s4);
+    vmHelper funcPtr = dvmJitHandleSparseSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitHandleSparseSwitch");
+        callFuncPtr((int)funcPtr, "dvmJitHandleSparseSwitch");
+        afterCall("dvmJitHandleSparseSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitHandleSparseSwitch");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmCanPutArrayElement
+
+//!
+int call_dvmCanPutArrayElement() {
+    typedef bool (*vmHelper)(const ClassObject*, const ClassObject*);
+    vmHelper funcPtr = dvmCanPutArrayElement;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmCanPutArrayElement");
+        callFuncPtr((int)funcPtr, "dvmCanPutArrayElement");
+        afterCall("dvmCanPutArrayElement");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmCanPutArrayElement");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmFindInterfaceMethodInCache
+
+//!
+int call_dvmFindInterfaceMethodInCache() {
+    typedef Method* (*vmHelper)(ClassObject*, u4, const Method*, DvmDex*);
+    vmHelper funcPtr = dvmFindInterfaceMethodInCache;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmFindInterfaceMethodInCache");
+        callFuncPtr((int)funcPtr, "dvmFindInterfaceMethodInCache");
+        afterCall("dvmFindInterfaceMethodInCache");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmFindInterfaceMethodInCache");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmHandleStackOverflow
+
+//!
+int call_dvmHandleStackOverflow() {
+    typedef void (*vmHelper)(Thread*, const Method*);
+    vmHelper funcPtr = dvmHandleStackOverflow;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmHandleStackOverflow");
+        callFuncPtr((int)funcPtr, "dvmHandleStackOverflow");
+        afterCall("dvmHandleStackOverflow");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmHandleStackOverflow");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveString
+
+//!
+int call_dvmResolveString() {
+    //StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx)
+    typedef StringObject* (*vmHelper)(const ClassObject*, u4);
+    vmHelper funcPtr = dvmResolveString;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveString");
+        callFuncPtr((int)funcPtr, "dvmResolveString");
+        afterCall("dvmResolveString");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveString");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveInstField
+
+//!
+int call_dvmResolveInstField() {
+    //InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx)
+    typedef InstField* (*vmHelper)(const ClassObject*, u4);
+    vmHelper funcPtr = dvmResolveInstField;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveInstField");
+        callFuncPtr((int)funcPtr, "dvmResolveInstField");
+        afterCall("dvmResolveInstField");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveInstField");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveStaticField
+
+//!
+int call_dvmResolveStaticField() {
+    //StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx)
+    typedef StaticField* (*vmHelper)(const ClassObject*, u4);
+    vmHelper funcPtr = dvmResolveStaticField;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveStaticField");
+        callFuncPtr((int)funcPtr, "dvmResolveStaticField");
+        afterCall("dvmResolveStaticField");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveStaticField");
+    }
+    return 0;
+}
+
+#define P_GPR_2 PhysicalReg_ECX
+/*!
+\brief This function is used to resolve a string reference
+
+INPUT: const pool index in %eax
+
+OUTPUT: resolved string in %eax
+
+The registers are hard-coded, 2 physical registers %esi and %edx are used as scratch registers;
+It calls a C function dvmResolveString;
+The only register that is still live after this function is ebx
+*/
+int const_string_resolve() {
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    insertLabel(".const_string_resolve", false);
+    //method stored in glue structure as well as on the interpreted stack
+    get_glue_method_class(P_GPR_2, true);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_2, true, 0, PhysicalReg_ESP, true);
+    call_dvmResolveString();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg( OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+    x86_return();
+    return 0;
+}
+#undef P_GPR_2
+/*!
+\brief This function is used to resolve a class
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved class in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveClass;
+The only register that is still live after this function is ebx
+*/
+int resolve_class2(
+           int startLR/*scratch register*/, bool isPhysical, int indexReg/*const pool index*/,
+           bool indexPhysical, int thirdArg) {
+    insertLabel(".class_resolve", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    //push index to stack first, to free indexReg
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+    get_glue_method_class(startLR, isPhysical);
+    move_imm_to_mem(OpndSize_32, thirdArg, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveClass();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+/*!
+\brief This function is used to resolve a method, and it is called once with %eax for both indexReg and startLR
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved method in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveMethod;
+The only register that is still live after this function is ebx
+*/
+int resolve_method2(
+            int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+            bool indexPhysical,
+            int thirdArg/*VIRTUAL*/) {
+    if(thirdArg == METHOD_VIRTUAL)
+        insertLabel(".virtual_method_resolve", false);
+    else if(thirdArg == METHOD_DIRECT)
+        insertLabel(".direct_method_resolve", false);
+    else if(thirdArg == METHOD_STATIC)
+        insertLabel(".static_method_resolve", false);
+
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_glue_method_class(startLR, isPhysical);
+
+    move_imm_to_mem(OpndSize_32, thirdArg, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveMethod();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+/*!
+\brief This function is used to resolve an instance field
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved field in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveInstField;
+The only register that is still live after this function is ebx
+*/
+int resolve_inst_field2(
+            int startLR/*logical register index*/, bool isPhysical,
+            int indexReg/*const pool index*/, bool indexPhysical) {
+    insertLabel(".inst_field_resolve", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+    //method stored in glue structure as well as interpreted stack
+    get_glue_method_class(startLR, isPhysical);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveInstField();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+/*!
+\brief This function is used to resolve a static field
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved field in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveStaticField;
+The only register that is still live after this function is ebx
+*/
+int resolve_static_field2(
+              int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+              bool indexPhysical) {
+    insertLabel(".static_field_resolve", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+    get_glue_method_class(startLR, isPhysical);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveStaticField();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+
+int pushAllRegs() {
+    load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EAX, true, 24, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EBX, true, 20, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_ECX, true, 16, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EDX, true, 12, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_ESI, true, 8, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EDI, true, 4, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EBP, true, 0, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    return 0;
+}
+int popAllRegs() {
+    move_mem_to_reg_noalloc(OpndSize_32, 24, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EAX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 20, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EBX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 16, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_ECX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 12, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EDX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 8, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_ESI, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 4, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EDI, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 0, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EBP, true);
+    load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    return 0;
+}
+
+void dump_nop(int size) {
+    switch(size) {
+        case 1:
+          *stream = 0x90;
+          break;
+        case 2:
+          *stream = 0x66;
+          *(stream +1) = 0x90;
+          break;
+        case 3:
+          *stream = 0x0f;
+          *(stream + 1) = 0x1f;
+          *(stream + 2) = 0x00;
+          break;
+        default:
+          //TODO: add more cases.
+          break;
+    }
+    stream += size;
+}
diff --git a/vm/compiler/codegen/x86/LowerInvoke.cpp b/vm/compiler/codegen/x86/LowerInvoke.cpp
new file mode 100644
index 0000000..3d02190
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerInvoke.cpp
@@ -0,0 +1,1661 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerInvoke.cpp
+    \brief This file lowers the following bytecodes: INVOKE_XXX
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "mterp/Mterp.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+char* streamMisPred = NULL;
+
+/* according to callee, decide the ArgsDoneType*/
+ArgsDoneType convertCalleeToType(const Method* calleeMethod) {
+    if(calleeMethod == NULL)
+        return ArgsDone_Full;
+    if(dvmIsNativeMethod(calleeMethod))
+        return ArgsDone_Native;
+    return ArgsDone_Normal;
+}
+int common_invokeMethodRange(ArgsDoneType);
+int common_invokeMethodNoRange(ArgsDoneType);
+void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg);
+
+//inputs to common_invokeMethodRange: %ecx
+//          common_errNoSuchMethod: %edx
+#define P_GPR_1 PhysicalReg_ESI
+#define P_GPR_2 PhysicalReg_EBX
+#define P_GPR_3 PhysicalReg_ECX
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+
+#ifdef WITH_JIT_INLINING
+/*
+ * The function here takes care the
+ * branch over if prediction is correct and the misprediction target for misPredBranchOver.
+ */
+static void genLandingPadForMispredictedCallee(MIR* mir) {
+    BasicBlock *fallThrough = traceCurrentBB->fallThrough;
+    /* Bypass the move-result block if there is one */
+    if (fallThrough->firstMIRInsn) {
+        assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
+        fallThrough = fallThrough->fallThrough;
+    }
+    /* Generate a branch over if the predicted inlining is correct */
+    jumpToBasicBlock(stream, fallThrough->id);
+    /* Hook up the target to the verification branch */
+    int relativeNCG = stream - streamMisPred;
+    unsigned instSize = encoder_get_inst_size(streamMisPred);
+    relativeNCG -= instSize; //size of the instruction
+    updateJumpInst(streamMisPred, OpndSize_8, relativeNCG);
+}
+#endif
+
+//! LOWER bytecode INVOKE_VIRTUAL without usage of helper function
+
+//!
+int common_invoke_virtual_nohelper(bool isRange, u2 tmp, u2 vD) {
+#ifdef WITH_JIT_INLINING
+    /*
+     * If the invoke has non-null misPredBranchOver, we need to generate
+     * the non-inlined version of the invoke here to handle the
+     * mispredicted case.
+     */
+    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
+        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
+    }
+#endif
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+
+    get_virtual_reg(vD, OpndSize_32, 5, false);
+    simpleNullCheck(5, false, vD);
+#ifndef PREDICTED_CHAINING
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 5, false, 6, false); //clazz of "this"
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 6, false, 7, false); //vtable
+    /* method is already resolved in trace-based JIT */
+    int methodIndex =
+                currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
+    move_mem_to_reg(OpndSize_32, methodIndex*4, 7, false, PhysicalReg_ECX, true);
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+#else
+    int methodIndex =
+                currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
+    gen_predicted_chain(isRange, tmp, methodIndex*4, false, 5/*tmp5*/);
+#endif
+    ///////////////////////////////////
+    return 0;
+}
+//! wrapper to call either common_invoke_virtual_helper or common_invoke_virtual_nohelper
+
+//!
+int common_invoke_virtual(bool isRange, u2 tmp, u2 vD) {
+    return common_invoke_virtual_nohelper(isRange, tmp, vD);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+#define P_GPR_1 PhysicalReg_ESI
+#define P_GPR_2 PhysicalReg_EBX
+#define P_GPR_3 PhysicalReg_EDX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_SUPER
+
+//! It will use helper function if the switch is on
+int common_invoke_super(bool isRange, u2 tmp) {
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ///////////////////////
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    /* method is already resolved in trace-based JIT */
+    int mIndex = currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
+    const Method *calleeMethod =
+        currentMethod->clazz->super->vtable[mIndex];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    if(isRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    ///////////////////////////////
+    return 0;
+}
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+//! helper function to handle no such method error
+
+//!
+int invoke_super_nsm() {
+    insertLabel(".invoke_super_nsm", false);
+    //NOTE: it seems that the name in %edx is not used in common_errNoSuchMethod
+    move_mem_to_reg(OpndSize_32, offMethod_name, PhysicalReg_EAX, true, PhysicalReg_EDX, true); //method name
+    unconditional_jump("common_errNoSuchMethod", false);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ESI
+#define P_GPR_3 PhysicalReg_ECX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_DIRECT
+
+//! It will use helper function if the switch is on
+int common_invoke_direct(bool isRange, u2 tmp, u2 vD) {
+    //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ////////////////////////////////////
+    get_virtual_reg(vD, OpndSize_32, 5, false);
+    simpleNullCheck(5, false, vD);
+    /* method is already resolved in trace-based JIT */
+    const Method *calleeMethod =
+        currentMethod->clazz->pDvmDex->pResMethods[tmp];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    //%ecx passed to common_invokeMethod...
+    if(isRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    ////////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+#define P_GPR_1  PhysicalReg_EBX
+#define P_GPR_3  PhysicalReg_ECX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_STATIC
+
+//! It will use helper function if the switch is on
+int common_invoke_static(bool isRange, u2 tmp) {
+    //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ////////////////////////////
+    /* method is already resolved in trace-based JIT */
+    const Method *calleeMethod =
+        currentMethod->clazz->pDvmDex->pResMethods[tmp];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    //%ecx passed to common_invokeMethod...
+    if(isRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    ////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_EAX //scratch
+#define P_GPR_3 PhysicalReg_ECX
+#define P_SCRATCH_1 PhysicalReg_ESI //clazz of object
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_INTERFACE
+
+//! It will use helper function if the switch is on
+int common_invoke_interface(bool isRange, u2 tmp, u2 vD) {
+#ifdef WITH_JIT_INLINING
+    /*
+     * If the invoke has non-null misPredBranchOver, we need to generate
+     * the non-inlined version of the invoke here to handle the
+     * mispredicted case.
+     */
+    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
+        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
+    }
+#endif
+    export_pc(); //use %edx
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ///////////////////////
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_virtual_reg(vD, OpndSize_32, 1, false);
+    simpleNullCheck(1, false, vD);
+
+#ifndef PREDICTED_CHAINING
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
+    /* for trace-based JIT, pDvmDex is a constant at JIT time
+       4th argument to dvmFindInterfaceMethodInCache at -4(%esp) */
+    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 5, false);
+    /* for trace-based JIT, method is a constant at JIT time
+       3rd argument to dvmFindInterfaceMethodInCache at 8(%esp) */
+    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null;
+    call_dvmFindInterfaceMethodInCache();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+
+    conditional_jump_global_API(Condition_E, "common_exceptionThrown", false);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+#else
+    gen_predicted_chain(isRange, tmp, -1, true /*interface*/, 1/*tmp1*/);
+#endif
+    ///////////////////////
+    return 0;
+}
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+//! lower bytecode INVOKE_VIRTUAL by calling common_invoke_virtual
+
+//!
+int op_invoke_virtual() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument, which is the "this" pointer
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 vD = FETCH(2) & 0xf;
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_virtual(false/*not range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_SUPER by calling common_invoke_super
+
+//!
+int op_invoke_super() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_super(false/*not range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_DIRECT by calling common_invoke_direct
+
+//!
+int op_invoke_direct() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument, which is the "this" pointer
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 vD = FETCH(2) & 0xf;
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_direct(false/*not range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_STATIC by calling common_invoke_static
+
+//!
+int op_invoke_static() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_static(false/*not range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_INTERFACE by calling common_invoke_interface
+
+//!
+int op_invoke_interface() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument, which is the "this" pointer
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 vD = FETCH(2) & 0xf;
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_interface(false/*not range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_VIRTUAL_RANGE by calling common_invoke_virtual
+
+//!
+int op_invoke_virtual_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //AA|op BBBB CCCC
+    //CCCC: the first argument, which is the "this" pointer
+    //AA: argument count
+    u2 tmp = FETCH(1); //BBBB, method index
+    u2 vD = FETCH(2); //the first argument
+    int retval = common_invoke_virtual(true/*range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_SUPER_RANGE by calling common_invoke_super
+
+//!
+int op_invoke_super_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    int retval = common_invoke_super(true/*range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_DIRECT_RANGE by calling common_invoke_direct
+
+//!
+int op_invoke_direct_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    u2 vD = FETCH(2); //the first argument
+    int retval = common_invoke_direct(true/*range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_STATIC_RANGE by calling common_invoke_static
+
+//!
+int op_invoke_static_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    int retval = common_invoke_static(true/*range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_INTERFACE_RANGE by calling common_invoke_interface
+
+//!
+int op_invoke_interface_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    u2 vD = FETCH(2); //the first argument
+    int retval = common_invoke_interface(true/*range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+
+//used %ecx, %edi, %esp %ebp
+#define P_GPR_1 PhysicalReg_EBX
+#define P_SCRATCH_1 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EAX
+#define P_SCRATCH_3 PhysicalReg_EDX
+#define P_SCRATCH_4 PhysicalReg_ESI
+#define P_SCRATCH_5 PhysicalReg_EAX
+//! pass the arguments for invoking method without range
+
+//!
+int common_invokeMethodNoRange_noJmp() {
+    u2 count = INST_B(inst);
+    u2 vD = FETCH(2) & 0xf;
+    u2 vE = (FETCH(2) >> 4) & 0xf;
+    u2 vF = (FETCH(2) >> 8) & 0xf;
+    u2 vG = (FETCH(2) >> 12) & 0xf;
+    u2 vA = INST_A(inst); //5th argument
+    int offsetFromSaveArea = -4;
+    if(count == 5) {
+        get_virtual_reg(vA, OpndSize_32, 22, false);
+        move_reg_to_mem(OpndSize_32, 22, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 4) {
+        get_virtual_reg(vG, OpndSize_32, 23, false);
+        move_reg_to_mem(OpndSize_32, 23, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 3) {
+        get_virtual_reg(vF, OpndSize_32, 24, false);
+        move_reg_to_mem(OpndSize_32, 24, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 2) {
+        get_virtual_reg(vE, OpndSize_32, 25, false);
+        move_reg_to_mem(OpndSize_32, 25, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 1) {
+        get_virtual_reg(vD, OpndSize_32, 26, false);
+        move_reg_to_mem(OpndSize_32, 26, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+    }
+    return 0;
+}
+
+int common_invokeMethod_Jmp(ArgsDoneType form) {
+    nextVersionOfHardReg(PhysicalReg_EDX, 1);
+    move_imm_to_reg(OpndSize_32, (int)rPC, PhysicalReg_EDX, true);
+    //arguments needed in ArgsDone:
+    //    start of HotChainingCell for next bytecode: -4(%esp)
+    //    start of InvokeSingletonChainingCell for callee: -8(%esp)
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->fallThrough->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->fallThrough->id, 4, PhysicalReg_ESP, true);
+    // for honeycomb: JNI call doesn't need a chaining cell, so the taken branch is null
+    if(traceCurrentBB->taken)
+        insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    int takenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+    move_chain_to_mem(OpndSize_32, takenId, 0, PhysicalReg_ESP, true);
+    if(form == ArgsDone_Full)
+        unconditional_jump_global_API(".invokeArgsDone_jit", false);
+    else if(form == ArgsDone_Native)
+        unconditional_jump_global_API(".invokeArgsDone_native", false);
+    else
+        unconditional_jump_global_API(".invokeArgsDone_normal", false);
+    return 0;
+}
+
+int common_invokeMethodNoRange(ArgsDoneType form) {
+    common_invokeMethodNoRange_noJmp();
+    common_invokeMethod_Jmp(form);
+    return 0;
+}
+
+#undef P_GPR_1
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+#undef P_SCRATCH_5
+
+//input: %ecx (method to call)
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ESI
+#define P_GPR_3 PhysicalReg_EDX //not used with P_SCRATCH_2
+#define P_SCRATCH_1 PhysicalReg_EAX
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define P_SCRATCH_3 PhysicalReg_EAX
+#define P_SCRATCH_4 PhysicalReg_EDX
+#define P_SCRATCH_5 PhysicalReg_EAX
+#define P_SCRATCH_6 PhysicalReg_EDX
+#define P_SCRATCH_7 PhysicalReg_EAX
+#define P_SCRATCH_8 PhysicalReg_EDX
+#define P_SCRATCH_9 PhysicalReg_EAX
+#define P_SCRATCH_10 PhysicalReg_EDX
+//! pass the arguments for invoking method with range
+
+//! loop is unrolled when count <= 10
+int common_invokeMethodRange_noJmp() {
+    u2 count = INST_AA(inst);
+    u2 vD = FETCH(2); //the first argument
+    savearea_from_fp(21, false);
+    //vD to rFP-4*count-20
+    //vD+1 to rFP-4*count-20+4 = rFP-20-4*(count-1)
+    if(count >= 1 && count <= 10) {
+        get_virtual_reg(vD, OpndSize_32, 22, false);
+        move_reg_to_mem(OpndSize_32, 22, false, -4*count, 21, false);
+    }
+    if(count >= 2 && count <= 10) {
+        get_virtual_reg(vD+1, OpndSize_32, 23, false);
+        move_reg_to_mem(OpndSize_32, 23, false, -4*(count-1), 21, false);
+    }
+    if(count >= 3 && count <= 10) {
+        get_virtual_reg(vD+2, OpndSize_32, 24, false);
+        move_reg_to_mem(OpndSize_32, 24, false, -4*(count-2), 21, false);
+    }
+    if(count >= 4 && count <= 10) {
+        get_virtual_reg(vD+3, OpndSize_32, 25, false);
+        move_reg_to_mem(OpndSize_32, 25, false, -4*(count-3), 21, false);
+    }
+    if(count >= 5 && count <= 10) {
+        get_virtual_reg(vD+4, OpndSize_32, 26, false);
+        move_reg_to_mem(OpndSize_32, 26, false, -4*(count-4), 21, false);
+    }
+    if(count >= 6 && count <= 10) {
+        get_virtual_reg(vD+5, OpndSize_32, 27, false);
+        move_reg_to_mem(OpndSize_32, 27, false, -4*(count-5), 21, false);
+    }
+    if(count >= 7 && count <= 10) {
+        get_virtual_reg(vD+6, OpndSize_32, 28, false);
+        move_reg_to_mem(OpndSize_32, 28, false, -4*(count-6), 21, false);
+    }
+    if(count >= 8 && count <= 10) {
+        get_virtual_reg(vD+7, OpndSize_32, 29, false);
+        move_reg_to_mem(OpndSize_32, 29, false, -4*(count-7), 21, false);
+    }
+    if(count >= 9 && count <= 10) {
+        get_virtual_reg(vD+8, OpndSize_32, 30, false);
+        move_reg_to_mem(OpndSize_32, 30, false, -4*(count-8), 21, false);
+    }
+    if(count == 10) {
+        get_virtual_reg(vD+9, OpndSize_32, 31, false);
+        move_reg_to_mem(OpndSize_32, 31, false, -4*(count-9), 21, false);
+    }
+    if(count > 10) {
+        //dump to memory first, should we set physicalReg to Null?
+        //this bytecodes uses a set of virtual registers (update getVirtualInfo)
+        //this is necessary to correctly insert transfer points
+        int k;
+        for(k = 0; k < count; k++) {
+            spillVirtualReg(vD+k, LowOpndRegType_gp, true); //will update refCount
+        }
+        load_effective_addr(4*vD, PhysicalReg_FP, true, 12, false);
+        alu_binary_imm_reg(OpndSize_32, sub_opc, 4*count, 21, false);
+        move_imm_to_reg(OpndSize_32, count, 13, false);
+        insertLabel(".invokeMethod_1", true); //if checkDup: will perform work from ShortWorklist
+        rememberState(1);
+        move_mem_to_reg(OpndSize_32, 0, 12, false, 14, false);
+        move_reg_to_mem(OpndSize_32, 14, false, 0, 21, false);
+        load_effective_addr(4, 12, false, 12, false);
+        alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 13, false);
+        load_effective_addr(4, 21, false, 21, false);
+        transferToState(1);
+        conditional_jump(Condition_NE, ".invokeMethod_1", true); //backward branch
+    }
+    return 0;
+}
+
+int common_invokeMethodRange(ArgsDoneType form) {
+    common_invokeMethodRange_noJmp();
+    common_invokeMethod_Jmp(form);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+#undef P_SCRATCH_5
+#undef P_SCRATCH_6
+#undef P_SCRATCH_7
+#undef P_SCRATCH_8
+#undef P_SCRATCH_9
+#undef P_SCRATCH_10
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EAX
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define P_SCRATCH_3 PhysicalReg_EAX
+#define P_SCRATCH_4 PhysicalReg_EDX
+#define P_SCRATCH_5 PhysicalReg_EAX
+#define P_SCRATCH_6 PhysicalReg_EDX
+
+//! spill a register to native stack
+
+//! decrease %esp by 4, then store a register at 0(%esp)
+int spill_reg(int reg, bool isPhysical) {
+    load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, 0, PhysicalReg_ESP, true);
+    return 0;
+}
+//! get a register from native stack
+
+//! load a register from 0(%esp), then increase %esp by 4
+int unspill_reg(int reg, bool isPhysical) {
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, reg, isPhysical);
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    return 0;
+}
+
+void generate_invokeNative(bool generateForNcg); //forward declaration
+void generate_stackOverflow(); //forward declaration
+
+//! common code to invoke a method after all arguments are handled
+
+//!
+//takes one argument to generate code
+//  for invokeNativeSingle (form == ArgsDone_Native)
+//   or invokeNonNativeSingle (form == ArgsDone_Normal) when WITH_JIT is true
+//   to dynamically determine which one to choose (form == ArgsDone_Full)
+/* common_invokeArgsDone is called at NCG time and
+     at execution time during relocation
+   generate invokeArgsDone for NCG if isJitFull is false && form == Full */
+int common_invokeArgsDone(ArgsDoneType form, bool isJitFull) {
+    bool generateForNcg = false;
+    if(form == ArgsDone_Full) {
+        if(isJitFull)
+            insertLabel(".invokeArgsDone_jit", false);
+        else {
+            insertLabel(".invokeArgsDone", false);
+            generateForNcg = true;
+        }
+    }
+    else if(form == ArgsDone_Normal)
+        insertLabel(".invokeArgsDone_normal", false);
+    else if(form == ArgsDone_Native)
+        insertLabel(".invokeArgsDone_native", false);
+    //%ecx: methodToCall
+    movez_mem_to_reg(OpndSize_16, offMethod_registersSize, PhysicalReg_ECX, true, P_SCRATCH_1, true); //regSize
+    scratchRegs[0] = PhysicalReg_EBX; scratchRegs[1] = PhysicalReg_ESI;
+    scratchRegs[2] = PhysicalReg_EDX; scratchRegs[3] = PhysicalReg_Null;
+    savearea_from_fp(P_GPR_3, true);
+    alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_1, true);
+    alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_1, true, P_GPR_3, true);
+    //update newSaveArea->savedPc, here P_GPR_3 is new FP
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true);
+    movez_mem_to_reg(OpndSize_16, offMethod_outsSize, PhysicalReg_ECX, true, P_SCRATCH_2, true); //outsSize
+    move_reg_to_reg(OpndSize_32, P_GPR_3, true, P_GPR_1, true); //new FP
+    alu_binary_imm_reg(OpndSize_32, sub_opc, sizeofStackSaveArea, P_GPR_3, true);
+
+    alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_2, true);
+    alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_2, true, P_GPR_3, true);
+    get_self_pointer(P_SCRATCH_3, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offStackSaveArea_prevFrame-sizeofStackSaveArea, P_GPR_1, true); //set stack->prevFrame
+    compare_mem_reg(OpndSize_32, offsetof(Thread, interpStackEnd), P_SCRATCH_3, true, P_GPR_3, true);
+    conditional_jump(Condition_L, ".stackOverflow", true);
+
+    if(form == ArgsDone_Full) {
+        test_imm_mem(OpndSize_32, ACC_NATIVE, offMethod_accessFlags, PhysicalReg_ECX, true);
+    }
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offStackSaveArea_method-sizeofStackSaveArea, P_GPR_1, true); //set stack->method
+
+    if(form == ArgsDone_Native || form == ArgsDone_Full) {
+        /* to correctly handle code cache reset:
+           update returnAddr and check returnAddr after done with the native method
+           if returnAddr is set to NULL during code cache reset,
+           the execution will correctly continue with interpreter */
+        //get returnAddr from 4(%esp) and update stack
+        move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
+                        PhysicalReg_EDX, true);
+        move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
+                        offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_1, true);
+    }
+    if(form == ArgsDone_Native) {
+        generate_invokeNative(generateForNcg);
+        return 0;
+    }
+    if(form == ArgsDone_Full) {
+        conditional_jump(Condition_NE, ".invokeNative", true);
+    }
+    move_mem_to_reg(OpndSize_32, offMethod_clazz, PhysicalReg_ECX, true, P_SCRATCH_4, true); //get method->claz
+    move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, P_SCRATCH_4, true, P_SCRATCH_4, true); //get method->clazz->pDvmDex
+    move_reg_to_reg(OpndSize_32, P_GPR_1, true, PhysicalReg_FP, true); //update rFP
+    get_self_pointer(P_GPR_1, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offsetof(Thread, interpSave.method), P_GPR_1, true); //glue->method
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_4, true, offsetof(Thread, interpSave.methodClassDex), P_GPR_1, true); //set_glue_dvmdex
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set glue->self->frame
+    if(!generateForNcg) {
+        /* returnAddr updated already for Full */
+        //get returnAddr from 4(%esp) and update stack
+        if(form == ArgsDone_Normal)
+            move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
+                            PhysicalReg_EDX, true);
+        //for JIT: starting bytecode in %ebx to invoke JitToInterp
+        move_mem_to_reg(OpndSize_32, offMethod_insns, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
+        if(form == ArgsDone_Normal)
+            move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
+                            offStackSaveArea_returnAddr-sizeofStackSaveArea, PhysicalReg_FP, true);
+    }
+
+    insertLabel(".invokeInterp", true);
+    if(!generateForNcg) {
+        bool callNoChain = false;
+#ifdef PREDICTED_CHAINING
+        if(form == ArgsDone_Full) callNoChain = true;
+#endif
+        if(callNoChain) {
+            scratchRegs[0] = PhysicalReg_EAX;
+            load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            call_dvmJitToInterpTraceSelectNoChain(); //input: rPC in %ebx
+        } else {
+            //jump to the stub at (%esp)
+            move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true,
+                            PhysicalReg_EDX, true);
+            load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            unconditional_jump_reg(PhysicalReg_EDX, true);
+        }
+    }
+
+    if(form == ArgsDone_Full) generate_invokeNative(generateForNcg);
+    generate_stackOverflow();
+    return 0;
+}
+
+/* when WITH_JIT is true,
+     JIT'ed code invokes native method, after invoke, execution will continue
+     with the interpreter or with JIT'ed code if chained
+*/
+void generate_invokeNative(bool generateForNcg) {
+    insertLabel(".invokeNative", true);
+    //if(!generateForNcg)
+    //    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 20, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EDX;
+    get_self_pointer(P_SCRATCH_1, true); //glue->self
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 12, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 24, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offThread_jniLocal_nextEntry, P_SCRATCH_1, true, P_SCRATCH_2, true); //get self->local_next
+    scratchRegs[1] = PhysicalReg_EAX;
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_1, true); //update jniLocalRef of stack
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, offThread_curFrame, P_SCRATCH_1, true); //set self->curFrame
+    move_imm_to_mem(OpndSize_32, 0, offThread_inJitCodeCache, P_SCRATCH_1, true); //clear self->inJitCodeCache
+    load_effective_addr(offsetof(Thread, interpSave.retval), P_SCRATCH_1, true, P_SCRATCH_3, true); //self->retval
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_3, true, 4, PhysicalReg_ESP, true);
+    //NOTE: native method checks the interpreted stack for arguments
+    //      The immediate arguments on native stack: address of return value, new FP, self
+    call_mem(40, PhysicalReg_ECX, true);//*40(%ecx)
+    //we can't assume the argument stack is unmodified after the function call
+    //duplicate newFP & glue->self on stack: newFP (-28 & -8) glue->self (-16 & -4)
+    move_mem_to_reg(OpndSize_32, 20, PhysicalReg_ESP, true, P_GPR_3, true); //new FP
+    move_mem_to_reg(OpndSize_32, 24, PhysicalReg_ESP, true, P_GPR_1, true); //glue->self
+    load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_1, true); //newSaveArea->jniLocal
+    compare_imm_mem(OpndSize_32, 0, offThread_exception, P_GPR_1, true); //self->exception
+    if(!generateForNcg)
+        load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //NOTE: PhysicalReg_FP should be callee-saved register
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set self->curFrame
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, offThread_jniLocal_nextEntry, P_GPR_1, true); //set self->jniLocal
+    conditional_jump(Condition_NE, "common_exceptionThrown", false);
+    if(!generateForNcg) {
+        //get returnAddr, if it is not NULL,
+        //    return to JIT'ed returnAddr after executing the native method
+        /* to correctly handle code cache reset:
+           update returnAddr and check returnAddr after done with the native method
+           if returnAddr is set to NULL during code cache reset,
+           the execution will correctly continue with interpreter */
+        move_mem_to_reg(OpndSize_32, offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_2, true);
+        //set self->inJitCodeCache to returnAddr (P_GPR_1 is in %ebx)
+        move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offThread_inJitCodeCache, P_GPR_1, true);
+        move_mem_to_reg(OpndSize_32, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true, PhysicalReg_EBX, true); //savedPc
+        compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
+        conditional_jump(Condition_E, ".nativeToInterp", true);
+        unconditional_jump_reg(P_SCRATCH_2, true);
+        //if returnAddr is NULL, return to interpreter after executing the native method
+        insertLabel(".nativeToInterp", true);
+        //move rPC by 6 (3 bytecode units for INVOKE)
+        alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EBX, true);
+        scratchRegs[0] = PhysicalReg_EAX;
+        call_dvmJitToInterpTraceSelectNoChain(); //rPC in %ebx
+    }
+    return;
+}
+void generate_stackOverflow() {
+    insertLabel(".stackOverflow", true);
+    //load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 4, PhysicalReg_ESP, true);
+    get_self_pointer(P_GPR_1, true); //glue->self
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
+    call_dvmHandleStackOverflow();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    unconditional_jump("common_exceptionThrown", false);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+#undef P_SCRATCH_5
+#undef P_SCRATCH_6
+
+/////////////////////////////////////////////
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_SCRATCH_1 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define P_SCRATCH_3 PhysicalReg_ESI
+#define P_SCRATCH_4 PhysicalReg_EDX
+//! lower bytecode EXECUTE_INLINE
+
+//!
+int op_execute_inline(bool isRange) {
+    //tmp, vC, vD, vE, vF
+    int num;
+    if(!isRange) num = INST_B(inst);
+    else num = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    u2 vC, vD, vE, vF;
+    if(!isRange) {
+        vC = FETCH(2) & 0xf;
+        vD = (FETCH(2) >> 4) & 0xf;
+        vE = (FETCH(2) >> 8) & 0xf;
+        vF = FETCH(2) >> 12;
+    } else {
+        vC = FETCH(2);
+        vD = vC + 1;
+        vE = vC + 2;
+        vF = vC + 3;
+    }
+    export_pc();
+    switch (tmp) {
+        case INLINE_EMPTYINLINEMETHOD:
+            return 0;  /* Nop */
+        case INLINE_STRING_LENGTH:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 1, false);
+            conditional_jump(Condition_NE, ".do_inlined_string_length", true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_1;
+            jumpToExceptionThrown(1/*exception number*/);
+            insertLabel(".do_inlined_string_length", true);
+            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 2, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 2, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_STRING_IS_EMPTY:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 1, false);
+            conditional_jump(Condition_NE, ".do_inlined_string_length", true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_1;
+            jumpToExceptionThrown(1/*exception number*/);
+            insertLabel(".do_inlined_string_length", true);
+            compare_imm_mem(OpndSize_32, 0, 0x14, 1, false);
+            conditional_jump(Condition_E, ".inlined_string_length_return_true",
+                             true);
+            get_self_pointer(2, false);
+            move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, interpSave.retval), 2, false);
+            unconditional_jump(".inlined_string_length_done", true);
+            insertLabel(".inlined_string_length_return_true", true);
+            get_self_pointer(2, false);
+            move_imm_to_mem(OpndSize_32, 1, offsetof(Thread, interpSave.retval), 2, false);
+            insertLabel(".inlined_string_length_done", true);
+            return 0;
+        case INLINE_MATH_ABS_INT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
+            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 2, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 1, false);
+            alu_binary_reg_reg(OpndSize_32, sub_opc, 2, false, 1, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_MATH_ABS_LONG:
+            get_virtual_reg(vD, OpndSize_32, 1, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
+            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 1, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 3, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 4, false);
+            get_virtual_reg(vC, OpndSize_32, 5, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 5, false, 1, false);
+            get_self_pointer(6, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 6, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 3, false);
+            move_reg_to_mem(OpndSize_32, 3, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
+            alu_binary_reg_mem(OpndSize_32, sub_opc, 4, false, offsetof(Thread, interpSave.retval), 6, false);
+            alu_binary_reg_mem(OpndSize_32, sbb_opc, 4, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
+            return 0;
+        case INLINE_MATH_MAX_INT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            compare_reg_reg(1, false, 2, false);
+            conditional_move_reg_to_reg(OpndSize_32, Condition_GE, 2,
+                                        false/*src*/, 1, false/*dst*/);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_MATH_ABS_FLOAT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 1, false);
+            get_self_pointer(2, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
+            return 0;
+        case INLINE_MATH_ABS_DOUBLE:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 2, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_STRING_FASTINDEXOF_II:
+#if defined(USE_GLOBAL_STRING_DEFS)
+            break;
+#else
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            get_virtual_reg(vE, OpndSize_32, 3, false);
+            conditional_jump(Condition_NE, ".do_inlined_string_fastIndexof",
+                             true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_1;
+            jumpToExceptionThrown(1/*exception number*/);
+            insertLabel(".do_inlined_string_fastIndexof", true);
+            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 4, false);
+            move_mem_to_reg(OpndSize_32, 0x8, 1, false, 5, false);
+            move_mem_to_reg(OpndSize_32, 0x10, 1, false, 6, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 1, false, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 3, false);
+            conditional_move_reg_to_reg(OpndSize_32, Condition_NS, 3, false, 1,
+                                        false);
+            compare_reg_reg(4, false, 1, false);
+            conditional_jump(Condition_GE,
+                             ".do_inlined_string_fastIndexof_exitfalse", true);
+            dump_mem_scale_reg(Mnemonic_LEA, OpndSize_32, 5, false, 0xc/*disp*/,
+                               6, false, 2, 5, false, LowOpndRegType_gp);
+            movez_mem_disp_scale_to_reg(OpndSize_16, 5, false, 0, 1, false, 2,
+                                        3, false);
+            compare_reg_reg(3, false, 2, false);
+            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
+                             true);
+            load_effective_addr(0x1, 1, false, 3, false);
+            load_effective_addr_scale(5, false, 3, false, 2, 5, false);
+            unconditional_jump(".do_inlined_string_fastIndexof_iter", true);
+            insertLabel(".do_inlined_string_fastIndexof_ch_cmp", true);
+            if(gDvm.executionMode == kExecutionModeNcgO1) {
+                rememberState(1);
+            }
+            movez_mem_to_reg(OpndSize_16, 0, 5, false, 6, false);
+            load_effective_addr(0x2, 5, false, 5, false);
+            compare_reg_reg(6, false, 2, false);
+            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
+                             true);
+            load_effective_addr(0x1, 3, false, 3, false);
+            insertLabel(".do_inlined_string_fastIndexof_iter", true);
+            compare_reg_reg(4, false, 3, false);
+            move_reg_to_reg(OpndSize_32, 3, false, 1, false);
+            if(gDvm.executionMode == kExecutionModeNcgO1) {
+                transferToState(1);
+            }
+            conditional_jump(Condition_NE,
+                             ".do_inlined_string_fastIndexof_ch_cmp", true);
+            insertLabel(".do_inlined_string_fastIndexof_exitfalse", true);
+            move_imm_to_reg(OpndSize_32, 0xffffffff, 1,  false);
+            insertLabel(".do_inlined_string_fastIndexof_exit", true);
+            get_self_pointer(7, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 7, false);
+            return 0;
+        case INLINE_FLOAT_TO_RAW_INT_BITS:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_self_pointer(2, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
+            return 0;
+        case INLINE_INT_BITS_TO_FLOAT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_self_pointer(2, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
+            return 0;
+        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_LONG_BITS_TO_DOUBLE:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        default:
+                break;
+    }
+#endif
+    get_self_pointer(PhysicalReg_SCRATCH_1, false);
+    load_effective_addr(offsetof(Thread, interpSave.retval), PhysicalReg_SCRATCH_1, false, 1, false);
+    load_effective_addr(-24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 16, PhysicalReg_ESP, true);
+    if(num >= 1) {
+        get_virtual_reg(vC, OpndSize_32, 2, false);
+        move_reg_to_mem(OpndSize_32, 2, false, 0, PhysicalReg_ESP, true);
+    }
+    if(num >= 2) {
+        get_virtual_reg(vD, OpndSize_32, 3, false);
+        move_reg_to_mem(OpndSize_32, 3, false, 4, PhysicalReg_ESP, true);
+    }
+    if(num >= 3) {
+        get_virtual_reg(vE, OpndSize_32, 4, false);
+        move_reg_to_mem(OpndSize_32, 4, false, 8, PhysicalReg_ESP, true);
+    }
+    if(num >= 4) {
+        get_virtual_reg(vF, OpndSize_32, 5, false);
+        move_reg_to_mem(OpndSize_32, 5, false, 12, PhysicalReg_ESP, true);
+    }
+    beforeCall("execute_inline");
+    load_imm_global_data_API("gDvmInlineOpsTable", OpndSize_32, 6, false);
+    call_mem(16*tmp, 6, false);//
+    afterCall("execute_inline");
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+
+    load_effective_addr(24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    conditional_jump(Condition_NE, ".execute_inline_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    jumpToExceptionThrown(1/*exception number*/);
+    insertLabel(".execute_inline_done", true);
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+
+//! lower bytecode INVOKE_OBJECT_INIT_RANGE
+
+//!
+int op_invoke_object_init_range() {
+    return -1;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_SCRATCH_1 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common code for INVOKE_VIRTUAL_QUICK
+
+//! It uses helper function if the switch is on
+int common_invoke_virtual_quick(bool hasRange, u2 vD, u2 IMMC) {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+    /*
+     * If the invoke has non-null misPredBranchOver, we need to generate
+     * the non-inlined version of the invoke here to handle the
+     * mispredicted case.
+     */
+    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
+        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
+    }
+#endif
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    /////////////////////////////////////////////////
+    get_virtual_reg(vD, OpndSize_32, 1, false);
+    simpleNullCheck(1, false, vD);
+#ifndef PREDICTED_CHAINING
+    move_mem_to_reg(OpndSize_32, 0, 1, false, 2, false);
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 2, false, 3, false);
+    move_mem_to_reg(OpndSize_32, IMMC, 3, false, PhysicalReg_ECX, true);
+
+    if(hasRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+#else
+    gen_predicted_chain(hasRange, -1, IMMC, false, 1/*tmp1*/);
+#endif
+    ////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+//! lower bytecode INVOKE_VIRTUAL_QUICK by calling common_invoke_virtual_quick
+
+//!
+int op_invoke_virtual_quick() {
+    u2 vD = FETCH(2) & 0xf;
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_virtual_quick(false, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_VIRTUAL_QUICK_RANGE by calling common_invoke_virtual_quick
+
+//!
+int op_invoke_virtual_quick_range() {
+    u2 vD = FETCH(2);
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_virtual_quick(true, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EDX
+//! common code to lower INVOKE_SUPER_QUICK
+
+//!
+int common_invoke_super_quick(bool hasRange, u2 vD, u2 IMMC) {
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    compare_imm_VR(OpndSize_32, 0, vD);
+
+    conditional_jump_global_API(Condition_E, "common_errNullObject", false);
+    /* for trace-based JIT, callee is already resolved */
+    int mIndex = IMMC/4;
+    const Method *calleeMethod = currentMethod->clazz->super->vtable[mIndex];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    if(hasRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_SCRATCH_1
+//! lower bytecode INVOKE_SUPER_QUICK by calling common_invoke_super_quick
+
+//!
+int op_invoke_super_quick() {
+    u2 vD = FETCH(2) & 0xf;
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_super_quick(false, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_SUPER_QUICK_RANGE by calling common_invoke_super_quick
+
+//!
+int op_invoke_super_quick_range() {
+    u2 vD = FETCH(2);
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_super_quick(true, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+/////// code to predict the callee method for invoke_virtual & invoke_interface
+#define offChainingCell_clazz 8
+#define offChainingCell_method 12
+#define offChainingCell_counter 16
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_EAX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EDX
+/* TODO gingerbread: implemented for O1, but not for O0:
+   valid input to JitToPatch & use icRechainCount */
+/* update predicted method for invoke interface */
+// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
+void predicted_chain_interface_O0(u2 tmp) {
+    ALOGI("TODO chain_interface_O0");
+
+    /* set up arguments to dvmFindInterfaceMethodInCache */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EDX;
+    call_dvmFindInterfaceMethodInCache();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
+       otherwise, jump to .find_interface_done */
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".find_interface_done", true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    jumpToExceptionThrown(1/*exception number*/);
+
+    /* the interface method is found */
+    insertLabel(".find_interface_done", true);
+    /* reduce counter in chaining cell by 1 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_SCRATCH_2, true); //counter
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_SCRATCH_2, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offChainingCell_counter, P_GPR_1, true);
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    insertLabel(".skipPrediction", true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+}
+
+// 2 inputs: ChainingCell in temp 41, current class object in temp 40
+void predicted_chain_interface_O1(u2 tmp) {
+
+    /* set up arguments to dvmFindInterfaceMethodInCache */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 40, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_10;
+    call_dvmFindInterfaceMethodInCache();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
+       otherwise, jump to .find_interface_done */
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".find_interface_done", true);
+    rememberState(3);
+    scratchRegs[0] = PhysicalReg_SCRATCH_9;
+    jumpToExceptionThrown(1/*exception number*/);
+
+    goToState(3);
+    /* the interface method is found */
+    insertLabel(".find_interface_done", true);
+#if 1 //
+    /* for gingerbread, counter is stored in glue structure
+       if clazz is not initialized, set icRechainCount to 0, otherwise, reduce it by 1 */
+    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 45, false);
+    move_imm_to_reg(OpndSize_32, 0, 43, false);
+    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
+    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
+    /* sub_opc will update control flags, so compare_imm_reg must happen after */
+    compare_imm_reg(OpndSize_32, 0, 45, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
+    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
+#else
+    /* reduce counter in chaining cell by 1 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_counter, 41, false, 33, false); //counter
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
+    move_reg_to_mem(OpndSize_32, 33, false, offChainingCell_counter, 41, false);
+#endif
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, 43, false);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    rememberState(4);
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_8;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    transferToState(4);
+
+    insertLabel(".skipPrediction", true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+}
+
+/* update predicted method for invoke virtual */
+// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
+void predicted_chain_virtual_O0(u2 IMMC) {
+    ALOGI("TODO chain_virtual_O0");
+
+    /* reduce counter in chaining cell by 1 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_GPR_2, true); //counter
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, P_GPR_3, true, P_SCRATCH_2, true);
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_GPR_2, true);
+    move_mem_to_reg(OpndSize_32, IMMC, P_SCRATCH_2, true, PhysicalReg_ECX, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_2, true, offChainingCell_counter, P_GPR_1, true);
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, P_GPR_2, true);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0,  PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //callee method in %ecx for invoke virtual
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+    insertLabel(".skipPrediction", true);
+}
+
+// 2 inputs: ChainingCell in temp 41, current class object in temp 40
+// extra input: predicted clazz in temp 32
+void predicted_chain_virtual_O1(u2 IMMC) {
+
+    /* reduce counter in chaining cell by 1 */
+    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
+    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
+    move_imm_to_reg(OpndSize_32, 0, 43, false);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 40, false, 34, false);
+    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
+    compare_imm_reg(OpndSize_32, 0, 32, false); // after sub_opc
+    move_mem_to_reg(OpndSize_32, IMMC, 34, false, PhysicalReg_ECX, true);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
+    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, 43, false);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    rememberState(2);
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
+    if(traceCurrentBB->taken)
+        insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+    move_chain_to_mem(OpndSize_32, traceTakenId, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_10;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //callee method in %ecx for invoke virtual
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+    transferToState(2);
+
+    insertLabel(".skipPrediction", true);
+}
+
+static int invokeChain_inst = 0;
+/* object "this" is in %ebx */
+void gen_predicted_chain_O0(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
+    ALOGI("TODO predicted_chain_O0");
+
+    /* get current class object */
+    move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true,
+             P_GPR_3, true);
+#ifdef DEBUG_CALL_STACK3
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    move_imm_to_reg(OpndSize_32, 0xdd11, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+#endif
+
+    /* get predicted clazz
+       get predicted method
+    */
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, P_GPR_1, true); //predictedChainCell
+    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, P_GPR_1, true, P_SCRATCH_2, true);//predicted clazz
+    move_mem_to_reg(OpndSize_32, offChainingCell_method, P_GPR_1, true, PhysicalReg_ECX, true);//predicted method
+
+#ifdef DEBUG_CALL_STACK3
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
+
+    move_reg_to_reg(OpndSize_32, P_SCRATCH_2, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_imm_to_reg(OpndSize_32, 0xdd22, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    move_reg_to_reg(OpndSize_32, P_GPR_3, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+
+    move_mem_to_reg(OpndSize_32, 8, PhysicalReg_ESP, true, P_GPR_1, true);
+    move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, P_SCRATCH_2, true);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, P_GPR_3, true);
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+#endif
+
+    /* compare current class object against predicted clazz
+       if equal, prediction is still valid, jump to .invokeChain */
+    //live registers: P_GPR_1, P_GPR_3, P_SCRATCH_2
+    compare_reg_reg(P_GPR_3, true, P_SCRATCH_2, true);
+    conditional_jump(Condition_E, ".invokeChain", true);
+    invokeChain_inst++;
+
+    //get callee method and update predicted method if necessary
+    if(isInterface) {
+        predicted_chain_interface_O0(tmp);
+    } else {
+        predicted_chain_virtual_O0(IMMC);
+    }
+
+#ifdef DEBUG_CALL_STACK3
+    move_imm_to_reg(OpndSize_32, 0xeeee, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+#endif
+
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+
+    insertLabel(".invokeChain", true);
+#ifdef DEBUG_CALL_STACK3
+    move_imm_to_reg(OpndSize_32, 0xdddd, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+#endif
+
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Normal);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Normal);
+    }
+}
+
+/* object "this" is in inputReg: 5 for virtual, 1 for interface, 1 for virtual_quick */
+void gen_predicted_chain_O1(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
+
+    /* get current class object */
+    move_mem_to_reg(OpndSize_32, offObject_clazz, inputReg, false,
+             40, false);
+
+    /* get predicted clazz
+       get predicted method
+    */
+    if(traceCurrentBB->taken)
+        insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+    move_chain_to_reg(OpndSize_32, traceTakenId, 41, false); //predictedChainCell
+    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 32, false);//predicted clazz
+    move_mem_to_reg(OpndSize_32, offChainingCell_method, 41, false, PhysicalReg_ECX, true);//predicted method
+
+    /* update stack with parameters first, then decide the callee */
+    if(isRange) common_invokeMethodRange_noJmp();
+    else common_invokeMethodNoRange_noJmp();
+
+    /* compare current class object against predicted clazz
+       if equal, prediction is still valid, jump to .invokeChain */
+    compare_reg_reg(40, false, 32, false);
+    conditional_jump(Condition_E, ".invokeChain", true);
+    rememberState(1);
+    invokeChain_inst++;
+
+    //get callee method and update predicted method if necessary
+    if(isInterface) {
+        predicted_chain_interface_O1(tmp);
+    } else {
+        predicted_chain_virtual_O1(IMMC);
+    }
+
+    common_invokeMethod_Jmp(ArgsDone_Full); //will touch %ecx
+
+    insertLabel(".invokeChain", true);
+    goToState(1);
+    common_invokeMethod_Jmp(ArgsDone_Normal);
+}
+
+void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
+    return gen_predicted_chain_O1(isRange, tmp, IMMC, isInterface, inputReg);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_2
diff --git a/vm/compiler/codegen/x86/LowerJump.cpp b/vm/compiler/codegen/x86/LowerJump.cpp
new file mode 100644
index 0000000..2b10d6b
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerJump.cpp
@@ -0,0 +1,1568 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerJump.cpp
+    \brief This file lowers the following bytecodes: IF_XXX, GOTO
+*/
+#include <math.h>
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+#include "interp/InterpDefs.h"
+#include "NcgHelper.h"
+
+LabelMap* globalMap;
+LabelMap* globalShortMap;//make sure for each bytecode, there is no duplicated label
+LabelMap* globalWorklist = NULL;
+LabelMap* globalShortWorklist;
+
+int globalMapNum;
+int globalWorklistNum;
+int globalDataWorklistNum;
+int VMAPIWorklistNum;
+int globalPCWorklistNum;
+int chainingWorklistNum;
+
+LabelMap* globalDataWorklist = NULL;
+LabelMap* globalPCWorklist = NULL;
+LabelMap* chainingWorklist = NULL;
+LabelMap* VMAPIWorklist = NULL;
+
+char* ncgClassData;
+char* ncgClassDataPtr;
+char* ncgMethodData;
+char* ncgMethodDataPtr;
+int   ncgClassNum;
+int   ncgMethodNum;
+
+NCGWorklist* globalNCGWorklist;
+DataWorklist* methodDataWorklist;
+#ifdef ENABLE_TRACING
+MapWorklist* methodMapWorklist;
+#endif
+/*!
+\brief search globalShortMap to find the entry for the given label
+
+*/
+LabelMap* findItemForShortLabel(const char* label) {
+    LabelMap* ptr = globalShortMap;
+    while(ptr != NULL) {
+        if(!strcmp(label, ptr->label)) {
+            return ptr;
+        }
+        ptr = ptr->nextItem;
+    }
+    return NULL;
+}
+//assume size of "jump reg" is 2
+#define JUMP_REG_SIZE 2
+#define ADD_REG_REG_SIZE 3
+/*!
+\brief update value of the immediate in the given jump instruction
+
+check whether the immediate is out of range for the pre-set size
+*/
+int updateJumpInst(char* jumpInst, OpndSize immSize, int relativeNCG) {
+#ifdef DEBUG_NCG_JUMP
+    ALOGI("update jump inst @ %p with %d", jumpInst, relativeNCG);
+#endif
+    if(immSize == OpndSize_8) { //-128 to 127
+        if(relativeNCG >= 128 || relativeNCG < -128) {
+            ALOGE("pre-allocated space for a forward jump is not big enough");
+            dvmAbort();
+        }
+    }
+    if(immSize == OpndSize_16) { //-2^16 to 2^16-1
+        if(relativeNCG >= 32768 || relativeNCG < -32768) {
+            ALOGE("pre-allocated space for a forward jump is not big enough");
+            dvmAbort();
+        }
+    }
+    encoder_update_imm(relativeNCG, jumpInst);
+    return 0;
+}
+
+/*!
+\brief insert a label
+
+It takes argument checkDup, if checkDup is true, an entry is created in globalShortMap, entries in globalShortWorklist are checked, if there exists a match, the immediate in the jump instruction is updated and the entry is removed from globalShortWorklist;
+otherwise, an entry is created in globalMap.
+*/
+int insertLabel(const char* label, bool checkDup) {
+    LabelMap* item = NULL;
+    if(!checkDup) {
+        item = (LabelMap*)malloc(sizeof(LabelMap));
+        if(item == NULL) {
+            ALOGE("Memory allocation failed");
+            return -1;
+        }
+        snprintf(item->label, LABEL_SIZE, "%s", label);
+        item->codePtr = stream;
+        item->nextItem = globalMap;
+        globalMap = item;
+#ifdef DEBUG_NCG_CODE_SIZE
+        ALOGI("insert global label %s %p", label, stream);
+#endif
+        globalMapNum++;
+        return 0;
+    }
+
+    item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = stream;
+    item->nextItem = globalShortMap;
+    globalShortMap = item;
+#ifdef DEBUG_NCG
+    ALOGI("insert short-term label %s %p", label, stream);
+#endif
+    LabelMap* ptr = globalShortWorklist;
+    LabelMap* ptr_prevItem = NULL;
+    while(ptr != NULL) {
+        if(!strcmp(ptr->label, label)) {
+            //perform work
+            int relativeNCG = stream - ptr->codePtr;
+            unsigned instSize = encoder_get_inst_size(ptr->codePtr);
+            relativeNCG -= instSize; //size of the instruction
+#ifdef DEBUG_NCG
+            ALOGI("perform work short-term %p for label %s relative %d", ptr->codePtr, label, relativeNCG);
+#endif
+            updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
+            //remove work
+            if(ptr_prevItem == NULL) {
+                globalShortWorklist = ptr->nextItem;
+                free(ptr);
+                ptr = globalShortWorklist; //ptr_prevItem is still NULL
+            }
+            else {
+                ptr_prevItem->nextItem = ptr->nextItem;
+                free(ptr);
+                ptr = ptr_prevItem->nextItem;
+            }
+        }
+        else {
+            ptr_prevItem = ptr;
+            ptr = ptr->nextItem;
+        }
+    } //while
+    return 0;
+}
+/*!
+\brief search globalMap to find the entry for the given label
+
+*/
+char* findCodeForLabel(const char* label) {
+    LabelMap* ptr = globalMap;
+    while(ptr != NULL) {
+        if(!strcmp(label, ptr->label)) {
+            return ptr->codePtr;
+        }
+        ptr = ptr->nextItem;
+    }
+    return NULL;
+}
+/*!
+\brief search globalShortMap to find the entry for the given label
+
+*/
+char* findCodeForShortLabel(const char* label) {
+    LabelMap* ptr = globalShortMap;
+    while(ptr != NULL) {
+        if(!strcmp(label, ptr->label)) {
+            return ptr->codePtr;
+        }
+        ptr = ptr->nextItem;
+    }
+    return NULL;
+}
+int insertLabelWorklist(const char* label, OpndSize immSize) {
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = stream;
+    item->size = immSize;
+    item->nextItem = globalWorklist;
+    globalWorklist = item;
+#ifdef DEBUG_NCG
+    ALOGI("insert globalWorklist: %s %p", label, stream);
+#endif
+    return 0;
+}
+
+int insertShortWorklist(const char* label, OpndSize immSize) {
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = stream;
+    item->size = immSize;
+    item->nextItem = globalShortWorklist;
+    globalShortWorklist = item;
+#ifdef DEBUG_NCG
+    ALOGI("insert globalShortWorklist: %s %p", label, stream);
+#endif
+    return 0;
+}
+/*!
+\brief free memory allocated for globalMap
+
+*/
+void freeLabelMap() {
+    LabelMap* ptr = globalMap;
+    while(ptr != NULL) {
+        globalMap = ptr->nextItem;
+        free(ptr);
+        ptr = globalMap;
+    }
+}
+/*!
+\brief free memory allocated for globalShortMap
+
+*/
+void freeShortMap() {
+    LabelMap* ptr = globalShortMap;
+    while(ptr != NULL) {
+        globalShortMap = ptr->nextItem;
+        free(ptr);
+        ptr = globalShortMap;
+    }
+    globalShortMap = NULL;
+}
+
+int insertGlobalPCWorklist(char * offset, char * codeStart)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", "export_pc");
+    item->size = OpndSize_32;
+    item->codePtr = offset; //points to the immediate operand
+    item->addend = codeStart - streamMethodStart; //relative code pointer
+    item->nextItem = globalPCWorklist;
+    globalPCWorklist = item;
+    globalPCWorklistNum ++;
+
+#ifdef DEBUG_NCG
+    ALOGI("insert globalPCWorklist: %p %p %p %x %p", globalDvmNcg->streamCode,  codeStart, streamCode, item->addend, item->codePtr);
+#endif
+    return 0;
+}
+
+int insertChainingWorklist(int bbId, char * codeStart)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    item->size = OpndSize_32;
+    item->codePtr = codeStart; //points to the move instruction
+    item->addend = bbId; //relative code pointer
+    item->nextItem = chainingWorklist;
+    chainingWorklist = item;
+
+#ifdef DEBUG_NCG
+    ALOGI("insertChainingWorklist: %p basic block %d", codeStart, bbId);
+#endif
+    return 0;
+}
+
+int insertGlobalDataWorklist(char * offset, const char* label)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = offset;
+    item->size = OpndSize_32;
+    item->nextItem = globalDataWorklist;
+    globalDataWorklist = item;
+    globalDataWorklistNum ++;
+
+#ifdef DEBUG_NCG
+    ALOGI("insert globalDataWorklist: %s %p", label, offset);
+#endif
+
+    return 0;
+}
+
+int insertVMAPIWorklist(char * offset, const char* label)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = offset;
+    item->size = OpndSize_32;
+
+    item->nextItem = VMAPIWorklist;
+    VMAPIWorklist = item;
+
+    VMAPIWorklistNum ++;
+
+#ifdef DEBUG_NCG
+    ALOGI("insert VMAPIWorklist: %s %p", label, offset);
+#endif
+    return 0;
+}
+////////////////////////////////////////////////
+
+
+int updateImmRMInst(char* moveInst, const char* label, int relativeNCG); //forward declaration
+//////////////////// performLabelWorklist is defined differently for code cache
+void performChainingWorklist() {
+    LabelMap* ptr = chainingWorklist;
+    while(ptr != NULL) {
+        int tmpNCG = traceLabelList[ptr->addend].lop.generic.offset;
+        char* NCGaddr = streamMethodStart + tmpNCG;
+        updateImmRMInst(ptr->codePtr, "", (int)NCGaddr);
+        chainingWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = chainingWorklist;
+    }
+}
+void freeChainingWorklist() {
+    LabelMap* ptr = chainingWorklist;
+    while(ptr != NULL) {
+        chainingWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = chainingWorklist;
+    }
+}
+
+//Work only for initNCG
+void performLabelWorklist() {
+    LabelMap* ptr = globalWorklist;
+    while(ptr != NULL) {
+#ifdef DEBUG_NCG
+        ALOGI("perform work global %p for label %s", ptr->codePtr, ptr->label);
+#endif
+        char* targetCode = findCodeForLabel(ptr->label);
+        assert(targetCode != NULL);
+        int relativeNCG = targetCode - ptr->codePtr;
+        unsigned instSize = encoder_get_inst_size(ptr->codePtr);
+        relativeNCG -= instSize; //size of the instruction
+        updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
+        globalWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalWorklist;
+    }
+}
+void freeLabelWorklist() {
+    LabelMap* ptr = globalWorklist;
+    while(ptr != NULL) {
+        globalWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalWorklist;
+    }
+}
+
+///////////////////////////////////////////////////
+/*!
+\brief update value of the immediate in the given move instruction
+
+*/
+int updateImmRMInst(char* moveInst, const char* label, int relativeNCG) {
+#ifdef DEBUG_NCG
+    ALOGI("perform work ImmRM inst @ %p for label %s with %d", moveInst, label, relativeNCG);
+#endif
+    encoder_update_imm_rm(relativeNCG, moveInst);
+    return 0;
+}
+//! maximum instruction size for jump,jcc,call: 6 for jcc rel32
+#define MAX_JCC_SIZE 6
+//! minimum instruction size for jump,jcc,call: 2
+#define MIN_JCC_SIZE 2
+/*!
+\brief estimate size of the immediate
+
+Somehow, 16 bit jump does not work. This function will return either 8 bit or 32 bit
+EXAMPLE:
+  native code at A: ...
+  native code at B: jump relOffset (target is A)
+  native code at B':
+  --> relOffset = A - B' = A - B - size of the jump instruction
+  Argument "target" is equal to A - B. To determine size of the immediate, we check tha value of "target - size of the jump instructoin"
+*/
+OpndSize estOpndSizeFromImm(int target) {
+    if(target-MIN_JCC_SIZE < 128 && target-MAX_JCC_SIZE >= -128) return OpndSize_8;
+#ifdef SUPPORT_IMM_16
+    if(target-MIN_JCC_SIZE < 32768 && target-MAX_JCC_SIZE >= -32768) return OpndSize_16;
+#endif
+    return OpndSize_32;
+}
+/*!
+\brief return size of a jump or call instruction
+
+*/
+unsigned getJmpCallInstSize(OpndSize size, JmpCall_type type) {
+    if(type == JmpCall_uncond) {
+        if(size == OpndSize_8) return 2;
+        if(size == OpndSize_16) return 4;
+        return 5;
+    }
+    if(type == JmpCall_cond) {
+        if(size == OpndSize_8) return 2;
+        if(size == OpndSize_16) return 5;
+        return 6;
+    }
+    if(type == JmpCall_reg) {
+        assert(size == OpndSize_32);
+        return JUMP_REG_SIZE;
+    }
+    if(type == JmpCall_call) {
+        assert(size != OpndSize_8);
+        if(size == OpndSize_16) return 4;
+        return 5;
+    }
+    return 0;
+}
+/*!
+\brief check whether a branch target is already handled, if yes, return the size of the immediate; otherwise, call insertShortWorklist or insertLabelWorklist.
+
+If the branch target is not handled, call insertShortWorklist or insertLabelWorklist depending on isShortTerm, unknown is set to true, immSize is set to 32 if isShortTerm is false, set to 32 if isShortTerm is true and target is check_cast_null, set to 8 otherwise.
+
+If the branch target is handled, call estOpndSizeFromImm to set immSize for jump instruction, returns the value of the immediate
+*/
+int getRelativeOffset(const char* target, bool isShortTerm, JmpCall_type type, bool* unknown, OpndSize* immSize) {
+    char* targetPtrInStream = NULL;
+    if(isShortTerm) targetPtrInStream = findCodeForShortLabel(target);
+    else targetPtrInStream = findCodeForLabel(target);
+
+    int relOffset;
+    *unknown = false;
+    if(targetPtrInStream == NULL) {
+        //branch target is not handled yet
+        relOffset = 0;
+        *unknown = true;
+        if(isShortTerm) {
+            /* for backward jump, at this point, we don't know how far the target is from this jump
+               since the lable is only used within a single bytecode, we assume OpndSize_8 is big enough
+               but there are special cases where we should use 32 bit offset
+            */
+            if(!strcmp(target, ".check_cast_null") || !strcmp(target, ".stackOverflow") ||
+               !strcmp(target, ".invokeChain") ||
+               !strcmp(target, ".new_instance_done") ||
+               !strcmp(target, ".new_array_done") ||
+               !strcmp(target, ".fill_array_data_done") ||
+               !strcmp(target, ".inlined_string_compare_done") ||
+               !strncmp(target, "after_exception", 15)) {
+#ifdef SUPPORT_IMM_16
+                *immSize = OpndSize_16;
+#else
+                *immSize = OpndSize_32;
+#endif
+            } else {
+                *immSize = OpndSize_8;
+            }
+#ifdef DEBUG_NCG_JUMP
+            ALOGI("insert to short worklist %s %d", target, *immSize);
+#endif
+            insertShortWorklist(target, *immSize);
+        }
+        else {
+#ifdef SUPPORT_IMM_16
+            *immSize = OpndSize_16;
+#else
+            *immSize = OpndSize_32;
+#endif
+            insertLabelWorklist(target, *immSize);
+        }
+        if(type == JmpCall_call) { //call sz16 does not work in gdb
+            *immSize = OpndSize_32;
+        }
+        return 0;
+    }
+    else if (!isShortTerm) {
+#ifdef SUPPORT_IMM_16
+        *immSize = OpndSize_16;
+#else
+        *immSize = OpndSize_32;
+#endif
+        insertLabelWorklist(target, *immSize);
+    }
+
+#ifdef DEBUG_NCG
+    ALOGI("backward branch @ %p for label %s", stream, target);
+#endif
+    relOffset = targetPtrInStream - stream;
+    if(type == JmpCall_call) *immSize = OpndSize_32;
+    else
+        *immSize = estOpndSizeFromImm(relOffset);
+
+    relOffset -= getJmpCallInstSize(*immSize, type);
+    return relOffset;
+}
+
+/*!
+\brief generate a single native instruction "jcc imm" to jump to a label
+
+*/
+void conditional_jump(ConditionCode cc, const char* target, bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        condJumpToBasicBlock(stream, cc, currentExceptionBlockIdx);
+        return;
+    }
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cc);
+    bool unknown;
+    OpndSize size;
+    int imm = 0;
+    imm = getRelativeOffset(target, isShortTerm, JmpCall_cond, &unknown, &size);
+    dump_label(m, size, imm, target, isShortTerm);
+}
+/*!
+\brief generate a single native instruction "jmp imm" to jump to ".invokeArgsDone"
+
+*/
+void goto_invokeArgsDone() {
+    unconditional_jump_global_API(".invokeArgsDone", false);
+}
+/*!
+\brief generate a single native instruction "jmp imm" to jump to a label
+
+If the target is ".invokeArgsDone" and mode is NCG O1, extra work is performed to dump content of virtual registers to memory.
+*/
+void unconditional_jump(const char* target, bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        jumpToBasicBlock(stream, currentExceptionBlockIdx);
+        return;
+    }
+    Mnemonic m = Mnemonic_JMP;
+    bool unknown;
+    OpndSize size;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //for other three labels used by JIT: invokeArgsDone_formal, _native, _jit
+        if(!strncmp(target, ".invokeArgsDone", 15)) {
+            touchEcx(); //keep ecx live, if ecx was spilled, it is loaded here
+            beforeCall(target); //
+        }
+        if(!strcmp(target, ".invokeArgsDone")) {
+            nextVersionOfHardReg(PhysicalReg_EDX, 1); //edx will be used in a function
+            call("ncgGetEIP"); //must be immediately before JMP
+        }
+    }
+    int imm = 0;
+    imm = getRelativeOffset(target, isShortTerm, JmpCall_uncond, &unknown, &size);
+    dump_label(m, size, imm, target, isShortTerm);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(!strncmp(target, ".invokeArgsDone", 15)) {
+            afterCall(target); //un-spill before executing the next bytecode
+        }
+    }
+}
+/*!
+\brief generate a single native instruction "jcc imm"
+
+*/
+void conditional_jump_int(ConditionCode cc, int target, OpndSize size) {
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cc);
+    dump_ncg(m, size, target);
+}
+/*!
+\brief generate a single native instruction "jmp imm"
+
+*/
+void unconditional_jump_int(int target, OpndSize size) {
+    Mnemonic m = Mnemonic_JMP;
+    dump_ncg(m, size, target);
+}
+/*!
+\brief generate a single native instruction "jmp reg"
+
+*/
+void unconditional_jump_reg(int reg, bool isPhysical) {
+    dump_reg(Mnemonic_JMP, ATOM_NORMAL, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
+}
+
+/*!
+\brief generate a single native instruction to call a function
+
+If mode is NCG O1, extra work is performed to dump content of virtual registers to memory.
+*/
+void call(const char* target) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall(target);
+    }
+    Mnemonic m = Mnemonic_CALL;
+    bool dummy;
+    OpndSize size;
+    int relOffset = 0;
+    relOffset = getRelativeOffset(target, false, JmpCall_call, &dummy, &size);
+    dump_label(m, size, relOffset, target, false);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        afterCall(target);
+    }
+}
+/*!
+\brief generate a single native instruction to call a function
+
+*/
+void call_reg(int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CALL;
+    dump_reg(m, ATOM_NORMAL, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
+}
+void call_reg_noalloc(int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CALL;
+    dump_reg_noalloc(m, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
+}
+
+/*!
+\brief generate a single native instruction to call a function
+
+*/
+void call_mem(int disp, int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CALL;
+    dump_mem(m, ATOM_NORMAL, OpndSize_32, disp, reg, isPhysical);
+}
+
+/*!
+\brief insert an entry to globalNCGWorklist
+
+*/
+int insertNCGWorklist(s4 relativePC, OpndSize immSize) {
+    int offsetNCG2 = stream - streamMethodStart;
+#ifdef DEBUG_NCG
+    ALOGI("insert NCGWorklist (goto forward) @ %p offsetPC %x relativePC %x offsetNCG %x", stream, offsetPC, relativePC, offsetNCG2);
+#endif
+    NCGWorklist* item = (NCGWorklist*)malloc(sizeof(NCGWorklist));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    item->relativePC = relativePC;
+    item->offsetPC = offsetPC;
+    item->offsetNCG = offsetNCG2;
+    item->codePtr = stream;
+    item->size = immSize;
+    item->nextItem = globalNCGWorklist;
+    globalNCGWorklist = item;
+    return 0;
+}
+#ifdef ENABLE_TRACING
+int insertMapWorklist(s4 BCOffset, s4 NCGOffset, int isStartOfPC) {
+    return 0;
+}
+#endif
+/*!
+\brief insert an entry to methodDataWorklist
+
+This function is used by bytecode FILL_ARRAY_DATA, PACKED_SWITCH, SPARSE_SWITCH
+*/
+int insertDataWorklist(s4 relativePC, char* codePtr1) {
+    //insert according to offsetPC+relativePC, smallest at the head
+    DataWorklist* item = (DataWorklist*)malloc(sizeof(DataWorklist));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    item->relativePC = relativePC;
+    item->offsetPC = offsetPC;
+    item->codePtr = codePtr1;
+    item->codePtr2 = stream; //jump_reg for switch
+    DataWorklist* ptr = methodDataWorklist;
+    DataWorklist* prev_ptr = NULL;
+    while(ptr != NULL) {
+        int tmpPC = ptr->offsetPC + ptr->relativePC;
+        int tmpPC2 = relativePC + offsetPC;
+        if(tmpPC2 < tmpPC) {
+            break;
+        }
+        prev_ptr = ptr;
+        ptr = ptr->nextItem;
+    }
+    //insert item before ptr
+    if(prev_ptr != NULL) {
+        prev_ptr->nextItem = item;
+    }
+    else methodDataWorklist = item;
+    item->nextItem = ptr;
+    return 0;
+}
+
+/*!
+\brief work on globalNCGWorklist
+
+*/
+int performNCGWorklist() {
+    NCGWorklist* ptr = globalNCGWorklist;
+    while(ptr != NULL) {
+        ALOGV("perform NCG worklist: @ %p target block %d target NCG %x",
+             ptr->codePtr, ptr->relativePC, traceLabelList[ptr->relativePC].lop.generic.offset);
+        int tmpNCG = traceLabelList[ptr->relativePC].lop.generic.offset;
+        assert(tmpNCG >= 0);
+        int relativeNCG = tmpNCG - ptr->offsetNCG;
+        unsigned instSize = encoder_get_inst_size(ptr->codePtr);
+        relativeNCG -= instSize;
+        updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
+        globalNCGWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalNCGWorklist;
+    }
+    return 0;
+}
+void freeNCGWorklist() {
+    NCGWorklist* ptr = globalNCGWorklist;
+    while(ptr != NULL) {
+        globalNCGWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalNCGWorklist;
+    }
+}
+
+/*!
+\brief used by bytecode SWITCH
+
+targetPC points to start of the data section
+Code sequence for SWITCH
+  call ncgGetEIP
+  @codeInst: add_reg_reg %eax, %edx
+  jump_reg %edx
+This function returns the offset in native code between add_reg_reg and the data section
+*/
+int getRelativeNCGForSwitch(int targetPC, char* codeInst) {
+    int tmpNCG = mapFromBCtoNCG[targetPC];
+    int offsetNCG2 = codeInst - streamMethodStart;
+    int relativeOff = tmpNCG - offsetNCG2;
+    return relativeOff;
+}
+/*!
+\brief work on methodDataWorklist
+
+*/
+int performDataWorklist() {
+    DataWorklist* ptr = methodDataWorklist;
+    if(ptr == NULL) return 0;
+
+    char* codeCacheEnd = ((char *) gDvmJit.codeCache) + gDvmJit.codeCacheSize - CODE_CACHE_PADDING;
+    u2 insnsSize = dvmGetMethodInsnsSize(currentMethod); //bytecode
+    //align stream to multiple of 4
+    int alignBytes = (int)stream & 3;
+    if(alignBytes != 0) alignBytes = 4-alignBytes;
+    stream += alignBytes;
+
+    while(ptr != NULL) {
+        int tmpPC = ptr->offsetPC + ptr->relativePC;
+        int endPC = insnsSize;
+        if(ptr->nextItem != NULL) endPC = ptr->nextItem->offsetPC + ptr->nextItem->relativePC;
+        mapFromBCtoNCG[tmpPC] = stream - streamMethodStart; //offsetNCG in byte
+
+        //handle fill_array_data, packed switch & sparse switch
+        u2 tmpInst = *(currentMethod->insns + ptr->offsetPC);
+        u2* sizePtr;
+        s4* entryPtr_bytecode;
+        u2 tSize, iVer;
+        u4 sz;
+
+        if (gDvmJit.codeCacheFull == true) {
+            // We are out of code cache space. Skip writing data/code to
+            //   code cache. Simply free the item.
+            methodDataWorklist = ptr->nextItem;
+            free(ptr);
+            ptr = methodDataWorklist;
+        }
+
+        switch (INST_INST(tmpInst)) {
+        case OP_FILL_ARRAY_DATA:
+            sz = (endPC-tmpPC)*sizeof(u2);
+            if ((stream + sz) < codeCacheEnd) {
+                memcpy(stream, (u2*)currentMethod->insns+tmpPC, sz);
+#ifdef DEBUG_NCG_CODE_SIZE
+                ALOGI("copy data section to stream %p: start at %d, %d bytes", stream, tmpPC, sz);
+#endif
+#ifdef DEBUG_NCG
+                ALOGI("update data section at %p with %d", ptr->codePtr, stream-ptr->codePtr);
+#endif
+                updateImmRMInst(ptr->codePtr, "", stream - ptr->codePtr);
+                stream += sz;
+            } else {
+                gDvmJit.codeCacheFull = true;
+            }
+            break;
+        case OP_PACKED_SWITCH:
+            updateImmRMInst(ptr->codePtr, "", stream-ptr->codePtr);
+            sizePtr = (u2*)currentMethod->insns+tmpPC + 1 /*signature*/;
+            entryPtr_bytecode = (s4*)(sizePtr + 1 /*size*/ + 2 /*firstKey*/);
+            tSize = *(sizePtr);
+            sz = tSize * 4;     /* expected size needed in stream */
+            if ((stream + sz) < codeCacheEnd) {
+                for(iVer = 0; iVer < tSize; iVer++) {
+                    //update entries
+                    s4 relativePC = *entryPtr_bytecode; //relative to ptr->offsetPC
+                    //need stream, offsetPC,
+                    int relativeNCG = getRelativeNCGForSwitch(relativePC+ptr->offsetPC, ptr->codePtr2);
+#ifdef DEBUG_NCG_CODE_SIZE
+                    ALOGI("convert target from %d to %d", relativePC+ptr->offsetPC, relativeNCG);
+#endif
+                    *((s4*)stream) = relativeNCG;
+                    stream += 4;
+                    entryPtr_bytecode++;
+                }
+            } else {
+                gDvmJit.codeCacheFull = true;
+            }
+            break;
+        case OP_SPARSE_SWITCH:
+            updateImmRMInst(ptr->codePtr, "", stream-ptr->codePtr);
+            sizePtr = (u2*)currentMethod->insns+tmpPC + 1 /*signature*/;
+            s4* keyPtr_bytecode = (s4*)(sizePtr + 1 /*size*/);
+            tSize = *(sizePtr);
+            entryPtr_bytecode = (s4*)(keyPtr_bytecode + tSize);
+            sz = tSize * (sizeof(s4) + 4); /* expected size needed in stream */
+            if ((stream + sz) < codeCacheEnd) {
+                memcpy(stream, keyPtr_bytecode, tSize*sizeof(s4));
+                stream += tSize*sizeof(s4);
+                for(iVer = 0; iVer < tSize; iVer++) {
+                    //update entries
+                    s4 relativePC = *entryPtr_bytecode; //relative to ptr->offsetPC
+                    //need stream, offsetPC,
+                    int relativeNCG = getRelativeNCGForSwitch(relativePC+ptr->offsetPC, ptr->codePtr2);
+                    *((s4*)stream) = relativeNCG;
+                    stream += 4;
+                    entryPtr_bytecode++;
+                }
+            } else {
+                gDvmJit.codeCacheFull = true;
+            }
+            break;
+        }
+
+        //remove the item
+        methodDataWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = methodDataWorklist;
+    }
+    return 0;
+}
+void freeDataWorklist() {
+    DataWorklist* ptr = methodDataWorklist;
+    while(ptr != NULL) {
+        methodDataWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = methodDataWorklist;
+    }
+}
+
+//////////////////////////
+/*!
+\brief check whether a branch target (specified by relative offset in bytecode) is already handled, if yes, return the size of the immediate; otherwise, call insertNCGWorklist.
+
+If the branch target is not handled, call insertNCGWorklist, unknown is set to true, immSize is set to 32.
+
+If the branch target is handled, call estOpndSizeFromImm to set immSize for jump instruction, returns the value of the immediate
+*/
+int getRelativeNCG(s4 tmp, JmpCall_type type, bool* unknown, OpndSize* size) {//tmp: relativePC
+    int tmpNCG = traceLabelList[tmp].lop.generic.offset;
+
+    *unknown = false;
+    if(tmpNCG <0) {
+        *unknown = true;
+#ifdef SUPPORT_IMM_16
+        *size = OpndSize_16;
+#else
+        *size = OpndSize_32;
+#endif
+        insertNCGWorklist(tmp, *size);
+        return 0;
+    }
+    int offsetNCG2 = stream - streamMethodStart;
+#ifdef DEBUG_NCG
+    ALOGI("goto backward @ %p offsetPC %d relativePC %d offsetNCG %d relativeNCG %d", stream, offsetPC, tmp, offsetNCG2, tmpNCG-offsetNCG2);
+#endif
+    int relativeOff = tmpNCG - offsetNCG2;
+    *size = estOpndSizeFromImm(relativeOff);
+    return relativeOff - getJmpCallInstSize(*size, type);
+}
+/*!
+\brief a helper function to handle backward branch
+
+input: jump target in %eax; at end of the function, jump to %eax
+*/
+int common_backwardBranch() {
+    insertLabel("common_backwardBranch", false);
+    spill_reg(PhysicalReg_EAX, true);
+    call("common_periodicChecks_entry");
+    unspill_reg(PhysicalReg_EAX, true);
+    unconditional_jump_reg(PhysicalReg_EAX, true);
+    return 0;
+}
+//when this is called from JIT, there is no need to check GC
+int common_goto(s4 tmp) { //tmp: target basic block id
+    bool unknown;
+    OpndSize size;
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+
+    int relativeNCG = tmp;
+    relativeNCG = getRelativeNCG(tmp, JmpCall_uncond, &unknown, &size);
+    unconditional_jump_int(relativeNCG, size);
+    return 1;
+}
+int common_if(s4 tmp, ConditionCode cc_next, ConditionCode cc) {
+    bool unknown;
+    OpndSize size;
+    int relativeNCG = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+
+    if(traceCurrentBB->taken)
+        relativeNCG = getRelativeNCG(traceCurrentBB->taken->id, JmpCall_cond, &unknown, &size);
+    conditional_jump_int(cc, relativeNCG, size);
+    relativeNCG = traceCurrentBB->fallThrough ? traceCurrentBB->fallThrough->id : 0;
+    if(traceCurrentBB->fallThrough)
+        relativeNCG = getRelativeNCG(traceCurrentBB->fallThrough->id, JmpCall_uncond, &unknown, &size);
+    unconditional_jump_int(relativeNCG, size);
+    return 2;
+}
+
+/*!
+\brief helper function to handle null object error
+
+*/
+int common_errNullObject() {
+    insertLabel("common_errNullObject", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrNullPointerException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle string index error
+
+*/
+int common_StringIndexOutOfBounds() {
+    insertLabel("common_StringIndexOutOfBounds", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrStringIndexOutOfBoundsException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+
+/*!
+\brief helper function to handle array index error
+
+*/
+int common_errArrayIndex() {
+    insertLabel("common_errArrayIndex", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrArrayIndexException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle array store error
+
+*/
+int common_errArrayStore() {
+    insertLabel("common_errArrayStore", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrArrayStoreException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle negative array size error
+
+*/
+int common_errNegArraySize() {
+    insertLabel("common_errNegArraySize", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrNegativeArraySizeException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle divide-by-zero error
+
+*/
+int common_errDivideByZero() {
+    insertLabel("common_errDivideByZero", false);
+    move_imm_to_reg(OpndSize_32, LstrDivideByZero, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrArithmeticException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle no such method error
+
+*/
+int common_errNoSuchMethod() {
+    insertLabel("common_errNoSuchMethod", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrNoSuchMethodError, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+int call_dvmFindCatchBlock();
+
+#define P_GPR_1 PhysicalReg_ESI //self callee-saved
+#define P_GPR_2 PhysicalReg_EBX //exception callee-saved
+#define P_GPR_3 PhysicalReg_EAX //method that caused exception
+/*!
+\brief helper function common_exceptionThrown
+
+*/
+int common_exceptionThrown() {
+    insertLabel("common_exceptionThrown", false);
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToExceptionThrown;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+/*!
+\brief helper function to throw an exception with message
+
+INPUT: obj_reg(%eax), exceptionPtrReg(%ecx)
+SCRATCH: C_SCRATCH_1(%esi) & C_SCRATCH_2(%edx)
+OUTPUT: no
+*/
+int throw_exception_message(int exceptionPtrReg, int obj_reg, bool isPhysical,
+                            int startLR/*logical register index*/, bool startPhysical) {
+    insertLabel("common_throw_message", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    move_mem_to_reg(OpndSize_32, offObject_clazz, obj_reg, isPhysical, C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offClassObject_descriptor, C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, C_SCRATCH_2, isScratchPhysical, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, exceptionPtrReg, true, 0, PhysicalReg_ESP, true);
+    call_dvmThrowWithMessage();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    unconditional_jump("common_exceptionThrown", false);
+    return 0;
+}
+/*!
+\brief helper function to throw an exception
+
+scratch: C_SCRATCH_1(%edx)
+*/
+int throw_exception(int exceptionPtrReg, int immReg,
+                    int startLR/*logical register index*/, bool startPhysical) {
+    insertLabel("common_throw", false);
+    scratchRegs[0] = PhysicalReg_EDX; scratchRegs[1] = PhysicalReg_Null;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, immReg, true, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, exceptionPtrReg, true, 0, PhysicalReg_ESP, true);
+    call_dvmThrow();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    unconditional_jump("common_exceptionThrown", false);
+    return 0;
+}
+
+//! lower bytecode GOTO
+
+//!
+int op_goto() {
+    s2 tmp = traceCurrentBB->taken->id;
+    int retval = common_goto(tmp);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode GOTO_16
+
+//!
+int op_goto_16() {
+    s2 tmp = traceCurrentBB->taken->id;
+    int retval = common_goto(tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode GOTO_32
+
+//!
+int op_goto_32() {
+    s2 tmp = traceCurrentBB->taken->id;
+    int retval = common_goto((s4)tmp);
+    rPC += 3;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode PACKED_SWITCH
+
+//!
+int op_packed_switch() {
+    u4 tmp = (u4)FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    u2 vA = INST_AA(inst);
+
+#ifdef DEBUG_EACH_BYTECODE
+    u2 tSize = 0;
+    s4 firstKey = 0;
+    s4* entries = NULL;
+#else
+    u2* switchData = rPC + (s4)tmp;
+    if (*switchData++ != kPackedSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError(
+                          "bad packed switch magic");
+        return 0; //no_op
+    }
+    u2 tSize = *switchData++;
+    assert(tSize > 0);
+    s4 firstKey = *switchData++;
+    firstKey |= (*switchData++) << 16;
+    s4* entries = (s4*) switchData;
+    assert(((u4)entries & 0x3) == 0);
+#endif
+
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //dvmNcgHandlePackedSwitch: testVal, size, first_key, targets
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tSize, 8, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, firstKey, 4, PhysicalReg_ESP, true);
+
+    /* "entries" is constant for JIT
+       it is the 1st argument to dvmJitHandlePackedSwitch */
+    move_imm_to_mem(OpndSize_32, (int)entries, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 12, PhysicalReg_ESP, true);
+
+    //if value out of range, fall through (no_op)
+    //return targets[testVal - first_key]
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_dvmJitHandlePackedSwitch();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //TODO: eax should be absolute address, call globalVREndOfBB, constVREndOfBB
+    //conditional_jump_global_API(Condition_LE, "common_backwardBranch", false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod); //update GG VRs
+    //get rPC, %eax has the relative PC offset
+    alu_binary_imm_reg(OpndSize_32, add_opc, (int)rPC, PhysicalReg_EAX, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    jumpToInterpNoChain();
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode SPARSE_SWITCH
+
+//!
+int op_sparse_switch() {
+    u4 tmp = (u4)FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    u2 vA = INST_AA(inst);
+#ifdef DEBUG_EACH_BYTECODE
+    u2 tSize = 0;
+    const s4* keys = NULL;
+    s4* entries = NULL;
+#else
+    u2* switchData = rPC + (s4)tmp;
+
+    if (*switchData++ != kSparseSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError(
+                          "bad sparse switch magic");
+        return 0; //no_op
+    }
+    u2 tSize = *switchData++;
+    assert(tSize > 0);
+    const s4* keys = (const s4*) switchData;
+    assert(((u4)keys & 0x3) == 0);
+    s4* entries = (s4*)switchData + tSize;
+    assert(((u4)entries & 0x3) == 0);
+#endif
+
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //dvmNcgHandleSparseSwitch: keys, size, testVal
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tSize, 4, PhysicalReg_ESP, true);
+
+    /* "keys" is constant for JIT
+       it is the 1st argument to dvmJitHandleSparseSwitch */
+    move_imm_to_mem(OpndSize_32, (int)keys, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 8, PhysicalReg_ESP, true);
+
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    //if testVal is in keys, return the corresponding target
+    //otherwise, fall through (no_op)
+    call_dvmJitHandleSparseSwitch();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //TODO: eax should be absolute address, call globalVREndOfBB constVREndOfBB
+    //conditional_jump_global_API(Condition_LE, "common_backwardBranch", false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    //get rPC, %eax has the relative PC offset
+    alu_binary_imm_reg(OpndSize_32, add_opc, (int)rPC, PhysicalReg_EAX, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    jumpToInterpNoChain();
+    rPC += 3;
+    return 0;
+}
+
+#undef P_GPR_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode IF_EQ
+
+//!
+int op_if_eq() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_NE, Condition_E);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_NE
+
+//!
+int op_if_ne() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_E, Condition_NE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LT
+
+//!
+int op_if_lt() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_GE, Condition_L);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GE
+
+//!
+int op_if_ge() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_L, Condition_GE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GT
+
+//!
+int op_if_gt() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_LE, Condition_G);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LE
+
+//!
+int op_if_le() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_G, Condition_LE);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode IF_EQZ
+
+//!
+int op_if_eqz() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_NE, Condition_E);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_NEZ
+
+//!
+int op_if_nez() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_E, Condition_NE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LTZ
+
+//!
+int op_if_ltz() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_GE, Condition_L);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GEZ
+
+//!
+int op_if_gez() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_L, Condition_GE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GTZ
+
+//!
+int op_if_gtz() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_LE, Condition_G);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LEZ
+
+//!
+int op_if_lez() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_G, Condition_LE);
+    rPC += 2;
+    return 0;
+}
+
+#define P_GPR_1 PhysicalReg_ECX
+#define P_GPR_2 PhysicalReg_EBX
+/*!
+\brief helper function common_periodicChecks4 to check GC request
+BCOffset in %edx
+*/
+int common_periodicChecks4() {
+    insertLabel("common_periodicChecks4", false);
+#if (!defined(ENABLE_TRACING))
+    get_self_pointer(PhysicalReg_ECX, true);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, suspendCount), PhysicalReg_ECX, true, PhysicalReg_EAX, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //suspendCount
+    conditional_jump(Condition_NE, "common_handleSuspend4", true); //called once
+    x86_return();
+
+    insertLabel("common_handleSuspend4", true);
+    push_reg_to_stack(OpndSize_32, PhysicalReg_ECX, true);
+    call_dvmCheckSuspendPending();
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+
+#else
+    ///////////////////
+    //get debuggerActive: 3 memory accesses, and $7
+    move_mem_to_reg(OpndSize_32, offGlue_pSelfSuspendCount, PhysicalReg_Glue, true, P_GPR_1, true);
+    move_mem_to_reg(OpndSize_32, offGlue_pIntoDebugger, PhysicalReg_Glue, true, P_GPR_2, true);
+
+    compare_imm_mem(OpndSize_32, 0, 0, P_GPR_1, true); //suspendCount
+    conditional_jump(Condition_NE, "common_handleSuspend4_1", true); //called once
+
+    compare_imm_mem(OpndSize_32, 0, 0, P_GPR_2, true); //debugger active
+
+    conditional_jump(Condition_NE, "common_debuggerActive4", true);
+
+    //recover registers and return
+    x86_return();
+
+    insertLabel("common_handleSuspend4_1", true);
+    push_mem_to_stack(OpndSize_32, offGlue_self, PhysicalReg_Glue, true);
+    call_dvmCheckSuspendPending();
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+
+    insertLabel("common_debuggerActive4", true);
+    //%edx: offsetBC (at run time, get method->insns_bytecode, then calculate BCPointer)
+    move_mem_to_reg(OpndSize_32, offGlue_method, PhysicalReg_Glue, true, P_GPR_1, true);
+    move_mem_to_reg(OpndSize_32, offMethod_insns_bytecode, P_GPR_1, true, P_GPR_2, true);
+    alu_binary_reg_reg(OpndSize_32, add_opc, P_GPR_2, true, PhysicalReg_EDX, true);
+    move_imm_to_mem(OpndSize_32, 0, offGlue_entryPoint, PhysicalReg_Glue, true);
+    unconditional_jump("common_gotoBail", false); //update glue->rPC with edx
+#endif
+    return 0;
+}
+//input: %edx PC adjustment
+//CHECK: should %edx be saved before calling dvmCheckSuspendPending?
+/*!
+\brief helper function common_periodicChecks_entry to check GC request
+
+*/
+int common_periodicChecks_entry() {
+    insertLabel("common_periodicChecks_entry", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EAX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_suspendCount(P_GPR_1, true);
+
+    //get debuggerActive: 3 memory accesses, and $7
+#if 0 //defined(WITH_DEBUGGER)
+    get_debuggerActive(P_GPR_2, true);
+#endif
+
+    compare_imm_reg(OpndSize_32, 0, P_GPR_1, true); //suspendCount
+    conditional_jump(Condition_NE, "common_handleSuspend", true); //called once
+
+#if 0 //defined(WITH_DEBUGGER)
+#ifdef NCG_DEBUG
+    compare_imm_reg(OpndSize_32, 0, P_GPR_2, true); //debugger active
+    conditional_jump(Condition_NE, "common_debuggerActive", true);
+#endif
+#endif
+
+    //recover registers and return
+    x86_return();
+    insertLabel("common_handleSuspend", true);
+    get_self_pointer(P_GPR_1, true);
+    load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
+    call_dvmCheckSuspendPending();
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+#ifdef NCG_DEBUG
+    insertLabel("common_debuggerActive", true);
+    //adjust PC!!! use 0(%esp) TODO
+    set_glue_entryPoint_imm(0); //kInterpEntryInstr);
+    unconditional_jump("common_gotoBail", false);
+#endif
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+/*!
+\brief helper function common_gotoBail
+  input: %edx: BCPointer %esi: Glue
+  set %eax to 1 (switch interpreter = true), recover the callee-saved registers and return
+*/
+int common_gotoBail() {
+    insertLabel("common_gotoBail", false);
+    //scratchRegs[0] = PhysicalReg_EDX; scratchRegs[1] = PhysicalReg_ESI;
+    //scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    //save_pc_fp_to_glue();
+    get_self_pointer(PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offsetof(Thread, interpSave.pc), PhysicalReg_EAX, true);
+
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.bailPtr), PhysicalReg_EAX, true, PhysicalReg_ESP, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(FRAME_SIZE-4, PhysicalReg_EBP, true, PhysicalReg_EBP, true);
+    move_imm_to_reg(OpndSize_32, 1, PhysicalReg_EAX, true); //return value
+    move_mem_to_reg(OpndSize_32, -4, PhysicalReg_EBP, true, PhysicalReg_EDI, true);
+    move_mem_to_reg(OpndSize_32, -8, PhysicalReg_EBP, true, PhysicalReg_ESI, true);
+    move_mem_to_reg(OpndSize_32, -12, PhysicalReg_EBP, true, PhysicalReg_EBX, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EBP, true, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+    return 0;
+}
+/*!
+\brief helper function common_gotoBail_0
+
+  set %eax to 0, recover the callee-saved registers and return
+*/
+int common_gotoBail_0() {
+    insertLabel("common_gotoBail_0", false);
+
+    get_self_pointer(PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offsetof(Thread, interpSave.pc), PhysicalReg_EAX, true);
+
+    /*
+    movl    offThread_bailPtr(%ecx),%esp # Restore "setjmp" esp
+    movl    %esp,%ebp
+    addl    $(FRAME_SIZE-4), %ebp       # Restore %ebp at point of setjmp
+    movl    EDI_SPILL(%ebp),%edi
+    movl    ESI_SPILL(%ebp),%esi
+    movl    EBX_SPILL(%ebp),%ebx
+    movl    %ebp, %esp                   # strip frame
+    pop     %ebp                         # restore caller's ebp
+    ret                                  # return to dvmMterpStdRun's caller
+    */
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.bailPtr), PhysicalReg_EAX, true, PhysicalReg_ESP, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(FRAME_SIZE-4, PhysicalReg_EBP, true, PhysicalReg_EBP, true);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //return value
+    move_mem_to_reg(OpndSize_32, -4, PhysicalReg_EBP, true, PhysicalReg_EDI, true);
+    move_mem_to_reg(OpndSize_32, -8, PhysicalReg_EBP, true, PhysicalReg_ESI, true);
+    move_mem_to_reg(OpndSize_32, -12, PhysicalReg_EBP, true, PhysicalReg_EBX, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EBP, true, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/LowerMove.cpp b/vm/compiler/codegen/x86/LowerMove.cpp
new file mode 100644
index 0000000..2f0b5bc
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerMove.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerMove.cpp
+    \brief This file lowers the following bytecodes: MOVE_XXX
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "enc_wrapper.h"
+
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode MOVE
+
+//!
+int op_move() {
+    u2 vA, vB;
+    vA = INST_A(inst);
+    vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false/*isPhysical*/);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 2;
+}
+//! lower bytecode MOVE_FROM16
+
+//!
+int op_move_from16() {
+    u2 vA, vB;
+    vA = INST_AA(inst);
+    vB = FETCH(1);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode MOVE_16
+
+//!
+int op_move_16() {
+    u2 vA, vB;
+    vA = FETCH(1);
+    vB = FETCH(2);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 3;
+    return 2;
+}
+#undef P_GPR_1
+//! lower bytecode MOVE_WIDE
+
+//!
+int op_move_wide() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 1;
+    return 2;
+}
+//! lower bytecode MOVE_WIDE_FROM16
+
+//!
+int op_move_wide_from16() {
+    u2 vA = INST_AA(inst);
+    u2 vB = FETCH(1);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode MOVE_WIDE_16
+
+//!
+int op_move_wide_16() {
+    u2 vA = FETCH(1);
+    u2 vB = FETCH(2);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 3;
+    return 2;
+}
+//! lower bytecode MOVE_RESULT.
+
+//! the return value from bytecode INVOKE is stored in the glue structure
+int op_move_result() {
+#ifdef WITH_JIT_INLINING
+    /* An inlined move result is effectively no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return 0;
+#endif
+    u2 vA = INST_AA(inst);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    get_return_value(OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode MOVE_RESULT_WIDE.
+
+//! the return value from bytecode INVOKE is stored in the glue structure
+int op_move_result_wide() {
+#ifdef WITH_JIT_INLINING
+    /* An inlined move result is effectively no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return 0;
+#endif
+    u2 vA = INST_AA(inst);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    get_return_value(OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 1;
+    return 0;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//!lower bytecode MOVE_RESULT_EXCEPTION
+
+//!update a virtual register with exception from glue structure;
+//!clear the exception from glue structure
+int op_move_exception() {
+    u2 vA = INST_AA(inst);
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_Null;
+    get_self_pointer(2, false);
+    move_mem_to_reg(OpndSize_32, offThread_exception, 2, false, 3, false);
+    move_imm_to_mem(OpndSize_32, 0, offThread_exception, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 3, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
diff --git a/vm/compiler/codegen/x86/LowerObject.cpp b/vm/compiler/codegen/x86/LowerObject.cpp
new file mode 100644
index 0000000..67c4044
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerObject.cpp
@@ -0,0 +1,682 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*! \file LowerObject.cpp
+    \brief This file lowers the following bytecodes: CHECK_CAST,
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+extern void markCard_filled(int tgtAddrReg, bool isTgtPhysical, int scratchReg, bool isScratchPhysical);
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! LOWER bytecode CHECK_CAST and INSTANCE_OF
+//!   CALL class_resolve (%ebx is live across the call)
+//!        dvmInstanceofNonTrivial
+//!   NO register is live through function check_cast_helper
+int check_cast_nohelper(u2 vA, u4 tmp, bool instance, u2 vDest) {
+    get_virtual_reg(vA, OpndSize_32, 1, false); //object
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    /* for trace-based JIT, it is likely that the class is already resolved */
+    bool needToResolve = true;
+    ClassObject *classPtr =
+                (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    ALOGV("in check_cast, class is resolved to %p", classPtr);
+    if(classPtr != NULL) {
+        needToResolve = false;
+        ALOGV("check_cast class %s", classPtr->descriptor);
+    }
+    if(needToResolve) {
+        //get_res_classes is moved here for NCG O1 to improve performance of GLUE optimization
+        scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+        get_res_classes(4, false);
+    }
+    compare_imm_reg(OpndSize_32, 0, 1, false);
+
+    rememberState(1);
+    //for private code cache, previously it jumped to .instance_of_okay_1
+    //if object reference is null, jump to the handler for this special case
+    if(instance) {
+        conditional_jump(Condition_E, ".instance_of_null", true);
+    }
+    else {
+        conditional_jump(Condition_E, ".check_cast_null", true);
+    }
+    //check whether the class is already resolved
+    //if yes, jump to check_cast_resolved
+    //if not, call class_resolve
+    if(needToResolve) {
+        move_mem_to_reg(OpndSize_32, tmp*4, 4, false, PhysicalReg_EAX, true);
+        compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+        if(instance)
+            conditional_jump(Condition_NE, ".instance_of_resolved", true);
+        else
+            conditional_jump(Condition_NE, ".check_cast_resolved", true);
+        //try to resolve the class
+        rememberState(2);
+        move_imm_to_reg(OpndSize_32, tmp, PhysicalReg_EAX, true);
+        export_pc(); //trying to resolve the class
+        call_helper_API(".class_resolve");
+        transferToState(2);
+    } //needToResolve
+    else {
+        /* the class is already resolved and is constant */
+        move_imm_to_reg(OpndSize_32, (int)classPtr, PhysicalReg_EAX, true);
+    }
+    //class is resolved, and it is in %eax
+    if(!instance) {
+        insertLabel(".check_cast_resolved", true);
+    }
+    else insertLabel(".instance_of_resolved", true);
+
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 6, false); //object->clazz
+
+    //%eax: resolved class
+    //compare resolved class and object->clazz
+    //if the same, jump to the handler for this special case
+    compare_reg_reg(PhysicalReg_EAX, true, 6, false);
+    rememberState(3);
+    if(instance) {
+        conditional_jump(Condition_E, ".instance_of_equal", true);
+    } else {
+        conditional_jump(Condition_E, ".check_cast_equal", true);
+    }
+
+    //prepare to call dvmInstanceofNonTrivial
+    //INPUT: the resolved class & object reference
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 6, false, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true); //resolved class
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    nextVersionOfHardReg(PhysicalReg_EAX, 2); //next version has 2 refs
+    call_dvmInstanceofNonTrivial();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //
+    if(instance) {
+        //move return value to P_GPR_2
+        move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, 3, false);
+        rememberState(4);
+        unconditional_jump(".instance_of_okay", true);
+    } else {
+        //if return value of dvmInstanceofNonTrivial is zero, throw exception
+        compare_imm_reg(OpndSize_32, 0,  PhysicalReg_EAX, true);
+        rememberState(4);
+        conditional_jump(Condition_NE, ".check_cast_okay", true);
+        //two inputs for common_throw_message: object reference in eax, exception pointer in ecx
+        nextVersionOfHardReg(PhysicalReg_EAX, 1); //next version has 1 ref
+        move_reg_to_reg(OpndSize_32, 1, false, PhysicalReg_EAX, true);
+
+        load_imm_global_data_API("strClassCastExceptionPtr", OpndSize_32, PhysicalReg_ECX, true);
+
+        nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 ref count
+        export_pc();
+
+        unconditional_jump_global_API("common_throw_message", false);
+    }
+    //handler for speical case where object reference is null
+    if(instance)
+        insertLabel(".instance_of_null", true);
+    else insertLabel(".check_cast_null", true);
+    goToState(1);
+    if(instance) {
+        move_imm_to_reg(OpndSize_32, 0, 3, false);
+    }
+    transferToState(4);
+    if(instance)
+        unconditional_jump(".instance_of_okay", true);
+    else
+        unconditional_jump(".check_cast_okay", true);
+
+    //handler for special case where class of object is the same as the resolved class
+    if(instance)
+        insertLabel(".instance_of_equal", true);
+    else insertLabel(".check_cast_equal", true);
+    goToState(3);
+    if(instance) {
+        move_imm_to_reg(OpndSize_32, 1, 3, false);
+    }
+    transferToState(4);
+    if(instance)
+        insertLabel(".instance_of_okay", true);
+    else insertLabel(".check_cast_okay", true);
+    //all cases merge here and the value is put to virtual register
+    if(instance) {
+        set_virtual_reg(vDest, OpndSize_32, 3, false);
+    }
+    return 0;
+}
+//! common code to lower CHECK_CAST & INSTANCE_OF
+
+//!
+int common_check_cast_instance_of(u2 vA, u4 tmp, bool instance, u2 vDest) {
+    return check_cast_nohelper(vA, tmp, instance, vDest);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+//! LOWER bytecode CHECK_CAST
+
+//!
+int op_check_cast() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    common_check_cast_instance_of(vA, tmp, false, 0);
+    rPC += 2;
+    return 0;
+}
+//!LOWER bytecode INSTANCE_OF
+
+//!
+int op_instance_of() {
+    u2 vB = INST_B(inst);
+    u2 vA = INST_A(inst);
+    u4 tmp = (u4)FETCH(1);
+    common_check_cast_instance_of(vB, tmp, true, vA);
+    rPC += 2;
+    return 0;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! LOWER bytecode MONITOR_ENTER without usage of helper function
+
+//!   CALL dvmLockObject
+int monitor_enter_nohelper(u2 vA) {
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    requestVRFreeDelay(vA,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    //get_self_pointer is separated
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //to optimize redundant null check, NCG O1 wraps up null check in a function: nullCheck
+    get_self_pointer(3, false);
+    nullCheck(1, false, 1, vA); //maybe optimized away
+    cancelVRFreeDelayRequest(vA,VRDELAY_NULLCHECK);
+
+    /////////////////////////////
+    //prepare to call dvmLockObject, inputs: object reference and self
+    // TODO: Should reset inJitCodeCache before calling dvmLockObject
+    //       so that code cache can be reset if needed when locking object
+    //       taking a long time. Not resetting inJitCodeCache may delay
+    //       code cache reset when code cache is full, preventing traces from
+    //       JIT compilation. This has performance implication.
+    //       However, after resetting inJitCodeCache, the code should be
+    //       wrapped in a helper instead of directly inlined in code cache.
+    //       If the code after dvmLockObject call is in code cache and the code
+    //       cache is reset during dvmLockObject call, execution after
+    //       dvmLockObject will return to a cleared code cache region,
+    //       resulting in seg fault.
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 3, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    call_dvmLockObject();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    /////////////////////////////
+    return 0;
+}
+//! lower bytecode MONITOR_ENTER
+
+//! It will use helper function if switch is on
+int op_monitor_enter() {
+    u2 vA = INST_AA(inst);
+    export_pc();
+    monitor_enter_nohelper(vA);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! lower bytecode MONITOR_EXIT
+
+//! It will use helper function if switch is on
+int op_monitor_exit() {
+    u2 vA = INST_AA(inst);
+    ////////////////////
+    //LOWER bytecode MONITOR_EXIT without helper function
+    //   CALL dvmUnlockObject
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    requestVRFreeDelay(vA,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vA); //maybe optimized away
+    cancelVRFreeDelayRequest(vA,VRDELAY_NULLCHECK);
+
+    /////////////////////////////
+    //prepare to call dvmUnlockObject, inputs: object reference and self
+    push_reg_to_stack(OpndSize_32, 1, false);
+    push_mem_to_stack(OpndSize_32, offEBP_self, PhysicalReg_EBP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    call_dvmUnlockObject();
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    conditional_jump(Condition_NE, ".unlock_object_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    jumpToExceptionThrown(2/*exception number*/);
+    insertLabel(".unlock_object_done", true);
+    ///////////////////////////
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_EDX /*vA*/
+//! LOWER bytecode ARRAY_LENGTH
+
+//! It will use helper function if switch is on
+int op_array_length() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    ////////////////////
+    //no usage of helper function
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    move_mem_to_reg(OpndSize_32, offArrayObject_length, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 2, false);
+    ///////////////////////
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! lower bytecode NEW_INSTANCE
+
+//! It will use helper function if switch is on
+int op_new_instance() {
+    u4 tmp = (u4)FETCH(1);
+    u2 vA = INST_AA(inst);
+    export_pc();
+    /* for trace-based JIT, class is already resolved */
+    ClassObject *classPtr =
+        (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    assert(classPtr != NULL);
+    assert(classPtr->status & CLASS_INITIALIZED);
+    /*
+     * If it is going to throw, it should not make to the trace to begin
+     * with.  However, Alloc might throw, so we need to genExportPC()
+     */
+    assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
+    //prepare to call dvmAllocObject, inputs: resolved class & flag ALLOC_DONT_TRACK
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    /* 1st argument to dvmAllocObject at -8(%esp) */
+    move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 4, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    nextVersionOfHardReg(PhysicalReg_EAX, 3); //next version has 3 refs
+    call_dvmAllocObject();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //return value of dvmAllocObject is in %eax
+    //if return value is null, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".new_instance_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_4;
+    jumpToExceptionThrown(3/*exception number*/);
+    insertLabel(".new_instance_done", true);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    rPC += 2;
+    return 0;
+}
+
+//! function to initialize a class
+
+//!INPUT: %eax (class object) %eax is recovered before return
+//!OUTPUT: none
+//!CALL: dvmInitClass
+//!%eax, %esi, %ebx are live through function new_instance_needinit
+int new_instance_needinit() {
+    insertLabel(".new_instance_needinit", false);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_ECX;
+    call_dvmInitClass();
+    //if return value of dvmInitClass is zero, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    //recover EAX with the class object
+    move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, PhysicalReg_EAX, true);
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+    x86_return();
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX //live through C function, must in callee-saved reg
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_EDX
+//! lower bytecode NEW_ARRAY
+
+//! It will use helper function if switch is on
+int op_new_array() {
+    u4 tmp = (u4)FETCH(1);
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst); //length
+    /////////////////////////
+    //   REGS used: %esi, %eax, P_GPR_1, P_GPR_2
+    //   CALL class_resolve, dvmAllocArrayByClass
+    export_pc(); //use %edx
+    //check size of the array, if negative, throw exception
+    get_virtual_reg(vB, OpndSize_32, 5, false);
+    compare_imm_reg(OpndSize_32, 0, 5, false);
+    handlePotentialException(Condition_S, Condition_NS,
+                             1, "common_errNegArraySize");
+    void *classPtr = (void*)
+        (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    assert(classPtr != NULL);
+    //here, class is already resolved, the class object is in %eax
+    //prepare to call dvmAllocArrayByClass with inputs: resolved class, array length, flag ALLOC_DONT_TRACK
+    insertLabel(".new_array_resolved", true);
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    /* 1st argument to dvmAllocArrayByClass at 0(%esp) */
+    move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 5, false, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 8, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    nextVersionOfHardReg(PhysicalReg_EAX, 3); //next version has 3 refs
+    call_dvmAllocArrayByClass();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //the allocated object is in %eax
+    //check whether it is null, throw exception if null
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".new_array_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_4;
+    jumpToExceptionThrown(2/*exception number*/);
+    insertLabel(".new_array_done", true);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    //////////////////////////////////////
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! common code to lower FILLED_NEW_ARRAY
+
+//! call: class_resolve call_dvmAllocPrimitiveArray
+//! exception: filled_new_array_notimpl common_exceptionThrown
+int common_filled_new_array(u2 length, u4 tmp, bool hasRange) {
+    ClassObject *classPtr =
+              (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    if(classPtr != NULL) ALOGI("FILLED_NEW_ARRAY class %s", classPtr->descriptor);
+    //check whether class is resolved, if yes, jump to resolved
+    //if not, call class_resolve
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_res_classes(3, false);
+    move_mem_to_reg(OpndSize_32, tmp*4, 3, false, PhysicalReg_EAX, true);
+    export_pc();
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //resolved class
+    conditional_jump(Condition_NE, ".filled_new_array_resolved", true);
+    rememberState(1);
+    move_imm_to_reg(OpndSize_32, tmp, PhysicalReg_EAX, true);
+    call_helper_API(".class_resolve");
+    transferToState(1);
+    //here, class is already resolved
+    insertLabel(".filled_new_array_resolved", true);
+    //check descriptor of the class object, if not implemented, throws exception
+    move_mem_to_reg(OpndSize_32, 24, PhysicalReg_EAX, true, 5, false);
+    //load a single byte of the descriptor
+    movez_mem_to_reg(OpndSize_8, 1, 5, false, 6, false);
+    compare_imm_reg(OpndSize_32, 'I', 6, false);
+    conditional_jump(Condition_E, ".filled_new_array_impl", true);
+    compare_imm_reg(OpndSize_32, 'L', 6, false);
+    conditional_jump(Condition_E, ".filled_new_array_impl", true);
+    compare_imm_reg(OpndSize_32, '[', 6, false);
+    conditional_jump(Condition_NE, ".filled_new_array_notimpl", false);
+
+    insertLabel(".filled_new_array_impl", true);
+    //prepare to call dvmAllocArrayByClass with inputs: classObject, length, flag ALLOC_DONT_TRACK
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, length, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 8, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null;
+    if(hasRange) {
+        nextVersionOfHardReg(PhysicalReg_EAX, 5+(length >= 1 ? LOOP_COUNT : 0)); //next version
+    }
+    else {
+        nextVersionOfHardReg(PhysicalReg_EAX, 5+length); //next version
+    }
+    call_dvmAllocArrayByClass();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //return value of dvmAllocPrimitiveArray is in %eax
+    //if the return value is null, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    handlePotentialException(
+                                       Condition_E, Condition_NE,
+                                       3, "common_exceptionThrown");
+
+    /* we need to mark the card of the new array, if it's not an int */
+    compare_imm_reg(OpndSize_32, 'I', 6, false);
+    conditional_jump(Condition_E, ".dont_mark_filled_new_array", true);
+
+    // Need to make copy of EAX, because it's used later in op_filled_new_array()
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, 6, false);
+
+    markCard_filled(6, false, PhysicalReg_SCRATCH_4, false);
+
+    insertLabel(".dont_mark_filled_new_array", true);
+
+    //return value of bytecode FILLED_NEW_ARRAY is in GLUE structure
+    scratchRegs[0] = PhysicalReg_SCRATCH_4; scratchRegs[1] = PhysicalReg_Null;
+    set_return_value(OpndSize_32, PhysicalReg_EAX, true);
+    return 0;
+}
+//! LOWER bytecode FILLED_NEW_ARRAY
+
+//!
+int op_filled_new_array() {
+    u2 length = INST_B(inst);
+    u4 tmp = (u4)FETCH(1);
+    u2 v5 = INST_A(inst);
+    u2 vv = FETCH(2);
+    u2 v1 = vv & 0xf;
+    u2 v2 = (vv >> 4) & 0xf;
+    u2 v3 = (vv >> 8) & 0xf;
+    u2 v4 = (vv >> 12) & 0xf;
+    common_filled_new_array(length, tmp, false);
+    if(length >= 1) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v1, OpndSize_32, 7, false);
+        move_reg_to_mem(OpndSize_32, 7, false, offArrayObject_contents, PhysicalReg_EAX, true);
+    }
+    if(length >= 2) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v2, OpndSize_32, 8, false);
+        move_reg_to_mem(OpndSize_32, 8, false, offArrayObject_contents+4, PhysicalReg_EAX, true);
+    }
+    if(length >= 3) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v3, OpndSize_32, 9, false);
+        move_reg_to_mem(OpndSize_32, 9, false, offArrayObject_contents+8, PhysicalReg_EAX, true);
+    }
+    if(length >= 4) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v4, OpndSize_32, 10, false);
+        move_reg_to_mem(OpndSize_32, 10, false, offArrayObject_contents+12, PhysicalReg_EAX, true);
+    }
+    if(length >= 5) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v5, OpndSize_32, 11, false);
+        move_reg_to_mem(OpndSize_32, 11, false, offArrayObject_contents+16, PhysicalReg_EAX, true);
+    }
+    rPC += 3;
+    return 0;
+}
+//! function to handle the error of array not implemented
+
+//!
+int filled_new_array_notimpl() {
+    //two inputs for common_throw:
+    insertLabel(".filled_new_array_notimpl", false);
+    move_imm_to_reg(OpndSize_32, LstrFilledNewArrayNotImpl, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, (int) gDvm.exInternalError, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+
+#define P_SCRATCH_1 PhysicalReg_EDX
+//! LOWER bytecode FILLED_NEW_ARRAY_RANGE
+
+//!
+int op_filled_new_array_range() {
+    u2 length = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    u4 vC = (u4)FETCH(2);
+    common_filled_new_array(length, tmp, true/*hasRange*/);
+    //here, %eax points to the array object
+    if(length >= 1) {
+        //dump all virtual registers used by this bytecode to stack, for NCG O1
+        int k;
+        for(k = 0; k < length; k++) {
+            spillVirtualReg(vC+k, LowOpndRegType_gp, true); //will update refCount
+        }
+        //address of the first virtual register that will be moved to the array object
+        load_effective_addr(vC*4, PhysicalReg_FP, true, 7, false); //addr
+        //start address for contents of the array object
+        load_effective_addr(offArrayObject_contents, PhysicalReg_EAX, true, 8, false); //addr
+        //loop counter
+        move_imm_to_reg(OpndSize_32, length-1, 9, false); //counter
+        //start of the loop
+        insertLabel(".filled_new_array_range_loop1", true);
+        rememberState(1);
+        move_mem_to_reg(OpndSize_32, 0, 7, false, 10, false);
+        load_effective_addr(4, 7, false, 7, false);
+        move_reg_to_mem(OpndSize_32, 10, false, 0, 8, false);
+        load_effective_addr(4, 8, false, 8, false);
+        alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 9, false);
+        transferToState(1);
+        //jump back to the loop start
+        conditional_jump(Condition_NS, ".filled_new_array_range_loop1", true);
+    }
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode FILL_ARRAY_DATA
+
+//!use 1 GPR and scratch regs (export_pc dvmInterpHandleFillArrayData)
+//!CALL: dvmInterpHandleFillArrayData
+int op_fill_array_data() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    scratchRegs[1] = PhysicalReg_Null;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //prepare to call dvmInterpHandleFillArrayData, input: array object, address of the data
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
+    /* 2nd argument to dvmInterpHandleFillArrayData at 4(%esp) */
+    move_imm_to_mem(OpndSize_32, (int)(rPC+tmp), 4, PhysicalReg_ESP, true);
+    call_dvmInterpHandleFillArrayData();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //check return value of dvmInterpHandleFillArrayData, if zero, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".fill_array_data_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    jumpToExceptionThrown(2/*exception number*/);
+    insertLabel(".fill_array_data_done", true);
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode THROW
+
+//!
+int op_throw() {
+    u2 vA = INST_AA(inst);
+    export_pc();
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //null check
+    compare_imm_reg(OpndSize_32, 0, 1, false);
+    conditional_jump(Condition_E, "common_errNullObject", false);
+    //set glue->exception & throw exception
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+    set_exception(1, false);
+    unconditional_jump("common_exceptionThrown", false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode THROW_VERIFICATION_ERROR
+
+//! op AA, ref@BBBB
+int op_throw_verification_error() {
+    u2 vA, vB;
+    vA = INST_AA(inst);
+    vB = FETCH(1);
+
+    export_pc();
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    get_glue_method(1, false);
+
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, vB, 8, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, vA, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    call_dvmThrowVerificationError();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    unconditional_jump("common_exceptionThrown", false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
diff --git a/vm/compiler/codegen/x86/LowerReturn.cpp b/vm/compiler/codegen/x86/LowerReturn.cpp
new file mode 100644
index 0000000..928c05c
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerReturn.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*! \file LowerReturn.cpp
+    \brief This file lowers the following bytecodes: RETURN
+
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "mterp/Mterp.h"
+#include "Lower.h"
+#include "enc_wrapper.h"
+#include "NcgHelper.h"
+
+//4 GPRs and scratch registers used in get_self_pointer, set_glue_method and set_glue_dvmdex
+//will jump to "gotoBail" if caller method is NULL or if debugger is active
+//what is %edx for each case? for the latter case, it is 1
+#define P_GPR_1 PhysicalReg_ECX //must be ecx
+#define P_GPR_2 PhysicalReg_EBX
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define P_OLD_FP PhysicalReg_EAX
+/*!
+\brief common section to return from a method
+
+If the helper switch is on, this will generate a helper function
+*/
+int common_returnFromMethod() {
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC, mapFromBCtoNCG[offsetPC], 1); //check when helper switch is on
+#endif
+
+    scratchRegs[0] = PhysicalReg_SCRATCH_7;
+    get_self_pointer(2, false);
+
+    //update rFP to caller stack frame
+    move_reg_to_reg(OpndSize_32, PhysicalReg_FP, true, 10, false);
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_prevFrame, PhysicalReg_FP, true, PhysicalReg_FP, true); //update rFP
+    //get caller method by accessing the stack save area
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_method, PhysicalReg_FP, true, 6, false);
+    compare_imm_reg(OpndSize_32, 0, 6, false);
+    conditional_jump(Condition_E, "common_gotoBail_0", false);
+    get_self_pointer(3, false);
+    //update glue->method
+    move_reg_to_mem(OpndSize_32, 6, false, offsetof(Thread, interpSave.method), 2, false);
+    //get clazz of caller method
+    move_mem_to_reg(OpndSize_32, offMethod_clazz, 6, false, 14, false);
+    //update self->frame
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, 3, false);
+    //get method->clazz->pDvmDex
+    move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, 14, false, 7, false);
+    move_reg_to_mem(OpndSize_32, 7, false, offsetof(Thread, interpSave.methodClassDex), 2, false);
+
+    compare_imm_mem(OpndSize_32, 0, offsetof(Thread, suspendCount), 2, false); /* suspendCount */
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_returnAddr, 10, false, PhysicalReg_EBX, true);
+    move_imm_to_reg(OpndSize_32, 0, 17, false);
+    /* if suspendCount is not zero, clear the chaining cell address */
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 17, false/*src*/, PhysicalReg_EBX, true/*dst*/);
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_savedPc, 10, false, PhysicalReg_EAX, true);
+    //if returnAddr is not NULL, the thread is still in code cache
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EBX, true, offThread_inJitCodeCache, 3, false);
+
+    insertLabel(".LreturnToInterp", true); //local label
+    //move rPC by 6 (3 bytecode units for INVOKE)
+    alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EAX, true);
+
+    //returnAddr in %ebx, if not zero, jump to returnAddr
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EBX, true);
+    conditional_jump(Condition_E, ".LcontinueToInterp", true);
+#ifdef DEBUG_CALL_STACK3
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EBX, true, PhysicalReg_ESI, true);
+    move_imm_to_reg(OpndSize_32, 0xaabb, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESI, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESI, true, PhysicalReg_EBX, true);
+#endif
+    unconditional_jump_reg(PhysicalReg_EBX, true);
+    insertLabel(".LcontinueToInterp", true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_4;
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpNoChainNoProfile; //%eax is the input
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    touchEax();
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_SCRATCH_1
+
+//! lower bytecode RETURN_VOID
+
+//! It seems that shared code cache does not support helper switch
+int op_return_void() {
+    int retval;
+    retval = common_returnFromMethod();
+    rPC += 1;
+    return retval;
+}
+
+//! lower bytecode RETURN
+
+//! It seems that shared code cache does not support helper switch
+//! The return value is stored to glue->retval first
+int op_return() {
+    u2 vA = INST_AA(inst);
+
+    get_virtual_reg(vA, OpndSize_32, 22, false);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    set_return_value(OpndSize_32, 22, false);
+
+    common_returnFromMethod();
+    rPC += 1;
+    return 0;
+}
+
+//! lower bytecode RETURN_WIDE
+
+//! It seems that shared code cache does not support helper switch
+//! The return value is stored to glue->retval first
+int op_return_wide() {
+    u2 vA = INST_AA(inst);
+    get_virtual_reg(vA, OpndSize_64, 1, false);
+    scratchRegs[0] = PhysicalReg_SCRATCH_10; scratchRegs[1] = PhysicalReg_Null;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    set_return_value(OpndSize_64, 1, false);
+
+    common_returnFromMethod();
+    rPC += 1;
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/NcgAot.cpp b/vm/compiler/codegen/x86/NcgAot.cpp
new file mode 100644
index 0000000..7c29b6d
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgAot.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "Lower.h"
+#include "NcgAot.h"
+#include "NcgHelper.h"
+
+//returns # of ops generated by this function
+//entries relocatable: eip + relativePC
+int get_eip_API() {
+    call("ncgGetEIP");//%edx //will push eip to stack
+    return 1;
+}
+#define NEW_EXPORT_PC
+//!update current PC in the stack frame with %eip
+
+//!
+int export_pc() {
+    /* for trace-based JIT, pc points to bytecode
+       for NCG, pc points to native code */
+    move_imm_to_mem(OpndSize_32, (int)rPC,
+                    -sizeofStackSaveArea+offStackSaveArea_localRefTop, PhysicalReg_FP, true);
+    return 1; //return number of ops
+}
+
+/* jump from JIT'ed code to interpreter without chaining */
+int jumpToInterpNoChain() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpNoChain;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
+    return 0;
+}
+
+/* jump from JIT'ed code to interpreter becaues of exception */
+int jumpToInterpPunt() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpPunt;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    //if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
+    return 0;
+}
+
+/* jump to common_exceptionThrown from JIT'ed code */
+int jumpToExceptionThrown(int exceptionNum) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        rememberState(exceptionNum);
+        export_pc();
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+    }
+
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToExceptionThrown;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        goToState(exceptionNum);
+    }
+    return 0;
+}
+
+//! generate native code to call dvmNcgInvokeInterpreter
+
+//!the interpreter will start execution from %eax
+int invokeInterpreter(bool fromApp)
+{
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmNcgInvokeInterpreter;
+
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
+    return 0;
+}
+
+//!work to do before calling a function pointer with code cache enabled
+
+//!
+void callFuncPtr(int funcPtr, const char* funcName) {
+
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+    call_reg(C_SCRATCH_1, isScratchPhysical);
+}
+//.const_string_resolve: input in %eax, output in %eax
+//.const_string_helper:
+//.class_resolve: input in %eax, output in %eax
+int call_helper_API(const char* helperName) {
+    call(helperName);
+    return 1;
+}
+
+/* check whether we are throwing an exception */
+bool jumpToException(const char* target) {
+    bool isException = false;
+    if(!strncmp(target, "common_err", 10)) isException = true;
+    if(!strncmp(target, "common_throw", 12)) isException = true;
+    if(!strncmp(target, "common_exception", 16)) isException = true;
+    return isException;
+}
+
+int conditional_jump_global_API(
+                                ConditionCode cc, const char* target,
+                                bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        condJumpToBasicBlock(stream, cc, currentExceptionBlockIdx);
+        return 1; //return number of ops
+    }
+    conditional_jump(cc, target, isShortTerm);
+    return 1;
+}
+int unconditional_jump_global_API(
+                                  const char* target, bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        jumpToBasicBlock(stream, currentExceptionBlockIdx);
+        return 1; //return number of ops
+    }
+    unconditional_jump(target, isShortTerm);
+    return 1;
+}
+int getGlobalDataAddr(const char* dataName) {
+    int dataAddr = -1;
+    if(!strcmp(dataName, "doubNeg")) dataAddr = LdoubNeg;
+    else if(!strcmp(dataName, "intMax")) dataAddr = LintMax;
+    else if(!strcmp(dataName, "intMin")) dataAddr = LintMin;
+    else if(!strcmp(dataName, "valueNanLong")) dataAddr = LvalueNanLong;
+    else if(!strcmp(dataName, "valuePosInfLong")) dataAddr = LvaluePosInfLong;
+    else if(!strcmp(dataName, "valueNegInfLong")) dataAddr = LvalueNegInfLong;
+    else if(!strcmp(dataName, "shiftMask")) dataAddr = LshiftMask;
+    else if(!strcmp(dataName, "value64")) dataAddr = Lvalue64;
+    else if(!strcmp(dataName, "64bits")) dataAddr = L64bits;
+    else if(!strcmp(dataName, "strClassCastExceptionPtr")) dataAddr = LstrClassCastExceptionPtr;
+    else if(!strcmp(dataName, "strInstantiationError")) dataAddr = LstrInstantiationErrorPtr;
+    else if(!strcmp(dataName, "gDvmInlineOpsTable")) dataAddr = (int)gDvmInlineOpsTable;
+    else ALOGE("global data %s not supported", dataName);
+    return dataAddr;
+}
+
+//for shared code cache, we use scratchRegs[0] & [1]
+int load_imm_global_data_API(const char* dataName,
+                         OpndSize size,
+                         int reg, bool isPhysical) {
+
+    //find the address from name
+    int dataAddr = getGlobalDataAddr(dataName);
+    move_imm_to_reg(size, dataAddr, reg, isPhysical);
+    return 0;
+}
+//for shared code cache, we use scratchRegs[0] & [1] & [2]
+//FIXME: [2] is assumed to be hard-coded register
+int load_global_data_API(const char* dataName,
+                         OpndSize size,
+                         int reg, bool isPhysical) {
+
+    //find the address from name
+    int dataAddr = getGlobalDataAddr(dataName);
+    move_mem_to_reg(size, dataAddr, PhysicalReg_Null, true, reg, isPhysical);
+    return 0;
+}
+int load_sd_global_data_API(const char* dataName,
+                            int reg, bool isPhysical) {
+
+    //find the address from name
+    int dataAddr = getGlobalDataAddr(dataName);
+    move_sd_mem_to_reg(dataAddr, PhysicalReg_Null, true, reg, isPhysical);
+    return 0;
+}
+
+int load_fp_stack_global_data_API(const char* dataName,
+                                  OpndSize size) {
+
+    int dataAddr = getGlobalDataAddr(dataName);
+    load_int_fp_stack_imm(size, dataAddr); //fildl
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/NcgAot.h b/vm/compiler/codegen/x86/NcgAot.h
new file mode 100644
index 0000000..99bcc13
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgAot.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+#ifndef _DALVIK_NCG_AOT
+#define _DALVIK_NCG_AOT
+int ncgAppGetEIP();
+int get_eip_API();
+int invokeInterpreter(bool fromApp);
+int invokeNcg(bool fromApp);
+int jumpToInterpNoChain();
+int jumpToInterpPunt();
+int jumpToExceptionThrown(int exceptionNum);
+void callFuncPtr(int funcPtr, const char* funcName);
+int call_helper_API(const char* helperName);
+int conditional_jump_global_API(
+                                ConditionCode cc, const char* target,
+                                bool isShortTerm);
+int unconditional_jump_global_API(
+                                  const char* target, bool isShortTerm);
+int load_imm_global_data_API(const char* dataName,
+                             OpndSize size,
+                             int reg, bool isPhysical);
+int load_global_data_API(const char* dataName,
+                         OpndSize size,
+                         int reg, bool isPhysical);
+int load_sd_global_data_API(const char* dataName,
+                            int reg, bool isPhysical);
+int load_fp_stack_global_data_API(const char* dataName,
+                                  OpndSize size);
+#endif
+
diff --git a/vm/compiler/codegen/x86/NcgHelper.cpp b/vm/compiler/codegen/x86/NcgHelper.cpp
new file mode 100644
index 0000000..c603d09
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgHelper.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "Dalvik.h"
+#include "NcgHelper.h"
+#include "interp/InterpDefs.h"
+
+
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the packed-switch
+ * instruction).
+ */
+s4 dvmNcgHandlePackedSwitch(const s4* entries, s4 firstKey, u2 size, s4 testVal)
+{
+    //skip add_reg_reg (ADD_REG_REG_SIZE) and jump_reg (JUMP_REG_SIZE)
+    const int kInstrLen = 4; //default to next bytecode
+    if (testVal < firstKey || testVal >= firstKey + size) {
+        LOGVV("Value %d not found in switch (%d-%d)",
+            testVal, firstKey, firstKey+size-1);
+        return kInstrLen;
+    }
+
+    assert(testVal - firstKey >= 0 && testVal - firstKey < size);
+    LOGVV("Value %d found in slot %d (goto 0x%02x)",
+        testVal, testVal - firstKey,
+        s4FromSwitchData(&entries[testVal - firstKey]));
+    return s4FromSwitchData(&entries[testVal - firstKey]);
+
+}
+/* return the number of bytes to increase the bytecode pointer by */
+s4 dvmJitHandlePackedSwitch(const s4* entries, s4 firstKey, u2 size, s4 testVal)
+{
+    if (testVal < firstKey || testVal >= firstKey + size) {
+        LOGVV("Value %d not found in switch (%d-%d)",
+            testVal, firstKey, firstKey+size-1);
+        return 2*3;//bytecode packed_switch is 6(2*3) bytes long
+    }
+
+    LOGVV("Value %d found in slot %d (goto 0x%02x)",
+        testVal, testVal - firstKey,
+        s4FromSwitchData(&entries[testVal - firstKey]));
+    return 2*s4FromSwitchData(&entries[testVal - firstKey]); //convert from u2 to byte
+
+}
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the sparse-switch
+ * instruction).
+ */
+s4 dvmNcgHandleSparseSwitch(const s4* keys, u2 size, s4 testVal)
+{
+    const int kInstrLen = 4; //CHECK
+    const s4* entries = keys + size;
+    int i;
+    for (i = 0; i < size; i++) {
+        s4 k = s4FromSwitchData(&keys[i]);
+        if (k == testVal) {
+            LOGVV("Value %d found in entry %d (goto 0x%02x)",
+                testVal, i, s4FromSwitchData(&entries[i]));
+            return s4FromSwitchData(&entries[i]);
+        } else if (k > testVal) {
+            break;
+        }
+    }
+
+    LOGVV("Value %d not found in switch", testVal);
+    return kInstrLen;
+}
+/* return the number of bytes to increase the bytecode pointer by */
+s4 dvmJitHandleSparseSwitch(const s4* keys, u2 size, s4 testVal)
+{
+    const s4* entries = keys + size;
+    int i;
+    for (i = 0; i < size; i++) {
+        s4 k = s4FromSwitchData(&keys[i]);
+        if (k == testVal) {
+            LOGVV("Value %d found in entry %d (goto 0x%02x)",
+                testVal, i, s4FromSwitchData(&entries[i]));
+            return 2*s4FromSwitchData(&entries[i]); //convert from u2 to byte
+        } else if (k > testVal) {
+            break;
+        }
+    }
+
+    LOGVV("Value %d not found in switch", testVal);
+    return 2*3; //bytecode sparse_switch is 6(2*3) bytes long
+}
diff --git a/vm/compiler/codegen/x86/NcgHelper.h b/vm/compiler/codegen/x86/NcgHelper.h
new file mode 100644
index 0000000..713f424
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgHelper.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef _DALVIK_NCG_HELPER
+#define _DALVIK_NCG_HELPER
+#include "mterp/Mterp.h"
+s4 dvmNcgHandlePackedSwitch(const s4*, s4, u2, s4);
+s4 dvmNcgHandleSparseSwitch(const s4*, u2, s4);
+s4 dvmJitHandlePackedSwitch(const s4*, s4, u2, s4);
+s4 dvmJitHandleSparseSwitch(const s4*, u2, s4);
+extern "C" void dvmNcgInvokeInterpreter(int pc); //interpreter to execute at pc
+extern "C" void dvmNcgInvokeNcg(int pc);
+extern "C" void dvmJitToInterpNormal(int targetpc); //in %ebx
+extern "C" void dvmJitToInterpTraceSelect(int targetpc); //in %ebx
+extern "C" void dvmJitToInterpTraceSelectNoChain(int targetpc); //in %ebx
+extern "C" void dvmJitToInterpNoChain(int targetpc); //in %eax
+extern "C" void dvmJitToInterpNoChainNoProfile(int targetpc); //in %eax
+extern "C" void dvmJitToInterpPunt(int targetpc); //in currentPc
+extern "C" void dvmJitToExceptionThrown(int targetpc); //in currentPc
+#ifdef DEBUG_CALL_STACK3
+void debug_dumpSwitch(int); //in %ebx
+#endif
+
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz);
+#endif /*_DALVIK_NCG_HELPER*/
diff --git a/vm/compiler/codegen/x86/Translator.h b/vm/compiler/codegen/x86/Translator.h
new file mode 100644
index 0000000..0d7ef32
--- /dev/null
+++ b/vm/compiler/codegen/x86/Translator.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+#ifndef _DALVIK_TRANSLATOR
+#define _DALVIK_TRANSLATOR
+
+#include "Dalvik.h"
+#include "enc_wrapper.h"
+
+/* initialization for trace-based JIT */
+void initJIT(const char* curFileName, DvmDex *pDvmDex);
+
+#endif
diff --git a/vm/compiler/codegen/x86/libenc/Android.mk b/vm/compiler/codegen/x86/libenc/Android.mk
new file mode 100644
index 0000000..6fe9cdb
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/Android.mk
@@ -0,0 +1,65 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Only include the x86 encoder/decoder for x86 architecture
+ifeq ($(TARGET_ARCH),x86)
+
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(LIBENC_INCLUDED),true)
+
+LIBENC_INCLUDED := true
+
+enc_src_files := \
+        enc_base.cpp \
+        dec_base.cpp \
+        enc_wrapper.cpp \
+        enc_tabl.cpp
+
+enc_include_files :=
+
+##
+##
+## Build the device version of libenc
+##
+##
+ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(enc_src_files)
+LOCAL_C_INCLUDES += $(enc_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libenc
+include $(BUILD_STATIC_LIBRARY)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host version of libenc
+##
+##
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(enc_src_files)
+LOCAL_C_INCLUDES += $(enc_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libenc
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+endif   # ifneq ($(LIBENC_INCLUDED),true)
+
+endif   # ifeq ($(TARGET_ARCH),x86)
diff --git a/vm/compiler/codegen/x86/libenc/README.txt b/vm/compiler/codegen/x86/libenc/README.txt
new file mode 100644
index 0000000..30df760
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/README.txt
@@ -0,0 +1,26 @@
+Original source from Apache Harmony 5.0M15 (r991518 from 2010-09-01) at
+http://harmony.apache.org/.
+
+The following files are from drlvm/vm/port/src/encoder/ia32_em64t.
+
+    dec_base.cpp
+    dec_base.h
+    enc_base.cpp
+    enc_base.h
+    enc_defs.h
+    enc_prvt.h
+    enc_tabl.cpp
+    encoder.cpp
+    encoder.h
+    encoder.inl
+
+The following files are derived partially from the original Apache
+Harmony files.
+
+    enc_defs_ext.h -- derived from enc_defs.h
+    enc_wrapper.h  -- derived from encoder.h
+
+
+
+
+
diff --git a/vm/compiler/codegen/x86/libenc/dec_base.cpp b/vm/compiler/codegen/x86/libenc/dec_base.cpp
new file mode 100644
index 0000000..e0edc10
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/dec_base.cpp
@@ -0,0 +1,540 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+/**
+ * @file
+ * @brief Main decoding (disassembling) routines implementation.
+ */
+
+#include "dec_base.h"
+#include "enc_prvt.h"
+#include <stdio.h>
+//#include "open/common.h"
+
+bool DecoderBase::is_prefix(const unsigned char * bytes)
+{
+    unsigned char b0 = *bytes;
+    unsigned char b1 = *(bytes+1);
+    if (b0 == 0xF0) { // LOCK
+        return true;
+    }
+    if (b0==0xF2 || b0==0xF3) { // REPNZ/REPZ prefixes
+        if (b1 == 0x0F) {   // .... but may be a part of SIMD opcode
+            return false;
+        }
+        return true;
+    }
+    if (b0 == 0x2E || b0 == 0x36 || b0==0x3E || b0==0x26 || b0==0x64 || b0==0x3E) {
+        // branch hints, segment prefixes
+        return true;
+    }
+    if (b0==0x66) { // operand-size prefix
+        if (b1 == 0x0F) {   // .... but may be a part of SIMD opcode
+            return false;
+        }
+        return false; //XXX - currently considered as part of opcode//true;
+    }
+    if (b0==0x67) { // address size prefix
+        return true;
+    }
+    return false;
+}
+
+// Returns prefix count from 0 to 4, or ((unsigned int)-1) on error
+unsigned int DecoderBase::fill_prefs(const unsigned char * bytes, Inst * pinst)
+{
+    const unsigned char * my_bytes = bytes;
+
+    while( 1 )
+    {
+        unsigned char by1 = *my_bytes;
+        unsigned char by2 = *(my_bytes + 1);
+        Inst::PrefGroups where;
+
+        switch( by1 )
+        {
+        case InstPrefix_REPNE:
+        case InstPrefix_REP:
+        {
+            if( 0x0F == by2)
+            {
+                return pinst->prefc;
+            }
+        }
+        case InstPrefix_LOCK:
+        {
+            where = Inst::Group1;
+            break;
+        }
+        case InstPrefix_CS:
+        case InstPrefix_SS:
+        case InstPrefix_DS:
+        case InstPrefix_ES:
+        case InstPrefix_FS:
+        case InstPrefix_GS:
+//      case InstPrefix_HintTaken: the same as CS override
+//      case InstPrefix_HintNotTaken: the same as DS override
+        {
+            where = Inst::Group2;
+            break;
+        }
+        case InstPrefix_OpndSize:
+        {
+//NOTE:   prefix does not work for JMP Sz16, the opcode is 0x66 0xe9
+//        here 0x66 will be treated as prefix, try_mn will try to match the code starting at 0xe9
+//        it will match JMP Sz32 ...
+//HACK:   assume it is the last prefix, return any way
+            if( 0x0F == by2)
+            {
+                return pinst->prefc;
+            }
+            return pinst->prefc;
+            where = Inst::Group3;
+            break;
+        }
+        case InstPrefix_AddrSize:
+        {
+            where = Inst::Group4;
+            break;
+        }
+        default:
+        {
+            return pinst->prefc;
+        }
+        }
+        // Assertions are not allowed here.
+        // Error situations should result in returning error status
+        if (InstPrefix_Null != pinst->pref[where]) //only one prefix in each group
+            return (unsigned int)-1;
+
+        pinst->pref[where] = (InstPrefix)by1;
+
+        if (pinst->prefc >= 4) //no more than 4 prefixes
+            return (unsigned int)-1;
+
+        pinst->prefc++;
+        ++my_bytes;
+    }
+}
+
+
+
+unsigned DecoderBase::decode(const void * addr, Inst * pinst)
+{
+    Inst tmp;
+
+    //assert( *(unsigned char*)addr != 0x66);
+
+    const unsigned char * bytes = (unsigned char*)addr;
+
+    // Load up to 4 prefixes
+    // for each Mnemonic
+    unsigned int pref_count = fill_prefs(bytes, &tmp);
+
+    if (pref_count == (unsigned int)-1) // Wrong prefix sequence, or >4 prefixes
+        return 0; // Error
+
+    bytes += pref_count;
+
+    //  for each opcodedesc
+    //      if (raw_len == 0) memcmp(, raw_len)
+    //  else check the mixed state which is one of the following:
+    //      /digit /i /rw /rd /rb
+
+    bool found = false;
+    const unsigned char * saveBytes = bytes;
+    for (unsigned mn=1; mn<Mnemonic_Count; mn++) {
+        bytes = saveBytes;
+        found=try_mn((Mnemonic)mn, &bytes, &tmp);
+        if (found) {
+            tmp.mn = (Mnemonic)mn;
+            break;
+        }
+    }
+    if (!found) {
+        // Unknown opcode
+        return 0;
+    }
+    tmp.size = (unsigned)(bytes-(const unsigned char*)addr);
+    if (pinst) {
+        *pinst = tmp;
+    }
+    return tmp.size;
+}
+
+#ifdef _EM64T_
+#define EXTEND_REG(reg, flag)                        \
+    ((NULL == rex || 0 == rex->flag) ? reg : (reg + 8))
+#else
+#define EXTEND_REG(reg, flag) (reg)
+#endif
+
+//don't know the use of rex, seems not used when _EM64T_ is not enabled
+bool DecoderBase::decode_aux(const EncoderBase::OpcodeDesc& odesc, unsigned aux,
+    const unsigned char ** pbuf, Inst * pinst
+#ifdef _EM64T_
+    , const Rex UNREF *rex
+#endif
+    )
+{
+    OpcodeByteKind kind = (OpcodeByteKind)(aux & OpcodeByteKind_KindMask);
+    unsigned byte = (aux & OpcodeByteKind_OpcodeMask);
+    unsigned data_byte = **pbuf;
+    EncoderBase::Operand& opnd = pinst->operands[pinst->argc];
+    const EncoderBase::OpndDesc& opndDesc = odesc.opnds[pinst->argc];
+
+    switch (kind) {
+    case OpcodeByteKind_SlashR:
+        {
+            RegName reg;
+            OpndKind okind;
+            const ModRM& modrm = *(ModRM*)*pbuf;
+            if (opndDesc.kind & OpndKind_Mem) { // 1st operand is memory
+#ifdef _EM64T_
+                decodeModRM(odesc, pbuf, pinst, rex);
+#else
+                decodeModRM(odesc, pbuf, pinst);
+#endif
+                ++pinst->argc;
+                const EncoderBase::OpndDesc& opndDesc2 = odesc.opnds[pinst->argc];
+                okind = ((opndDesc2.kind & OpndKind_XMMReg) || opndDesc2.size==OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg;
+                EncoderBase::Operand& regOpnd = pinst->operands[pinst->argc];
+                reg = getRegName(okind, opndDesc2.size, EXTEND_REG(modrm.reg, r));
+                regOpnd = EncoderBase::Operand(reg);
+            } else {                            // 2nd operand is memory
+                okind = ((opndDesc.kind & OpndKind_XMMReg) || opndDesc.size==OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg;
+                EncoderBase::Operand& regOpnd = pinst->operands[pinst->argc];
+                reg = getRegName(okind, opndDesc.size, EXTEND_REG(modrm.reg, r));
+                regOpnd = EncoderBase::Operand(reg);
+                ++pinst->argc;
+#ifdef _EM64T_
+                decodeModRM(odesc, pbuf, pinst, rex);
+#else
+                decodeModRM(odesc, pbuf, pinst);
+#endif
+            }
+            ++pinst->argc;
+        }
+        return true;
+    case OpcodeByteKind_rb:
+    case OpcodeByteKind_rw:
+    case OpcodeByteKind_rd:
+        {
+            // Gregory -
+            // Here we don't parse register because for current needs
+            // disassembler doesn't require to parse all operands
+            unsigned regid = data_byte - byte;
+            if (regid>7) {
+                return false;
+            }
+            OpndSize opnd_size;
+            switch(kind)
+            {
+            case OpcodeByteKind_rb:
+            {
+                opnd_size = OpndSize_8;
+                break;
+            }
+            case OpcodeByteKind_rw:
+            {
+                opnd_size = OpndSize_16;
+                break;
+            }
+            case OpcodeByteKind_rd:
+            {
+                opnd_size = OpndSize_32;
+                break;
+            }
+            default:
+                opnd_size = OpndSize_32;  // so there is no compiler warning
+                assert( false );
+            }
+            opnd = EncoderBase::Operand( getRegName(OpndKind_GPReg, opnd_size, regid) );
+
+            ++pinst->argc;
+            ++*pbuf;
+            return true;
+        }
+    case OpcodeByteKind_cb:
+        {
+        char offset = *(char*)*pbuf;
+        *pbuf += 1;
+        opnd = EncoderBase::Operand(offset);
+        ++pinst->argc;
+        //pinst->direct_addr = (void*)(pinst->offset + *pbuf);
+        }
+        return true;
+    case OpcodeByteKind_cw:
+        // not an error, but not expected in current env
+        // Android x86
+        {
+        short offset = *(short*)*pbuf;
+        *pbuf += 2;
+        opnd = EncoderBase::Operand(offset);
+        ++pinst->argc;
+        }
+        return true;
+        //return false;
+    case OpcodeByteKind_cd:
+        {
+        int offset = *(int*)*pbuf;
+        *pbuf += 4;
+        opnd = EncoderBase::Operand(offset);
+        ++pinst->argc;
+        }
+        return true;
+    case OpcodeByteKind_SlashNum:
+        {
+        const ModRM& modrm = *(ModRM*)*pbuf;
+        if (modrm.reg != byte) {
+            return false;
+        }
+        decodeModRM(odesc, pbuf, pinst
+#ifdef _EM64T_
+                        , rex
+#endif
+                        );
+        ++pinst->argc;
+        }
+        return true;
+    case OpcodeByteKind_ib:
+        {
+        char ival = *(char*)*pbuf;
+        opnd = EncoderBase::Operand(ival);
+        ++pinst->argc;
+        *pbuf += 1;
+        }
+        return true;
+    case OpcodeByteKind_iw:
+        {
+        short ival = *(short*)*pbuf;
+        opnd = EncoderBase::Operand(ival);
+        ++pinst->argc;
+        *pbuf += 2;
+        }
+        return true;
+    case OpcodeByteKind_id:
+        {
+        int ival = *(int*)*pbuf;
+        opnd = EncoderBase::Operand(ival);
+        ++pinst->argc;
+        *pbuf += 4;
+        }
+        return true;
+#ifdef _EM64T_
+    case OpcodeByteKind_io:
+        {
+        long long int ival = *(long long int*)*pbuf;
+        opnd = EncoderBase::Operand(OpndSize_64, ival);
+        ++pinst->argc;
+        *pbuf += 8;
+        }
+        return true;
+#endif
+    case OpcodeByteKind_plus_i:
+        {
+            unsigned regid = data_byte - byte;
+            if (regid>7) {
+                return false;
+            }
+            ++*pbuf;
+            return true;
+        }
+    case OpcodeByteKind_ZeroOpcodeByte: // cant be here
+        return false;
+    default:
+        // unknown kind ? how comes ?
+        break;
+    }
+    return false;
+}
+
+bool DecoderBase::try_mn(Mnemonic mn, const unsigned char ** pbuf, Inst * pinst) {
+    const unsigned char * save_pbuf = *pbuf;
+    EncoderBase::OpcodeDesc * opcodes = EncoderBase::opcodes[mn];
+
+    for (unsigned i=0; !opcodes[i].last; i++) {
+        const EncoderBase::OpcodeDesc& odesc = opcodes[i];
+        char *opcode_ptr = const_cast<char *>(odesc.opcode);
+        int opcode_len = odesc.opcode_len;
+#ifdef _EM64T_
+        Rex *prex = NULL;
+        Rex rex;
+#endif
+
+        *pbuf = save_pbuf;
+#ifdef _EM64T_
+        // Match REX prefixes
+        unsigned char rex_byte = (*pbuf)[0];
+        if ((rex_byte & 0xf0) == 0x40)
+        {
+            if ((rex_byte & 0x08) != 0)
+            {
+                // Have REX.W
+                if (opcode_len > 0 && opcode_ptr[0] == 0x48)
+                {
+                    // Have REX.W in opcode. All mnemonics that allow
+                    // REX.W have to have specified it in opcode,
+                    // otherwise it is not allowed
+                    rex = *(Rex *)*pbuf;
+                    prex = &rex;
+                    (*pbuf)++;
+                    opcode_ptr++;
+                    opcode_len--;
+                }
+            }
+            else
+            {
+                // No REX.W, so it doesn't have to be in opcode. We
+                // have REX.B, REX.X, REX.R or their combination, but
+                // not in opcode, they may extend any part of the
+                // instruction
+                rex = *(Rex *)*pbuf;
+                prex = &rex;
+                (*pbuf)++;
+            }
+        }
+#endif
+        if (opcode_len != 0) {
+            if (memcmp(*pbuf, opcode_ptr, opcode_len)) {
+                continue;
+            }
+            *pbuf += opcode_len;
+        }
+        if (odesc.aux0 != 0) {
+
+            if (!decode_aux(odesc, odesc.aux0, pbuf, pinst
+#ifdef _EM64T_
+                            , prex
+#endif
+                            )) {
+                continue;
+            }
+            if (odesc.aux1 != 0) {
+                if (!decode_aux(odesc, odesc.aux1, pbuf, pinst
+#ifdef _EM64T_
+                            , prex
+#endif
+                            )) {
+                    continue;
+                }
+            }
+            pinst->odesc = &opcodes[i];
+            return true;
+        }
+        else {
+            // Can't have empty opcode
+            assert(opcode_len != 0);
+            pinst->odesc = &opcodes[i];
+            return true;
+        }
+    }
+    return false;
+}
+
+bool DecoderBase::decodeModRM(const EncoderBase::OpcodeDesc& odesc,
+    const unsigned char ** pbuf, Inst * pinst
+#ifdef _EM64T_
+    , const Rex *rex
+#endif
+    )
+{
+    EncoderBase::Operand& opnd = pinst->operands[pinst->argc];
+    const EncoderBase::OpndDesc& opndDesc = odesc.opnds[pinst->argc];
+
+    //XXX debug ///assert(0x66 != *(*pbuf-2));
+    const ModRM& modrm = *(ModRM*)*pbuf;
+    *pbuf += 1;
+
+    RegName base = RegName_Null;
+    RegName index = RegName_Null;
+    int disp = 0;
+    unsigned scale = 0;
+
+    // On x86_64 all mnemonics that allow REX.W have REX.W in opcode.
+    // Therefore REX.W is simply ignored, and opndDesc.size is used
+
+    if (modrm.mod == 3) {
+        // we have only modrm. no sib, no disp.
+        // Android x86: Use XMMReg for 64b operand.
+        OpndKind okind = ((opndDesc.kind & OpndKind_XMMReg) || opndDesc.size == OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg;
+        RegName reg = getRegName(okind, opndDesc.size, EXTEND_REG(modrm.rm, b));
+        opnd = EncoderBase::Operand(reg);
+        return true;
+    }
+    //Android x86: m16, m32, m64: mean a byte[word|doubleword] operand in memory
+    //base and index should be 32 bits!!!
+    const SIB& sib = *(SIB*)*pbuf;
+    // check whether we have a sib
+    if (modrm.rm == 4) {
+        // yes, we have SIB
+        *pbuf += 1;
+        // scale = sib.scale == 0 ? 0 : (1<<sib.scale);
+        scale = (1<<sib.scale);
+        if (sib.index != 4) {
+            index = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(sib.index, x)); //Android x86: OpndDesc.size
+        } else {
+            // (sib.index == 4) => no index
+            //%esp can't be sib.index
+        }
+
+        if (sib.base != 5 || modrm.mod != 0) {
+            base = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(sib.base, b)); //Android x86: OpndDesc.size
+        } else {
+            // (sib.base == 5 && modrm.mod == 0) => no base
+        }
+    }
+    else {
+        if (modrm.mod != 0 || modrm.rm != 5) {
+            base = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(modrm.rm, b)); //Android x86: OpndDesc.size
+        }
+        else {
+            // mod=0 && rm == 5 => only disp32
+        }
+    }
+
+    //update disp and pbuf
+    if (modrm.mod == 2) {
+        // have disp32
+        disp = *(int*)*pbuf;
+        *pbuf += 4;
+    }
+    else if (modrm.mod == 1) {
+        // have disp8
+        disp = *(char*)*pbuf;
+        *pbuf += 1;
+    }
+    else {
+        assert(modrm.mod == 0);
+        if (modrm.rm == 5) {
+            // have disp32 w/o sib
+            disp = *(int*)*pbuf;
+            *pbuf += 4;
+        }
+        else if (modrm.rm == 4 && sib.base == 5) {
+            // have disp32 with SI in sib
+            disp = *(int*)*pbuf;
+            *pbuf += 4;
+        }
+    }
+    opnd = EncoderBase::Operand(opndDesc.size, base, index, scale, disp);
+    return true;
+}
+
diff --git a/vm/compiler/codegen/x86/libenc/dec_base.h b/vm/compiler/codegen/x86/libenc/dec_base.h
new file mode 100644
index 0000000..909c743
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/dec_base.h
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+/**
+ * @file
+ * @brief Main decoding (disassembling) routines and structures.
+ *
+ * @note Quick and rough implementation, subject for a change.
+ */
+
+#ifndef __DEC_BASE_H_INCLUDED__
+#define __DEC_BASE_H_INCLUDED__
+
+
+#include "enc_base.h"
+#include "enc_prvt.h"
+
+#ifdef ENCODER_ISOLATE
+using namespace enc_ia32;
+#endif
+
+#define IF_CONDITIONAL  (0x00000000)
+#define IF_SYMMETRIC    (0x00000000)
+#define IF_BRANCH       (0x00000000)
+
+struct Inst {
+    Inst() {
+        mn = Mnemonic_Null;
+        prefc = 0;
+        size = 0;
+        flags = 0;
+        //offset = 0;
+        //direct_addr = NULL;
+        argc = 0;
+        for(int i = 0; i < 4; ++i)
+        {
+            pref[i] = InstPrefix_Null;
+        }
+    }
+    /**
+     * Mnemonic of the instruction.s
+     */
+    Mnemonic mn;
+    /**
+     * Enumerating of indexes in the pref array.
+     */
+    enum PrefGroups
+    {
+        Group1 = 0,
+        Group2,
+        Group3,
+        Group4
+    };
+    /**
+     * Number of prefixes (1 byte each).
+     */
+    unsigned int prefc;
+    /**
+     * Instruction prefixes. Prefix should be placed here according to its group.
+     */
+    InstPrefix pref[4];
+    /**
+     * Size, in bytes, of the instruction.
+     */
+    unsigned size;
+    /**
+     * Flags of the instruction.
+     * @see MF_
+     */
+    unsigned flags;
+    /**
+     * An offset of target address, in case of 'CALL offset',
+     * 'JMP/Jcc offset'.
+     */
+    //int      offset;
+    /**
+     * Direct address of the target (on Intel64/IA-32 is 'instruction IP' +
+     * 'instruction length' + offset).
+     */
+    //void *   direct_addr;
+    /**
+     * Number of arguments of the instruction.
+     */
+    unsigned argc;
+    //
+    EncoderBase::Operand operands[3];
+    //
+    const EncoderBase::OpcodeDesc * odesc;
+};
+
+inline bool is_jcc(Mnemonic mn)
+{
+    return Mnemonic_JO <= mn && mn<=Mnemonic_JG;
+}
+
+class DecoderBase {
+public:
+    static unsigned decode(const void * addr, Inst * pinst);
+private:
+    static bool decodeModRM(const EncoderBase::OpcodeDesc& odesc,
+        const unsigned char ** pbuf, Inst * pinst
+#ifdef _EM64T_
+        , const Rex *rex
+#endif
+        );
+    static bool decode_aux(const EncoderBase::OpcodeDesc& odesc,
+        unsigned aux, const unsigned char ** pbuf,
+        Inst * pinst
+#ifdef _EM64T_
+        , const Rex *rex
+#endif
+        );
+    static bool try_mn(Mnemonic mn, const unsigned char ** pbuf, Inst * pinst);
+    static unsigned int fill_prefs( const unsigned char * bytes, Inst * pinst);
+    static bool is_prefix(const unsigned char * bytes);
+};
+
+#endif  // ~ __DEC_BASE_H_INCLUDED__
+
diff --git a/vm/compiler/codegen/x86/libenc/enc_base.cpp b/vm/compiler/codegen/x86/libenc/enc_base.cpp
new file mode 100644
index 0000000..d141e1f
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_base.cpp
@@ -0,0 +1,1153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#include "enc_base.h"
+//#include <climits>
+#include <string.h>
+#define USE_ENCODER_DEFINES
+#include "enc_prvt.h"
+#include <stdio.h>
+
+//#define JET_PROTO
+
+#ifdef JET_PROTO
+#include "dec_base.h"
+#include "jvmti_dasm.h"
+#endif
+
+ENCODER_NAMESPACE_START
+
+/**
+ * @file
+ * @brief Main encoding routines and structures.
+ */
+
+#ifndef _WIN32
+    #define strcmpi strcasecmp
+#endif
+
+int EncoderBase::dummy = EncoderBase::buildTable();
+
+const unsigned char EncoderBase::size_hash[OpndSize_64+1] = {
+    //
+    0xFF,   // OpndSize_Null        = 0,
+    3,              // OpndSize_8           = 0x1,
+    2,              // OpndSize_16          = 0x2,
+    0xFF,   // 0x3
+    1,              // OpndSize_32          = 0x4,
+    0xFF,   // 0x5
+    0xFF,   // 0x6
+    0xFF,   // 0x7
+    0,              // OpndSize_64          = 0x8,
+    //
+};
+
+const unsigned char EncoderBase::kind_hash[OpndKind_Mem+1] = {
+    //
+    //gp reg                -> 000 = 0
+    //memory                -> 001 = 1
+    //immediate             -> 010 = 2
+    //xmm reg               -> 011 = 3
+    //segment regs  -> 100 = 4
+    //fp reg                -> 101 = 5
+    //mmx reg               -> 110 = 6
+    //
+    0xFF,                          // 0    OpndKind_Null=0,
+    0<<2,                          // 1    OpndKind_GPReg =
+                                   //           OpndKind_MinRegKind=0x1,
+    4<<2,                          // 2    OpndKind_SReg=0x2,
+
+#ifdef _HAVE_MMX_
+    6<<2,                          // 3
+#else
+    0xFF,                          // 3
+#endif
+
+    5<<2,                          // 4    OpndKind_FPReg=0x4,
+    0xFF, 0xFF, 0xFF,              // 5, 6, 7
+    3<<2,                                   //      OpndKind_XMMReg=0x8,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 9, 0xA, 0xB, 0xC, 0xD,
+                                              // 0xE, 0xF
+    0xFF,                          // OpndKind_MaxRegKind =
+                                   // OpndKind_StatusReg =
+                                   // OpndKind_OtherReg=0x10,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x11-0x18
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,               // 0x19-0x1F
+    2<<2,                                   // OpndKind_Immediate=0x20,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x21-0x28
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x29-0x30
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x31-0x38
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,               // 0x39-0x3F
+    1<<2,                                   // OpndKind_Memory=0x40
+};
+
+char * EncoderBase::curRelOpnd[3];
+
+char* EncoderBase::encode_aux(char* stream, unsigned aux,
+                              const Operands& opnds, const OpcodeDesc * odesc,
+                              unsigned * pargsCount, Rex * prex)
+{
+    const unsigned byte = aux;
+    OpcodeByteKind kind = (OpcodeByteKind)(byte & OpcodeByteKind_KindMask);
+    // The '>>' here is to force the switch to be table-based) instead of
+    // set of CMP+Jcc.
+    if (*pargsCount >= COUNTOF(opnds)) {
+        assert(false);
+        return stream;
+    }
+    switch(kind>>8) {
+    case OpcodeByteKind_SlashR>>8:
+        // /r - Indicates that the ModR/M byte of the instruction contains
+        // both a register operand and an r/m operand.
+        {
+        assert(opnds.count() > 1);
+    // not true anymore for MOVQ xmm<->r
+        //assert((odesc->opnds[0].kind & OpndKind_Mem) ||
+        //       (odesc->opnds[1].kind & OpndKind_Mem));
+        unsigned memidx = odesc->opnds[0].kind & OpndKind_Mem ? 0 : 1;
+        unsigned regidx = memidx == 0 ? 1 : 0;
+        memidx += *pargsCount;
+        regidx += *pargsCount;
+        ModRM& modrm = *(ModRM*)stream;
+        if (memidx >= COUNTOF(opnds) || regidx >= COUNTOF(opnds)) {
+            assert(false);
+            break;
+        }
+        if (opnds[memidx].is_mem()) {
+            stream = encodeModRM(stream, opnds, memidx, odesc, prex);
+        }
+        else {
+            modrm.mod = 3; // 11
+            modrm.rm = getHWRegIndex(opnds[memidx].reg());
+#ifdef _EM64T_
+            if (opnds[memidx].need_rex() && needs_rex_r(opnds[memidx].reg())) {
+                prex->b = 1;
+            }
+#endif
+            ++stream;
+        }
+        modrm.reg = getHWRegIndex(opnds[regidx].reg());
+#ifdef _EM64T_
+        if (opnds[regidx].need_rex() && needs_rex_r(opnds[regidx].reg())) {
+            prex->r = 1;
+        }
+#endif
+        *pargsCount += 2;
+        }
+        break;
+    case OpcodeByteKind_SlashNum>>8:
+        //  /digit - A digit between 0 and 7 indicates that the
+        //  ModR/M byte of the instruction uses only the r/m
+        //  (register or memory) operand. The reg field contains
+        //  the digit that provides an extension to the instruction's
+        //  opcode.
+        {
+        const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask);
+        assert(lowByte <= 7);
+        ModRM& modrm = *(ModRM*)stream;
+        unsigned idx = *pargsCount;
+        assert(opnds[idx].is_mem() || opnds[idx].is_reg());
+        if (opnds[idx].is_mem()) {
+            stream = encodeModRM(stream, opnds, idx, odesc, prex);
+        }
+        else {
+            modrm.mod = 3; // 11
+            modrm.rm = getHWRegIndex(opnds[idx].reg());
+#ifdef _EM64T_
+            if (opnds[idx].need_rex() && needs_rex_r(opnds[idx].reg())) {
+                prex->b = 1;
+            }
+#endif
+            ++stream;
+        }
+        modrm.reg = (char)lowByte;
+        *pargsCount += 1;
+        }
+        break;
+    case OpcodeByteKind_plus_i>>8:
+        //  +i - A number used in floating-point instructions when one
+        //  of the operands is ST(i) from the FPU register stack. The
+        //  number i (which can range from 0 to 7) is added to the
+        //  hexadecimal byte given at the left of the plus sign to form
+        //  a single opcode byte.
+        {
+            unsigned idx = *pargsCount;
+            const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask);
+            *stream = (char)lowByte + getHWRegIndex(opnds[idx].reg());
+            ++stream;
+            *pargsCount += 1;
+        }
+        break;
+    case OpcodeByteKind_ib>>8:
+    case OpcodeByteKind_iw>>8:
+    case OpcodeByteKind_id>>8:
+#ifdef _EM64T_
+    case OpcodeByteKind_io>>8:
+#endif //_EM64T_
+        //  ib, iw, id - A 1-byte (ib), 2-byte (iw), or 4-byte (id)
+        //  immediate operand to the instruction that follows the
+        //  opcode, ModR/M bytes or scale-indexing bytes. The opcode
+        //  determines if the operand is a signed value. All words
+        //  and double words are given with the low-order byte first.
+        {
+            unsigned idx = *pargsCount;
+            *pargsCount += 1;
+            assert(opnds[idx].is_imm());
+            if (kind == OpcodeByteKind_ib) {
+                *(unsigned char*)stream = (unsigned char)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 1;
+            }
+            else if (kind == OpcodeByteKind_iw) {
+                *(unsigned short*)stream = (unsigned short)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 2;
+            }
+            else if (kind == OpcodeByteKind_id) {
+                *(unsigned*)stream = (unsigned)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 4;
+            }
+#ifdef _EM64T_
+            else {
+                assert(kind == OpcodeByteKind_io);
+                *(long long*)stream = (long long)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 8;
+            }
+#else
+            else {
+                assert(false);
+            }
+#endif
+        }
+        break;
+    case OpcodeByteKind_cb>>8:
+        assert(opnds[*pargsCount].is_imm());
+        *(unsigned char*)stream = (unsigned char)opnds[*pargsCount].imm();
+        curRelOpnd[*pargsCount]= stream;
+        stream += 1;
+        *pargsCount += 1;
+        break;
+    case OpcodeByteKind_cw>>8:
+        assert(opnds[*pargsCount].is_imm());
+        *(unsigned short*)stream = (unsigned short)opnds[*pargsCount].imm();
+        curRelOpnd[*pargsCount]= stream;
+        stream += 2;
+        *pargsCount += 1;
+        break;
+    case OpcodeByteKind_cd>>8:
+        assert(opnds[*pargsCount].is_imm());
+        *(unsigned*)stream = (unsigned)opnds[*pargsCount].imm();
+        curRelOpnd[*pargsCount]= stream;
+        stream += 4;
+        *pargsCount += 1;
+        break;
+    //OpcodeByteKind_cp                             = 0x0B00,
+    //OpcodeByteKind_co                             = 0x0C00,
+    //OpcodeByteKind_ct                             = 0x0D00,
+    case OpcodeByteKind_rb>>8:
+    case OpcodeByteKind_rw>>8:
+    case OpcodeByteKind_rd>>8:
+        //  +rb, +rw, +rd - A register code, from 0 through 7,
+        //  added to the hexadecimal byte given at the left of
+        //  the plus sign to form a single opcode byte.
+        assert(opnds.count() > 0);
+        assert(opnds[*pargsCount].is_reg());
+        {
+        const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask);
+        *(unsigned char*)stream = (unsigned char)lowByte +
+                                   getHWRegIndex(opnds[*pargsCount].reg());
+#ifdef _EM64T_
+        if (opnds[*pargsCount].need_rex() && needs_rex_r(opnds[*pargsCount].reg())) {
+        prex->b = 1;
+        }
+#endif
+        ++stream;
+        *pargsCount += 1;
+        }
+        break;
+    default:
+        assert(false);
+        break;
+    }
+    return stream;
+}
+
+char * EncoderBase::encode(char * stream, Mnemonic mn, const Operands& opnds)
+{
+#ifdef _DEBUG
+    if (opnds.count() > 0) {
+        if (opnds[0].is_mem()) {
+            assert(getRegKind(opnds[0].base()) != OpndKind_SReg);
+        }
+        else if (opnds.count() >1 && opnds[1].is_mem()) {
+            assert(getRegKind(opnds[1].base()) != OpndKind_SReg);
+        }
+    }
+#endif
+
+#ifdef JET_PROTO
+    char* saveStream = stream;
+#endif
+
+    const OpcodeDesc * odesc = lookup(mn, opnds);
+#if !defined(_EM64T_)
+    bool copy_opcode = true;
+    Rex *prex = NULL;
+#else
+    // We need rex if
+    //  either of registers used as operand or address form is new extended register
+    //  it's explicitly specified by opcode
+    // So, if we don't have REX in opcode but need_rex, then set rex here
+    // otherwise, wait until opcode is set, and then update REX
+
+    bool copy_opcode = true;
+    unsigned char _1st = odesc->opcode[0];
+
+    Rex *prex = (Rex*)stream;
+    if (opnds.need_rex() &&
+        ((_1st == 0x66) || (_1st == 0xF2 || _1st == 0xF3) && odesc->opcode[1] == 0x0F)) {
+        // Special processing
+        //
+        copy_opcode = false;
+        //
+        *(unsigned char*)stream = _1st;
+        ++stream;
+        //
+        prex = (Rex*)stream;
+        prex->dummy = 4;
+        prex->w = 0;
+        prex->b = 0;
+        prex->x = 0;
+        prex->r = 0;
+        ++stream;
+        //
+        memcpy(stream, &odesc->opcode[1], odesc->opcode_len-1);
+        stream += odesc->opcode_len-1;
+    }
+    else if (_1st != 0x48 && opnds.need_rex()) {
+        prex = (Rex*)stream;
+        prex->dummy = 4;
+        prex->w = 0;
+        prex->b = 0;
+        prex->x = 0;
+        prex->r = 0;
+        ++stream;
+    }
+#endif  // ifndef EM64T
+
+    if (copy_opcode) {
+        if (odesc->opcode_len==1) {
+        *(unsigned char*)stream = *(unsigned char*)&odesc->opcode;
+        }
+        else if (odesc->opcode_len==2) {
+        *(unsigned short*)stream = *(unsigned short*)&odesc->opcode;
+        }
+        else if (odesc->opcode_len==3) {
+        *(unsigned short*)stream = *(unsigned short*)&odesc->opcode;
+        *(unsigned char*)(stream+2) = odesc->opcode[2];
+        }
+        else if (odesc->opcode_len==4) {
+        *(unsigned*)stream = *(unsigned*)&odesc->opcode;
+        }
+        stream += odesc->opcode_len;
+    }
+
+    unsigned argsCount = odesc->first_opnd;
+
+    if (odesc->aux0) {
+        stream = encode_aux(stream, odesc->aux0, opnds, odesc, &argsCount, prex);
+        if (odesc->aux1) {
+            stream = encode_aux(stream, odesc->aux1, opnds, odesc, &argsCount, prex);
+        }
+    }
+#ifdef JET_PROTO
+    //saveStream
+    Inst inst;
+    unsigned len = DecoderBase::decode(saveStream, &inst);
+    assert(inst.mn == mn);
+    assert(len == (unsigned)(stream-saveStream));
+    if (mn == Mnemonic_CALL || mn == Mnemonic_JMP ||
+        Mnemonic_RET == mn ||
+        (Mnemonic_JO<=mn && mn<=Mnemonic_JG)) {
+        assert(inst.argc == opnds.count());
+
+        InstructionDisassembler idi(saveStream);
+
+        for (unsigned i=0; i<inst.argc; i++) {
+            const EncoderBase::Operand& original = opnds[i];
+            const EncoderBase::Operand& decoded = inst.operands[i];
+            assert(original.kind() == decoded.kind());
+            assert(original.size() == decoded.size());
+            if (original.is_imm()) {
+                assert(original.imm() == decoded.imm());
+                assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Imm);
+                if (mn == Mnemonic_CALL) {
+                    assert(idi.get_type() == InstructionDisassembler::RELATIVE_CALL);
+                }
+                else if (mn == Mnemonic_JMP) {
+                    assert(idi.get_type() == InstructionDisassembler::RELATIVE_JUMP);
+                }
+                else if (mn == Mnemonic_RET) {
+                    assert(idi.get_type() == InstructionDisassembler::RET);
+                }
+                else {
+                    assert(idi.get_type() == InstructionDisassembler::RELATIVE_COND_JUMP);
+                }
+            }
+            else if (original.is_mem()) {
+                assert(original.base() == decoded.base());
+                assert(original.index() == decoded.index());
+                assert(original.scale() == decoded.scale());
+                assert(original.disp() == decoded.disp());
+                assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Mem);
+                if (mn == Mnemonic_CALL) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL);
+                }
+                else if (mn == Mnemonic_JMP) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP);
+                }
+                else {
+                    assert(false);
+                }
+            }
+            else {
+                assert(original.is_reg());
+                assert(original.reg() == decoded.reg());
+                assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Reg);
+                if (mn == Mnemonic_CALL) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL);
+                }
+                else if (mn == Mnemonic_JMP) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP);
+                }
+                else {
+                    assert(false);
+                }
+            }
+        }
+
+        Inst inst2;
+        len = DecoderBase::decode(saveStream, &inst2);
+    }
+
+ //   if(idi.get_length_with_prefix() != (int)len) {
+	//__asm { int 3 };
+ //   }
+#endif
+
+    return stream;
+}
+
+char* EncoderBase::encodeModRM(char* stream, const Operands& opnds,
+                               unsigned idx, const OpcodeDesc * odesc,
+                               Rex * prex)
+{
+    const Operand& op = opnds[idx];
+    assert(op.is_mem());
+    assert(idx < COUNTOF(curRelOpnd));
+    ModRM& modrm = *(ModRM*)stream;
+    ++stream;
+    SIB& sib = *(SIB*)stream;
+
+    // we need SIB if
+    //      we have index & scale (nb: having index w/o base and w/o scale
+    //      treated as error)
+    //      the base is EBP w/o disp, BUT let's use a fake disp8
+    //      the base is ESP (nb: cant have ESP as index)
+
+    RegName base = op.base();
+    // only disp ?..
+    if (base == RegName_Null && op.index() == RegName_Null) {
+        assert(op.scale() == 0); // 'scale!=0' has no meaning without index
+        // ... yes - only have disp
+        // On EM64T, the simply [disp] addressing means 'RIP-based' one -
+        // must have to use SIB to encode 'DS: based'
+#ifdef _EM64T_
+        modrm.mod = 0;  // 00 - ..
+        modrm.rm = 4;   // 100 - have SIB
+
+        sib.base = 5;   // 101 - none
+        sib.index = 4;  // 100 - none
+        sib.scale = 0;  //
+        ++stream; // bypass SIB
+#else
+        // ignore disp_fits8, always use disp32.
+        modrm.mod = 0;
+        modrm.rm = 5;
+#endif
+        *(unsigned*)stream = (unsigned)op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 4;
+        return stream;
+    }
+
+    //climits: error when targeting compal
+#define CHAR_MIN -127
+#define CHAR_MAX 127
+    const bool disp_fits8 = CHAR_MIN <= op.disp() && op.disp() <= CHAR_MAX;
+    /*&& op.base() != RegName_Null - just checked above*/
+    if (op.index() == RegName_Null && getHWRegIndex(op.base()) != getHWRegIndex(REG_STACK)) {
+        assert(op.scale() == 0); // 'scale!=0' has no meaning without index
+        // ... luckily no SIB, only base and may be a disp
+
+        // EBP base is a special case. Need to use [EBP] + disp8 form
+        if (op.disp() == 0  && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) {
+            modrm.mod = 0; // mod=00, no disp et all
+        }
+        else if (disp_fits8) {
+            modrm.mod = 1; // mod=01, use disp8
+            *(unsigned char*)stream = (unsigned char)op.disp();
+            curRelOpnd[idx]= stream;
+            ++stream;
+        }
+        else {
+            modrm.mod = 2; // mod=10, use disp32
+            *(unsigned*)stream = (unsigned)op.disp();
+            curRelOpnd[idx]= stream;
+            stream += 4;
+        }
+        modrm.rm = getHWRegIndex(op.base());
+    if (is_em64t_extra_reg(op.base())) {
+        prex->b = 1;
+    }
+        return stream;
+    }
+
+    // cool, we do have SIB.
+    ++stream; // bypass SIB in stream
+
+    // {E|R}SP cannot be scaled index, however, R12 which has the same index in modrm - can
+    assert(op.index() == RegName_Null || !equals(op.index(), REG_STACK));
+
+    // Only GPRegs can be encoded in the SIB
+    assert(op.base() == RegName_Null ||
+            getRegKind(op.base()) == OpndKind_GPReg);
+    assert(op.index() == RegName_Null ||
+            getRegKind(op.index()) == OpndKind_GPReg);
+
+    modrm.rm = 4;   // r/m = 100, means 'we have SIB here'
+    if (op.base() == RegName_Null) {
+        // no base.
+        // already checked above if
+        // the first if() //assert(op.index() != RegName_Null);
+
+        modrm.mod = 0;  // mod=00 - here it means 'no base, but disp32'
+        sib.base = 5;   // 101 with mod=00  ^^^
+
+        // encode at least fake disp32 to avoid having [base=ebp]
+        *(unsigned*)stream = op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 4;
+
+        unsigned sc = op.scale();
+        if (sc == 1 || sc==0)   { sib.scale = 0; }    // SS=00
+        else if (sc == 2)       { sib.scale = 1; }    // SS=01
+        else if (sc == 4)       { sib.scale = 2; }    // SS=10
+        else if (sc == 8)       { sib.scale = 3; }    // SS=11
+        sib.index = getHWRegIndex(op.index());
+    if (is_em64t_extra_reg(op.index())) {
+        prex->x = 1;
+    }
+
+        return stream;
+    }
+
+    if (op.disp() == 0 && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) {
+        modrm.mod = 0;  // mod=00, no disp
+    }
+    else if (disp_fits8) {
+        modrm.mod = 1;  // mod=01, use disp8
+        *(unsigned char*)stream = (unsigned char)op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 1;
+    }
+    else {
+        modrm.mod = 2;  // mod=10, use disp32
+        *(unsigned*)stream = (unsigned)op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 4;
+    }
+
+    if (op.index() == RegName_Null) {
+        assert(op.scale() == 0); // 'scale!=0' has no meaning without index
+        // the only reason we're here without index, is that we have {E|R}SP
+        // or R12 as a base. Another possible reason - EBP without a disp -
+        // is handled above by adding a fake disp8
+#ifdef _EM64T_
+        assert(op.base() != RegName_Null && (equals(op.base(), REG_STACK) ||
+                                             equals(op.base(), RegName_R12)));
+#else  // _EM64T_
+        assert(op.base() != RegName_Null && equals(op.base(), REG_STACK));
+#endif //_EM64T_
+        sib.scale = 0;  // SS = 00
+        sib.index = 4;  // SS + index=100 means 'no index'
+    }
+    else {
+        unsigned sc = op.scale();
+        if (sc == 1 || sc==0)   { sib.scale = 0; }    // SS=00
+        else if (sc == 2)       { sib.scale = 1; }    // SS=01
+        else if (sc == 4)       { sib.scale = 2; }    // SS=10
+        else if (sc == 8)       { sib.scale = 3; }    // SS=11
+        sib.index = getHWRegIndex(op.index());
+    if (is_em64t_extra_reg(op.index())) {
+        prex->x = 1;
+    }
+        // not an error by itself, but the usage of [index*1] instead
+        // of [base] is discouraged
+        assert(op.base() != RegName_Null || op.scale() != 1);
+    }
+    sib.base = getHWRegIndex(op.base());
+    if (is_em64t_extra_reg(op.base())) {
+    prex->b = 1;
+    }
+    return stream;
+}
+
+char * EncoderBase::nops(char * stream, unsigned howMany)
+{
+    // Recommended multi-byte NOPs from the Intel architecture manual
+    static const unsigned char nops[10][9] = {
+        { 0, },                                                     // 0, this line is dummy and not used in the loop below
+        { 0x90, },                                                  // 1-byte NOP
+        { 0x66, 0x90, },                                            // 2
+        { 0x0F, 0x1F, 0x00, },                                      // 3
+        { 0x0F, 0x1F, 0x40, 0x00, },                                // 4
+        { 0x0F, 0x1F, 0x44, 0x00, 0x00, },                          // 5
+        { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00, },                    // 6
+        { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, },              // 7
+        { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, },        // 8
+        { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 },   // 9-byte NOP
+    };
+
+    // Start from delivering the longest possible NOPs, then proceed with shorter ones
+    for (unsigned nopSize=9; nopSize!=0; nopSize--) {
+        while(howMany>=nopSize) {
+            const unsigned char* nopBytes = nops[nopSize];
+            for (unsigned i=0; i<nopSize; i++) {
+                stream[i] = nopBytes[i];
+            }
+            stream += nopSize;
+            howMany -= nopSize;
+        }
+    }
+    char* end = stream + howMany;
+    return end;
+}
+
+char * EncoderBase::prefix(char* stream, InstPrefix pref)
+{
+    if (pref== InstPrefix_Null) {
+        // nothing to do
+        return stream;
+    }
+    *stream = (char)pref;
+    return stream + 1;
+}
+
+
+/**
+ *
+ */
+bool EncoderBase::extAllowed(OpndExt opndExt, OpndExt instExt) {
+    if (instExt == opndExt || instExt == OpndExt_Any || opndExt == OpndExt_Any) {
+            return true;
+    }
+//asm("int3");
+assert(0);
+    return false;
+}
+
+/**
+ *
+ */
+static bool match(const EncoderBase::OpcodeDesc& odesc,
+                      const EncoderBase::Operands& opnds) {
+
+    assert(odesc.roles.count == opnds.count());
+
+    for(unsigned j = 0; j < odesc.roles.count; j++) {
+        const EncoderBase::OpndDesc& desc = odesc.opnds[j];
+        const EncoderBase::Operand& op = opnds[j];
+        // location must match exactly
+        if ((desc.kind & op.kind()) != op.kind()) {
+//assert(0);
+            return false;
+        }
+        // size must match exactly
+        if (desc.size != op.size()) {
+//assert(0);
+            return false;
+        }
+        // extentions should be consistent
+        if (!EncoderBase::extAllowed(op.ext(), desc.ext)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+
+static bool try_match(const EncoderBase::OpcodeDesc& odesc,
+                      const EncoderBase::Operands& opnds, bool strict) {
+
+    assert(odesc.roles.count == opnds.count());
+
+    for(unsigned j=0; j<odesc.roles.count; j++) {
+        // - the location must match exactly
+        if ((odesc.opnds[j].kind & opnds[j].kind()) != opnds[j].kind()) {
+            return false;
+        }
+        if (strict) {
+            // the size must match exactly
+            if (odesc.opnds[j].size != opnds[j].size()) {
+                return false;
+            }
+        }
+        else {
+            // must match only for def operands, and dont care about use ones
+            // situations like 'mov r8, imm32/mov r32, imm8' so the
+            // destination operand defines the overall size
+            if (EncoderBase::getOpndRoles(odesc.roles, j) & OpndRole_Def) {
+                if (odesc.opnds[j].size != opnds[j].size()) {
+                    return false;
+                }
+            }
+        }
+    }
+    return true;
+}
+
+//
+//Subhash implementaion - may be useful in case of many misses during fast
+//opcode lookup.
+//
+
+#ifdef ENCODER_USE_SUBHASH
+static unsigned subHash[32];
+
+static unsigned find(Mnemonic mn, unsigned hash)
+{
+    unsigned key = hash % COUNTOF(subHash);
+    unsigned pack = subHash[key];
+    unsigned _hash = pack & 0xFFFF;
+    if (_hash != hash) {
+        stat.miss(mn);
+        return EncoderBase::NOHASH;
+    }
+    unsigned _mn = (pack >> 24)&0xFF;
+    if (_mn != _mn) {
+        stat.miss(mn);
+        return EncoderBase::NOHASH;
+    }
+    unsigned idx = (pack >> 16) & 0xFF;
+    stat.hit(mn);
+    return idx;
+}
+
+static void put(Mnemonic mn, unsigned hash, unsigned idx)
+{
+    unsigned pack = hash | (idx<<16) | (mn << 24);
+    unsigned key = hash % COUNTOF(subHash);
+    subHash[key] = pack;
+}
+#endif
+
+const EncoderBase::OpcodeDesc *
+EncoderBase::lookup(Mnemonic mn, const Operands& opnds)
+{
+    const unsigned hash = opnds.hash();
+    unsigned opcodeIndex = opcodesHashMap[mn][hash];
+#ifdef ENCODER_USE_SUBHASH
+    if (opcodeIndex == NOHASH) {
+        opcodeIndex = find(mn, hash);
+    }
+#endif
+
+    if (opcodeIndex == NOHASH) {
+        // fast-path did no work. try to lookup sequentially
+        const OpcodeDesc * odesc = opcodes[mn];
+        int idx = -1;
+        bool found = false;
+        for (idx=0; !odesc[idx].last; idx++) {
+            const OpcodeDesc& opcode = odesc[idx];
+            if (opcode.platf == OpcodeInfo::decoder) {
+                continue;
+            }
+            if (opcode.roles.count != opnds.count()) {
+                continue;
+            }
+            if (try_match(opcode, opnds, true)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            for (idx=0; !odesc[idx].last; idx++) {
+                const OpcodeDesc& opcode = odesc[idx];
+                if (opcode.platf == OpcodeInfo::decoder) {
+                    continue;
+                }
+                if (opcode.roles.count != opnds.count()) {
+                    continue;
+                }
+                if (try_match(opcode, opnds, false)) {
+                    found = true;
+                    break;
+                }
+            }
+        }
+        assert(found);
+        opcodeIndex = idx;
+#ifdef ENCODER_USE_SUBHASH
+        put(mn, hash, opcodeIndex);
+#endif
+    }
+    assert(opcodeIndex != NOHASH);
+    const OpcodeDesc * odesc = &opcodes[mn][opcodeIndex];
+    assert(!odesc->last);
+    assert(odesc->roles.count == opnds.count());
+    assert(odesc->platf != OpcodeInfo::decoder);
+#if !defined(_EM64T_)
+    // tuning was done for IA32 only, so no size restriction on EM64T
+    //assert(sizeof(OpcodeDesc)==128);
+#endif
+    return odesc;
+}
+
+char* EncoderBase::getOpndLocation(int index) {
+     assert(index < 3);
+     return curRelOpnd[index];
+}
+
+
+Mnemonic EncoderBase::str2mnemonic(const char * mn_name)
+{
+    for (unsigned m = 1; m<Mnemonic_Count; m++) {
+        if (!strcmpi(mnemonics[m].name, mn_name)) {
+            return (Mnemonic)m;
+        }
+    }
+    return Mnemonic_Null;
+}
+
+static const char * conditionStrings[ConditionMnemonic_Count] = {
+    "O",
+    "NO",
+    "B",
+    "AE",
+    "Z",
+    "NZ",
+    "BE",
+    "A",
+
+    "S",
+    "NS",
+    "P",
+    "NP",
+    "L",
+    "GE",
+    "LE",
+    "G",
+};
+
+const char * getConditionString(ConditionMnemonic cm) {
+    return conditionStrings[cm];
+}
+
+static const struct {
+        char            sizeString[12];
+        OpndSize        size;
+}
+sizes[] = {
+    { "Sz8", OpndSize_8 },
+    { "Sz16", OpndSize_16 },
+    { "Sz32", OpndSize_32 },
+    { "Sz64", OpndSize_64 },
+#if !defined(TESTING_ENCODER)
+    { "Sz80", OpndSize_80 },
+    { "Sz128", OpndSize_128 },
+#endif
+    { "SzAny", OpndSize_Any },
+};
+
+
+OpndSize getOpndSize(const char * sizeString)
+{
+    assert(sizeString);
+    for (unsigned i = 0; i<COUNTOF(sizes); i++) {
+        if (!strcmpi(sizeString, sizes[i].sizeString)) {
+            return sizes[i].size;
+        }
+    }
+    return OpndSize_Null;
+}
+
+const char * getOpndSizeString(OpndSize size) {
+    for( unsigned i = 0; i<COUNTOF(sizes); i++ ) {
+        if( sizes[i].size==size ) {
+            return sizes[i].sizeString;
+        }
+    }
+    return NULL;
+}
+
+static const struct {
+    char            kindString[16];
+    OpndKind        kind;
+}
+kinds[] = {
+    { "Null", OpndKind_Null },
+    { "GPReg", OpndKind_GPReg },
+    { "SReg", OpndKind_SReg },
+    { "FPReg", OpndKind_FPReg },
+    { "XMMReg", OpndKind_XMMReg },
+#ifdef _HAVE_MMX_
+    { "MMXReg", OpndKind_MMXReg },
+#endif
+    { "StatusReg", OpndKind_StatusReg },
+    { "Reg", OpndKind_Reg },
+    { "Imm", OpndKind_Imm },
+    { "Mem", OpndKind_Mem },
+    { "Any", OpndKind_Any },
+};
+
+const char * getOpndKindString(OpndKind kind)
+{
+    for (unsigned i = 0; i<COUNTOF(kinds); i++) {
+        if (kinds[i].kind==kind) {
+            return kinds[i].kindString;
+        }
+    }
+    return NULL;
+}
+
+OpndKind getOpndKind(const char * kindString)
+{
+    assert(kindString);
+    for (unsigned i = 0; i<COUNTOF(kinds); i++) {
+        if (!strcmpi(kindString, kinds[i].kindString)) {
+            return kinds[i].kind;
+        }
+    }
+    return OpndKind_Null;
+}
+
+/**
+ * A mapping between register string representation and its RegName constant.
+ */
+static const struct {
+        char    regstring[7];
+        RegName regname;
+}
+
+registers[] = {
+#ifdef _EM64T_
+    {"RAX",         RegName_RAX},
+    {"RBX",         RegName_RBX},
+    {"RCX",         RegName_RCX},
+    {"RDX",         RegName_RDX},
+    {"RBP",         RegName_RBP},
+    {"RSI",         RegName_RSI},
+    {"RDI",         RegName_RDI},
+    {"RSP",         RegName_RSP},
+    {"R8",          RegName_R8},
+    {"R9",          RegName_R9},
+    {"R10",         RegName_R10},
+    {"R11",         RegName_R11},
+    {"R12",         RegName_R12},
+    {"R13",         RegName_R13},
+    {"R14",         RegName_R14},
+    {"R15",         RegName_R15},
+#endif
+
+    {"EAX",         RegName_EAX},
+    {"ECX",         RegName_ECX},
+    {"EDX",         RegName_EDX},
+    {"EBX",         RegName_EBX},
+    {"ESP",         RegName_ESP},
+    {"EBP",         RegName_EBP},
+    {"ESI",         RegName_ESI},
+    {"EDI",         RegName_EDI},
+#ifdef _EM64T_
+    {"R8D",         RegName_R8D},
+    {"R9D",         RegName_R9D},
+    {"R10D",        RegName_R10D},
+    {"R11D",        RegName_R11D},
+    {"R12D",        RegName_R12D},
+    {"R13D",        RegName_R13D},
+    {"R14D",        RegName_R14D},
+    {"R15D",        RegName_R15D},
+#endif
+
+    {"AX",          RegName_AX},
+    {"CX",          RegName_CX},
+    {"DX",          RegName_DX},
+    {"BX",          RegName_BX},
+    {"SP",          RegName_SP},
+    {"BP",          RegName_BP},
+    {"SI",          RegName_SI},
+    {"DI",          RegName_DI},
+
+    {"AL",          RegName_AL},
+    {"CL",          RegName_CL},
+    {"DL",          RegName_DL},
+    {"BL",          RegName_BL},
+#if !defined(_EM64T_)
+    {"AH",          RegName_AH},
+    {"CH",          RegName_CH},
+    {"DH",          RegName_DH},
+    {"BH",          RegName_BH},
+#else
+    {"SPL",         RegName_SPL},
+    {"BPL",         RegName_BPL},
+    {"SIL",         RegName_SIL},
+    {"DIL",         RegName_DIL},
+    {"R8L",         RegName_R8L},
+    {"R9L",         RegName_R9L},
+    {"R10L",        RegName_R10L},
+    {"R11L",        RegName_R11L},
+    {"R12L",        RegName_R12L},
+    {"R13L",        RegName_R13L},
+    {"R14L",        RegName_R14L},
+    {"R15L",        RegName_R15L},
+#endif
+    {"ES",          RegName_ES},
+    {"CS",          RegName_CS},
+    {"SS",          RegName_SS},
+    {"DS",          RegName_DS},
+    {"FS",          RegName_FS},
+    {"GS",          RegName_GS},
+
+    {"FP0",         RegName_FP0},
+/*
+    {"FP1",         RegName_FP1},
+    {"FP2",         RegName_FP2},
+    {"FP3",         RegName_FP3},
+    {"FP4",         RegName_FP4},
+    {"FP5",         RegName_FP5},
+    {"FP6",         RegName_FP6},
+    {"FP7",         RegName_FP7},
+*/
+    {"FP0S",        RegName_FP0S},
+    {"FP1S",        RegName_FP1S},
+    {"FP2S",        RegName_FP2S},
+    {"FP3S",        RegName_FP3S},
+    {"FP4S",        RegName_FP4S},
+    {"FP5S",        RegName_FP5S},
+    {"FP6S",        RegName_FP6S},
+    {"FP7S",        RegName_FP7S},
+
+    {"FP0D",        RegName_FP0D},
+    {"FP1D",        RegName_FP1D},
+    {"FP2D",        RegName_FP2D},
+    {"FP3D",        RegName_FP3D},
+    {"FP4D",        RegName_FP4D},
+    {"FP5D",        RegName_FP5D},
+    {"FP6D",        RegName_FP6D},
+    {"FP7D",        RegName_FP7D},
+
+    {"XMM0",        RegName_XMM0},
+    {"XMM1",        RegName_XMM1},
+    {"XMM2",        RegName_XMM2},
+    {"XMM3",        RegName_XMM3},
+    {"XMM4",        RegName_XMM4},
+    {"XMM5",        RegName_XMM5},
+    {"XMM6",        RegName_XMM6},
+    {"XMM7",        RegName_XMM7},
+#ifdef _EM64T_
+    {"XMM8",       RegName_XMM8},
+    {"XMM9",       RegName_XMM9},
+    {"XMM10",      RegName_XMM10},
+    {"XMM11",      RegName_XMM11},
+    {"XMM12",      RegName_XMM12},
+    {"XMM13",      RegName_XMM13},
+    {"XMM14",      RegName_XMM14},
+    {"XMM15",      RegName_XMM15},
+#endif
+
+
+    {"XMM0S",       RegName_XMM0S},
+    {"XMM1S",       RegName_XMM1S},
+    {"XMM2S",       RegName_XMM2S},
+    {"XMM3S",       RegName_XMM3S},
+    {"XMM4S",       RegName_XMM4S},
+    {"XMM5S",       RegName_XMM5S},
+    {"XMM6S",       RegName_XMM6S},
+    {"XMM7S",       RegName_XMM7S},
+#ifdef _EM64T_
+    {"XMM8S",       RegName_XMM8S},
+    {"XMM9S",       RegName_XMM9S},
+    {"XMM10S",      RegName_XMM10S},
+    {"XMM11S",      RegName_XMM11S},
+    {"XMM12S",      RegName_XMM12S},
+    {"XMM13S",      RegName_XMM13S},
+    {"XMM14S",      RegName_XMM14S},
+    {"XMM15S",      RegName_XMM15S},
+#endif
+
+    {"XMM0D",       RegName_XMM0D},
+    {"XMM1D",       RegName_XMM1D},
+    {"XMM2D",       RegName_XMM2D},
+    {"XMM3D",       RegName_XMM3D},
+    {"XMM4D",       RegName_XMM4D},
+    {"XMM5D",       RegName_XMM5D},
+    {"XMM6D",       RegName_XMM6D},
+    {"XMM7D",       RegName_XMM7D},
+#ifdef _EM64T_
+    {"XMM8D",       RegName_XMM8D},
+    {"XMM9D",       RegName_XMM9D},
+    {"XMM10D",      RegName_XMM10D},
+    {"XMM11D",      RegName_XMM11D},
+    {"XMM12D",      RegName_XMM12D},
+    {"XMM13D",      RegName_XMM13D},
+    {"XMM14D",      RegName_XMM14D},
+    {"XMM15D",      RegName_XMM15D},
+#endif
+
+    {"EFLGS",       RegName_EFLAGS},
+};
+
+
+const char * getRegNameString(RegName reg)
+{
+    for (unsigned i = 0; i<COUNTOF(registers); i++) {
+        if (registers[i].regname == reg) {
+            return registers[i].regstring;
+        }
+    }
+    return NULL;
+}
+
+RegName getRegName(const char * regname)
+{
+    if (NULL == regname) {
+        return RegName_Null;
+    }
+
+    for (unsigned i = 0; i<COUNTOF(registers); i++) {
+        if (!strcmpi(regname,registers[i].regstring)) {
+            return registers[i].regname;
+        }
+    }
+    return RegName_Null;
+}
+
+ENCODER_NAMESPACE_END
diff --git a/vm/compiler/codegen/x86/libenc/enc_base.h b/vm/compiler/codegen/x86/libenc/enc_base.h
new file mode 100644
index 0000000..e90ad2b
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_base.h
@@ -0,0 +1,740 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+/**
+ * @file
+ * @brief Main encoding routines and structures.
+ */
+
+#ifndef __ENC_BASE_H_INCLUDED__
+#define __ENC_BASE_H_INCLUDED__
+
+#include "enc_defs.h"
+
+
+#include <stdlib.h>
+#include <assert.h>
+#include <memory.h>
+
+ENCODER_NAMESPACE_START
+struct MnemonicInfo;
+struct OpcodeInfo;
+struct Rex;
+
+/**
+ * @brief Basic facilities for generation of processor's instructions.
+ *
+ * The class EncoderBase represents the basic facilities for the encoding of
+ * processor's instructions on IA32 and EM64T platforms.
+ *
+ * The class provides general interface to generate the instructions as well
+ * as to retrieve some static data about instructions (number of arguments,
+ * their roles, etc).
+ *
+ * Currently, the EncoderBase class is used for both LIL and Jitrino code
+ * generators. Each of these code generators has its own wrapper to adapt
+ * this general interface for specific needs - see encoder.h for LIL wrappers
+ * and Ia32Encoder.h for Jitrino's adapter.
+ *
+ * Interface is provided through static methods, no instances of EncoderBase
+ * to be created.
+ *
+ * @todo RIP-based addressing on EM64T - it's not yet supported currently.
+ */
+class EncoderBase {
+public:
+    class Operands;
+    struct MnemonicDesc;
+    /**
+     * @brief Generates processor's instruction.
+     *
+     * @param stream - a buffer to generate into
+     * @param mn - \link Mnemonic mnemonic \endlink of the instruction
+     * @param opnds - operands for the instruction
+     * @returns (stream + length of the just generated instruction)
+     */
+    static char * encode(char * stream, Mnemonic mn, const Operands& opnds);
+    static char * getOpndLocation(int index);
+
+    /**
+     * @brief Generates the smallest possible number of NOP-s.
+     *
+     * Effectively generates the smallest possible number of instructions,
+     * which are NOP-s for CPU. Normally used to make a code alignment.
+     *
+     * The method inserts exactly number of bytes specified. It's a caller's
+     * responsibility to make sure the buffer is big enough.
+     *
+     * @param stream - buffer where to generate code into, can not be NULL
+     * @param howMany - how many bytes to fill with NOP-s
+     * @return \c (stream+howMany)
+     */
+    static char * nops(char * stream, unsigned howMany);
+
+    /**
+     * @brief Inserts a prefix into the code buffer.
+     *
+     * The method writes no more than one byte into the buffer. This is a
+     * caller's responsibility to make sure the buffer is big enough.
+     *
+     * @param stream - buffer where to insert the prefix
+     * @param pref - prefix to be inserted. If it's InstPrefix_Null, then
+     *        no action performed and return value is \c stream.
+     * @return \c (stream+1) if pref is not InstPrefix_Null, or \c stream
+     *         otherwise
+     */
+     static char * prefix(char* stream, InstPrefix pref);
+
+    /**
+     * @brief Determines if operand with opndExt suites the position with instExt.
+     */
+    static bool extAllowed(OpndExt opndExt, OpndExt instExt);
+
+    /**
+     * @brief Returns #MnemonicDesc by the given Mnemonic.
+     */
+    static const MnemonicDesc * getMnemonicDesc(Mnemonic mn)
+    {
+        assert(mn < Mnemonic_Count);
+        return mnemonics + mn;
+    }
+
+    /**
+     * @brief Returns a Mnemonic for the given name.
+     *
+     * The lookup is case insensitive, if no mnemonic found for the given
+     * string, then Mnemonic_Null returned.
+     */
+    static Mnemonic str2mnemonic(const char * mn_name);
+
+    /**
+     * @brief Returns a string representation of the given Mnemonic.
+     *
+     * If invalid mnemonic passed, then the behavior is unpredictable.
+     */
+    static const char * getMnemonicString(Mnemonic mn)
+    {
+        return getMnemonicDesc(mn)->name;
+    }
+
+    static const char * toStr(Mnemonic mn)
+    {
+        return getMnemonicDesc(mn)->name;
+    }
+
+
+    /**
+     * @brief Description of operand.
+     *
+     * Description of an operand in opcode - its kind, size or RegName if
+     * operand must be a particular register.
+     */
+    struct OpndDesc {
+        /**
+         * @brief Location of the operand.
+         *
+         * May be a mask, i.e. OpndKind_Imm|OpndKind_Mem.
+         */
+        OpndKind        kind;
+        /**
+         * @brief Size of the operand.
+         */
+        OpndSize        size;
+        /**
+         * @brief Extention of the operand.
+         */
+        OpndExt         ext;
+        /**
+         * @brief Appropriate RegName if operand must reside on a particular
+         *        register (i.e. CWD/CDQ instructions), RegName_Null
+         *        otherwise.
+         */
+        RegName         reg;
+    };
+
+    /**
+     * @brief Description of operands' roles in instruction.
+     */
+    struct OpndRolesDesc {
+        /**
+         * @brief Total number of operands in the operation.
+         */
+        unsigned                count;
+        /**
+         * @brief Number of defs in the operation.
+         */
+        unsigned                defCount;
+        /**
+         * @brief Number of uses in the operation.
+         */
+        unsigned                useCount;
+        /**
+         * @brief Operand roles, bit-packed.
+         *
+         * A bit-packed info about operands' roles. Each operand's role is
+         * described by two bits, counted from right-to-left - the less
+         * significant bits (0,1) represent operand#0.
+         *
+         * The mask is build by ORing #OpndRole_Def and #OpndRole_Use
+         * appropriately and shifting left, i.e. operand#0's role would be
+         * - '(OpndRole_Def|OpndRole_Use)'
+         * - opnd#1's role would be 'OpndRole_Use<<2'
+         * - and operand#2's role would be, say, 'OpndRole_Def<<4'.
+         */
+        unsigned                roles;
+    };
+
+    /**
+     * @brief Extracts appropriate OpndRole for a given operand.
+     *
+     * The order of operands is left-to-right, i.e. for MOV, it
+     * would be 'MOV op0, op1'
+     */
+    static OpndRole getOpndRoles(OpndRolesDesc ord, unsigned idx)
+    {
+        assert(idx < ord.count);
+        return (OpndRole)(ord.roles>>((ord.count-1-idx)*2) & 0x3);
+    }
+
+    /**
+     * @brief Info about single opcode - its opcode bytes, operands,
+     *        operands' roles.
+     */
+   union OpcodeDesc {
+       char dummy[128]; // To make total size a power of 2
+
+       struct {
+           /**
+           * @brief Raw opcode bytes.
+           *
+           * 'Raw' opcode bytes which do not require any analysis and are
+           * independent from arguments/sizes/etc (may include opcode size
+           * prefix).
+           */
+           char        opcode[5];
+           unsigned    opcode_len;
+           unsigned    aux0;
+           unsigned    aux1;
+           /**
+           * @brief Info about opcode's operands.
+           *
+           * The [3] mostly comes from IDIV/IMUL which both may have up to 3
+           * operands.
+           */
+           OpndDesc        opnds[3];
+           unsigned        first_opnd;
+           /**
+           * @brief Info about operands - total number, number of uses/defs,
+           *        operands' roles.
+           */
+           OpndRolesDesc   roles;
+           /**
+           * @brief If not zero, then this is final OpcodeDesc structure in
+           *        the list of opcodes for a given mnemonic.
+           */
+           char            last;
+           char            platf;
+       };
+   };
+public:
+    /**
+     * @brief General info about mnemonic.
+     */
+    struct MnemonicDesc {
+        /**
+        * @brief The mnemonic itself.
+        */
+        Mnemonic        mn;
+        /**
+        * Various characteristics of mnemonic.
+        * @see MF_
+         */
+        unsigned    flags;
+        /**
+         * @brief Operation's operand's count and roles.
+         *
+         * For the operations whose opcodes may use different number of
+         * operands (i.e. IMUL/SHL) either most common value used, or empty
+         * value left.
+         */
+        OpndRolesDesc   roles;
+        /**
+         * @brief Print name of the mnemonic.
+         */
+        const char *    name;
+    };
+
+
+    /**
+     * @brief Magic number, shows a maximum value a hash code can take.
+     *
+     * For meaning and arithmetics see enc_tabl.cpp.
+     *
+     * The value was increased from '5155' to '8192' to make it aligned
+     * for faster access in EncoderBase::lookup().
+     */
+    static const unsigned int               HASH_MAX = 8192; //5155;
+    /**
+     * @brief Empty value, used in hash-to-opcode map to show an empty slot.
+     */
+    static const unsigned char              NOHASH = 0xFF;
+    /**
+     * @brief The name says it all.
+     */
+    static const unsigned char              HASH_BITS_PER_OPERAND = 5;
+
+    /**
+     * @brief Contains info about a single instructions's operand - its
+     *        location, size and a value for immediate or RegName for
+     *        register operands.
+     */
+    class Operand {
+    public:
+        /**
+         * @brief Initializes the instance with empty size and kind.
+         */
+        Operand() : m_kind(OpndKind_Null), m_size(OpndSize_Null), m_ext(OpndExt_None), m_need_rex(false) {}
+        /**
+         * @brief Creates register operand from given RegName.
+         */
+        Operand(RegName reg, OpndExt ext = OpndExt_None) : m_kind(getRegKind(reg)),
+                               m_size(getRegSize(reg)),
+                               m_ext(ext), m_reg(reg)
+        {
+            hash_it();
+        }
+        /**
+         * @brief Creates register operand from given RegName and with the
+         *        specified size and kind.
+         *
+         * Used to speedup Operand creation as there is no need to extract
+         * size and kind from the RegName.
+         * The provided size and kind must match the RegName's ones though.
+         */
+        Operand(OpndSize sz, OpndKind kind, RegName reg, OpndExt ext = OpndExt_None) :
+            m_kind(kind), m_size(sz), m_ext(ext), m_reg(reg)
+        {
+            assert(m_size == getRegSize(reg));
+            assert(m_kind == getRegKind(reg));
+            hash_it();
+        }
+        /**
+         * @brief Creates immediate operand with the given size and value.
+         */
+        Operand(OpndSize size, long long ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(size), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+        /**
+         * @brief Creates immediate operand of OpndSize_32.
+         */
+        Operand(int ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(OpndSize_32), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+        /**
+         * @brief Creates immediate operand of OpndSize_16.
+         */
+        Operand(short ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(OpndSize_16), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+
+        /**
+         * @brief Creates immediate operand of OpndSize_8.
+         */
+        Operand(char ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(OpndSize_8), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+
+        /**
+         * @brief Creates memory operand.
+         */
+        Operand(OpndSize size, RegName base, RegName index, unsigned scale,
+                int disp, OpndExt ext = OpndExt_None) : m_kind(OpndKind_Mem), m_size(size), m_ext(ext)
+        {
+            m_base = base;
+            m_index = index;
+            m_scale = scale;
+            m_disp = disp;
+            hash_it();
+        }
+
+        /**
+         * @brief Creates memory operand with only base and displacement.
+         */
+        Operand(OpndSize size, RegName base, int disp, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Mem), m_size(size), m_ext(ext)
+        {
+            m_base = base;
+            m_index = RegName_Null;
+            m_scale = 0;
+            m_disp = disp;
+            hash_it();
+        }
+        //
+        // general info
+        //
+        /**
+         * @brief Returns kind of the operand.
+         */
+        OpndKind kind(void) const { return m_kind; }
+        /**
+         * @brief Returns size of the operand.
+         */
+        OpndSize size(void) const { return m_size; }
+        /**
+         * @brief Returns extention of the operand.
+         */
+        OpndExt ext(void) const { return m_ext; }
+        /**
+         * @brief Returns hash of the operand.
+         */
+        unsigned hash(void) const { return m_hash; }
+        //
+#ifdef _EM64T_
+        bool need_rex(void) const { return m_need_rex; }
+#else
+        bool need_rex(void) const { return false; }
+#endif
+        /**
+         * @brief Tests whether operand is memory operand.
+         */
+        bool is_mem(void) const { return is_placed_in(OpndKind_Mem); }
+        /**
+         * @brief Tests whether operand is immediate operand.
+         */
+        bool is_imm(void) const { return is_placed_in(OpndKind_Imm); }
+        /**
+         * @brief Tests whether operand is register operand.
+         */
+        bool is_reg(void) const { return is_placed_in(OpndKind_Reg); }
+        /**
+         * @brief Tests whether operand is general-purpose register operand.
+         */
+        bool is_gpreg(void) const { return is_placed_in(OpndKind_GPReg); }
+        /**
+         * @brief Tests whether operand is float-point pseudo-register operand.
+         */
+        bool is_fpreg(void) const { return is_placed_in(OpndKind_FPReg); }
+        /**
+         * @brief Tests whether operand is XMM register operand.
+         */
+        bool is_xmmreg(void) const { return is_placed_in(OpndKind_XMMReg); }
+#ifdef _HAVE_MMX_
+        /**
+         * @brief Tests whether operand is MMX register operand.
+         */
+        bool is_mmxreg(void) const { return is_placed_in(OpndKind_MMXReg); }
+#endif
+        /**
+         * @brief Tests whether operand is signed immediate operand.
+         */
+        //bool is_signed(void) const { assert(is_imm()); return m_is_signed; }
+
+        /**
+         * @brief Returns base of memory operand (RegName_Null if not memory).
+         */
+        RegName base(void) const { return is_mem() ? m_base : RegName_Null; }
+        /**
+         * @brief Returns index of memory operand (RegName_Null if not memory).
+         */
+        RegName index(void) const { return is_mem() ? m_index : RegName_Null; }
+        /**
+         * @brief Returns scale of memory operand (0 if not memory).
+         */
+        unsigned scale(void) const { return is_mem() ? m_scale : 0; }
+        /**
+         * @brief Returns displacement of memory operand (0 if not memory).
+         */
+        int disp(void) const { return is_mem() ? m_disp : 0; }
+        /**
+         * @brief Returns RegName of register operand (RegName_Null if not
+         *        register).
+         */
+        RegName reg(void) const { return is_reg() ? m_reg : RegName_Null; }
+        /**
+         * @brief Returns value of immediate operand (0 if not immediate).
+         */
+        long long imm(void) const { return is_imm() ? m_imm64 : 0; }
+    private:
+        bool is_placed_in(OpndKind kd) const
+        {
+                return kd == OpndKind_Reg ?
+                        m_kind == OpndKind_GPReg ||
+#ifdef _HAVE_MMX_
+                        m_kind == OpndKind_MMXReg ||
+#endif
+                        m_kind == OpndKind_FPReg ||
+                        m_kind == OpndKind_XMMReg
+                        : kd == m_kind;
+        }
+        void hash_it(void)
+        {
+            m_hash = get_size_hash(m_size) | get_kind_hash(m_kind);
+#ifdef _EM64T_
+            m_need_rex = false;
+            if (is_reg() && is_em64t_extra_reg(m_reg)) {
+                m_need_rex = true;
+            }
+            else if (is_mem() && (is_em64t_extra_reg(m_base) ||
+                                  is_em64t_extra_reg(m_index))) {
+                m_need_rex = true;
+            }
+#endif
+        }
+        // general info
+        OpndKind    m_kind;
+        OpndSize    m_size;
+        OpndExt     m_ext;
+        // complex address form support
+        RegName     m_base;
+        RegName     m_index;
+        unsigned    m_scale;
+        union {
+            int         m_disp;
+            RegName     m_reg;
+            long long   m_imm64;
+        };
+        unsigned    m_hash;
+        bool        m_need_rex;
+        friend class EncoderBase::Operands;
+    };
+    /**
+     * @brief Simple container for up to 3 Operand-s.
+     */
+    class Operands {
+    public:
+        Operands(void)
+        {
+            clear();
+        }
+        Operands(const Operand& op0)
+        {
+            clear();
+            add(op0);
+        }
+
+        Operands(const Operand& op0, const Operand& op1)
+        {
+            clear();
+            add(op0); add(op1);
+        }
+
+        Operands(const Operand& op0, const Operand& op1, const Operand& op2)
+        {
+            clear();
+            add(op0); add(op1); add(op2);
+        }
+
+        unsigned count(void) const { return m_count; }
+        unsigned hash(void) const { return m_hash; }
+        const Operand& operator[](unsigned idx) const
+        {
+            assert(idx<m_count);
+            return m_operands[idx];
+        }
+
+        void add(const Operand& op)
+        {
+            assert(m_count < COUNTOF(m_operands));
+            m_hash = (m_hash<<HASH_BITS_PER_OPERAND) | op.hash();
+            m_operands[m_count++] = op;
+            m_need_rex = m_need_rex || op.m_need_rex;
+        }
+#ifdef _EM64T_
+        bool need_rex(void) const { return m_need_rex; }
+#else
+        bool need_rex(void) const { return false; }
+#endif
+        void clear(void)
+        {
+            m_count = 0; m_hash = 0; m_need_rex = false;
+        }
+    private:
+        unsigned    m_count;
+        Operand     m_operands[COUNTOF( ((OpcodeDesc*)NULL)->opnds )];
+        unsigned    m_hash;
+        bool        m_need_rex;
+    };
+public:
+#ifdef _DEBUG
+    /**
+     * Verifies some presumptions about encoding data table.
+     * Called automaticaly during statics initialization.
+     */
+    static int verify(void);
+#endif
+
+private:
+    /**
+     * @brief Returns found OpcodeDesc by the given Mnemonic and operands.
+     */
+    static const OpcodeDesc * lookup(Mnemonic mn, const Operands& opnds);
+    /**
+     * @brief Encodes mod/rm byte.
+     */
+    static char* encodeModRM(char* stream, const Operands& opnds,
+                             unsigned idx, const OpcodeDesc * odesc, Rex * prex);
+    /**
+     * @brief Encodes special things of opcode description - '/r', 'ib', etc.
+     */
+    static char* encode_aux(char* stream, unsigned aux,
+                            const Operands& opnds, const OpcodeDesc * odesc,
+                            unsigned * pargsCount, Rex* prex);
+#ifdef _EM64T_
+    /**
+     * @brief Returns true if the 'reg' argument represents one of the new
+     *        EM64T registers - R8(D)-R15(D).
+     *
+     * The 64 bits versions of 'old-fashion' registers, i.e. RAX are not
+     * considered as 'extra'.
+     */
+    static bool is_em64t_extra_reg(const RegName reg)
+    {
+        if (needs_rex_r(reg)) {
+            return true;
+        }
+        if (RegName_SPL <= reg && reg <= RegName_R15L) {
+            return true;
+        }
+        return false;
+    }
+    static bool needs_rex_r(const RegName reg)
+    {
+        if (RegName_R8 <= reg && reg <= RegName_R15) {
+            return true;
+        }
+        if (RegName_R8D <= reg && reg <= RegName_R15D) {
+            return true;
+        }
+        if (RegName_R8S <= reg && reg <= RegName_R15S) {
+            return true;
+        }
+        if (RegName_R8L <= reg && reg <= RegName_R15L) {
+            return true;
+        }
+        if (RegName_XMM8 <= reg && reg <= RegName_XMM15) {
+            return true;
+        }
+        if (RegName_XMM8D <= reg && reg <= RegName_XMM15D) {
+            return true;
+        }
+        if (RegName_XMM8S <= reg && reg <= RegName_XMM15S) {
+            return true;
+        }
+        return false;
+    }
+    /**
+     * @brief Returns an 'processor's index' of the register - the index
+     *        used to encode the register in ModRM/SIB bytes.
+     *
+     * For the new EM64T registers the 'HW index' differs from the index
+     * encoded in RegName. For old-fashion registers it's effectively the
+     * same as ::getRegIndex(RegName).
+     */
+    static unsigned char getHWRegIndex(const RegName reg)
+    {
+        if (getRegKind(reg) != OpndKind_GPReg) {
+            return getRegIndex(reg);
+        }
+        if (RegName_SPL <= reg && reg<=RegName_DIL) {
+            return getRegIndex(reg);
+        }
+        if (RegName_R8L<= reg && reg<=RegName_R15L) {
+            return getRegIndex(reg) - getRegIndex(RegName_R8L);
+        }
+        return is_em64t_extra_reg(reg) ?
+                getRegIndex(reg)-getRegIndex(RegName_R8D) : getRegIndex(reg);
+    }
+#else
+    static unsigned char getHWRegIndex(const RegName reg)
+    {
+        return getRegIndex(reg);
+    }
+    static bool is_em64t_extra_reg(const RegName reg)
+    {
+        return false;
+    }
+#endif
+public:
+    static unsigned char get_size_hash(OpndSize size) {
+        return (size <= OpndSize_64) ? size_hash[size] : 0xFF;
+    }
+    static unsigned char get_kind_hash(OpndKind kind) {
+        return (kind <= OpndKind_Mem) ? kind_hash[kind] : 0xFF;
+    }
+
+    /**
+     * @brief A table used for the fast computation of hash value.
+     *
+     * A change must be strictly balanced with hash-related functions and data
+     * in enc_base.h/.cpp.
+     */
+    static const unsigned char size_hash[OpndSize_64+1];
+    /**
+     * @brief A table used for the fast computation of hash value.
+     *
+     * A change must be strictly balanced with hash-related functions and data
+     * in enc_base.h/.cpp.
+     */
+    static const unsigned char kind_hash[OpndKind_Mem+1];
+    /**
+     * @brief Maximum number of opcodes used for a single mnemonic.
+     *
+     * No arithmetics behind the number, simply estimated.
+     */
+    static const unsigned int   MAX_OPCODES = 32; //20;
+    /**
+     * @brief Mapping between operands hash code and operands.
+     */
+    static unsigned char    opcodesHashMap[Mnemonic_Count][HASH_MAX];
+    /**
+     * @brief Array of mnemonics.
+     */
+    static MnemonicDesc         mnemonics[Mnemonic_Count];
+    /**
+     * @brief Array of available opcodes.
+     */
+    static OpcodeDesc opcodes[Mnemonic_Count][MAX_OPCODES];
+
+    static int buildTable(void);
+    static void buildMnemonicDesc(const MnemonicInfo * minfo);
+    /**
+     * @brief Computes hash value for the given operands.
+     */
+    static unsigned short getHash(const OpcodeInfo* odesc);
+    /**
+     * @brief Dummy variable, for automatic invocation of buildTable() at
+     *        startup.
+     */
+    static int dummy;
+
+    static char * curRelOpnd[3];
+};
+
+ENCODER_NAMESPACE_END
+
+#endif // ifndef __ENC_BASE_H_INCLUDED__
diff --git a/vm/compiler/codegen/x86/libenc/enc_defs.h b/vm/compiler/codegen/x86/libenc/enc_defs.h
new file mode 100644
index 0000000..ac0bb3b
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_defs.h
@@ -0,0 +1,786 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#ifndef _ENCODER_DEFS_H_
+#define _ENCODER_DEFS_H_
+
+
+// Used to isolate experimental or being tuned encoder into a separate
+// namespace so it can coexist with a stable one in the same bundle.
+#ifdef ENCODER_ISOLATE
+    #define ENCODER_NAMESPACE_START namespace enc_ia32 {
+    #define ENCODER_NAMESPACE_END };
+#else
+    #define ENCODER_NAMESPACE_START
+    #define ENCODER_NAMESPACE_END
+#endif
+
+#include <assert.h>
+#include "enc_defs_ext.h"
+
+#ifndef COUNTOF
+    /**
+     * Number of items in an array.
+     */
+    #define COUNTOF(a)      (sizeof(a)/sizeof(a[0]))
+#endif
+
+#ifdef _EM64T_
+    /**
+     * A stack pointer of default platform's size.
+     */
+    #define REG_STACK       RegName_RSP
+    /**
+     * A max GP register (with a highest index number)
+     */
+    #define REG_MAX         RegName_R15
+    /**
+     * Total number of GP registers including stack pointer.
+     */
+    #define MAX_REGS        15
+#else
+    #define REG_STACK       RegName_ESP
+    #define REG_MAX         RegName_EDI
+    #define MAX_REGS        8
+#endif
+
+ENCODER_NAMESPACE_START
+
+/**
+ * A number of bytes 'eaten' by an ordinary PUSH/POP.
+ */
+#define STACK_SLOT_SIZE (sizeof(void*))
+
+
+/**
+ * A recommended by Intel Arch Manual aligment for instructions that
+ * are targets for jmps.
+ */
+#define JMP_TARGET_ALIGMENT     (16)
+/**
+ * A maximum possible size of native instruction.
+ */
+#define MAX_NATIVE_INST_SIZE (15)
+/**
+ * The enum OpndKind describes an operand's location - memory, immediate or a register.
+ * It can be used as a bit mask.
+ */
+typedef enum OpndKind {
+    /**
+     * A change must be balanced with at least the following places:
+     *              Ia32::Constraint-s use the OpndKind as a mask
+     *              encoder.cpp & encoder_master_info.cpp uses OpndKind as an index for hashing
+     *              - perhaps there are much more places
+     *
+     * NOTE: an MMXReg kind is incompatible with the current constraints framework,
+     *              as it's not encoded as a mask.
+     */
+    OpndKind_Null=0,
+    OpndKind_GPReg          = 0x01, OpndKind_MinRegKind = OpndKind_GPReg,
+    OpndKind_SReg           = 0x02,
+#ifdef _HAVE_MMX_
+    OpndKind_MMXReg         = 0x03,
+#endif
+    OpndKind_FPReg          = 0x04,
+    OpndKind_XMMReg         = 0x08,
+    OpndKind_OtherReg       = 0x10,
+    OpndKind_StatusReg      = OpndKind_OtherReg,
+    OpndKind_MaxRegKind     = OpndKind_StatusReg,   // a max existing kind of register
+    OpndKind_MaxReg,                                // -'- + 1 to be used in array defs
+    //
+    OpndKind_Immediate      = 0x20, OpndKind_Imm=OpndKind_Immediate,
+    OpndKind_Memory         = 0x40, OpndKind_Mem=OpndKind_Memory,
+    //
+    OpndKind_Reg            = 0x1F,
+    OpndKind_Any            = 0x7F,
+    // syntetic constants. Normally not used anywhere, but are used for
+    // human-readable showing under the debugger
+    OpndKind_GPReg_Mem      = OpndKind_GPReg|OpndKind_Mem,
+#ifdef _HAVE_MMX_
+    OpndKind_MMXReg_Mem     = OpndKind_MMXReg|OpndKind_Mem,
+#endif
+    OpndKind_XMMReg_Mem     = OpndKind_XMMReg|OpndKind_Mem,
+} OpndKind;
+
+/**
+ * Defines type of extention allowed for particular operand.
+ * For example imul r32,r_m32,imm8 sign extend imm8 before performing multiplication.
+ * To satisfy instruction constraints immediate operand should be either OpndExt_Signed
+ * or OpndExt_Any.
+ */
+typedef enum OpndExt {
+    OpndExt_None    = 0x0,
+    OpndExt_Signed  = 0x1,
+    OpndExt_Zero    = 0x2,
+    OpndExt_Any     = 0x3,
+}OpndExt;
+
+/**
+ * enum OpndRole defines the role of an operand in an instruction
+ * Can be used as mask to combine def and use. The complete def+use
+ * info can be combined in 2 bits which is used, say in Encoder::OpndRole.
+ */
+//TODO: this duplicates an Role used in the Ia32::Inst. That duplicate enum should be removed.
+typedef enum OpndRole {
+    OpndRole_Null=0,
+    OpndRole_Use=0x1,
+    OpndRole_Def=0x2,
+    OpndRole_UseDef=OpndRole_Use|OpndRole_Def,
+    OpndRole_All=0xffff,
+} OpndRole;
+
+
+#define REGNAME(k,s,i) ( ((k & OpndKind_Any)<<24) | ((s & OpndSize_Any)<<16) | (i&0xFF) )
+
+// Gregory -
+// It is critical that all register indexes (3rd number) inside of the
+// following table go in ascending order. That is R8 goes after
+// RDI. It is necessary for decoder when extending registers from RAX-RDI
+// to R8-R15 by simply adding 8 to the index on EM64T architecture
+typedef enum RegName {
+
+    RegName_Null = 0,
+
+#ifdef _EM64T_
+    /*
+    An index part of the RegName-s for RAX-RDI, EAX-ESI, AX-SI and AL-BH is
+    the same as the index used during instructions encoding. The same rule
+    applies for XMM regsters for IA32.
+    For new EM64T registers (both GP and XMM) the index need to be corrected to
+    obtain the index used in processor's instructions.
+    */
+    RegName_RAX = REGNAME(OpndKind_GPReg,OpndSize_64,0),
+    RegName_RCX = REGNAME(OpndKind_GPReg,OpndSize_64,1),
+    RegName_RDX = REGNAME(OpndKind_GPReg,OpndSize_64,2),
+    RegName_RBX = REGNAME(OpndKind_GPReg,OpndSize_64,3),
+    RegName_RSP = REGNAME(OpndKind_GPReg,OpndSize_64,4),
+    RegName_RBP = REGNAME(OpndKind_GPReg,OpndSize_64,5),
+    RegName_RSI = REGNAME(OpndKind_GPReg,OpndSize_64,6),
+    RegName_RDI = REGNAME(OpndKind_GPReg,OpndSize_64,7),
+
+    RegName_R8  = REGNAME(OpndKind_GPReg,OpndSize_64,8),
+    RegName_R9  = REGNAME(OpndKind_GPReg,OpndSize_64,9),
+    RegName_R10 = REGNAME(OpndKind_GPReg,OpndSize_64,10),
+    RegName_R11 = REGNAME(OpndKind_GPReg,OpndSize_64,11),
+    RegName_R12 = REGNAME(OpndKind_GPReg,OpndSize_64,12),
+    RegName_R13 = REGNAME(OpndKind_GPReg,OpndSize_64,13),
+    RegName_R14 = REGNAME(OpndKind_GPReg,OpndSize_64,14),
+    RegName_R15 = REGNAME(OpndKind_GPReg,OpndSize_64,15),
+#endif //~_EM64T_
+
+    RegName_EAX=REGNAME(OpndKind_GPReg,OpndSize_32,0),
+    RegName_ECX=REGNAME(OpndKind_GPReg,OpndSize_32,1),
+    RegName_EDX=REGNAME(OpndKind_GPReg,OpndSize_32,2),
+    RegName_EBX=REGNAME(OpndKind_GPReg,OpndSize_32,3),
+    RegName_ESP=REGNAME(OpndKind_GPReg,OpndSize_32,4),
+    RegName_EBP=REGNAME(OpndKind_GPReg,OpndSize_32,5),
+    RegName_ESI=REGNAME(OpndKind_GPReg,OpndSize_32,6),
+    RegName_EDI=REGNAME(OpndKind_GPReg,OpndSize_32,7),
+
+#ifdef _EM64T_
+    RegName_R8D  = REGNAME(OpndKind_GPReg,OpndSize_32,8),
+    RegName_R9D  = REGNAME(OpndKind_GPReg,OpndSize_32,9),
+    RegName_R10D = REGNAME(OpndKind_GPReg,OpndSize_32,10),
+    RegName_R11D = REGNAME(OpndKind_GPReg,OpndSize_32,11),
+    RegName_R12D = REGNAME(OpndKind_GPReg,OpndSize_32,12),
+    RegName_R13D = REGNAME(OpndKind_GPReg,OpndSize_32,13),
+    RegName_R14D = REGNAME(OpndKind_GPReg,OpndSize_32,14),
+    RegName_R15D = REGNAME(OpndKind_GPReg,OpndSize_32,15),
+#endif //~_EM64T_
+
+    RegName_AX=REGNAME(OpndKind_GPReg,OpndSize_16,0),
+    RegName_CX=REGNAME(OpndKind_GPReg,OpndSize_16,1),
+    RegName_DX=REGNAME(OpndKind_GPReg,OpndSize_16,2),
+    RegName_BX=REGNAME(OpndKind_GPReg,OpndSize_16,3),
+    RegName_SP=REGNAME(OpndKind_GPReg,OpndSize_16,4),
+    RegName_BP=REGNAME(OpndKind_GPReg,OpndSize_16,5),
+    RegName_SI=REGNAME(OpndKind_GPReg,OpndSize_16,6),
+    RegName_DI=REGNAME(OpndKind_GPReg,OpndSize_16,7),
+
+#ifdef _EM64T_
+    RegName_R8S  = REGNAME(OpndKind_GPReg,OpndSize_16,8),
+    RegName_R9S  = REGNAME(OpndKind_GPReg,OpndSize_16,9),
+    RegName_R10S = REGNAME(OpndKind_GPReg,OpndSize_16,10),
+    RegName_R11S = REGNAME(OpndKind_GPReg,OpndSize_16,11),
+    RegName_R12S = REGNAME(OpndKind_GPReg,OpndSize_16,12),
+    RegName_R13S = REGNAME(OpndKind_GPReg,OpndSize_16,13),
+    RegName_R14S = REGNAME(OpndKind_GPReg,OpndSize_16,14),
+    RegName_R15S = REGNAME(OpndKind_GPReg,OpndSize_16,15),
+#endif //~_EM64T_
+
+    RegName_AL=REGNAME(OpndKind_GPReg,OpndSize_8,0),
+    RegName_CL=REGNAME(OpndKind_GPReg,OpndSize_8,1),
+    RegName_DL=REGNAME(OpndKind_GPReg,OpndSize_8,2),
+    RegName_BL=REGNAME(OpndKind_GPReg,OpndSize_8,3),
+    // FIXME: Used in enc_tabl.cpp
+    // AH is not accessible on EM64T, instead encoded register is SPL, so decoded
+    // register will return incorrect enum
+    RegName_AH=REGNAME(OpndKind_GPReg,OpndSize_8,4),
+#if !defined(_EM64T_)
+    RegName_CH=REGNAME(OpndKind_GPReg,OpndSize_8,5),
+    RegName_DH=REGNAME(OpndKind_GPReg,OpndSize_8,6),
+    RegName_BH=REGNAME(OpndKind_GPReg,OpndSize_8,7),
+#else
+    RegName_SPL=REGNAME(OpndKind_GPReg,OpndSize_8,4),
+    RegName_BPL=REGNAME(OpndKind_GPReg,OpndSize_8,5),
+    RegName_SIL=REGNAME(OpndKind_GPReg,OpndSize_8,6),
+    RegName_DIL=REGNAME(OpndKind_GPReg,OpndSize_8,7),
+    RegName_R8L=REGNAME(OpndKind_GPReg,OpndSize_8,8),
+    RegName_R9L=REGNAME(OpndKind_GPReg,OpndSize_8,9),
+    RegName_R10L=REGNAME(OpndKind_GPReg,OpndSize_8,10),
+    RegName_R11L=REGNAME(OpndKind_GPReg,OpndSize_8,11),
+    RegName_R12L=REGNAME(OpndKind_GPReg,OpndSize_8,12),
+    RegName_R13L=REGNAME(OpndKind_GPReg,OpndSize_8,13),
+    RegName_R14L=REGNAME(OpndKind_GPReg,OpndSize_8,14),
+    RegName_R15L=REGNAME(OpndKind_GPReg,OpndSize_8,15),
+#endif
+
+    RegName_ES=REGNAME(OpndKind_SReg,OpndSize_16,0),
+    RegName_CS=REGNAME(OpndKind_SReg,OpndSize_16,1),
+    RegName_SS=REGNAME(OpndKind_SReg,OpndSize_16,2),
+    RegName_DS=REGNAME(OpndKind_SReg,OpndSize_16,3),
+    RegName_FS=REGNAME(OpndKind_SReg,OpndSize_16,4),
+    RegName_GS=REGNAME(OpndKind_SReg,OpndSize_16,5),
+
+    RegName_EFLAGS=REGNAME(OpndKind_StatusReg,OpndSize_32,0),
+
+#if !defined(TESTING_ENCODER)
+    RegName_FP0=REGNAME(OpndKind_FPReg,OpndSize_80,0),
+    RegName_FP1=REGNAME(OpndKind_FPReg,OpndSize_80,1),
+    RegName_FP2=REGNAME(OpndKind_FPReg,OpndSize_80,2),
+    RegName_FP3=REGNAME(OpndKind_FPReg,OpndSize_80,3),
+    RegName_FP4=REGNAME(OpndKind_FPReg,OpndSize_80,4),
+    RegName_FP5=REGNAME(OpndKind_FPReg,OpndSize_80,5),
+    RegName_FP6=REGNAME(OpndKind_FPReg,OpndSize_80,6),
+    RegName_FP7=REGNAME(OpndKind_FPReg,OpndSize_80,7),
+#endif
+    RegName_FP0S=REGNAME(OpndKind_FPReg,OpndSize_32,0),
+    RegName_FP1S=REGNAME(OpndKind_FPReg,OpndSize_32,1),
+    RegName_FP2S=REGNAME(OpndKind_FPReg,OpndSize_32,2),
+    RegName_FP3S=REGNAME(OpndKind_FPReg,OpndSize_32,3),
+    RegName_FP4S=REGNAME(OpndKind_FPReg,OpndSize_32,4),
+    RegName_FP5S=REGNAME(OpndKind_FPReg,OpndSize_32,5),
+    RegName_FP6S=REGNAME(OpndKind_FPReg,OpndSize_32,6),
+    RegName_FP7S=REGNAME(OpndKind_FPReg,OpndSize_32,7),
+
+    RegName_FP0D=REGNAME(OpndKind_FPReg,OpndSize_64,0),
+    RegName_FP1D=REGNAME(OpndKind_FPReg,OpndSize_64,1),
+    RegName_FP2D=REGNAME(OpndKind_FPReg,OpndSize_64,2),
+    RegName_FP3D=REGNAME(OpndKind_FPReg,OpndSize_64,3),
+    RegName_FP4D=REGNAME(OpndKind_FPReg,OpndSize_64,4),
+    RegName_FP5D=REGNAME(OpndKind_FPReg,OpndSize_64,5),
+    RegName_FP6D=REGNAME(OpndKind_FPReg,OpndSize_64,6),
+    RegName_FP7D=REGNAME(OpndKind_FPReg,OpndSize_64,7),
+
+#if !defined(TESTING_ENCODER)
+    RegName_XMM0=REGNAME(OpndKind_XMMReg,OpndSize_128,0),
+    RegName_XMM1=REGNAME(OpndKind_XMMReg,OpndSize_128,1),
+    RegName_XMM2=REGNAME(OpndKind_XMMReg,OpndSize_128,2),
+    RegName_XMM3=REGNAME(OpndKind_XMMReg,OpndSize_128,3),
+    RegName_XMM4=REGNAME(OpndKind_XMMReg,OpndSize_128,4),
+    RegName_XMM5=REGNAME(OpndKind_XMMReg,OpndSize_128,5),
+    RegName_XMM6=REGNAME(OpndKind_XMMReg,OpndSize_128,6),
+    RegName_XMM7=REGNAME(OpndKind_XMMReg,OpndSize_128,7),
+
+#ifdef _EM64T_
+    RegName_XMM8  = REGNAME(OpndKind_XMMReg,OpndSize_128,0),
+    RegName_XMM9  = REGNAME(OpndKind_XMMReg,OpndSize_128,1),
+    RegName_XMM10 = REGNAME(OpndKind_XMMReg,OpndSize_128,2),
+    RegName_XMM11 = REGNAME(OpndKind_XMMReg,OpndSize_128,3),
+    RegName_XMM12 = REGNAME(OpndKind_XMMReg,OpndSize_128,4),
+    RegName_XMM13 = REGNAME(OpndKind_XMMReg,OpndSize_128,5),
+    RegName_XMM14 = REGNAME(OpndKind_XMMReg,OpndSize_128,6),
+    RegName_XMM15 = REGNAME(OpndKind_XMMReg,OpndSize_128,7),
+#endif //~_EM64T_
+
+#endif  // ~TESTING_ENCODER
+
+    RegName_XMM0S=REGNAME(OpndKind_XMMReg,OpndSize_32,0),
+    RegName_XMM1S=REGNAME(OpndKind_XMMReg,OpndSize_32,1),
+    RegName_XMM2S=REGNAME(OpndKind_XMMReg,OpndSize_32,2),
+    RegName_XMM3S=REGNAME(OpndKind_XMMReg,OpndSize_32,3),
+    RegName_XMM4S=REGNAME(OpndKind_XMMReg,OpndSize_32,4),
+    RegName_XMM5S=REGNAME(OpndKind_XMMReg,OpndSize_32,5),
+    RegName_XMM6S=REGNAME(OpndKind_XMMReg,OpndSize_32,6),
+    RegName_XMM7S=REGNAME(OpndKind_XMMReg,OpndSize_32,7),
+#ifdef _EM64T_
+    RegName_XMM8S=REGNAME(OpndKind_XMMReg,OpndSize_32,8),
+    RegName_XMM9S=REGNAME(OpndKind_XMMReg,OpndSize_32,9),
+    RegName_XMM10S=REGNAME(OpndKind_XMMReg,OpndSize_32,10),
+    RegName_XMM11S=REGNAME(OpndKind_XMMReg,OpndSize_32,11),
+    RegName_XMM12S=REGNAME(OpndKind_XMMReg,OpndSize_32,12),
+    RegName_XMM13S=REGNAME(OpndKind_XMMReg,OpndSize_32,13),
+    RegName_XMM14S=REGNAME(OpndKind_XMMReg,OpndSize_32,14),
+    RegName_XMM15S=REGNAME(OpndKind_XMMReg,OpndSize_32,15),
+#endif // ifdef _EM64T_
+    RegName_XMM0D=REGNAME(OpndKind_XMMReg,OpndSize_64,0),
+    RegName_XMM1D=REGNAME(OpndKind_XMMReg,OpndSize_64,1),
+    RegName_XMM2D=REGNAME(OpndKind_XMMReg,OpndSize_64,2),
+    RegName_XMM3D=REGNAME(OpndKind_XMMReg,OpndSize_64,3),
+    RegName_XMM4D=REGNAME(OpndKind_XMMReg,OpndSize_64,4),
+    RegName_XMM5D=REGNAME(OpndKind_XMMReg,OpndSize_64,5),
+    RegName_XMM6D=REGNAME(OpndKind_XMMReg,OpndSize_64,6),
+    RegName_XMM7D=REGNAME(OpndKind_XMMReg,OpndSize_64,7),
+#ifdef _EM64T_
+    RegName_XMM8D=REGNAME(OpndKind_XMMReg,OpndSize_64,8),
+    RegName_XMM9D=REGNAME(OpndKind_XMMReg,OpndSize_64,9),
+    RegName_XMM10D=REGNAME(OpndKind_XMMReg,OpndSize_64,10),
+    RegName_XMM11D=REGNAME(OpndKind_XMMReg,OpndSize_64,11),
+    RegName_XMM12D=REGNAME(OpndKind_XMMReg,OpndSize_64,12),
+    RegName_XMM13D=REGNAME(OpndKind_XMMReg,OpndSize_64,13),
+    RegName_XMM14D=REGNAME(OpndKind_XMMReg,OpndSize_64,14),
+    RegName_XMM15D=REGNAME(OpndKind_XMMReg,OpndSize_64,15),
+#endif // ifdef _EM64T_
+#ifdef _HAVE_MMX_
+    RegName_MMX0=REGNAME(OpndKind_MMXReg,OpndSize_64,0),
+    RegName_MMX1=REGNAME(OpndKind_MMXReg,OpndSize_64,1),
+    RegName_MMX2=REGNAME(OpndKind_MMXReg,OpndSize_64,2),
+    RegName_MMX3=REGNAME(OpndKind_MMXReg,OpndSize_64,3),
+    RegName_MMX4=REGNAME(OpndKind_MMXReg,OpndSize_64,4),
+    RegName_MMX5=REGNAME(OpndKind_MMXReg,OpndSize_64,5),
+    RegName_MMX6=REGNAME(OpndKind_MMXReg,OpndSize_64,6),
+    RegName_MMX7=REGNAME(OpndKind_MMXReg,OpndSize_64,7),
+#endif  // _HAVE_MMX_
+} RegName;
+
+#if 0   // Android x86: use mnemonics defined in enc_defs_ext.h
+/**
+ * Conditional mnemonics.
+ * The values match the 'real' (==processor's) values of the appropriate
+ * condition values used in the opcodes.
+ */
+enum ConditionMnemonic {
+
+    ConditionMnemonic_O=0,
+    ConditionMnemonic_NO=1,
+    ConditionMnemonic_B=2, ConditionMnemonic_NAE=ConditionMnemonic_B, ConditionMnemonic_C=ConditionMnemonic_B,
+    ConditionMnemonic_NB=3, ConditionMnemonic_AE=ConditionMnemonic_NB, ConditionMnemonic_NC=ConditionMnemonic_NB,
+    ConditionMnemonic_Z=4, ConditionMnemonic_E=ConditionMnemonic_Z,
+    ConditionMnemonic_NZ=5, ConditionMnemonic_NE=ConditionMnemonic_NZ,
+    ConditionMnemonic_BE=6, ConditionMnemonic_NA=ConditionMnemonic_BE,
+    ConditionMnemonic_NBE=7, ConditionMnemonic_A=ConditionMnemonic_NBE,
+
+    ConditionMnemonic_S=8,
+    ConditionMnemonic_NS=9,
+    ConditionMnemonic_P=10, ConditionMnemonic_PE=ConditionMnemonic_P,
+    ConditionMnemonic_NP=11, ConditionMnemonic_PO=ConditionMnemonic_NP,
+    ConditionMnemonic_L=12, ConditionMnemonic_NGE=ConditionMnemonic_L,
+    ConditionMnemonic_NL=13, ConditionMnemonic_GE=ConditionMnemonic_NL,
+    ConditionMnemonic_LE=14, ConditionMnemonic_NG=ConditionMnemonic_LE,
+    ConditionMnemonic_NLE=15, ConditionMnemonic_G=ConditionMnemonic_NLE,
+    ConditionMnemonic_Count=16
+};
+
+
+#define CCM(prefix,cond) Mnemonic_##prefix##cond=Mnemonic_##prefix##cc+ConditionMnemonic_##cond
+
+//=========================================================================================================
+enum Mnemonic {
+
+Mnemonic_NULL=0, Mnemonic_Null=Mnemonic_NULL,
+Mnemonic_ADC,                           // Add with Carry
+Mnemonic_ADD,                           // Add
+Mnemonic_ADDSD,                         // Add Scalar Double-Precision Floating-Point Values
+Mnemonic_ADDSS,                         // Add Scalar Single-Precision Floating-Point Values
+Mnemonic_AND,                           // Logical AND
+
+Mnemonic_BSF,                           // Bit scan forward
+Mnemonic_BSR,                           // Bit scan reverse
+
+Mnemonic_CALL,                          // Call Procedure
+Mnemonic_CMC,                           // Complement Carry Flag
+Mnemonic_CWD, Mnemonic_CDQ=Mnemonic_CWD,// Convert Word to Doubleword/Convert Doubleword to Qua T dword
+Mnemonic_CMOVcc,                        // Conditional Move
+    CCM(CMOV,O),
+    CCM(CMOV,NO),
+    CCM(CMOV,B), CCM(CMOV,NAE), CCM(CMOV,C),
+    CCM(CMOV,NB), CCM(CMOV,AE), CCM(CMOV,NC),
+    CCM(CMOV,Z), CCM(CMOV,E),
+    CCM(CMOV,NZ), CCM(CMOV,NE),
+    CCM(CMOV,BE), CCM(CMOV,NA),
+    CCM(CMOV,NBE), CCM(CMOV,A),
+
+    CCM(CMOV,S),
+    CCM(CMOV,NS),
+    CCM(CMOV,P), CCM(CMOV,PE),
+    CCM(CMOV,NP), CCM(CMOV,PO),
+    CCM(CMOV,L), CCM(CMOV,NGE),
+    CCM(CMOV,NL), CCM(CMOV,GE),
+    CCM(CMOV,LE), CCM(CMOV,NG),
+    CCM(CMOV,NLE), CCM(CMOV,G),
+
+Mnemonic_CMP,                           // Compare Two Operands
+Mnemonic_CMPXCHG,                       // Compare and exchange
+Mnemonic_CMPXCHG8B,                     // Compare and Exchange 8 Bytes
+Mnemonic_CMPSB,                         // Compare Two Bytes at DS:ESI and ES:EDI
+Mnemonic_CMPSW,                         // Compare Two Words at DS:ESI and ES:EDI
+Mnemonic_CMPSD,                         // Compare Two Doublewords at DS:ESI and ES:EDI
+//
+// double -> float
+Mnemonic_CVTSD2SS,                      // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value
+// double -> I_32
+Mnemonic_CVTSD2SI,                      // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer
+// double [truncated] -> I_32
+Mnemonic_CVTTSD2SI,                     // Convert with Truncation Scalar Double-Precision Floating-Point Value to Signed Doubleword Integer
+//
+// float -> double
+Mnemonic_CVTSS2SD,                      // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value
+// float -> I_32
+Mnemonic_CVTSS2SI,                      // Convert Scalar Single-Precision Floating-Point Value to Doubleword Integer
+// float [truncated] -> I_32
+Mnemonic_CVTTSS2SI,                     // Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer
+//
+// I_32 -> double
+Mnemonic_CVTSI2SD,                      // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value
+// I_32 -> float
+Mnemonic_CVTSI2SS,                      // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value
+
+Mnemonic_COMISD,                        // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_COMISS,                        // Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_DEC,                           // Decrement by 1
+//Mnemonic_DIV,                         // Unsigned Divide
+Mnemonic_DIVSD,                         // Divide Scalar Double-Precision Floating-Point Values
+Mnemonic_DIVSS,                         // Divide Scalar Single-Precision Floating-Point Values
+
+#ifdef _HAVE_MMX_
+Mnemonic_EMMS,                          // Empty MMX Technology State
+#endif
+
+Mnemonic_ENTER,                         // ENTER-Make Stack Frame for Procedure Parameters
+Mnemonic_FLDCW,                         // Load FPU control word
+Mnemonic_FADDP,
+Mnemonic_FLDZ,
+Mnemonic_FADD,
+Mnemonic_FSUBP,
+Mnemonic_FSUB,
+Mnemonic_FISUB,
+Mnemonic_FMUL,
+Mnemonic_FMULP,
+Mnemonic_FDIVP,
+Mnemonic_FDIV,
+Mnemonic_FUCOMPP,
+Mnemonic_FRNDINT,
+Mnemonic_FNSTCW,                        // Store FPU control word
+Mnemonic_FSTSW,                         // Store FPU status word
+Mnemonic_FNSTSW,                         // Store FPU status word
+//Mnemonic_FDECSTP,                     // Decrement Stack-Top Pointer
+Mnemonic_FILD,                          // Load Integer
+Mnemonic_FLD,                           // Load Floating Point Value
+Mnemonic_FLDLG2,
+Mnemonic_FLDLN2,
+Mnemonic_FLD1,
+
+Mnemonic_FCLEX,                         // Clear Exceptions
+Mnemonic_FCHS,                          // Change sign of ST0
+Mnemonic_FNCLEX,                        // Clear Exceptions
+
+//Mnemonic_FINCSTP,                     // Increment Stack-Top Pointer
+Mnemonic_FIST,                          // Store Integer
+Mnemonic_FISTP,                         // Store Integer, pop FPU stack
+Mnemonic_FISTTP,                        // Store Integer with Truncation
+Mnemonic_FPREM,                         // Partial Remainder
+Mnemonic_FPREM1,                        // Partial Remainder
+Mnemonic_FST,                           // Store Floating Point Value
+Mnemonic_FSTP,                          // Store Floating Point Value and pop the FP stack
+Mnemonic_FSQRT,                         //Computes the square root of the source value in the stack and pop the FP stack
+Mnemonic_FABS,                          //Computes the absolute value of the source value in the stack and pop the FP stack
+Mnemonic_FSIN,                          //Computes the sine of the source value in the stack and pop the FP stack
+Mnemonic_FCOS,                          //Computes the cosine of the source value in the stack and pop the FP stack
+Mnemonic_FPTAN,                         //Computes the tangent of the source value in the stack and pop the FP stack
+Mnemonic_FYL2X,
+Mnemonic_FYL2XP1,
+Mnemonic_F2XM1,
+Mnemonic_FPATAN,
+Mnemonic_FXCH,
+Mnemonic_FSCALE,
+
+Mnemonic_XCHG,
+Mnemonic_DIV,                           // Unsigned Divide
+Mnemonic_IDIV,                          // Signed Divide
+Mnemonic_MUL,                           // Unsigned Multiply
+Mnemonic_IMUL,                          // Signed Multiply
+Mnemonic_INC,                           // Increment by 1
+Mnemonic_INT3,                          // Call break point
+Mnemonic_Jcc,                           // Jump if Condition Is Met
+    CCM(J,O),
+    CCM(J,NO),
+    CCM(J,B), CCM(J,NAE), CCM(J,C),
+    CCM(J,NB), CCM(J,AE), CCM(J,NC),
+    CCM(J,Z), CCM(J,E),
+    CCM(J,NZ), CCM(J,NE),
+    CCM(J,BE), CCM(J,NA),
+    CCM(J,NBE), CCM(J,A),
+    CCM(J,S),
+    CCM(J,NS),
+    CCM(J,P), CCM(J,PE),
+    CCM(J,NP), CCM(J,PO),
+    CCM(J,L), CCM(J,NGE),
+    CCM(J,NL), CCM(J,GE),
+    CCM(J,LE), CCM(J,NG),
+    CCM(J,NLE), CCM(J,G),
+Mnemonic_JMP,                           // Jump
+Mnemonic_LEA,                           // Load Effective Address
+Mnemonic_LEAVE,                         // High Level Procedure Exit
+Mnemonic_LOOP,                          // Loop according to ECX counter
+Mnemonic_LOOPE,                          // Loop according to ECX counter
+Mnemonic_LOOPNE, Mnemonic_LOOPNZ = Mnemonic_LOOPNE, // Loop according to ECX
+Mnemonic_LAHF,                          // Load Flags into AH
+Mnemonic_MOV,                           // Move
+Mnemonic_MOVD,                          // Move Double word
+Mnemonic_MOVQ,                          // Move Quadword
+/*Mnemonic_MOVS,                        // Move Data from String to String*/
+// MOVS is a special case: see encoding table for more details,
+Mnemonic_MOVS8, Mnemonic_MOVS16, Mnemonic_MOVS32, Mnemonic_MOVS64,
+//
+Mnemonic_MOVAPD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSS,                         // Move Scalar Single-Precision Floating-Point Values
+Mnemonic_MOVSX,                         // Move with Sign-Extension
+Mnemonic_MOVZX,                         // Move with Zero-Extend
+//Mnemonic_MUL,                         // Unsigned Multiply
+Mnemonic_MULSD,                         // Multiply Scalar Double-Precision Floating-Point Values
+Mnemonic_MULSS,                         // Multiply Scalar Single-Precision Floating-Point Values
+Mnemonic_NEG,                           // Two's Complement Negation
+Mnemonic_NOP,                           // No Operation
+Mnemonic_NOT,                           // One's Complement Negation
+Mnemonic_OR,                            // Logical Inclusive OR
+Mnemonic_PREFETCH,                      // prefetch
+
+#ifdef _HAVE_MMX_
+    Mnemonic_PADDQ,                     // Add Packed Quadword Integers
+    Mnemonic_PAND,                      // Logical AND
+    Mnemonic_POR,                       // Bitwise Logical OR
+    Mnemonic_PSUBQ,                     // Subtract Packed Quadword Integers
+#endif
+
+Mnemonic_PXOR,                          // Logical Exclusive OR
+Mnemonic_POP,                           // Pop a Value from the Stack
+Mnemonic_POPFD,                         // Pop a Value of EFLAGS register from the Stack
+Mnemonic_PUSH,                          // Push Word or Doubleword Onto the Stack
+Mnemonic_PUSHFD,                        // Push EFLAGS Doubleword Onto the Stack
+Mnemonic_RET,                           // Return from Procedure
+
+Mnemonic_SETcc,                         // Set Byte on Condition
+    CCM(SET,O),
+    CCM(SET,NO),
+    CCM(SET,B), CCM(SET,NAE), CCM(SET,C),
+    CCM(SET,NB), CCM(SET,AE), CCM(SET,NC),
+    CCM(SET,Z), CCM(SET,E),
+    CCM(SET,NZ), CCM(SET,NE),
+    CCM(SET,BE), CCM(SET,NA),
+    CCM(SET,NBE), CCM(SET,A),
+    CCM(SET,S),
+    CCM(SET,NS),
+    CCM(SET,P), CCM(SET,PE),
+    CCM(SET,NP), CCM(SET,PO),
+    CCM(SET,L), CCM(SET,NGE),
+    CCM(SET,NL), CCM(SET,GE),
+    CCM(SET,LE), CCM(SET,NG),
+    CCM(SET,NLE), CCM(SET,G),
+
+Mnemonic_SAL, Mnemonic_SHL=Mnemonic_SAL,// Shift left
+Mnemonic_SAR,                           // Shift right
+Mnemonic_ROR,                           // Rotate right
+Mnemonic_RCR,                           // Rotate right through CARRY flag
+Mnemonic_ROL,                           // Rotate left
+Mnemonic_RCL,                           // Rotate left through CARRY flag
+Mnemonic_SHR,                           // Unsigned shift right
+Mnemonic_SHRD,                          // Double Precision Shift Right
+Mnemonic_SHLD,                          // Double Precision Shift Left
+
+Mnemonic_SBB,                           // Integer Subtraction with Borrow
+Mnemonic_SUB,                           // Subtract
+Mnemonic_SUBSD,                         // Subtract Scalar Double-Precision Floating-Point Values
+Mnemonic_SUBSS,                         // Subtract Scalar Single-Precision Floating-Point Values
+
+Mnemonic_TEST,                          // Logical Compare
+
+Mnemonic_UCOMISD,                       // Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_UCOMISS,                       // Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS
+
+Mnemonic_XOR,                           // Logical Exclusive OR
+//
+// packed things,
+//
+Mnemonic_XORPD,                         // Bitwise Logical XOR for Double-Precision Floating-Point Values
+Mnemonic_XORPS,                         // Bitwise Logical XOR for Single-Precision Floating-Point Values
+
+Mnemonic_CVTDQ2PD,                      // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values
+Mnemonic_CVTTPD2DQ,                     // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
+
+Mnemonic_CVTDQ2PS,                      // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values
+Mnemonic_CVTTPS2DQ,                     // Convert with Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers
+//
+// String operations
+//
+Mnemonic_STD,                           // Set direction flag
+Mnemonic_CLD,                           // Clear direction flag
+Mnemonic_SCAS,                          // Scan string
+Mnemonic_STOS,                          // Store string
+
+//
+Mnemonic_WAIT,                          // Check pending pending unmasked floating-point exception
+//
+Mnemonic_Count
+};
+
+#undef CCM
+#endif
+
+/**
+ * @brief Instruction prefixes, according to arch manual.
+ */
+typedef enum InstPrefix {
+    InstPrefix_Null = 0,
+    // Group 1
+    InstPrefix_LOCK = 0xF0,
+    InstPrefix_REPNE = 0xF2,
+    InstPrefix_REPNZ = InstPrefix_REPNE,
+    InstPrefix_REP = 0xF3, InstPrefix_REPZ = InstPrefix_REP,
+    // Group 2
+    InstPrefix_CS = 0x2E,
+    InstPrefix_SS = 0x36,
+    InstPrefix_DS = 0x3E,
+    InstPrefix_ES = 0x26,
+    InstPrefix_FS = 0x64,
+    InstPrefix_GS = 0x65,
+    //
+    InstPrefix_HintTaken = 0x3E,
+    InstPrefix_HintNotTaken = 0x2E,
+    // Group 3
+    InstPrefix_OpndSize = 0x66,
+    // Group 4
+    InstPrefix_AddrSize = 0x67
+} InstPrefix;
+
+inline unsigned getSizeBytes(OpndSize sz)
+{
+    if (sz==OpndSize_64) { return 8; }
+    if (sz==OpndSize_32) { return 4; }
+    if (sz==OpndSize_16) { return 2; }
+    if (sz==OpndSize_8)  { return 1; }
+    assert(false);
+    return 0;
+}
+
+inline bool isRegKind(OpndKind kind)
+{
+    return OpndKind_GPReg<= kind && kind<=OpndKind_MaxRegKind;
+}
+
+/**
+ * @brief Returns #RegName for a given name.
+ *
+ * Name is case-insensitive.
+ * @param regname - string name of a register
+ * @return #RegName for the given name, or #RegName_Null if name is invalid
+ */
+RegName         getRegName(const char * regname);
+/**
+ * Constructs RegName from the given OpndKind, size and index.
+ */
+inline RegName  getRegName(OpndKind k, OpndSize s, int idx)
+{
+    return (RegName)REGNAME(k,s,idx);
+}
+/**
+ * Extracts a bit mask with a bit set at the position of the register's index.
+ */
+inline unsigned getRegMask(RegName reg)
+{
+    return 1<<(reg&0xff);
+}
+/**
+ * @brief Extracts #RegKind from the #RegName.
+ */
+inline OpndKind getRegKind(RegName reg)
+{
+    return (OpndKind)(reg>>24);
+}
+/**
+ * @brief Extracts #OpndSize from #RegName.
+ */
+inline OpndSize getRegSize(RegName reg)
+{
+    return (OpndSize)((reg>>16)&0xFF);
+}
+/**
+ * Extracts an index from the given RegName.
+ */
+inline unsigned char getRegIndex(RegName reg)
+{
+    return (unsigned char)(reg&0xFF);
+}
+/**
+ * Returns a string name of the given RegName. The name returned is in upper-case.
+ * Returns NULL if invalid RegName specified.
+ */
+const char *    getRegNameString(RegName reg);
+/**
+ * Returns string name of a given OpndSize.
+ * Returns NULL if invalid OpndSize passed.
+ */
+const char *    getOpndSizeString(OpndSize size);
+/**
+ * Returns OpndSize passed by its string representation (case insensitive).
+ * Returns OpndSize_Null if invalid string specified.
+ * The 'sizeString' can not be NULL.
+ */
+OpndSize        getOpndSize(const char * sizeString);
+/**
+ * Returns string name of a given OpndKind.
+ * Returns NULL if the passed kind is invalid.
+ */
+const char *    getOpndKindString(OpndKind kind);
+/**
+ * Returns OpndKind found by its string representation (case insensitive).
+ * Returns OpndKind_Null if the name is invalid.
+ * The 'kindString' can not be NULL.
+ */
+OpndKind        getOpndKind(const char * kindString);
+/**
+ *
+ */
+const char *    getConditionString(ConditionMnemonic cm);
+
+/**
+ * Constructs an RegName with the same index and kind, but with a different size from
+ * the given RegName (i.e. getRegAlias(EAX, OpndSize_16) => AX; getRegAlias(BL, OpndSize_32) => EBX).
+ * The constructed RegName is not checked in any way and thus may be invalid.
+ * Note, that the aliasing does not work for at least AH,BH,CH,DH, ESI, EDI, ESP and EBP regs.
+ */
+inline RegName getAliasReg(RegName reg, OpndSize sz)
+{
+    return (RegName)REGNAME(getRegKind(reg), sz, getRegIndex(reg));
+}
+
+/**
+ * brief Tests two RegName-s of the same kind for equality.
+ *
+ * @note Does work for 8 bit general purpose registers (AH, AL, BH, BL, etc).
+ */
+inline bool equals(RegName r0, RegName r1)
+{
+    return getRegKind(r0) == getRegKind(r1) &&
+           getRegIndex(r0) == getRegIndex(r1);
+}
+
+ENCODER_NAMESPACE_END
+
+#endif  // ifndef _ENCODER_DEFS_H_
diff --git a/vm/compiler/codegen/x86/libenc/enc_defs_ext.h b/vm/compiler/codegen/x86/libenc/enc_defs_ext.h
new file mode 100644
index 0000000..3592513
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_defs_ext.h
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ENCODER_DEFS_EXT_H_
+#define _ENCODER_DEFS_EXT_H_
+
+
+// Used to isolate experimental or being tuned encoder into a separate
+// namespace so it can coexist with a stable one in the same bundle.
+#ifdef ENCODER_ISOLATE
+    #define ENCODER_NAMESPACE_START namespace enc_ia32 {
+    #define ENCODER_NAMESPACE_END };
+#else
+    #define ENCODER_NAMESPACE_START
+    #define ENCODER_NAMESPACE_END
+#endif
+
+ENCODER_NAMESPACE_START
+typedef enum OpndSize {
+    /**
+     * A change must be balanced with at least the following places:
+     *              Ia32IRConstants.h :: getByteSize() uses some presumptions about OpndSize_ values
+     *              Ia32::Constraint-s use the OpndSize as a mask
+     *              encoder.cpp & encoder_master_info.cpp uses OpndSize as an index for hashing
+     *              - perhaps there are much more places
+     */
+    OpndSize_Null           = 0,
+    OpndSize_8             = 0x01,
+    OpndSize_16            = 0x02,
+    OpndSize_32            = 0x04,
+    OpndSize_64            = 0x08,
+#if !defined(TESTING_ENCODER)
+    OpndSize_80            = 0x10,
+    OpndSize_128           = 0x20,
+#endif
+    OpndSize_Max,
+    OpndSize_Any            = 0x3F,
+    OpndSize_Default        = OpndSize_Any
+} OpndSize;
+
+/**
+ * Conditional mnemonics.
+ * The values match the 'real' (==processor's) values of the appropriate
+ * condition values used in the opcodes.
+ */
+typedef enum ConditionMnemonic {
+
+    ConditionMnemonic_O=0,
+    ConditionMnemonic_NO=1,
+    ConditionMnemonic_B=2, ConditionMnemonic_NAE=ConditionMnemonic_B, ConditionMnemonic_C=ConditionMnemonic_B,
+    ConditionMnemonic_NB=3, ConditionMnemonic_AE=ConditionMnemonic_NB, ConditionMnemonic_NC=ConditionMnemonic_NB,
+    ConditionMnemonic_Z=4, ConditionMnemonic_E=ConditionMnemonic_Z,
+    ConditionMnemonic_NZ=5, ConditionMnemonic_NE=ConditionMnemonic_NZ,
+    ConditionMnemonic_BE=6, ConditionMnemonic_NA=ConditionMnemonic_BE,
+    ConditionMnemonic_NBE=7, ConditionMnemonic_A=ConditionMnemonic_NBE,
+
+    ConditionMnemonic_S=8,
+    ConditionMnemonic_NS=9,
+    ConditionMnemonic_P=10, ConditionMnemonic_PE=ConditionMnemonic_P,
+    ConditionMnemonic_NP=11, ConditionMnemonic_PO=ConditionMnemonic_NP,
+    ConditionMnemonic_L=12, ConditionMnemonic_NGE=ConditionMnemonic_L,
+    ConditionMnemonic_NL=13, ConditionMnemonic_GE=ConditionMnemonic_NL,
+    ConditionMnemonic_LE=14, ConditionMnemonic_NG=ConditionMnemonic_LE,
+    ConditionMnemonic_NLE=15, ConditionMnemonic_G=ConditionMnemonic_NLE,
+    ConditionMnemonic_Count=16
+} ConditionMnemonic;
+
+
+#define CCM(prefix,cond) Mnemonic_##prefix##cond=Mnemonic_##prefix##cc+ConditionMnemonic_##cond
+
+//=========================================================================================================
+typedef enum Mnemonic {
+
+Mnemonic_NULL=0, Mnemonic_Null=Mnemonic_NULL,
+Mnemonic_ADC,                           // Add with Carry
+Mnemonic_ADD,                           // Add
+Mnemonic_ADDSD,                         // Add Scalar Double-Precision Floating-Point Values
+Mnemonic_ADDSS,                         // Add Scalar Single-Precision Floating-Point Values
+Mnemonic_AND,                           // Logical AND
+
+Mnemonic_BSF,                           // Bit scan forward
+Mnemonic_BSR,                           // Bit scan reverse
+
+Mnemonic_CALL,                          // Call Procedure
+Mnemonic_CMC,                           // Complement Carry Flag
+Mnemonic_CWD, Mnemonic_CDQ=Mnemonic_CWD,// Convert Word to Doubleword/Convert Doubleword to Qua T dword
+Mnemonic_CMOVcc,                        // Conditional Move
+    CCM(CMOV,O),
+    CCM(CMOV,NO),
+    CCM(CMOV,B), CCM(CMOV,NAE), CCM(CMOV,C),
+    CCM(CMOV,NB), CCM(CMOV,AE), CCM(CMOV,NC),
+    CCM(CMOV,Z), CCM(CMOV,E),
+    CCM(CMOV,NZ), CCM(CMOV,NE),
+    CCM(CMOV,BE), CCM(CMOV,NA),
+    CCM(CMOV,NBE), CCM(CMOV,A),
+
+    CCM(CMOV,S),
+    CCM(CMOV,NS),
+    CCM(CMOV,P), CCM(CMOV,PE),
+    CCM(CMOV,NP), CCM(CMOV,PO),
+    CCM(CMOV,L), CCM(CMOV,NGE),
+    CCM(CMOV,NL), CCM(CMOV,GE),
+    CCM(CMOV,LE), CCM(CMOV,NG),
+    CCM(CMOV,NLE), CCM(CMOV,G),
+
+Mnemonic_CMP,                           // Compare Two Operands
+Mnemonic_CMPXCHG,                       // Compare and exchange
+Mnemonic_CMPXCHG8B,                     // Compare and Exchange 8 Bytes
+Mnemonic_CMPSB,                         // Compare Two Bytes at DS:ESI and ES:EDI
+Mnemonic_CMPSW,                         // Compare Two Words at DS:ESI and ES:EDI
+Mnemonic_CMPSD,                         // Compare Two Doublewords at DS:ESI and ES:EDI
+//
+// double -> float
+Mnemonic_CVTSD2SS,                      // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value
+// double -> I_32
+Mnemonic_CVTSD2SI,                      // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer
+// double [truncated] -> I_32
+Mnemonic_CVTTSD2SI,                     // Convert with Truncation Scalar Double-Precision Floating-Point Value to Signed Doubleword Integer
+//
+// float -> double
+Mnemonic_CVTSS2SD,                      // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value
+// float -> I_32
+Mnemonic_CVTSS2SI,                      // Convert Scalar Single-Precision Floating-Point Value to Doubleword Integer
+// float [truncated] -> I_32
+Mnemonic_CVTTSS2SI,                     // Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer
+//
+// I_32 -> double
+Mnemonic_CVTSI2SD,                      // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value
+// I_32 -> float
+Mnemonic_CVTSI2SS,                      // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value
+
+Mnemonic_COMISD,                        // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_COMISS,                        // Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_DEC,                           // Decrement by 1
+//Mnemonic_DIV,                         // Unsigned Divide
+Mnemonic_DIVSD,                         // Divide Scalar Double-Precision Floating-Point Values
+Mnemonic_DIVSS,                         // Divide Scalar Single-Precision Floating-Point Values
+
+#ifdef _HAVE_MMX_
+Mnemonic_EMMS,                          // Empty MMX Technology State
+#endif
+
+Mnemonic_ENTER,                         // ENTER-Make Stack Frame for Procedure Parameters
+Mnemonic_FLDCW,                         // Load FPU control word
+Mnemonic_FADDP,
+Mnemonic_FLDZ,
+Mnemonic_FADD,
+Mnemonic_FSUBP,
+Mnemonic_FSUB,
+Mnemonic_FISUB,
+Mnemonic_FMUL,
+Mnemonic_FMULP,
+Mnemonic_FDIVP,
+Mnemonic_FDIV,
+Mnemonic_FUCOM,
+Mnemonic_FUCOMI,
+Mnemonic_FUCOMP,
+Mnemonic_FUCOMIP,
+Mnemonic_FUCOMPP,
+Mnemonic_FRNDINT,
+Mnemonic_FNSTCW,                        // Store FPU control word
+Mnemonic_FSTSW,                         // Store FPU status word
+Mnemonic_FNSTSW,                         // Store FPU status word
+//Mnemonic_FDECSTP,                     // Decrement Stack-Top Pointer
+Mnemonic_FILD,                          // Load Integer
+Mnemonic_FLD,                           // Load Floating Point Value
+Mnemonic_FLDLG2,
+Mnemonic_FLDLN2,
+Mnemonic_FLD1,
+
+Mnemonic_FCLEX,                         // Clear Exceptions
+Mnemonic_FCHS,                          // Change sign of ST0
+Mnemonic_FNCLEX,                        // Clear Exceptions
+
+//Mnemonic_FINCSTP,                     // Increment Stack-Top Pointer
+Mnemonic_FIST,                          // Store Integer
+Mnemonic_FISTP,                         // Store Integer, pop FPU stack
+Mnemonic_FISTTP,                        // Store Integer with Truncation
+Mnemonic_FPREM,                         // Partial Remainder
+Mnemonic_FPREM1,                        // Partial Remainder
+Mnemonic_FST,                           // Store Floating Point Value
+Mnemonic_FSTP,                          // Store Floating Point Value and pop the FP stack
+Mnemonic_FSQRT,                         //Computes the square root of the source value in the stack and pop the FP stack
+Mnemonic_FABS,                          //Computes the absolute value of the source value in the stack and pop the FP stack
+Mnemonic_FSIN,                          //Computes the sine of the source value in the stack and pop the FP stack
+Mnemonic_FCOS,                          //Computes the cosine of the source value in the stack and pop the FP stack
+Mnemonic_FPTAN,                         //Computes the tangent of the source value in the stack and pop the FP stack
+Mnemonic_FYL2X,
+Mnemonic_FYL2XP1,
+Mnemonic_F2XM1,
+Mnemonic_FPATAN,
+Mnemonic_FXCH,
+Mnemonic_FSCALE,
+
+Mnemonic_XCHG,
+Mnemonic_DIV,                           // Unsigned Divide
+Mnemonic_IDIV,                          // Signed Divide
+Mnemonic_MUL,                           // Unsigned Multiply
+Mnemonic_IMUL,                          // Signed Multiply
+Mnemonic_INC,                           // Increment by 1
+Mnemonic_INT3,                          // Call break point
+Mnemonic_Jcc,                           // Jump if Condition Is Met
+    CCM(J,O),
+    CCM(J,NO),
+    CCM(J,B), CCM(J,NAE), CCM(J,C),
+    CCM(J,NB), CCM(J,AE), CCM(J,NC),
+    CCM(J,Z), CCM(J,E),
+    CCM(J,NZ), CCM(J,NE),
+    CCM(J,BE), CCM(J,NA),
+    CCM(J,NBE), CCM(J,A),
+    CCM(J,S),
+    CCM(J,NS),
+    CCM(J,P), CCM(J,PE),
+    CCM(J,NP), CCM(J,PO),
+    CCM(J,L), CCM(J,NGE),
+    CCM(J,NL), CCM(J,GE),
+    CCM(J,LE), CCM(J,NG),
+    CCM(J,NLE), CCM(J,G),
+Mnemonic_JMP,                           // Jump
+Mnemonic_LEA,                           // Load Effective Address
+Mnemonic_LEAVE,                         // High Level Procedure Exit
+Mnemonic_LOOP,                          // Loop according to ECX counter
+Mnemonic_LOOPE,                          // Loop according to ECX counter
+Mnemonic_LOOPNE, Mnemonic_LOOPNZ = Mnemonic_LOOPNE, // Loop according to ECX
+Mnemonic_LAHF,                          // Load Flags into AH
+Mnemonic_MOV,                           // Move
+Mnemonic_MOVD,                          // Move Double word
+Mnemonic_MOVQ,                          // Move Quadword
+/*Mnemonic_MOVS,                        // Move Data from String to String*/
+// MOVS is a special case: see encoding table for more details,
+Mnemonic_MOVS8, Mnemonic_MOVS16, Mnemonic_MOVS32, Mnemonic_MOVS64,
+//
+Mnemonic_MOVAPD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSS,                         // Move Scalar Single-Precision Floating-Point Values
+Mnemonic_MOVSX,                         // Move with Sign-Extension
+Mnemonic_MOVZX,                         // Move with Zero-Extend
+//Mnemonic_MUL,                         // Unsigned Multiply
+Mnemonic_MULSD,                         // Multiply Scalar Double-Precision Floating-Point Values
+Mnemonic_MULSS,                         // Multiply Scalar Single-Precision Floating-Point Values
+Mnemonic_NEG,                           // Two's Complement Negation
+Mnemonic_NOP,                           // No Operation
+Mnemonic_NOT,                           // One's Complement Negation
+Mnemonic_OR,                            // Logical Inclusive OR
+Mnemonic_PREFETCH,                      // prefetch
+
+#if 1 //def _HAVE_MMX_
+    Mnemonic_PADDQ,                     // Add Packed Quadword Integers
+    Mnemonic_PAND,                      // Logical AND
+    Mnemonic_POR,                       // Bitwise Logical OR
+    Mnemonic_PSUBQ,                     // Subtract Packed Quadword Integers
+#endif
+Mnemonic_PANDN,
+Mnemonic_PSLLQ,
+Mnemonic_PSRLQ,
+Mnemonic_PXOR,                          // Logical Exclusive OR
+Mnemonic_POP,                           // Pop a Value from the Stack
+Mnemonic_POPFD,                         // Pop a Value of EFLAGS register from the Stack
+Mnemonic_PUSH,                          // Push Word or Doubleword Onto the Stack
+Mnemonic_PUSHFD,                        // Push EFLAGS Doubleword Onto the Stack
+Mnemonic_RET,                           // Return from Procedure
+
+Mnemonic_SETcc,                         // Set Byte on Condition
+    CCM(SET,O),
+    CCM(SET,NO),
+    CCM(SET,B), CCM(SET,NAE), CCM(SET,C),
+    CCM(SET,NB), CCM(SET,AE), CCM(SET,NC),
+    CCM(SET,Z), CCM(SET,E),
+    CCM(SET,NZ), CCM(SET,NE),
+    CCM(SET,BE), CCM(SET,NA),
+    CCM(SET,NBE), CCM(SET,A),
+    CCM(SET,S),
+    CCM(SET,NS),
+    CCM(SET,P), CCM(SET,PE),
+    CCM(SET,NP), CCM(SET,PO),
+    CCM(SET,L), CCM(SET,NGE),
+    CCM(SET,NL), CCM(SET,GE),
+    CCM(SET,LE), CCM(SET,NG),
+    CCM(SET,NLE), CCM(SET,G),
+
+Mnemonic_SAL, Mnemonic_SHL=Mnemonic_SAL,// Shift left
+Mnemonic_SAR,                           // Unsigned shift right
+Mnemonic_ROR,                           // Rotate right
+Mnemonic_RCR,                           // Rotate right through CARRY flag
+Mnemonic_ROL,                           // Rotate left
+Mnemonic_RCL,                           // Rotate left through CARRY flag
+Mnemonic_SHR,                           // Signed shift right
+Mnemonic_SHRD,                          // Double Precision Shift Right
+Mnemonic_SHLD,                          // Double Precision Shift Left
+
+Mnemonic_SBB,                           // Integer Subtraction with Borrow
+Mnemonic_SUB,                           // Subtract
+Mnemonic_SUBSD,                         // Subtract Scalar Double-Precision Floating-Point Values
+Mnemonic_SUBSS,                         // Subtract Scalar Single-Precision Floating-Point Values
+
+Mnemonic_TEST,                          // Logical Compare
+
+Mnemonic_UCOMISD,                       // Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_UCOMISS,                       // Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS
+
+Mnemonic_XOR,                           // Logical Exclusive OR
+//
+// packed things,
+//
+Mnemonic_XORPD,                         // Bitwise Logical XOR for Double-Precision Floating-Point Values
+Mnemonic_XORPS,                         // Bitwise Logical XOR for Single-Precision Floating-Point Values
+
+Mnemonic_CVTDQ2PD,                      // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values
+Mnemonic_CVTTPD2DQ,                     // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
+
+Mnemonic_CVTDQ2PS,                      // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values
+Mnemonic_CVTTPS2DQ,                     // Convert with Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers
+//
+// String operations
+//
+Mnemonic_STD,                           // Set direction flag
+Mnemonic_CLD,                           // Clear direction flag
+Mnemonic_SCAS,                          // Scan string
+Mnemonic_STOS,                          // Store string
+
+//
+Mnemonic_WAIT,                          // Check pending pending unmasked floating-point exception
+//
+Mnemonic_Count
+} Mnemonic;
+
+#undef CCM
+
+ENCODER_NAMESPACE_END
+
+#endif  // ifndef _ENCODER_DEFS_EXT_H_
diff --git a/vm/compiler/codegen/x86/libenc/enc_prvt.h b/vm/compiler/codegen/x86/libenc/enc_prvt.h
new file mode 100644
index 0000000..4300574
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_prvt.h
@@ -0,0 +1,382 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#ifndef __ENC_PRVT_H_INCLUDED__
+#define __ENC_PRVT_H_INCLUDED__
+
+#include "enc_base.h"
+
+ENCODER_NAMESPACE_START
+/*
+ * @file
+ * @brief Contains some definitions/constants and other stuff used by the
+ *        Encoder internally.
+ */
+
+enum OpcodeByteKind {
+    //OpcodeByteKind_Opcode = 0x0000,
+    OpcodeByteKind_ZeroOpcodeByte           = 0x0100,
+    //
+    // The names _SlashR,  _SlahsNum, _ib, _iw, etc
+    // represent the appropriate abbreviations used
+    // in the mnemonic descriptions in the Intel's arch manual.
+    //
+    OpcodeByteKind_SlashR                   = 0x0200,
+    OpcodeByteKind_SlashNum                 = 0x0300,
+    OpcodeByteKind_ib                       = 0x0400,
+    OpcodeByteKind_iw                       = 0x0500,
+    OpcodeByteKind_id                       = 0x0600,
+#ifdef _EM64T_
+    OpcodeByteKind_io                       = 0x0700,
+#endif
+    OpcodeByteKind_cb                       = 0x0800,
+    OpcodeByteKind_cw                       = 0x0900,
+    OpcodeByteKind_cd                       = 0x0A00,
+    //OpcodeByteKind_cp                     = 0x0B00,
+    //OpcodeByteKind_co                     = 0x0C00,
+    //OpcodeByteKind_ct                     = 0x0D00,
+
+    OpcodeByteKind_rb                       = 0x0E00,
+    OpcodeByteKind_rw                       = 0x0F00,
+    OpcodeByteKind_rd                       = 0x1000,
+#ifdef _EM64T_
+    OpcodeByteKind_ro                       = 0x1100,
+    //OpcodeByteKind_REX                    = 0x1200,
+    OpcodeByteKind_REX_W                    = 0x1300,
+#endif
+    OpcodeByteKind_plus_i                   = 0x1400,
+    /**
+        * a special marker, means 'no opcode on the given position'
+        * used in opcodes array, to specify the empty slot, say
+        * to fill an em64t-specific opcode on ia32.
+        * last 'e' made lowercase to avoid a mess with 'F' in
+        * OpcodeByteKind_LAST .
+        */
+    OpcodeByteKind_EMPTY                    = 0xFFFE,
+    /**
+        * a special marker, means 'no more opcodes in the array'
+        * used in in opcodes array to show that there are no more
+        * opcodes in the array for a given mnemonic.
+        */
+    OpcodeByteKind_LAST                     = 0xFFFF,
+    /**
+        * a mask to extract the OpcodeByteKind
+        */
+    OpcodeByteKind_KindMask                 = 0xFF00,
+    /**
+        * a mask to extract the opcode byte when presented
+        */
+    OpcodeByteKind_OpcodeMask               = 0x00FF
+};
+
+#ifdef USE_ENCODER_DEFINES
+
+#define N           {0, 0, 0, 0 }
+#define U           {1, 0, 1, OpndRole_Use }
+#define D           {1, 1, 0, OpndRole_Def }
+#define DU          {1, 1, 1, OpndRole_Def|OpndRole_Use }
+
+#define U_U         {2, 0, 2, OpndRole_Use<<2 | OpndRole_Use }
+#define D_U         {2, 1, 1, OpndRole_Def<<2 | OpndRole_Use }
+#define D_DU        {2, 2, 1, OpndRole_Def<<2 | (OpndRole_Def|OpndRole_Use) }
+#define DU_U        {2, 1, 2, ((OpndRole_Def|OpndRole_Use)<<2 | OpndRole_Use) }
+#define DU_DU       {2, 2, 2, ((OpndRole_Def|OpndRole_Use)<<2 | (OpndRole_Def|OpndRole_Use)) }
+
+#define DU_DU_DU    {3, 3, 3, ((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | (OpndRole_Def|OpndRole_Use) }
+#define DU_DU_U     {3, 2, 3, (((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) }
+#define D_DU_U      {3, 2, 2, (((OpndRole_Def)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) }
+#define D_U_U       {3, 1, 2, (((OpndRole_Def)<<4) | ((OpndRole_Use)<<2) | OpndRole_Use) }
+
+// Special encoding of 0x00 opcode byte. Note: it's all O-s, not zeros.
+#define OxOO        OpcodeByteKind_ZeroOpcodeByte
+
+#define Size16      InstPrefix_OpndSize
+
+#define _r          OpcodeByteKind_SlashR
+
+#define _0          OpcodeByteKind_SlashNum|0
+#define _1          OpcodeByteKind_SlashNum|1
+#define _2          OpcodeByteKind_SlashNum|2
+#define _3          OpcodeByteKind_SlashNum|3
+#define _4          OpcodeByteKind_SlashNum|4
+#define _5          OpcodeByteKind_SlashNum|5
+#define _6          OpcodeByteKind_SlashNum|6
+#define _7          OpcodeByteKind_SlashNum|7
+
+// '+i' for floating-point instructions
+#define _i          OpcodeByteKind_plus_i
+
+
+#define ib          OpcodeByteKind_ib
+#define iw          OpcodeByteKind_iw
+#define id          OpcodeByteKind_id
+
+#define cb          OpcodeByteKind_cb
+#define cw          OpcodeByteKind_cw
+#define cd          OpcodeByteKind_cd
+
+#define rb          OpcodeByteKind_rb
+#define rw          OpcodeByteKind_rw
+#define rd          OpcodeByteKind_rd
+
+#define AL          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AL}
+#define AH          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AH}
+#define AX          {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_AX}
+#define EAX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EAX}
+#ifdef _EM64T_
+    #define RAX     {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RAX }
+#endif
+
+#define CL          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_CL}
+#define ECX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ECX}
+#ifdef _EM64T_
+    #define RCX         {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RCX}
+#endif
+
+#define DX          {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_DX}
+#define EDX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDX}
+#ifdef _EM64T_
+    #define RDX     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDX }
+#endif
+
+#define ESI         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ESI}
+#ifdef _EM64T_
+    #define RSI     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RSI }
+#endif
+
+#define EDI         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDI}
+#ifdef _EM64T_
+    #define RDI     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDI }
+#endif
+
+#define r8          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_Null}
+#define r16         {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_Null}
+#define r32         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_Null}
+#ifdef _EM64T_
+    #define r64     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_Null }
+#endif
+
+#define r_m8        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Any, RegName_Null}
+#define r_m16       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Any, RegName_Null}
+#define r_m32       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define r_m8s        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Signed, RegName_Null}
+#define r_m16s       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Signed, RegName_Null}
+#define r_m32s       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Signed, RegName_Null}
+
+#define r_m8u        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Zero, RegName_Null}
+#define r_m16u       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Zero, RegName_Null}
+#define r_m32u       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Zero, RegName_Null}
+
+//'m' was only used in LEA mnemonic, but is replaced with
+// set of exact sizes. See more comments for LEA instruction in TheTable.
+//#define m           {OpndKind_Mem, OpndSize_Null, RegName_Null}
+#define m8          {OpndKind_Mem, OpndSize_8, OpndExt_Any, RegName_Null}
+#define m16         {OpndKind_Mem, OpndSize_16, OpndExt_Any, RegName_Null}
+#define m32         {OpndKind_Mem, OpndSize_32, OpndExt_Any, RegName_Null}
+#define m64         {OpndKind_Mem, OpndSize_64, OpndExt_Any, RegName_Null}
+#ifdef _EM64T_
+    #define r_m64   { (OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null }
+#endif
+
+#define imm8        {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null}
+#define imm16       {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null}
+#define imm32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define imm8s        {OpndKind_Imm, OpndSize_8, OpndExt_Signed, RegName_Null}
+#define imm16s       {OpndKind_Imm, OpndSize_16, OpndExt_Signed, RegName_Null}
+#define imm32s       {OpndKind_Imm, OpndSize_32, OpndExt_Signed, RegName_Null}
+
+#define imm8u        {OpndKind_Imm, OpndSize_8, OpndExt_Zero, RegName_Null}
+#define imm16u       {OpndKind_Imm, OpndSize_16, OpndExt_Zero, RegName_Null}
+#define imm32u       {OpndKind_Imm, OpndSize_32, OpndExt_Zero, RegName_Null}
+
+#ifdef _EM64T_
+    #define imm64   {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null }
+#endif
+
+//FIXME: moff-s are in fact memory refs, but presented as immediate.
+// Need to specify this in OpndDesc.
+#define moff8        {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+#define moff16       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+#define moff32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+#ifdef _EM64T_
+    #define moff64       {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null}
+#endif
+
+
+#define rel8        {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null}
+#define rel16       {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null}
+#define rel32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define mm64        {OpndKind_MMXReg, OpndSize_64, OpndExt_Any, RegName_Null}
+#define mm_m64      {(OpndKind)(OpndKind_MMXReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null}
+
+#define xmm64       {OpndKind_XMMReg, OpndSize_64, OpndExt_Any, RegName_Null}
+#define xmm_m64     {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null}
+
+#define xmm32       {OpndKind_XMMReg, OpndSize_32, OpndExt_Any, RegName_Null}
+#define xmm_m32     {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define FP0S        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP0S}
+#define FP0D        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP0D}
+#define FP1S        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP1S}
+#define FP1D        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP1D}
+#define fp32        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_Null}
+#define fp64        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_Null}
+
+#ifdef _EM64T_
+    #define io      OpcodeByteKind_io
+    #define REX_W   OpcodeByteKind_REX_W
+
+#endif
+
+#endif // USE_ENCODER_DEFINES
+
+/**
+ * @brief Represents the REX part of instruction.
+ */
+struct  Rex {
+    unsigned char b : 1;
+    unsigned char x : 1;
+    unsigned char r : 1;
+    unsigned char w : 1;
+    unsigned char dummy : 4;        // must be '0100'b
+    unsigned int  :24;
+};
+
+/**
+ * @brief Describes SIB (scale,index,base) byte.
+ */
+struct SIB {
+    unsigned char base:3;
+    unsigned char index:3;
+    unsigned char scale:2;
+    unsigned int  padding:24;
+};
+/**
+ * @brief Describes ModRM byte.
+ */
+struct ModRM
+{
+    unsigned char rm:3;
+    unsigned char reg:3;
+    unsigned char mod:2;
+    unsigned int  padding:24;
+};
+
+
+
+/**
+* exactly the same as EncoderBase::OpcodeDesc, but also holds info about
+* platform on which the opcode is applicable.
+*/
+struct OpcodeInfo {
+    enum platform {
+        /// an opcode is valid on all platforms
+        all,
+        // opcode is valid on IA-32 only
+        em64t,
+        // opcode is valid on Intel64 only
+        ia32,
+        // opcode is added for the sake of disassembling, should not be used in encoding
+        decoder,
+        // only appears in master table, replaced with 'decoder' in hashed version
+        decoder32,
+        // only appears in master table, replaced with 'decoder' in hashed version
+        decoder64,
+    };
+    platform                        platf;
+    unsigned                        opcode[4+1+1];
+    EncoderBase::OpndDesc           opnds[3];
+    EncoderBase::OpndRolesDesc      roles;
+};
+
+/**
+ * @defgroup MF_ Mnemonic flags
+*/
+
+    /**
+ * Operation has no special properties.
+    */
+#define MF_NONE             (0x00000000)
+    /**
+ * Operation affects flags
+    */
+#define MF_AFFECTS_FLAGS    (0x00000001)
+    /**
+ * Operation uses flags - conditional operations, ADC/SBB/ETC
+    */
+#define MF_USES_FLAGS       (0x00000002)
+    /**
+ * Operation is conditional - MOVcc/SETcc/Jcc/ETC
+    */
+#define MF_CONDITIONAL      (0x00000004)
+/**
+ * Operation is symmetric - its args can be swapped (ADD/MUL/etc).
+ */
+#define MF_SYMMETRIC        (0x00000008)
+/**
+ * Operation is XOR-like - XOR, SUB - operations of 'arg,arg' is pure def,
+ * without use.
+ */
+#define MF_SAME_ARG_NO_USE  (0x00000010)
+
+///@} // ~MNF
+
+/**
+ * @see same structure as EncoderBase::MnemonicDesc, but carries
+ * MnemonicInfo::OpcodeInfo[] instead of OpcodeDesc[].
+ * Only used during prebuilding the encoding tables, thus it's hidden under
+ * the appropriate define.
+ */
+struct MnemonicInfo {
+    /**
+    * The mnemonic itself
+    */
+    Mnemonic    mn;
+    /**
+     * Various characteristics of mnemonic.
+     * @see MF_
+     */
+    unsigned    flags;
+    /**
+     * Number of args/des/uses/roles for the operation. For the operations
+     * which may use different number of operands (i.e. IMUL/SHL) use the
+     * most common value, or leave '0' if you are sure this info is not
+     * required.
+     */
+    EncoderBase::OpndRolesDesc              roles;
+    /**
+     * Print name of the mnemonic
+     */
+    const char *                            name;
+    /**
+     * Array of opcodes.
+     * The terminating opcode description always have OpcodeByteKind_LAST
+     * at the opcodes[i].opcode[0].
+     * The size of '25' has nothing behind it, just counted the max
+     * number of opcodes currently used (MOV instruction).
+     */
+    OpcodeInfo                              opcodes[25];
+};
+
+ENCODER_NAMESPACE_END
+
+#endif  // ~__ENC_PRVT_H_INCLUDED__
diff --git a/vm/compiler/codegen/x86/libenc/enc_tabl.cpp b/vm/compiler/codegen/x86/libenc/enc_tabl.cpp
new file mode 100644
index 0000000..8a0789a
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_tabl.cpp
@@ -0,0 +1,1975 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h> //qsort
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <stdlib.h>
+
+
+// need to use EM64T-specifics - new registers, defines from enc_prvt, etc...
+#if !defined(_EM64T_)
+    #define UNDEF_EM64T
+    #define _EM64T_
+#endif
+
+#define USE_ENCODER_DEFINES
+#include "enc_prvt.h"
+#include "enc_defs.h"
+
+#ifdef UNDEF_EM64T
+    #undef _EM64T_
+#endif
+
+//Android x86
+#if 0 //!defined(_HAVE_MMX_)
+    #define Mnemonic_PADDQ  Mnemonic_Null
+    #define Mnemonic_PAND   Mnemonic_Null
+    #define Mnemonic_POR    Mnemonic_Null
+    #define Mnemonic_PSUBQ  Mnemonic_Null
+#endif
+
+ENCODER_NAMESPACE_START
+
+
+EncoderBase::MnemonicDesc EncoderBase::mnemonics[Mnemonic_Count];
+EncoderBase::OpcodeDesc EncoderBase::opcodes[Mnemonic_Count][MAX_OPCODES];
+unsigned char EncoderBase::opcodesHashMap[Mnemonic_Count][HASH_MAX];
+
+
+/**
+ * @file
+ * @brief 'Master' copy of encoding data.
+ */
+
+/*
+This file contains a 'master copy' of encoding table - this is the info used
+by both generator of native instructions (EncoderBase class) and by
+disassembling routines. The first one uses an info how to encode the
+instruction, and the second does an opposite - several separate tables are
+built at runtime from this main table.
+
+=============================================================================
+
+The table was designed for easy support and maintenance. Thus, it was made as
+much close as possible to the Intel's IA32 Architecture Manual descriptions.
+The info is based on the latest (at the moment of writing) revision which is
+June 2005, order number 253666-016.
+
+Normally, almost all of opcodes in the 'master' table represented exactly as
+they are shown in the Intel's Architecture manual (well, with slashes
+replaced with underscore). There are several exclusions especially marked.
+
+Normally, to add an opcode/instruction, one only need to copy the whole
+string from the manual, and simply replace '/' with '_'.
+
+I.e., TheManual reads for DEC:
+    (1)     FE /1 DEC r/m8 Valid Valid Decrement r/m8 by 1.
+    (2)     REX + FE /1 DEC r/m8* Valid N.E. Decrement r/m8 by 1.
+    (3)     REX.W + FF /1 DEC r/m64 Valid N.E. Decrement r/m64 by 1.
+
+1. Note, that there is no need to explicitly specify REX-based opcodes for
+    instruction to handle additional registers on EM64T:
+
+    (1)     FE /1 DEC r/m8 Valid Valid Decrement r/m8 by 1.
+    (3)     REX.W + FF /1 DEC r/m64 Valid N.E. Decrement r/m64 by 1.
+
+2. Copy the string, strip off the text comments, replace '/'=>'_'. Note, that
+    the second line is for EM64T only
+
+    (1)     FE /1 DEC r/m8
+    (3)     REX.W + FF /1 DEC r/m64
+
+3. Fill out the mnemonic, opcode parameters parts
+
+    BEGIN_MNEMONIC(DEC, MF_AFFECTS_FLAGS, DU)
+    BEGIN_OPCODES()
+        {OpcodeInfo::all,   {0xFE, _1},         {r_m8},         DU },
+        {OpcodeInfo::em64t, {REX_W, 0xFF, _1},  {r_m64},        DU },
+
+    DU here - one argument, it's used and defined
+
+4. That's it, that simple !
+
+The operand roles (DU here) are used by Jitrino's optimizing engine to
+perform data flow analysis. It also used to store/obtain number of operands.
+
+Special cases are (see the table for details):
+LEA
+Some FPU operations (i.e. FSTP)
+packed things (XORPD, XORPS, CVTDQ2PD, CVTTPD2DQ)
+
+Also, the Jitrino's needs require to specify all operands - including
+implicit ones (see IMUL).
+
+The master table iself does not need to be ordered - it's get sorted before
+processing. It's recommended (though it's not a law) to group similar
+instructions together - i.e. FPU instructions, MMX, etc.
+
+=============================================================================
+
+The encoding engine builds several tables basing on the 'master' one (here
+'mnemonic' is a kind of synonim for 'instruction'):
+
+- list of mnemonics which holds general info about instructions
+    (EncoderBase::mnemonics)
+- an array of opcodes descriptions (EncodeBase::opcodes)
+- a mapping between a hash value and an opcode description record for a given
+    mnemonic (EncoderBase::opcodesHashMap)
+
+The EncoderBase::mnemonics holds general info about instructions.
+The EncoderBase::opcodesHashMap is used for fast opcode selection basing on
+a hash value.
+The EncodeBase::opcodes is used for the encoding itself.
+
+=============================================================================
+The hash value is calculated and used as follows:
+
+JIT-ted code uses the following operand sizes: 8-, 16-, 32- and 64-bits and
+size for an operand can be encoded in just 2 bits.
+
+The following operand locations are available: one of registers - GP, FP,
+MMX, XMM (not taking segment registers), a memory and an immediate, which
+gives us 6 variants and can be enumerated in 3 bits.
+
+As a grand total, the the whole operand's info needed for opcode selection
+can be packed in 5 bits. Taking into account the IMUL mnemonic with its 3
+operands (including implicit ones), we're getting 15 bits per instruction and
+the complete table is about 32768 items per single instruction.
+
+Seems too many, but luckily, the 15 bit limit will never be reached: the
+worst case is IMUL with its 3 operands:
+(IMUL r64, r/m64, imm32)/(IMUL r32, r/m32, imm32).
+So, assigning lowest value to GP register, the max value of hash can be
+reduced.
+
+The hash values to use are:
+sizes:
+        8               -> 11
+        16              -> 10
+        32              -> 01
+        64              -> 00
+locations:
+        gp reg          -> 000
+        memory          -> 001
+        fp reg          -> 010
+        mmx reg         -> 011
+        xmm reg         -> 100
+        immediate       -> 101
+and the grand total for the worst case would be
+[ GP 32] [GP  32] [Imm 32]
+[000-01] [000-01] [101 01] = 1077
+
+However, the implicit operands adds additional value, and the worstest case
+is 'SHLD r_m32, r32, CL=r8'. This gives us the maximum number of:
+
+[mem 32] [GP  32] [GP  8b]
+[001-01] [000-01] [000-11] = 5155.
+
+The max number is pretty big and the hash functions is quite rare, thus it
+is not resonable to use a direct addressing i.e.
+OpcodeDesc[mnemonic][hash_code] - there would be a huge waste of space.
+
+Instead, we use a kind of mapping: the opcodes info is stored in packed
+(here: non rare) array. The max number of opcodes will not exceed 255 for
+each instruction. And we have an index array in which we store a mapping
+between a hash code value and opcode position for each given instruction.
+
+Sounds a bit sophisticated, but in real is simple, the opcode gets selected
+in 2 simple steps:
+
+1. Select [hash,mnemonic] => 'n'.
+
+The array is pretty rare - many cells contain 0xFF which
+means 'invalid hash - no opcode with given characteristics'
+
+char EnbcoderBase::opcodesHashMap[Mnemonic_Count][HASH_MAX] =
+
++----+----+----+----+----+----+
+| 00 | 05 | FF | FF | 03 | 12 | ...
+|---------+-------------------+
+| 12 | FF | FF |  n | 04 | 25 | ...   <- Mnemonic
+|-----------------------------+
+| FF | 11 | FF | 10 | 13 | .. | ...
++-----------------------------+
+     ...         ^
+                 |
+                hash
+
+2. Select [n,mnemonic] => 'opcode_desc11'
+
+OpcodeDesc      EncoderBase::opcodes[Mnemonic_Count][MAX_OPCODES] =
+
++---------------+---------------+---------------+---------------+
+| opcode_desc00 | opcode_desc01 | opcode_desc02 | last_opcode   | ...
++---------------+---------------+---------------+---------------+
+| opcode_desc10 | opcode_desc11 | last_opcode   | xxx           | <- Mnemonic
++---------------+---------------+---------------+---------------+
+| opcode_desc20 | opcode_desc21 | opcode_desc22 | opcode_desc23 | ...
++---------------+---------------+---------------+---------------+
+     ...
+                      ^
+                      |
+                      n
+
+Now, use 'opcode_desc11'.
+
+=============================================================================
+The array of opcodes descriptions (EncodeBase::opcodes) is specially prepared
+to maximize performance - the EncoderBase::encode() is quite hot on client
+applications for the Jitrino/Jitrino.JET.
+The preparation is that opcode descriptions from the 'master' encoding table
+are preprocessed and a special set of OpcodeDesc prepared:
+First, the 'raw' opcode bytes are extracted. Here, 'raw' means the bytes that
+do not depened on any operands values, do not require any analysis and can be
+simply copied into the output buffer during encoding. Also, number of these
+'raw' bytes is counted. The fields are OpcodeDesc::opcode and
+OpcodeDesc::opcode_len.
+
+Then the fisrt non-implicit operand found and its index is stored in
+OpcodeDesc::first_opnd.
+
+The bytes that require processing and analysis ('/r', '+i', etc) are
+extracted and stored in OpcodeDesc::aux0 and OpcodeDesc::aux1 fields.
+
+Here, a special trick is performed:
+    Some opcodes have register/memory operand, but this is not reflected in
+    opcode column - for example, (MOVQ xmm64, xmm_m64). In this case, a fake
+    '_r' added to OpcodeDesc::aux field.
+    Some other opcodes have immediate operands, but this is again not
+    reflected in opcode column - for example, CALL cd or PUSH imm32.
+    In this case, a fake '/cd' or fake '/id' added to appropriate
+    OpcodeDesc::aux field.
+
+The OpcodeDesc::last is non-zero for the final OpcodeDesc record (which does
+not have valid data itself).
+*/
+
+// TODO: To extend flexibility, replace bool fields in MnemonicDesc &
+// MnemonicInfo with a set of flags packed into integer field.
+
+unsigned short EncoderBase::getHash(const OpcodeInfo* odesc)
+{
+    /*
+    NOTE: any changes in the hash computation must be stricty balanced with
+    EncoderBase::Operand::hash_it and EncoderBase::Operands()
+    */
+    unsigned short hash = 0;
+    // The hash computation, uses fast way - table selection instead of if-s.
+    if (odesc->roles.count > 0) {
+        OpndKind kind = odesc->opnds[0].kind;
+        OpndSize size = odesc->opnds[0].size;
+        assert(kind<COUNTOF(kind_hash));
+        assert(size<COUNTOF(size_hash));
+        hash = get_kind_hash(kind) | get_size_hash(size);
+    }
+
+    if (odesc->roles.count > 1) {
+        OpndKind kind = odesc->opnds[1].kind;
+        OpndSize size = odesc->opnds[1].size;
+        assert(kind<COUNTOF(kind_hash));
+        assert(size<COUNTOF(size_hash));
+        hash = (hash<<HASH_BITS_PER_OPERAND) |
+               (get_kind_hash(kind) | get_size_hash(size));
+    }
+
+    if (odesc->roles.count > 2) {
+        OpndKind kind = odesc->opnds[2].kind;
+        OpndSize size = odesc->opnds[2].size;
+        assert(kind<COUNTOF(kind_hash));
+        assert(size<COUNTOF(size_hash));
+        hash = (hash<<HASH_BITS_PER_OPERAND) |
+            (get_kind_hash(kind) | get_size_hash(size));
+    }
+    assert(hash <= HASH_MAX);
+    return hash;
+}
+
+
+#define BEGIN_MNEMONIC(mn, flags, roles)     \
+        { Mnemonic_##mn, flags, roles, #mn,
+#define END_MNEMONIC() },
+#define BEGIN_OPCODES() {
+#define END_OPCODES()   { OpcodeInfo::all, {OpcodeByteKind_LAST} }}
+
+//#define BEGIN_MNEMONIC(mn, affflags, ulags, cond, symm, roles)     \
+//        { Mnemonic_##mn, affflags, ulags, cond, symm, roles, #mn,
+
+
+static MnemonicInfo masterEncodingTable[] = {
+//
+// Null
+//
+BEGIN_MNEMONIC(Null, MF_NONE, N)
+BEGIN_OPCODES()
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LAHF, MF_USES_FLAGS, D)
+BEGIN_OPCODES()
+// TheManual says it's not always supported in em64t mode, thus excluding it
+    {OpcodeInfo::ia32,    {0x9F},         {EAX}, D },
+END_OPCODES()
+END_MNEMONIC()
+//
+// ALU mnemonics - add, adc, or, xor, and, cmp, sub, sbb
+// as they differ only in the opcode extention (/digit) number and
+// in which number the opcode start from, the opcode definitions
+// for those instructions are packed together
+//
+// The 'opcode_starts_from' and 'opcode_ext' in DEFINE_ALU_OPCODES()
+// are enough to define OpcodeInfo::all opcodes and the 'first_opcode'
+// parameter is only due to ADD instruction, which requires an zero opcode
+// byte which, in turn, is coded especially in the current coding scheme.
+//
+
+#define DEFINE_ALU_OPCODES( opc_ext, opcode_starts_from, first_opcode, def_use ) \
+\
+    {OpcodeInfo::decoder,   {opcode_starts_from + 4, ib},           {AL,    imm8},  DU_U },\
+    {OpcodeInfo::decoder,   {Size16, opcode_starts_from + 5, iw},   {AX,    imm16}, DU_U },\
+    {OpcodeInfo::decoder,   {opcode_starts_from + 5, id},           {EAX,   imm32}, DU_U },\
+    {OpcodeInfo::decoder64, {REX_W, opcode_starts_from+5, id},      {RAX,   imm32s},DU_U },\
+\
+    {OpcodeInfo::all,       {0x80, opc_ext, ib},          {r_m8,  imm8},    def_use },\
+    {OpcodeInfo::all,       {Size16, 0x81, opc_ext, iw},  {r_m16, imm16},   def_use },\
+    {OpcodeInfo::all,       {0x81, opc_ext, id},          {r_m32, imm32},   def_use },\
+    {OpcodeInfo::em64t,     {REX_W, 0x81, opc_ext, id},   {r_m64, imm32s},  def_use },\
+\
+    {OpcodeInfo::all,       {Size16, 0x83, opc_ext, ib},  {r_m16, imm8s},   def_use },\
+    {OpcodeInfo::all,       {0x83, opc_ext, ib},          {r_m32, imm8s},   def_use },\
+    {OpcodeInfo::em64t,     {REX_W, 0x83, opc_ext, ib},   {r_m64, imm8s},   def_use },\
+\
+    {OpcodeInfo::all,       {first_opcode,  _r},          {r_m8,  r8},      def_use },\
+\
+    {OpcodeInfo::all,       {Size16, opcode_starts_from+1,  _r},  {r_m16, r16},   def_use },\
+    {OpcodeInfo::all,       {opcode_starts_from+1,  _r},  {r_m32, r32},   def_use },\
+    {OpcodeInfo::em64t,     {REX_W, opcode_starts_from+1, _r},    {r_m64, r64},   def_use },\
+\
+    {OpcodeInfo::all,       {opcode_starts_from+2,  _r},  {r8,    r_m8},  def_use },\
+\
+    {OpcodeInfo::all,       {Size16, opcode_starts_from+3,  _r},  {r16,   r_m16}, def_use },\
+    {OpcodeInfo::all,       {opcode_starts_from+3,  _r},  {r32,   r_m32}, def_use },\
+    {OpcodeInfo::em64t,     {REX_W, opcode_starts_from+3, _r},    {r64,   r_m64}, def_use },
+
+BEGIN_MNEMONIC(ADD, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_0, 0x00, OxOO, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(OR, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_1, 0x08, 0x08, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(ADC, MF_AFFECTS_FLAGS|MF_USES_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_2, 0x10, 0x10, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SBB, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_3, 0x18, 0x18, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(AND, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_4, 0x20, 0x20, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(SUB, MF_AFFECTS_FLAGS|MF_SAME_ARG_NO_USE, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_5, 0x28, 0x28, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(XOR, MF_AFFECTS_FLAGS|MF_SYMMETRIC|MF_SAME_ARG_NO_USE, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES( _6, 0x30, 0x30, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMP, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES( _7, 0x38, 0x38, U_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPXCHG, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xB0, _r},           {r_m8, r8, AL},     DU_DU_DU },
+    {OpcodeInfo::all,   {Size16, 0x0F, 0xB1, _r},   {r_m16, r16, AX},   DU_DU_DU },
+    {OpcodeInfo::all,   {0x0F, 0xB1, _r},           {r_m32, r32, EAX},  DU_DU_DU},
+    {OpcodeInfo::em64t, {REX_W, 0x0F, 0xB1, _r},    {r_m64, r64, RAX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPXCHG8B, MF_AFFECTS_FLAGS, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xC7, _1},         {m64},     DU },
+END_OPCODES()
+END_MNEMONIC()
+
+#undef DEFINE_ALU_OPCODES
+//
+//
+//
+BEGIN_MNEMONIC(ADDSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x58, _r},   {xmm64, xmm_m64},   DU_U},
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(ADDSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x58, _r},   {xmm32, xmm_m32},   DU_U},
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(BSF, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xBC},   {r32, r_m32},   D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(BSR, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xBD},   {r32, r_m32},   D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(CALL, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE8, cd},        {rel32},     U },
+    {OpcodeInfo::ia32,  {Size16, 0xE8, cw}, {rel16},    U },
+    {OpcodeInfo::ia32,  {0xFF, _2},        {r_m32},     U },
+    {OpcodeInfo::em64t, {0xFF, _2},        {r_m64},     U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMC, MF_USES_FLAGS|MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::decoder,   {0xF5},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+//TODO: Workaround. Actually, it's D_DU, but Jitrino's CG thinks it's D_U
+BEGIN_MNEMONIC(CDQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,       {0x99},         {DX, AX},       D_U },
+    {OpcodeInfo::all,       {0x99},         {EDX, EAX},     D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x99},  {RDX, RAX},     D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+#define DEFINE_CMOVcc_MNEMONIC( cc ) \
+        BEGIN_MNEMONIC(CMOV##cc, MF_USES_FLAGS|MF_CONDITIONAL, DU_U ) \
+BEGIN_OPCODES() \
+    {OpcodeInfo::all,   {Size16, 0x0F, 0x40 + ConditionMnemonic_##cc, _r},  {r16, r_m16},   DU_U }, \
+    {OpcodeInfo::all,   {0x0F, 0x40 + ConditionMnemonic_##cc, _r},          {r32, r_m32},   DU_U }, \
+    {OpcodeInfo::em64t, {REX_W, 0x0F, 0x40 + ConditionMnemonic_##cc, _r},   {r64, r_m64},   DU_U }, \
+END_OPCODES() \
+END_MNEMONIC()
+
+DEFINE_CMOVcc_MNEMONIC(O)
+DEFINE_CMOVcc_MNEMONIC(NO)
+DEFINE_CMOVcc_MNEMONIC(B)
+DEFINE_CMOVcc_MNEMONIC(NB)
+DEFINE_CMOVcc_MNEMONIC(Z)
+DEFINE_CMOVcc_MNEMONIC(NZ)
+DEFINE_CMOVcc_MNEMONIC(BE)
+DEFINE_CMOVcc_MNEMONIC(NBE)
+DEFINE_CMOVcc_MNEMONIC(S)
+DEFINE_CMOVcc_MNEMONIC(NS)
+DEFINE_CMOVcc_MNEMONIC(P)
+DEFINE_CMOVcc_MNEMONIC(NP)
+DEFINE_CMOVcc_MNEMONIC(L)
+DEFINE_CMOVcc_MNEMONIC(NL)
+DEFINE_CMOVcc_MNEMONIC(LE)
+DEFINE_CMOVcc_MNEMONIC(NLE)
+
+#undef DEFINE_CMOVcc_MNEMONIC
+
+/*****************************************************************************
+                                ***** SSE conversion routines *****
+*****************************************************************************/
+//
+// double -> float
+BEGIN_MNEMONIC(CVTSD2SS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x5A, _r},   {xmm32, xmm_m64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// double -> I_32
+BEGIN_MNEMONIC(CVTSD2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x2D, _r},         {r32, xmm_m64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2D, _r},  {r64, xmm_m64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// double [truncated] -> I_32
+BEGIN_MNEMONIC(CVTTSD2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x2C, _r},         {r32, xmm_m64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2C, _r},  {r64, xmm_m64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// float -> double
+BEGIN_MNEMONIC(CVTSS2SD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x5A, _r},         {xmm64, xmm_m32}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// float -> I_32
+BEGIN_MNEMONIC(CVTSS2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x2D, _r},         {r32, xmm_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2D, _r},  {r64, xmm_m32}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+// float [truncated] -> I_32
+BEGIN_MNEMONIC(CVTTSS2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x2C, _r},         {r32, xmm_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2C, _r},  {r64, xmm_m32}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+// I_32 -> double
+BEGIN_MNEMONIC(CVTSI2SD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x2A, _r},         {xmm64, r_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2A, _r},  {xmm64, r_m64}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+// I_32 -> float
+BEGIN_MNEMONIC(CVTSI2SS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x2A, _r},         {xmm32, r_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2A, _r},  {xmm32, r_m64}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// ~ SSE conversions
+//
+
+BEGIN_MNEMONIC(DEC, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFE, _1},         {r_m8},     DU },
+
+    {OpcodeInfo::all,   {Size16, 0xFF, _1}, {r_m16},    DU },
+    {OpcodeInfo::all,   {0xFF, _1},         {r_m32},    DU },
+    {OpcodeInfo::em64t, {REX_W, 0xFF, _1},  {r_m64},    DU },
+
+    {OpcodeInfo::ia32,  {Size16, 0x48|rw},  {r16},      DU },
+    {OpcodeInfo::ia32,  {0x48|rd},          {r32},      DU },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(DIVSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF2, 0x0F, 0x5E, _r},   {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(DIVSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF3, 0x0F, 0x5E, _r},   {xmm32, xmm_m32},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+/****************************************************************************
+                 ***** FPU operations *****
+****************************************************************************/
+
+BEGIN_MNEMONIC(FADDP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xC1},       {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xC1},       {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDZ,  MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xEE},   {FP0D}, D },
+    {OpcodeInfo::all,   {0xD9, 0xEE},   {FP0S}, D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FADD,  MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDC, _0},     {FP0D, m64}, DU_U },
+    {OpcodeInfo::all,   {0xD8, _0},     {FP0S, m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSUBP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xE9},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xE9},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSUB,   MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDC, _4},     {FP0D, m64}, DU_U },
+    {OpcodeInfo::all,   {0xD8, _4},     {FP0S, m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FISUB,   MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDA, _4},       {FP0S, m32}, DU_U },
+//    {OpcodeInfo::all,   {0xDE, _4},       {FP0S, m16}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+
+BEGIN_MNEMONIC(FMUL,   MF_NONE, DU_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD8, _1},     {FP0S, m32}, DU_U },
+    {OpcodeInfo::all,   {0xDC, _1},     {FP0D, m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FMULP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xC9},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xC9},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FDIVP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xF9},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xF9},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FDIV,   MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDC, _6},     {FP0D, m64}, DU_U },
+    {OpcodeInfo::all,   {0xD8, _6},     {FP0S, m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FUCOM, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDD, 0xE1},         {FP0D, FP1D},    DU_U },
+    {OpcodeInfo::all,   {0xDD, 0xE1},         {FP0S, FP1S},    DU_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xE0|_i},    {fp32},         DU },
+    {OpcodeInfo::all,   {0xDD, 0xE0|_i},    {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDB, 0xE8|_i},    {fp32},         DU },
+    {OpcodeInfo::all,   {0xDB, 0xE8|_i},    {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDD, 0xE9},             {FP0D, FP1D},    DU_U },
+    {OpcodeInfo::all,   {0xDD, 0xE9},             {FP0S, FP1S},    DU_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xE8|_i},        {fp32},         DU },
+    {OpcodeInfo::all,   {0xDD, 0xE8|_i},        {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMIP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDF, 0xE8|_i},        {fp32},         DU },
+    {OpcodeInfo::all,   {0xDF, 0xE8|_i},        {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMPP, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDA, 0xE9},   {FP0D, FP1D}, DU_U },
+    {OpcodeInfo::all,   {0xDA, 0xE9},   {FP0S, FP1S}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDCW, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _5},     {m16},  U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FNSTCW, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _7},     {m16},  D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSTSW, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9B, 0xDF, 0xE0}, {EAX},  D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FNSTSW, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDF, 0xE0},   {EAX},  D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FCHS, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xE0},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xD9, 0xE0},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FCLEX, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9B, 0xDB, 0xE2}, {}, N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FNCLEX, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, 0xE2},       {}, N },
+END_OPCODES()
+END_MNEMONIC()
+
+//BEGIN_MNEMONIC(FDECSTP, MF_NONE, N)
+//  BEGIN_OPCODES()
+//          {OpcodeInfo::all, {0xD9, 0xF6},       {},     N },
+//  END_OPCODES()
+//END_MNEMONIC()
+
+BEGIN_MNEMONIC(FILD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, _0}, {FP0S, m32},    D_U },
+    {OpcodeInfo::all,   {0xDF, _5}, {FP0D, m64},    D_U },
+    {OpcodeInfo::all,   {0xDB, _0}, {FP0S, m32},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+//BEGIN_MNEMONIC(FINCSTP, MF_NONE, N)
+//  BEGIN_OPCODES()
+//          {OpcodeInfo::all, {0xD9, 0xF7},       {},     N },
+//  END_OPCODES()
+//END_MNEMONIC()
+
+BEGIN_MNEMONIC(FIST, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, _2}, {m32, FP0S},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FISTP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, _3}, {m32, FP0S},    D_U },
+    {OpcodeInfo::all,   {0xDF, _7}, {m64, FP0D},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FISTTP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDD, _1}, {m64, FP0D},    D_U },
+    {OpcodeInfo::all,   {0xDB, _1}, {m32, FP0S},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FRNDINT, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xFC}, {FP0S},    DU },
+    {OpcodeInfo::all,   {0xD9, 0xFC}, {FP0D},    DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _0}, {FP0S, m32},    D_U },
+    {OpcodeInfo::all,   {0xDD, _0}, {FP0D, m64},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDLG2, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xEC}, {FP0S},    D },
+    {OpcodeInfo::all,   {0xD9, 0xEC}, {FP0D},    D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDLN2, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xED}, {FP0S},    D },
+    {OpcodeInfo::all,   {0xD9, 0xED}, {FP0D},    D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLD1, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xE8}, {FP0S},    D },
+    {OpcodeInfo::all,   {0xD9, 0xE8}, {FP0D},    D },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FPREM, MF_NONE, N)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF8},       {},     N },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FPREM1, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xF5},       {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FST, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _2},         {m32, FP0S},    D_U },
+    {OpcodeInfo::all,   {0xDD, _2},         {m64, FP0D},    D_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xD0|_i},    {fp32},         D },
+    {OpcodeInfo::all,   {0xDD, 0xD0|_i},    {fp64},         D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSTP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _3},             {m32, FP0S},    D_U },
+    {OpcodeInfo::all,   {0xDD, _3},             {m64, FP0D},    D_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xD8|_i},        {fp32},         D },
+    {OpcodeInfo::all,   {0xDD, 0xD8|_i},        {fp64},         D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSQRT, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFA},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFA},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FYL2X, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF1},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF1},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FYL2XP1, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF9},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF9},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(F2XM1, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF0},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF0},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FPATAN, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF3},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF3},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FXCH, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xC9},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xC9},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSCALE, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFD},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFD},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FABS, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xE1},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xE1},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSIN, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFE},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFE},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FCOS, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFF},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFF},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FPTAN, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF2},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF2},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+//
+// ~ FPU
+//
+
+BEGIN_MNEMONIC(DIV, MF_AFFECTS_FLAGS, DU_DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF7, _6},         {EDX, EAX, r_m32},  DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(IDIV, MF_AFFECTS_FLAGS, DU_DU_U)
+BEGIN_OPCODES()
+#if !defined(_EM64T_)
+    {OpcodeInfo::all,   {0xF6, _7},         {AH, AL, r_m8},     DU_DU_U },
+    {OpcodeInfo::all,   {Size16, 0xF7, _7}, {DX, AX, r_m16},    DU_DU_U },
+#endif
+    {OpcodeInfo::all,   {0xF7, _7},         {EDX, EAX, r_m32},  DU_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _7},  {RDX, RAX, r_m64},  DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(IMUL, MF_AFFECTS_FLAGS, D_DU_U)
+BEGIN_OPCODES()
+    /*{OpcodeInfo::all,   {0xF6, _5},               {AH, AL,        r_m8},  D_DU_U },
+    {OpcodeInfo::all,     {Size16, 0xF7, _5},       {DX, AX,        r_m16}, D_DU_U },
+    */
+    //
+    {OpcodeInfo::all,     {0xF7, _5},               {EDX, EAX, r_m32},  D_DU_U },
+    //todo: this opcode's hash conflicts with IMUL r64,r_m64 - they're both 0.
+    // this particular is not currently used, so we may safely drop it, but need to
+    // revisit the hash implementation
+    // {OpcodeInfo::em64t,   {REX_W, 0xF7, _5},        {RDX, RAX, r_m64},  D_DU_U },
+    //
+    {OpcodeInfo::all,   {Size16, 0x0F, 0xAF, _r}, {r16,r_m16},        DU_U },
+    {OpcodeInfo::all,   {0x0F, 0xAF, _r},         {r32,r_m32},        DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0x0F, 0xAF, _r},  {r64,r_m64},        DU_U },
+    {OpcodeInfo::all,   {Size16, 0x6B, _r, ib},   {r16,r_m16,imm8s},  D_DU_U },
+    {OpcodeInfo::all,   {0x6B, _r, ib},           {r32,r_m32,imm8s},  D_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0x6B, _r, ib},    {r64,r_m64,imm8s},  D_DU_U },
+    {OpcodeInfo::all,   {Size16, 0x6B, _r, ib},   {r16,imm8s},        DU_U },
+    {OpcodeInfo::all,   {0x6B, _r, ib},           {r32,imm8s},        DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0x6B, _r, ib},    {r64,imm8s},        DU_U },
+    {OpcodeInfo::all,   {Size16, 0x69, _r, iw},   {r16,r_m16,imm16},  D_U_U },
+    {OpcodeInfo::all,   {0x69, _r, id},           {r32,r_m32,imm32},  D_U_U },
+    {OpcodeInfo::em64t, {REX_W, 0x69, _r, id},    {r64,r_m64,imm32s}, D_U_U },
+    {OpcodeInfo::all,   {Size16, 0x69, _r, iw},   {r16,imm16},        DU_U },
+    {OpcodeInfo::all,   {0x69, _r, id},           {r32,imm32},        DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MUL, MF_AFFECTS_FLAGS, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF6, _4},           {AX, AL, r_m8},     D_DU_U },
+    {OpcodeInfo::all,   {Size16, 0xF7, _4},   {DX, AX, r_m16},    D_DU_U },
+    {OpcodeInfo::all,   {0xF7, _4},           {EDX, EAX, r_m32},  D_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _4},    {RDX, RAX, r_m64},  D_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(INC, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFE, _0},           {r_m8},         DU },
+    {OpcodeInfo::all,   {Size16, 0xFF, _0},   {r_m16},        DU },
+    {OpcodeInfo::all,   {0xFF, _0},           {r_m32},        DU },
+    {OpcodeInfo::em64t, {REX_W, 0xFF, _0},    {r_m64},        DU },
+    {OpcodeInfo::ia32,  {Size16, 0x40|rw},    {r16},          DU },
+    {OpcodeInfo::ia32,  {0x40|rd},            {r32},          DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(INT3, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xCC},     {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+#define DEFINE_Jcc_MNEMONIC( cc ) \
+        BEGIN_MNEMONIC(J##cc, MF_USES_FLAGS|MF_CONDITIONAL, U ) \
+BEGIN_OPCODES() \
+    {OpcodeInfo::all,   {0x70 + ConditionMnemonic_##cc, cb },           { rel8 },       U }, \
+    {OpcodeInfo::ia32,  {Size16, 0x0F, 0x80 + ConditionMnemonic_##cc, cw},      { rel16 },      U }, \
+    {OpcodeInfo::all,   {0x0F, 0x80 + ConditionMnemonic_##cc, cd},      { rel32 },      U }, \
+END_OPCODES() \
+END_MNEMONIC()
+
+
+DEFINE_Jcc_MNEMONIC(O)
+DEFINE_Jcc_MNEMONIC(NO)
+DEFINE_Jcc_MNEMONIC(B)
+DEFINE_Jcc_MNEMONIC(NB)
+DEFINE_Jcc_MNEMONIC(Z)
+DEFINE_Jcc_MNEMONIC(NZ)
+DEFINE_Jcc_MNEMONIC(BE)
+DEFINE_Jcc_MNEMONIC(NBE)
+
+DEFINE_Jcc_MNEMONIC(S)
+DEFINE_Jcc_MNEMONIC(NS)
+DEFINE_Jcc_MNEMONIC(P)
+DEFINE_Jcc_MNEMONIC(NP)
+DEFINE_Jcc_MNEMONIC(L)
+DEFINE_Jcc_MNEMONIC(NL)
+DEFINE_Jcc_MNEMONIC(LE)
+DEFINE_Jcc_MNEMONIC(NLE)
+
+#undef DEFINE_Jcc_MNEMONIC
+
+BEGIN_MNEMONIC(JMP, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xEB, cb},         {rel8},     U },
+    {OpcodeInfo::ia32,  {Size16, 0xE9, cw}, {rel16},    U },
+    {OpcodeInfo::all,   {0xE9, cd},         {rel32},    U },
+    {OpcodeInfo::ia32,  {Size16, 0xFF, _4}, {r_m16},    U },
+    {OpcodeInfo::ia32,  {0xFF, _4},         {r_m32},    U },
+    {OpcodeInfo::em64t, {0xFF, _4},         {r_m64},    U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LEA, MF_NONE, D_U )
+BEGIN_OPCODES()
+    /*
+    A special case: the LEA instruction itself does not care about size of
+    second operand. This is obviuos why it is, and thus in The Manual, a
+    simple 'm' without size is used.
+    However, in the Jitrino's instrucitons we'll have an operand with a size.
+    Also, the hashing scheme is not supposed to handle OpndSize_Null, and
+    making it to do so will lead to unnecessary complication of hashing
+    scheme. Thus, instead of handling it as a special case, we simply make
+    copies of the opcodes with sizes set.
+        {OpcodeInfo::all,     {0x8D, _r},             {r32, m},       D_U },
+        {OpcodeInfo::em64t, {0x8D, _r},               {r64, m},       D_U },
+    */
+    //Android x86: keep r32, m32 only, otherwise, will have decoding error
+    //{OpcodeInfo::all,   {0x8D, _r},     {r32, m8},      D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m8},      D_U },
+    //{OpcodeInfo::all,   {0x8D, _r},     {r32, m16},     D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m16},     D_U },
+    {OpcodeInfo::all,   {0x8D, _r},     {r32, m32},     D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m32},     D_U },
+    {OpcodeInfo::all,   {0x8D, _r},     {r32, m64},     D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m64},     D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LOOP, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE2, cb},     {ECX, rel8},    DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LOOPE, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE1, cb},     {ECX, rel8},    DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LOOPNE, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE0, cb},     {ECX, rel8},    DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOV, MF_NONE, D_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x88, _r},         {r_m8,r8},      D_U },
+
+    {OpcodeInfo::all,   {Size16, 0x89, _r}, {r_m16,r16},    D_U },
+    {OpcodeInfo::all,   {0x89, _r},         {r_m32,r32},    D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x89, _r},  {r_m64,r64},    D_U },
+    {OpcodeInfo::all,   {0x8A, _r},         {r8,r_m8},      D_U },
+
+    {OpcodeInfo::all,   {Size16, 0x8B, _r}, {r16,r_m16},    D_U },
+    {OpcodeInfo::all,   {0x8B, _r},         {r32,r_m32},    D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8B, _r},  {r64,r_m64},    D_U },
+
+    {OpcodeInfo::all,   {0xB0|rb},          {r8,imm8},      D_U },
+
+    {OpcodeInfo::all,   {Size16, 0xB8|rw},  {r16,imm16},    D_U },
+    {OpcodeInfo::all,   {0xB8|rd},          {r32,imm32},    D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xB8|rd},   {r64,imm64},    D_U },
+    {OpcodeInfo::all,   {0xC6, _0},         {r_m8,imm8},    D_U },
+
+    {OpcodeInfo::all,   {Size16, 0xC7, _0}, {r_m16,imm16},  D_U },
+    {OpcodeInfo::all,   {0xC7, _0},         {r_m32,imm32},  D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xC7, _0},  {r_m64,imm32s}, D_U },
+
+    {OpcodeInfo::decoder,   {0xA0},         {AL,  moff8},  D_U },
+    {OpcodeInfo::decoder,   {Size16, 0xA1}, {AX,  moff16},  D_U },
+    {OpcodeInfo::decoder,   {0xA1},         {EAX, moff32},  D_U },
+    //{OpcodeInfo::decoder64,   {REX_W, 0xA1},  {RAX, moff64},  D_U },
+
+    {OpcodeInfo::decoder,   {0xA2},         {moff8, AL},  D_U },
+    {OpcodeInfo::decoder,   {Size16, 0xA3}, {moff16, AX},  D_U },
+    {OpcodeInfo::decoder,   {0xA3},         {moff32, EAX},  D_U },
+    //{OpcodeInfo::decoder64,   {REX_W, 0xA3},  {moff64, RAX},  D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+
+BEGIN_MNEMONIC(XCHG, MF_NONE, DU_DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x87, _r},   {r_m32,r32},    DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0x6F, _r},   {mm64, mm_m64}, D_U },
+    {OpcodeInfo::all,   {0x0F, 0x7F, _r},   {mm_m64, mm64}, D_U },
+#endif
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x7E },  {xmm64, xmm_m64},       D_U },
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xD6 },  {xmm_m64, xmm64},       D_U },
+//    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x6E, _r},  {xmm64, r_m64}, D_U },
+//    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x7E, _r},  {r_m64, xmm64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x6E, _r},  {xmm64, r64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x7E, _r},  {r64, xmm64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x6E, _r}, {xmm32, r_m32}, D_U },
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x7E, _r}, {r_m32, xmm32}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// A bunch of MMX instructions
+//
+#ifdef _HAVE_MMX_
+
+BEGIN_MNEMONIC(EMMS, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x77},       {},             N },
+END_OPCODES()
+END_MNEMONIC()
+
+#endif
+
+BEGIN_MNEMONIC(PADDQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xD4, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xD4, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PAND, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xDB, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xDB, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(POR, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xEB, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xEB, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PSUBQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xFB, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xFB, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PANDN, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xDF, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xDF, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+BEGIN_MNEMONIC(PSLLQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xF3, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xF3, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+BEGIN_MNEMONIC(PSRLQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xD3, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xD3, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PXOR, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xEF, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xEF, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVAPD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x28, _r},   {xmm64, xmm_m64},   D_U },
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x29, _r},   {xmm_m64, xmm64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVSD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF2, 0x0F, 0x10, _r},   {xmm64, xmm_m64},   D_U },
+    {OpcodeInfo::all, {0xF2, 0x0F, 0x11, _r},   {xmm_m64, xmm64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVSS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF3, 0x0F, 0x10, _r},   {xmm32, xmm_m32}, D_U },
+    {OpcodeInfo::all, {0xF3, 0x0F, 0x11, _r},   {xmm_m32, xmm32}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVSX, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,       {Size16, 0x0F, 0xBE, _r}, {r16, r_m8s},     D_U },
+    {OpcodeInfo::all,       {0x0F, 0xBE, _r},         {r32, r_m8s},     D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xBE, _r},  {r64, r_m8s},     D_U },
+
+    {OpcodeInfo::all,       {0x0F, 0xBF, _r},         {r32, r_m16s},    D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xBF, _r},  {r64, r_m16s},    D_U },
+
+    {OpcodeInfo::em64t,     {REX_W, 0x63, _r},        {r64, r_m32s},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVZX, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,       {Size16, 0x0F, 0xB6, _r}, {r16, r_m8u},     D_U },
+    {OpcodeInfo::all,       {0x0F, 0xB6, _r},         {r32, r_m8u},     D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xB6, _r},  {r64, r_m8u},     D_U },
+
+    {OpcodeInfo::all,       {0x0F, 0xB7, _r},         {r32, r_m16u},    D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xB7, _r},  {r64, r_m16u},    D_U },
+    //workaround to get r/rm32->r64 ZX mov functionality:
+    //simple 32bit reg copying zeros high bits in 64bit reg
+    {OpcodeInfo::em64t,     {0x8B, _r},               {r64, r_m32u},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MULSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x59, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MULSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x59, _r}, {xmm32, xmm_m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(NEG, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF6, _3},         {r_m8},         DU },
+
+    {OpcodeInfo::all,   {Size16, 0xF7, _3}, {r_m16},        DU },
+    {OpcodeInfo::all,   {0xF7, _3},         {r_m32},        DU },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _3},  {r_m64},        DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(NOP, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x90}, {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(NOT, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF6, _2},           {r_m8},         DU },
+    {OpcodeInfo::all,   {Size16, 0xF7, _2},   {r_m16},        DU },
+    {OpcodeInfo::all,   {0xF7, _2},           {r_m32},        DU },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _2},    {r_m64},        DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(POP, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {Size16, 0x8F, _0}, {r_m16},    D },
+    {OpcodeInfo::ia32,  {0x8F, _0},         {r_m32},    D },
+    {OpcodeInfo::em64t, {0x8F, _0},         {r_m64},    D },
+
+    {OpcodeInfo::all,   {Size16, 0x58|rw }, {r16},      D },
+    {OpcodeInfo::ia32,  {0x58|rd },         {r32},      D },
+    {OpcodeInfo::em64t, {0x58|rd },         {r64},      D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(POPFD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9D},     {},         N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PREFETCH, MF_NONE, U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x18, _0},   {m8},         U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PUSH, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {Size16, 0xFF, _6}, {r_m16},    U },
+    {OpcodeInfo::ia32,  {0xFF, _6},         {r_m32},    U },
+    {OpcodeInfo::em64t, {0xFF, _6},         {r_m64},    U },
+
+    {OpcodeInfo::all,   {Size16, 0x50|rw }, {r16},      U },
+    {OpcodeInfo::ia32,  {0x50|rd },         {r32},      U },
+    {OpcodeInfo::em64t, {0x50|rd },         {r64},      U },
+
+    {OpcodeInfo::all,   {0x6A},         {imm8},     U },
+    {OpcodeInfo::all,   {Size16, 0x68}, {imm16},    U },
+    {OpcodeInfo::ia32,  {0x68},         {imm32},    U },
+//          {OpcodeInfo::em64t,   {0x68},   {imm64},    U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PUSHFD, MF_USES_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9C},             {},        N },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(RET, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xC3},       {},         N },
+    {OpcodeInfo::all,   {0xC2, iw},   {imm16},    U },
+END_OPCODES()
+END_MNEMONIC()
+
+#define DEFINE_SETcc_MNEMONIC( cc ) \
+        BEGIN_MNEMONIC(SET##cc, MF_USES_FLAGS|MF_CONDITIONAL, DU) \
+BEGIN_OPCODES() \
+    {OpcodeInfo::all,   {0x0F,     0x90 + ConditionMnemonic_##cc}, {r_m8},  DU }, \
+END_OPCODES() \
+END_MNEMONIC()
+
+DEFINE_SETcc_MNEMONIC(O)
+DEFINE_SETcc_MNEMONIC(NO)
+DEFINE_SETcc_MNEMONIC(B)
+DEFINE_SETcc_MNEMONIC(NB)
+DEFINE_SETcc_MNEMONIC(Z)
+DEFINE_SETcc_MNEMONIC(NZ)
+DEFINE_SETcc_MNEMONIC(BE)
+DEFINE_SETcc_MNEMONIC(NBE)
+
+DEFINE_SETcc_MNEMONIC(S)
+DEFINE_SETcc_MNEMONIC(NS)
+DEFINE_SETcc_MNEMONIC(P)
+DEFINE_SETcc_MNEMONIC(NP)
+DEFINE_SETcc_MNEMONIC(L)
+DEFINE_SETcc_MNEMONIC(NL)
+DEFINE_SETcc_MNEMONIC(LE)
+DEFINE_SETcc_MNEMONIC(NLE)
+
+#undef DEFINE_SETcc_MNEMONIC
+
+#define DEFINE_SHIFT_MNEMONIC(nam, slash_num, flags) \
+BEGIN_MNEMONIC(nam, flags, DU_U) \
+BEGIN_OPCODES()\
+    /* D0 & D1 opcodes are added w/o 2nd operand (1) because */\
+    /* they are used for decoding only so only instruction length is needed */\
+    {OpcodeInfo::decoder,   {0xD0, slash_num},            {r_m8/*,const_1*/},   DU },\
+    {OpcodeInfo::all,       {0xD2, slash_num},              {r_m8,  CL},        DU_U },\
+    {OpcodeInfo::all,       {0xC0, slash_num, ib},          {r_m8,  imm8},      DU_U },\
+\
+    {OpcodeInfo::decoder,   {Size16, 0xD1, slash_num},    {r_m16/*,const_1*/},  DU },\
+    {OpcodeInfo::all,       {Size16, 0xD3, slash_num},      {r_m16, CL},        DU_U },\
+    {OpcodeInfo::all,       {Size16, 0xC1, slash_num, ib},  {r_m16, imm8 },     DU_U },\
+\
+    {OpcodeInfo::decoder,   {0xD1, slash_num},              {r_m32/*,const_1*/}, DU },\
+    {OpcodeInfo::decoder64, {REX_W, 0xD1, slash_num},       {r_m64/*,const_1*/}, DU },\
+\
+    {OpcodeInfo::all,       {0xD3, slash_num},              {r_m32, CL},        DU_U },\
+    {OpcodeInfo::em64t,     {REX_W, 0xD3, slash_num},       {r_m64, CL},        DU_U },\
+\
+    {OpcodeInfo::all,       {0xC1, slash_num, ib},          {r_m32, imm8},      DU_U },\
+    {OpcodeInfo::em64t,     {REX_W, 0xC1, slash_num, ib},   {r_m64, imm8},      DU_U },\
+END_OPCODES()\
+END_MNEMONIC()
+
+
+DEFINE_SHIFT_MNEMONIC(ROL, _0, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(ROR, _1, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(RCL, _2, MF_AFFECTS_FLAGS|MF_USES_FLAGS)
+DEFINE_SHIFT_MNEMONIC(RCR, _3, MF_AFFECTS_FLAGS|MF_USES_FLAGS)
+
+DEFINE_SHIFT_MNEMONIC(SAL, _4, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(SHR, _5, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(SAR, _7, MF_AFFECTS_FLAGS)
+
+#undef DEFINE_SHIFT_MNEMONIC
+
+BEGIN_MNEMONIC(SHLD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xA5},   {r_m32, r32, CL}, DU_DU_U },
+    {OpcodeInfo::all,   {0x0F, 0xA4},   {r_m32, r32, imm8}, DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SHRD, MF_AFFECTS_FLAGS, N)
+// TODO: the def/use info is wrong
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xAD},   {r_m32, r32, CL}, DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(SUBSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x5C, _r}, {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SUBSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x5C, _r}, {xmm32, xmm_m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(TEST, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+
+    {OpcodeInfo::decoder,   {0xA8, ib},             { AL, imm8},    U_U },
+    {OpcodeInfo::decoder,   {0xA9, iw},             { AX, imm16},   U_U },
+    {OpcodeInfo::decoder,   {0xA9, id},             { EAX, imm32},  U_U },
+    {OpcodeInfo::decoder64, {REX_W, 0xA9, id},      { RAX, imm32s}, U_U },
+
+    {OpcodeInfo::all,       {0xF6, _0, ib},         {r_m8,imm8},    U_U },
+
+    {OpcodeInfo::all,       {Size16, 0xF7, _0, iw}, {r_m16,imm16},  U_U },
+    {OpcodeInfo::all,       {0xF7, _0, id},         {r_m32,imm32},  U_U },
+    {OpcodeInfo::em64t,     {REX_W, 0xF7, _0, id},  {r_m64,imm32s}, U_U },
+
+    {OpcodeInfo::all,       {0x84, _r},             {r_m8,r8},      U_U },
+
+    {OpcodeInfo::all,       {Size16, 0x85, _r},     {r_m16,r16},    U_U },
+    {OpcodeInfo::all,       {0x85, _r},             {r_m32,r32},    U_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x85, _r},      {r_m64,r64},    U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(UCOMISD, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x2E, _r}, {xmm64, xmm_m64}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(UCOMISS, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x2E, _r},       {xmm32, xmm_m32}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(COMISD, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x2F, _r}, {xmm64, xmm_m64}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(COMISS, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x2F, _r},       {xmm32, xmm_m32}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(XORPD, MF_SAME_ARG_NO_USE|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x57, _r},   {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(XORPS, MF_SAME_ARG_NO_USE|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x0F, 0x57, _r},   {xmm32, xmm_m32},       DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTDQ2PD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0xE6}, {xmm64, xmm_m64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTDQ2PS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x0F, 0x5B, _r},   {xmm32, xmm_m32},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTTPD2DQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xE6}, {xmm64, xmm_m64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTTPS2DQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x5B, _r},   {xmm32, xmm_m32},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// String operations
+//
+BEGIN_MNEMONIC(STD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFD},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CLD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFC},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SCAS, MF_AFFECTS_FLAGS, N)
+// to be symmetric, this mnemonic must have either m32 or RegName_EAX
+// but as long, as Jitrino's CG does not use the mnemonic, leaving it
+// in its natural form
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xAF},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(STOS, MF_AFFECTS_FLAGS, DU_DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xAB},         {EDI, ECX, EAX},   DU_DU_U },
+    {OpcodeInfo::all,   {0xAA},         {EDI, ECX, AL},    DU_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0xAB},  {RDI, RCX, RAX},   DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+/*
+MOVS and CMPS are the special cases.
+Most the code in both CG and Encoder do not expect 2 memory operands.
+Also, they are not supposed to setup constrains on which register the
+memory reference must reside - m8,m8 or m32,m32 is not the choice.
+We can't use r8,r8 either - will have problem with 8bit EDI, ESI.
+So, as the workaround we do r32,r32 and specify size of the operand through
+the specific mnemonic - the same is in the codegen.
+*/
+BEGIN_MNEMONIC(MOVS8, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA4},         {r32,r32,ECX},    DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA4},         {r64,r64,RCX},    DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVS16, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {Size16, 0xA5}, {r32,r32,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {Size16, 0xA5}, {r64,r64,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVS32, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA5},         {r32,r32,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA5},         {r64,r64,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVS64, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::em64t, {REX_W,0xA5},   {r64,r64,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPSB, MF_AFFECTS_FLAGS, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA6},         {ESI,EDI,ECX},    DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA6},         {RSI,RDI,RCX},    DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPSW, MF_AFFECTS_FLAGS, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {Size16, 0xA7}, {ESI,EDI,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {Size16, 0xA7}, {RSI,RDI,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPSD, MF_AFFECTS_FLAGS, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA7},         {ESI,EDI,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA7},         {RSI,RDI,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(WAIT, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9B},         {},       N },
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// ~String operations
+//
+
+//
+//Note: the instructions below added for the sake of disassembling routine.
+// They need to have flags, params and params usage to be defined more precisely.
+//
+BEGIN_MNEMONIC(LEAVE, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::decoder,   {0xC9},         {},       N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(ENTER, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::decoder,   {0xC8, iw, ib},           {imm16, imm8},  N },
+END_OPCODES()
+END_MNEMONIC()
+
+};      // ~masterEncodingTable[]
+
+ENCODER_NAMESPACE_END
+
+//#include <algorithm>
+
+ENCODER_NAMESPACE_START
+
+static bool mnemonic_info_comparator(const MnemonicInfo& one,
+                                     const MnemonicInfo& two)
+{
+    return one.mn < two.mn;
+}
+
+
+static int compareMnemonicInfo(const void* info1, const void* info2)
+{
+    Mnemonic id1, id2;
+
+    id1 = ((const MnemonicInfo*) info1)->mn;
+    id2 = ((const MnemonicInfo*) info2)->mn;
+    if (id1 < id2)
+        return -1;
+    if (id1 > id2)
+        return 1;
+    return 0;
+}
+
+int EncoderBase::buildTable(void)
+{
+    // A check: all mnemonics must be covered
+    assert(COUNTOF(masterEncodingTable) == Mnemonic_Count);
+    // sort out the mnemonics so the list become ordered
+#if 0 //Android x86
+    std::sort(masterEncodingTable, masterEncodingTable+Mnemonic_Count,
+              mnemonic_info_comparator);
+#else
+    qsort(masterEncodingTable, Mnemonic_Count, sizeof(MnemonicInfo), compareMnemonicInfo);
+#endif
+    //
+    // clear the things
+    //
+    memset(opcodesHashMap, NOHASH, sizeof(opcodesHashMap));
+    memset(opcodes, 0, sizeof(opcodes));
+    //
+    // and, finally, build it
+    for (unsigned i=0; i<Mnemonic_Count; i++) {
+        assert((Mnemonic)i == (masterEncodingTable + i)->mn);
+        buildMnemonicDesc(masterEncodingTable+i);
+    }
+    return 0;
+}
+
+void EncoderBase::buildMnemonicDesc(const MnemonicInfo * minfo)
+{
+    MnemonicDesc& mdesc = mnemonics[minfo->mn];
+    mdesc.mn = minfo->mn;
+    mdesc.flags = minfo->flags;
+    mdesc.roles = minfo->roles;
+    mdesc.name = minfo->name;
+
+    //
+    // fill the used opcodes
+    //
+    for (unsigned i=0, oindex=0; i<COUNTOF(minfo->opcodes); i++) {
+
+        const OpcodeInfo& oinfo = minfo->opcodes[i];
+        OpcodeDesc& odesc = opcodes[minfo->mn][oindex];
+        // last opcode ?
+        if (oinfo.opcode[0] == OpcodeByteKind_LAST) {
+            // mark the opcode 'last', exit
+            odesc.opcode_len = 0;
+            odesc.last = 1;
+            break;
+        }
+        odesc.last = 0;
+#ifdef _EM64T_
+        if (oinfo.platf == OpcodeInfo::ia32) { continue; }
+        if (oinfo.platf == OpcodeInfo::decoder32) { continue; }
+#else
+        if (oinfo.platf == OpcodeInfo::em64t) { continue; }
+        if (oinfo.platf == OpcodeInfo::decoder64) { continue; }
+#endif
+        if (oinfo.platf == OpcodeInfo::decoder64 ||
+            oinfo.platf == OpcodeInfo::decoder32) {
+             odesc.platf = OpcodeInfo::decoder;
+        }
+        else {
+            odesc.platf = (char)oinfo.platf;
+        }
+        //
+        // fill out opcodes
+        //
+        unsigned j = 0;
+        odesc.opcode_len = 0;
+        for(; oinfo.opcode[j]; j++) {
+            unsigned opcod = oinfo.opcode[j];
+            unsigned kind = opcod&OpcodeByteKind_KindMask;
+            if (kind == OpcodeByteKind_REX_W) {
+                odesc.opcode[odesc.opcode_len++] = (unsigned char)0x48;
+                continue;
+            }
+            else if(kind != 0 && kind != OpcodeByteKind_ZeroOpcodeByte) {
+                break;
+            }
+            unsigned lowByte = (opcod & OpcodeByteKind_OpcodeMask);
+            odesc.opcode[odesc.opcode_len++] = (unsigned char)lowByte;
+        }
+        assert(odesc.opcode_len<5);
+        odesc.aux0 = odesc.aux1 = 0;
+        if (oinfo.opcode[j] != 0) {
+            odesc.aux0 = oinfo.opcode[j];
+            assert((odesc.aux0 & OpcodeByteKind_KindMask) != 0);
+            ++j;
+            if(oinfo.opcode[j] != 0) {
+                odesc.aux1 = oinfo.opcode[j];
+                assert((odesc.aux1 & OpcodeByteKind_KindMask) != 0);
+            }
+        }
+        else if (oinfo.roles.count>=2) {
+            if (((oinfo.opnds[0].kind&OpndKind_Mem) &&
+                 (isRegKind(oinfo.opnds[1].kind))) ||
+                ((oinfo.opnds[1].kind&OpndKind_Mem) &&
+                 (isRegKind(oinfo.opnds[0].kind)))) {
+                 // Example: MOVQ xmm1, xmm/m64 has only opcodes
+                 // same with SHRD
+                 // Adding fake /r
+                 odesc.aux0 = _r;
+            }
+        }
+        else if (oinfo.roles.count==1) {
+            if (oinfo.opnds[0].kind&OpndKind_Mem) {
+                 // Example: SETcc r/m8, adding fake /0
+                 odesc.aux0 = _0;
+            }
+        }
+        // check imm
+        if (oinfo.roles.count > 0 &&
+            (oinfo.opnds[0].kind == OpndKind_Imm ||
+            oinfo.opnds[oinfo.roles.count-1].kind == OpndKind_Imm)) {
+            // Example: CALL cd, PUSH imm32 - they fit both opnds[0] and
+            // opnds[oinfo.roles.count-1].
+            // The A3 opcode fits only opnds[0] - it's currently have
+            // MOV imm32, EAX. Looks ridiculous, but this is how the
+            // moffset is currently implemented. Will need to fix together
+            // with other usages of moff.
+            // adding fake /cd or fake /id
+            unsigned imm_opnd_index =
+                oinfo.opnds[0].kind == OpndKind_Imm ? 0 : oinfo.roles.count-1;
+            OpndSize sz = oinfo.opnds[imm_opnd_index].size;
+            unsigned imm_encode, coff_encode;
+            if (sz==OpndSize_8) {imm_encode = ib; coff_encode=cb; }
+            else if (sz==OpndSize_16) {imm_encode = iw; coff_encode=cw;}
+            else if (sz==OpndSize_32) {imm_encode = id; coff_encode=cd; }
+            else if (sz==OpndSize_64) {imm_encode = io; coff_encode=0xCC; }
+            else { assert(false); imm_encode=0xCC; coff_encode=0xCC; }
+            if (odesc.aux1 == 0) {
+                if (odesc.aux0==0) {
+                    odesc.aux0 = imm_encode;
+                }
+                else {
+                    if (odesc.aux0 != imm_encode && odesc.aux0 != coff_encode) {
+                        odesc.aux1 = imm_encode;
+                    }
+                }
+            }
+            else {
+                assert(odesc.aux1==imm_encode);
+            }
+
+        }
+
+        assert(sizeof(odesc.opnds) == sizeof(oinfo.opnds));
+        memcpy(odesc.opnds, oinfo.opnds, sizeof(odesc.opnds));
+        odesc.roles = oinfo.roles;
+        odesc.first_opnd = 0;
+        if (odesc.opnds[0].reg != RegName_Null) {
+            ++odesc.first_opnd;
+            if (odesc.opnds[1].reg != RegName_Null) {
+                ++odesc.first_opnd;
+            }
+        }
+
+        if (odesc.platf == OpcodeInfo::decoder) {
+            // if the opcode is only for decoding info, then do not hash it.
+            ++oindex;
+            continue;
+        }
+
+        //
+        // check whether the operand info is a mask (i.e. r_m*).
+        // in this case, split the info to have separate entries for 'r'
+        // and for 'm'.
+        // the good news is that there can be only one such operand.
+        //
+        int opnd2split = -1;
+        for (unsigned k=0; k<oinfo.roles.count; k++) {
+            if ((oinfo.opnds[k].kind & OpndKind_Mem) &&
+                (OpndKind_Mem != oinfo.opnds[k].kind)) {
+                opnd2split = k;
+                break;
+            }
+        };
+
+        if (opnd2split == -1) {
+            // not a mask, hash it, store it, continue.
+            unsigned short hash = getHash(&oinfo);
+            opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex;
+            ++oindex;
+            continue;
+        };
+
+        OpcodeInfo storeItem = oinfo;
+        unsigned short hash;
+
+        // remove the memory part of the mask, and store only 'r' part
+        storeItem.opnds[opnd2split].kind = (OpndKind)(storeItem.opnds[opnd2split].kind & ~OpndKind_Mem);
+        hash = getHash(&storeItem);
+        if (opcodesHashMap[minfo->mn][hash] == NOHASH) {
+            opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex;
+        }
+        // else {
+        // do not overwrite if there is something there, just check that operands match
+        // the reason is that for some instructions there are several possibilities:
+        // say 'DEC r' may be encode as either '48+r' or 'FF /1', and I believe
+        // the first one is better for 'dec r'.
+        // as we're currently processing an opcode with memory part in operand,
+        // leave already filled items intact, so if there is 'OP reg' there, this
+        // better choice will be left in the table instead of 'OP r_m'
+        // }
+
+        // compute hash of memory-based operand, 'm' part in 'r_m'
+        storeItem.opnds[opnd2split].kind = OpndKind_Mem;
+        hash = getHash(&storeItem);
+        // should not happen: for the r_m opcodes, there is a possibility
+        // that hash value of 'r' part intersects with 'OP r' value, but it's
+        // impossible for 'm' part.
+        assert(opcodesHashMap[minfo->mn][hash] == NOHASH);
+        opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex;
+
+        ++oindex;
+    }
+}
+
+ENCODER_NAMESPACE_END
diff --git a/vm/compiler/codegen/x86/libenc/enc_wrapper.cpp b/vm/compiler/codegen/x86/libenc/enc_wrapper.cpp
new file mode 100644
index 0000000..cf53cea
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_wrapper.cpp
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include "enc_base.h"
+#include "enc_wrapper.h"
+#include "dec_base.h"
+#include "utils/Log.h"
+
+//#define PRINT_ENCODER_STREAM
+bool dump_x86_inst = false;
+//map_reg
+const RegName map_of_regno_2_regname[] = {
+    RegName_EAX,    RegName_EBX,    RegName_ECX,    RegName_EDX,
+    RegName_EDI,    RegName_ESI,    RegName_ESP,    RegName_EBP,
+    RegName_XMM0,   RegName_XMM1,   RegName_XMM2,   RegName_XMM3,
+    RegName_XMM4,   RegName_XMM5,   RegName_XMM6,   RegName_XMM7,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   //SCRATCH
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null
+};
+
+//getRegSize, getAliasReg:
+//OpndSize, RegName, OpndExt: enum enc_defs.h
+inline void add_r(EncoderBase::Operands & args, int physicalReg, OpndSize sz, OpndExt ext = OpndExt_None) {
+    RegName reg = map_of_regno_2_regname[physicalReg];
+    if (sz != getRegSize(reg)) {
+       reg = getAliasReg(reg, sz);
+    }
+    args.add(EncoderBase::Operand(reg, ext));
+}
+inline void add_m(EncoderBase::Operands & args, int baseReg, int disp, OpndSize sz, OpndExt ext = OpndExt_None) {
+    args.add(EncoderBase::Operand(sz,
+                                  map_of_regno_2_regname[baseReg],
+                                  RegName_Null, 0,
+                                  disp, ext));
+}
+inline void add_m_scale(EncoderBase::Operands & args, int baseReg, int indexReg, int scale,
+                        OpndSize sz, OpndExt ext = OpndExt_None) {
+    args.add(EncoderBase::Operand(sz,
+                                  map_of_regno_2_regname[baseReg],
+                                  map_of_regno_2_regname[indexReg], scale,
+                                  0, ext));
+}
+inline void add_m_disp_scale(EncoderBase::Operands & args, int baseReg, int disp, int indexReg, int scale,
+                        OpndSize sz, OpndExt ext = OpndExt_None) {
+    args.add(EncoderBase::Operand(sz,
+                                  map_of_regno_2_regname[baseReg],
+                                  map_of_regno_2_regname[indexReg], scale,
+                                  disp, ext));
+}
+
+inline void add_fp(EncoderBase::Operands & args, unsigned i, bool dbl) {
+    return args.add((RegName)( (dbl ? RegName_FP0D : RegName_FP0S) + i));
+}
+inline void add_imm(EncoderBase::Operands & args, OpndSize sz, int value, bool is_signed) {
+    //assert(n_size != imm.get_size());
+    args.add(EncoderBase::Operand(sz, value,
+             is_signed ? OpndExt_Signed : OpndExt_Zero));
+}
+
+#define MAX_DECODED_STRING_LEN 1024
+char tmpBuffer[MAX_DECODED_STRING_LEN];
+
+void printOperand(const EncoderBase::Operand & opnd) {
+    unsigned int sz;
+    if(!dump_x86_inst) return;
+    sz = strlen(tmpBuffer);
+    if(opnd.size() != OpndSize_32) {
+        sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s ",
+                       getOpndSizeString(opnd.size()));
+    }
+    if(opnd.is_mem()) {
+        if(opnd.scale() != 0) {
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz,
+                           "%d(%s,%s,%d)", opnd.disp(),
+                           getRegNameString(opnd.base()),
+                           getRegNameString(opnd.index()), opnd.scale());
+        } else {
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%d(%s)",
+                           opnd.disp(), getRegNameString(opnd.base()));
+        }
+    }
+    if(opnd.is_imm()) {
+        sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "#%x",
+                       (int)opnd.imm());
+    }
+    if(opnd.is_reg()) {
+        sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s",
+                       getRegNameString(opnd.reg()));
+    }
+}
+//TODO: the order of operands
+//to make the printout have the same order as assembly in .S
+//I reverse the order here
+void printDecoderInst(Inst & decInst) {
+    unsigned int sz;
+    if(!dump_x86_inst) return;
+    sz = strlen(tmpBuffer);
+    sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s ",
+                   EncoderBase::toStr(decInst.mn));
+    for(unsigned int k = 0; k < decInst.argc; k++) {
+        if(k > 0) {
+            sz = strlen(tmpBuffer);
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", ");
+        }
+        printOperand(decInst.operands[decInst.argc-1-k]);
+    }
+    ALOGE("%s", tmpBuffer);
+}
+void printOperands(EncoderBase::Operands& opnds) {
+    unsigned int sz;
+    if(!dump_x86_inst) return;
+    for(unsigned int k = 0; k < opnds.count(); k++) {
+        if(k > 0) {
+            sz = strlen(tmpBuffer);
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", ");
+        }
+        printOperand(opnds[opnds.count()-1-k]);
+    }
+}
+void printEncoderInst(Mnemonic m, EncoderBase::Operands& opnds) {
+    if(!dump_x86_inst) return;
+    snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- ENC %s ",
+             EncoderBase::toStr(m));
+    printOperands(opnds);
+    ALOGE("%s", tmpBuffer);
+}
+int decodeThenPrint(char* stream_start) {
+    if(!dump_x86_inst) return 0;
+    snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- INST @ %p: ",
+             stream_start);
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream_start, &decInst);
+    printDecoderInst(decInst);
+    return numBytes;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm(Mnemonic m, OpndSize size, int imm, char * stream) {
+    EncoderBase::Operands args;
+    //assert(imm.get_size() == size_32);
+    add_imm(args, size, imm, true/*is_signed*/);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT unsigned encoder_get_inst_size(char * stream) {
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    return numBytes;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT unsigned encoder_get_cur_operand_offset(int opnd_id)
+{
+    return (unsigned)EncoderBase::getOpndLocation(opnd_id);
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm(int imm, char * stream) {
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    EncoderBase::Operands args;
+    //assert(imm.get_size() == size_32);
+    add_imm(args, decInst.operands[0].size(), imm, true/*is_signed*/);
+    char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(decInst.mn, args);
+    decodeThenPrint(stream);
+#endif
+    return stream_next;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem(Mnemonic m, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    if(m == Mnemonic_IDIV || m == Mnemonic_MUL || m == Mnemonic_IMUL) {
+      add_r(args, 0/*eax*/, size);
+      add_r(args, 3/*edx*/, size);
+    }
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+//both operands have same size
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_reg(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type, char * stream) {
+    if((m == Mnemonic_MOV || m == Mnemonic_MOVQ) && reg == reg2) return stream;
+    EncoderBase::Operands args;
+    add_r(args, reg2, size); //destination
+    if(m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL || m == Mnemonic_SAR)
+      add_r(args, reg, OpndSize_8);
+    else
+      add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_reg(Mnemonic m, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size);
+    add_m_scale(args, base_reg, index_reg, scale, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_m_scale(args, base_reg, index_reg, scale, size);
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size);
+    add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_movzs_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, OpndSize_32);
+    add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char* encoder_reg_mem_disp_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char* stream) {
+    EncoderBase::Operands args;
+    add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_reg(Mnemonic m, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size); //dst
+    if(m == Mnemonic_IMUL) add_r(args, reg, size); //src CHECK
+    if(m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL
+       || m == Mnemonic_SAR || m == Mnemonic_ROR)  //fix for shift opcodes
+      add_imm(args, OpndSize_8, imm, true/*is_signed*/);
+    else
+      add_imm(args, size, imm, true/*is_signed*/);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm_rm(int imm, char * stream) {
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    EncoderBase::Operands args;
+    args.add(decInst.operands[0]);
+    add_imm(args, decInst.operands[1].size(), imm, true/*is_signed*/);
+    char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(decInst.mn, args);
+    decodeThenPrint(stream);
+#endif
+    return stream_next;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_mem(Mnemonic m, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    if (m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL
+        || m == Mnemonic_SAR || m == Mnemonic_ROR)
+        size = OpndSize_8;
+    add_imm(args, size, imm, true);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    // a fake FP register as operand
+    add_fp(args, reg, size == OpndSize_64/*is_double*/);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  int reg, char * stream) {
+    EncoderBase::Operands args;
+    // a fake FP register as operand
+    add_fp(args, reg, size == OpndSize_64/*is_double*/);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_return(char * stream) {
+    EncoderBase::Operands args;
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_RET, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_RET, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_compare_fp_stack(bool pop, int reg, bool isDouble, char * stream) {
+    //Mnemonic m = pop ? Mnemonic_FUCOMP : Mnemonic_FUCOM;
+    Mnemonic m = pop ? Mnemonic_FUCOMIP : Mnemonic_FUCOMI;
+    //a single operand or 2 operands?
+    //FST ST(i) has a single operand in encoder.inl?
+    EncoderBase::Operands args;
+    add_fp(args, reg, isDouble);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, OpndSize_32);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVZX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, OpndSize_32);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVSX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical, int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg2, OpndSize_32); //destination
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVZX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg2, OpndSize_32); //destination
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVSX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+// Disassemble the operand "opnd" and put the readable format in "strbuf"
+// up to a string length of "len".
+unsigned int DisassembleOperandToBuf(const EncoderBase::Operand& opnd, char* strbuf, unsigned int len)
+{
+    unsigned int sz = 0;
+    if(opnd.size() != OpndSize_32) {
+        sz += snprintf(&strbuf[sz], len-sz, "%s ",
+                       getOpndSizeString(opnd.size()));
+    }
+    if(opnd.is_mem()) {
+        if(opnd.scale() != 0) {
+            sz += snprintf(&strbuf[sz], len-sz, "%d(%s,%s,%d)", opnd.disp(),
+                           getRegNameString(opnd.base()),
+                           getRegNameString(opnd.index()), opnd.scale());
+        } else {
+            sz += snprintf(&strbuf[sz], len-sz, "%d(%s)",
+                           opnd.disp(), getRegNameString(opnd.base()));
+        }
+    } else if(opnd.is_imm()) {
+        sz += snprintf(&strbuf[sz], len-sz, "#%x", (int)opnd.imm());
+    } else if(opnd.is_reg()) {
+        sz += snprintf(&strbuf[sz], len-sz, "%s",
+                       getRegNameString(opnd.reg()));
+    }
+    return sz;
+}
+
+// Disassemble the instruction "decInst" and put the readable format
+// in "strbuf" up to a string length of "len".
+void DisassembleInstToBuf(Inst& decInst, char* strbuf, unsigned int len)
+{
+    unsigned int sz = 0;
+    int k;
+    sz += snprintf(&strbuf[sz], len-sz, "%s ", EncoderBase::toStr(decInst.mn));
+    if (decInst.argc > 0) {
+        sz += DisassembleOperandToBuf(decInst.operands[decInst.argc-1],
+                                 &strbuf[sz], len-sz);
+        for(k = decInst.argc-2; k >= 0; k--) {
+            sz += snprintf(&strbuf[sz], len-sz, ", ");
+            sz += DisassembleOperandToBuf(decInst.operands[k], &strbuf[sz], len-sz);
+        }
+    }
+}
+
+// Disassmble the x86 instruction pointed to by code pointer "stream."
+// Put the disassemble text in the "strbuf" up to string length "len".
+// Return the code pointer after the disassemble x86 instruction.
+extern "C" ENCODER_DECLARE_EXPORT
+char* decoder_disassemble_instr(char* stream, char* strbuf, unsigned int len)
+{
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    DisassembleInstToBuf(decInst, strbuf, len);
+    return (stream + numBytes);
+}
diff --git a/vm/compiler/codegen/x86/libenc/enc_wrapper.h b/vm/compiler/codegen/x86/libenc/enc_wrapper.h
new file mode 100644
index 0000000..a2a223e
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_wrapper.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VM_ENC_WRAPPER_H_
+#define _VM_ENC_WRAPPER_H_
+
+#include "enc_defs_ext.h"
+
+extern bool dump_x86_inst;
+typedef enum PhysicalReg {
+  PhysicalReg_EAX = 0, PhysicalReg_EBX, PhysicalReg_ECX, PhysicalReg_EDX,
+  PhysicalReg_EDI, PhysicalReg_ESI, PhysicalReg_ESP, PhysicalReg_EBP,
+  PhysicalReg_XMM0, PhysicalReg_XMM1, PhysicalReg_XMM2, PhysicalReg_XMM3,
+  PhysicalReg_XMM4, PhysicalReg_XMM5, PhysicalReg_XMM6, PhysicalReg_XMM7,
+  PhysicalReg_ST0,  PhysicalReg_ST1, PhysicalReg_ST2,  PhysicalReg_ST3,
+  PhysicalReg_ST4, PhysicalReg_ST5, PhysicalReg_ST6, PhysicalReg_ST7,
+  PhysicalReg_Null,
+  //used as scratch logical register in NCG O1
+  //should not overlap with regular logical register, start from 100
+  PhysicalReg_SCRATCH_1 = 100, PhysicalReg_SCRATCH_2, PhysicalReg_SCRATCH_3, PhysicalReg_SCRATCH_4,
+  PhysicalReg_SCRATCH_5, PhysicalReg_SCRATCH_6, PhysicalReg_SCRATCH_7, PhysicalReg_SCRATCH_8,
+  PhysicalReg_SCRATCH_9, PhysicalReg_SCRATCH_10,
+  PhysicalReg_GLUE_DVMDEX = 900,
+  PhysicalReg_GLUE = 901
+} PhysicalReg;
+
+typedef enum Reg_No {
+#ifdef _EM64T_
+    rax_reg = 0,rbx_reg,    rcx_reg,    rdx_reg,
+    rdi_reg,    rsi_reg,    rsp_reg,    rbp_reg,
+    r8_reg,     r9_reg,     r10_reg,    r11_reg,
+    r12_reg,    r13_reg,    r14_reg,    r15_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    xmm8_reg,   xmm9_reg,   xmm10_reg,  xmm11_reg,
+    xmm12_reg,  xmm13_reg,  xmm14_reg,  xmm15_reg,
+
+#else   // !defined(_EM64T_)
+
+    eax_reg = 0,ebx_reg,    ecx_reg,    edx_reg,
+    edi_reg,    esi_reg,    esp_reg,    ebp_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    fs_reg,
+#endif
+    /** @brief Total number of registers.*/
+    n_reg
+} Reg_No;
+//
+// instruction operand sizes: 8,16,32,64 bits
+//
+typedef enum Opnd_Size {
+    size_8 = 0,
+    size_16,
+    size_32,
+    size_64,
+    n_size,
+#ifdef _EM64T_
+    size_platf = size_64
+#else
+    size_platf = size_32
+#endif
+} Opnd_Size;
+
+//
+// opcodes for alu instructions
+//
+typedef enum ALU_Opcode {
+    add_opc = 0,or_opc,     adc_opc,    sbb_opc,
+    and_opc,    sub_opc,    xor_opc,    cmp_opc,
+    mul_opc,    imul_opc,   div_opc,    idiv_opc,
+    sll_opc,    srl_opc,    sra_opc, //shift right arithmetic
+    shl_opc,    shr_opc,
+    sal_opc,    sar_opc,
+    neg_opc,    not_opc,    andn_opc,
+    n_alu
+} ALU_Opcode;
+
+typedef enum ConditionCode {
+    Condition_O     = 0,
+    Condition_NO    = 1,
+    Condition_B     = 2,
+    Condition_NAE   = Condition_B,
+    Condition_C     = Condition_B,
+    Condition_NB    = 3,
+    Condition_AE    = Condition_NB,
+    Condition_NC    = Condition_NB,
+    Condition_Z     = 4,
+    Condition_E     = Condition_Z,
+    Condition_NZ    = 5,
+    Condition_NE    = Condition_NZ,
+    Condition_BE    = 6,
+    Condition_NA    = Condition_BE,
+    Condition_NBE   = 7,
+    Condition_A     = Condition_NBE,
+
+    Condition_S     = 8,
+    Condition_NS    = 9,
+    Condition_P     = 10,
+    Condition_PE    = Condition_P,
+    Condition_NP    = 11,
+    Condition_PO    = Condition_NP,
+    Condition_L     = 12,
+    Condition_NGE   = Condition_L,
+    Condition_NL    = 13,
+    Condition_GE    = Condition_NL,
+    Condition_LE    = 14,
+    Condition_NG    = Condition_LE,
+    Condition_NLE   = 15,
+    Condition_G     = Condition_NLE,
+    Condition_Count = 16
+} ConditionCode;
+
+//
+// prefix code
+//
+typedef enum InstrPrefix {
+    no_prefix,
+    lock_prefix                     = 0xF0,
+    hint_branch_taken_prefix        = 0x2E,
+    hint_branch_not_taken_prefix    = 0x3E,
+    prefix_repne                    = 0xF2,
+    prefix_repnz                    = prefix_repne,
+    prefix_repe                     = 0xF3,
+    prefix_repz                     = prefix_repe,
+    prefix_rep                      = 0xF3,
+    prefix_cs                       = 0x2E,
+    prefix_ss                       = 0x36,
+    prefix_ds                       = 0x3E,
+    prefix_es                       = 0x26,
+    prefix_fs                       = 0x64,
+    prefix_gs                       = 0x65
+} InstrPrefix;
+
+//last 2 bits: decide xmm, gp, fs
+//virtual, scratch, temp, hard match with ncg_o1_data.h
+typedef enum LowOpndRegType {
+  LowOpndRegType_gp = 0,
+  LowOpndRegType_fs = 1,
+  LowOpndRegType_xmm = 2,
+  LowOpndRegType_fs_s = 3,
+  LowOpndRegType_ss = 4,
+  LowOpndRegType_scratch = 8, //used by NCG O1
+  LowOpndRegType_temp = 16,
+  LowOpndRegType_hard = 32,   //NCG O1
+  LowOpndRegType_virtual = 64, //used by NCG O1
+  LowOpndRegType_glue = 128
+} LowOpndRegType;
+
+//if inline, separte enc_wrapper.cpp into two files, one of them is .inl
+//           enc_wrapper.cpp needs to handle both cases
+#ifdef ENCODER_INLINE
+    #define ENCODER_DECLARE_EXPORT inline
+    #include "enc_wrapper.inl"
+#else
+    #define ENCODER_DECLARE_EXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ENCODER_DECLARE_EXPORT char* encoder_imm(Mnemonic m, OpndSize size,
+                  int imm, char* stream);
+ENCODER_DECLARE_EXPORT unsigned encoder_get_inst_size(char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_update_imm(int imm, char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem(Mnemonic m, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_reg(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem_reg(Mnemonic m, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT char * encoder_movzs_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_mem_disp_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_mem(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_imm_reg(Mnemonic m, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char * encoder_update_imm_rm(int imm, char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_imm_mem(Mnemonic m, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  int reg, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_return(char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_compare_fp_stack(bool pop, int reg, bool isDouble, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_movez_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_moves_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char* stream);
+ENCODER_DECLARE_EXPORT char * encoder_movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical, int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT char * encoder_moves_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical, int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT int decodeThenPrint(char* stream_start);
+ENCODER_DECLARE_EXPORT char* decoder_disassemble_instr(char* stream, char* strbuf, unsigned int len);
+#ifdef __cplusplus
+}
+#endif
+#endif // _VM_ENC_WRAPPER_H_
diff --git a/vm/compiler/codegen/x86/libenc/encoder.cpp b/vm/compiler/codegen/x86/libenc/encoder.cpp
new file mode 100644
index 0000000..ef08a4d
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/encoder.cpp
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#include <stdio.h>
+#include <assert.h>
+
+#include <limits.h>
+
+#include "enc_base.h"
+
+#ifdef NO_ENCODER_INLINE
+    #include "encoder.h"
+    #include "encoder.inl"
+#else
+    #define NO_ENCODER_INLINE
+    #include "encoder.h"
+    #undef NO_ENCODER_INLINE
+#endif
+
+
+
+#ifdef _EM64T_
+
+R_Opnd rax_opnd(rax_reg);
+R_Opnd rcx_opnd(rcx_reg);
+R_Opnd rdx_opnd(rdx_reg);
+R_Opnd rbx_opnd(rbx_reg);
+R_Opnd rsp_opnd(rsp_reg);
+R_Opnd rbp_opnd(rbp_reg);
+R_Opnd rsi_opnd(rsi_reg);
+R_Opnd rdi_opnd(rdi_reg);
+
+R_Opnd r8_opnd(r8_reg);
+R_Opnd r9_opnd(r9_reg);
+R_Opnd r10_opnd(r10_reg);
+R_Opnd r11_opnd(r11_reg);
+R_Opnd r12_opnd(r12_reg);
+R_Opnd r13_opnd(r13_reg);
+R_Opnd r14_opnd(r14_reg);
+R_Opnd r15_opnd(r15_reg);
+
+XMM_Opnd xmm8_opnd(xmm8_reg);
+XMM_Opnd xmm9_opnd(xmm9_reg);
+XMM_Opnd xmm10_opnd(xmm10_reg);
+XMM_Opnd xmm11_opnd(xmm11_reg);
+XMM_Opnd xmm12_opnd(xmm12_reg);
+XMM_Opnd xmm13_opnd(xmm13_reg);
+XMM_Opnd xmm14_opnd(xmm14_reg);
+XMM_Opnd xmm15_opnd(xmm15_reg);
+
+#else
+
+R_Opnd eax_opnd(eax_reg);
+R_Opnd ecx_opnd(ecx_reg);
+R_Opnd edx_opnd(edx_reg);
+R_Opnd ebx_opnd(ebx_reg);
+R_Opnd esp_opnd(esp_reg);
+R_Opnd ebp_opnd(ebp_reg);
+R_Opnd esi_opnd(esi_reg);
+R_Opnd edi_opnd(edi_reg);
+
+#endif //_EM64T_
+
+XMM_Opnd xmm0_opnd(xmm0_reg);
+XMM_Opnd xmm1_opnd(xmm1_reg);
+XMM_Opnd xmm2_opnd(xmm2_reg);
+XMM_Opnd xmm3_opnd(xmm3_reg);
+XMM_Opnd xmm4_opnd(xmm4_reg);
+XMM_Opnd xmm5_opnd(xmm5_reg);
+XMM_Opnd xmm6_opnd(xmm6_reg);
+XMM_Opnd xmm7_opnd(xmm7_reg);
+
+
+#define countof(a)      (sizeof(a)/sizeof(a[0]))
+
+extern const RegName map_of_regno_2_regname[];
+extern const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[];
+extern const Mnemonic map_of_alu_opcode_2_mnemonic[];
+extern const Mnemonic map_of_shift_opcode_2_mnemonic[];
+
+const RegName map_of_regno_2_regname [] = {
+#ifdef _EM64T_
+    RegName_RAX,    RegName_RBX,    RegName_RCX,    RegName_RDX,
+    RegName_RDI,    RegName_RSI,    RegName_RSP,    RegName_RBP,
+    RegName_R8,     RegName_R9,     RegName_R10,    RegName_R11,
+    RegName_R12,    RegName_R13,    RegName_R14,    RegName_R15,
+    RegName_XMM0,   RegName_XMM1,   RegName_XMM2,   RegName_XMM3,
+    RegName_XMM4,   RegName_XMM5,   RegName_XMM6,   RegName_XMM7,
+    RegName_XMM8,   RegName_XMM9,   RegName_XMM10,  RegName_XMM11,
+    RegName_XMM12,  RegName_XMM13,   RegName_XMM14, RegName_XMM15,
+
+#else
+    RegName_EAX,    RegName_EBX,    RegName_ECX,    RegName_EDX,
+    RegName_EDI,    RegName_ESI,    RegName_ESP,    RegName_EBP,
+    RegName_XMM0,   RegName_XMM1,   RegName_XMM2,   RegName_XMM3,
+    RegName_XMM4,   RegName_XMM5,   RegName_XMM6,   RegName_XMM7,
+    RegName_FS,
+#endif  // _EM64T_
+
+    RegName_Null,
+};
+
+const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[] = {
+    OpndSize_8, OpndSize_16, OpndSize_32, OpndSize_64, OpndSize_Any
+};
+
+const Mnemonic map_of_alu_opcode_2_mnemonic[] = {
+    //add_opc=0,  or_opc,           adc_opc,        sbb_opc,
+    //and_opc,      sub_opc,        xor_opc,        cmp_opc,
+    //n_alu
+    Mnemonic_ADD,   Mnemonic_OR,    Mnemonic_ADC,   Mnemonic_SBB,
+    Mnemonic_AND,   Mnemonic_SUB,   Mnemonic_XOR,   Mnemonic_CMP,
+};
+
+const Mnemonic map_of_shift_opcode_2_mnemonic[] = {
+    //shld_opc, shrd_opc,
+    // shl_opc, shr_opc, sar_opc, ror_opc, max_shift_opcode=6,
+    // n_shift = 6
+    Mnemonic_SHLD,  Mnemonic_SHRD,
+    Mnemonic_SHL,   Mnemonic_SHR,   Mnemonic_SAR, Mnemonic_ROR
+};
+
+#ifdef _DEBUG
+
+static int debug_check() {
+    // Checks some assumptions.
+
+    // 1. all items of Encoder.h:enum Reg_No  must be mapped plus n_reg->RegName_Null
+    assert(countof(map_of_regno_2_regname) == n_reg + 1);
+    assert(countof(map_of_alu_opcode_2_mnemonic) == n_alu);
+    assert(countof(map_of_shift_opcode_2_mnemonic) == n_shift);
+    return 0;
+}
+
+static int dummy = debug_check();
+
+// can have this - initialization order problems.... static int dummy_run_the_debug_test = debug_check();
+
+#endif
diff --git a/vm/compiler/codegen/x86/libenc/encoder.h b/vm/compiler/codegen/x86/libenc/encoder.h
new file mode 100644
index 0000000..e29efce
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/encoder.h
@@ -0,0 +1,716 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+/**
+ * @file
+ * @brief Simple interface for generating processor instructions.
+ *
+ * The interface works for both IA32 and EM64T. By default, only IA32
+ * capabilities are presented. To enable EM64T feature, the _EM64T_ macro
+ * must be defined (and, of course, a proper library version to be used).
+ *
+ * The interface is based on the original ia32.h encoder interface,
+ * with some simplifications and add-ons - EM64T-specific, SSE and SSE2.
+ *
+ * The interface mostly intended for existing legacy code like LIL code
+ * generator. From the implementation point of view, it's just a wrapper
+ * around the EncoderBase functionality.
+ */
+
+#ifndef _VM_ENCODER_H_
+#define _VM_ENCODER_H_
+
+#include <limits.h>
+#include "enc_base.h"
+//#include "open/types.h"
+
+#ifdef _EM64T_
+// size of general-purpose value on the stack in bytes
+#define GR_STACK_SIZE 8
+// size of floating-point value on the stack in bytes
+#define FR_STACK_SIZE 8
+
+#if defined(WIN32) || defined(_WIN64)
+    // maximum number of GP registers for inputs
+    const int MAX_GR = 4;
+    // maximum number of FP registers for inputs
+    const int MAX_FR = 4;
+    // WIN64 reserves 4 words for shadow space
+    const int SHADOW = 4 * GR_STACK_SIZE;
+#else
+    // maximum number of GP registers for inputs
+    const int MAX_GR = 6;
+    // maximum number of FP registers for inputs
+    const int MAX_FR = 8;
+    // Linux x64 doesn't reserve shadow space
+    const int SHADOW = 0;
+#endif
+
+#else
+// size of general-purpose value on the stack in bytes
+#define GR_STACK_SIZE 4
+// size of general-purpose value on the stack in bytes
+#define FR_STACK_SIZE 8
+
+// maximum number of GP registers for inputs
+const int MAX_GR = 0;
+// maximum number of FP registers for inputs
+const int MAX_FR = 0;
+#endif
+
+typedef enum Reg_No {
+#ifdef _EM64T_
+    rax_reg = 0,rbx_reg,    rcx_reg,    rdx_reg,
+    rdi_reg,    rsi_reg,    rsp_reg,    rbp_reg,
+    r8_reg,     r9_reg,     r10_reg,    r11_reg,
+    r12_reg,    r13_reg,    r14_reg,    r15_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    xmm8_reg,   xmm9_reg,   xmm10_reg,  xmm11_reg,
+    xmm12_reg,  xmm13_reg,  xmm14_reg,  xmm15_reg,
+
+#else   // !defined(_EM64T_)
+
+    eax_reg = 0,ebx_reg,    ecx_reg,    edx_reg,
+    edi_reg,    esi_reg,    esp_reg,    ebp_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    fs_reg,
+#endif
+    /** @brief Total number of registers.*/
+    n_reg
+} Reg_No;
+//
+// instruction operand sizes: 8,16,32,64 bits
+//
+typedef enum Opnd_Size {
+    size_8 = 0,
+    size_16,
+    size_32,
+    size_64,
+    n_size,
+#ifdef _EM64T_
+    size_platf = size_64
+#else
+    size_platf = size_32
+#endif
+} Opnd_Size;
+
+//
+// opcodes for alu instructions
+//
+typedef enum ALU_Opcode {
+    add_opc = 0,or_opc,     adc_opc,    sbb_opc,
+    and_opc,    sub_opc,    xor_opc,    cmp_opc,
+    n_alu
+} ALU_Opcode;
+
+//
+// opcodes for shift instructions
+//
+typedef enum Shift_Opcode {
+    shld_opc,   shrd_opc,   shl_opc,    shr_opc,
+    sar_opc,    ror_opc, max_shift_opcode=6,     n_shift = 6
+} Shift_Opcode;
+
+typedef enum ConditionCode {
+    Condition_O     = 0,
+    Condition_NO    = 1,
+    Condition_B     = 2,
+    Condition_NAE   = Condition_B,
+    Condition_C     = Condition_B,
+    Condition_NB    = 3,
+    Condition_AE    = Condition_NB,
+    Condition_NC    = Condition_NB,
+    Condition_Z     = 4,
+    Condition_E     = Condition_Z,
+    Condition_NZ    = 5,
+    Condition_NE    = Condition_NZ,
+    Condition_BE    = 6,
+    Condition_NA    = Condition_BE,
+    Condition_NBE   = 7,
+    Condition_A     = Condition_NBE,
+
+    Condition_S     = 8,
+    Condition_NS    = 9,
+    Condition_P     = 10,
+    Condition_PE    = Condition_P,
+    Condition_NP    = 11,
+    Condition_PO    = Condition_NP,
+    Condition_L     = 12,
+    Condition_NGE   = Condition_L,
+    Condition_NL    = 13,
+    Condition_GE    = Condition_NL,
+    Condition_LE    = 14,
+    Condition_NG    = Condition_LE,
+    Condition_NLE   = 15,
+    Condition_G     = Condition_NLE,
+    Condition_Count = 16
+} ConditionCode;
+
+//
+// prefix code
+//
+typedef enum InstrPrefix {
+    no_prefix,
+    lock_prefix                     = 0xF0,
+    hint_branch_taken_prefix        = 0x2E,
+    hint_branch_not_taken_prefix    = 0x3E,
+    prefix_repne                    = 0xF2,
+    prefix_repnz                    = prefix_repne,
+    prefix_repe                     = 0xF3,
+    prefix_repz                     = prefix_repe,
+    prefix_rep                      = 0xF3,
+    prefix_cs                       = 0x2E,
+    prefix_ss                       = 0x36,
+    prefix_ds                       = 0x3E,
+    prefix_es                       = 0x26,
+    prefix_fs                       = 0x64,
+    prefix_gs                       = 0x65
+} InstrPrefix;
+
+
+//
+// an instruction operand
+//
+class Opnd {
+
+protected:
+    enum Tag { SignedImm, UnsignedImm, Reg, Mem, FP, XMM };
+
+    const Tag  tag;
+
+    Opnd(Tag t): tag(t) {}
+
+public:
+    void * operator new(size_t, void * mem) {
+        return mem;
+    }
+
+    void operator delete(void *) {}
+
+    void operator delete(void *, void *) {}
+
+private:
+    // disallow copying
+    Opnd(const Opnd &): tag(Mem) { assert(false); }
+    Opnd& operator=(const Opnd &) { assert(false); return *this; }
+};
+typedef int I_32;
+class Imm_Opnd: public Opnd {
+
+protected:
+    union {
+#ifdef _EM64T_
+        int64           value;
+        unsigned char   bytes[8];
+#else
+        I_32           value;
+        unsigned char   bytes[4];
+#endif
+    };
+    Opnd_Size           size;
+
+public:
+    Imm_Opnd(I_32 val, bool isSigned = true):
+        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(size_32) {
+        if (isSigned) {
+            if (CHAR_MIN <= val && val <= CHAR_MAX) {
+                size = size_8;
+            } else if (SHRT_MIN <= val && val <= SHRT_MAX) {
+                size = size_16;
+            }
+        } else {
+            assert(val >= 0);
+            if (val <= UCHAR_MAX) {
+                size = size_8;
+            } else if (val <= USHRT_MAX) {
+                size = size_16;
+            }
+        }
+    }
+    Imm_Opnd(const Imm_Opnd& that): Opnd(that.tag), value(that.value), size(that.size) {};
+
+#ifdef _EM64T_
+    Imm_Opnd(Opnd_Size sz, int64 val, bool isSigned = true):
+        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) {
+#ifndef NDEBUG
+        switch (size) {
+        case size_8:
+            assert(val == (int64)(I_8)val);
+            break;
+        case size_16:
+            assert(val == (int64)(int16)val);
+            break;
+        case size_32:
+            assert(val == (int64)(I_32)val);
+            break;
+        case size_64:
+            break;
+        case n_size:
+            assert(false);
+            break;
+        }
+#endif // NDEBUG
+    }
+
+    int64 get_value() const { return value; }
+
+#else
+
+    Imm_Opnd(Opnd_Size sz, I_32 val, int isSigned = true):
+        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) {
+#ifndef NDEBUG
+        switch (size) {
+        case size_8:
+            assert((I_32)val == (I_32)(I_8)val);
+            break;
+        case size_16:
+            assert((I_32)val == (I_32)(int16)val);
+            break;
+        case size_32:
+            break;
+        case size_64:
+        case n_size:
+            assert(false);
+            break;
+        }
+#endif // NDEBUG
+    }
+
+    I_32 get_value() const { return value; }
+
+#endif
+    Opnd_Size get_size() const { return size; }
+    bool      is_signed() const { return tag == SignedImm; }
+};
+
+class RM_Opnd: public Opnd {
+
+public:
+    bool is_reg() const { return tag != SignedImm && tag != UnsignedImm && tag != Mem; }
+
+protected:
+    RM_Opnd(Tag t): Opnd(t) {}
+
+private:
+    // disallow copying
+    RM_Opnd(const RM_Opnd &): Opnd(Reg) { assert(false); }
+};
+
+class R_Opnd: public RM_Opnd {
+
+protected:
+    Reg_No      _reg_no;
+
+public:
+    R_Opnd(Reg_No r): RM_Opnd(Reg), _reg_no(r) {}
+    Reg_No  reg_no() const { return _reg_no; }
+
+private:
+    // disallow copying
+    R_Opnd(const R_Opnd &): RM_Opnd(Reg) { assert(false); }
+};
+
+//
+// a memory operand with displacement
+// Can also serve as a full memory operand with base,index, displacement and scale.
+// Use n_reg to specify 'no register', say, for index.
+class M_Opnd: public RM_Opnd {
+
+protected:
+    Imm_Opnd        m_disp;
+    Imm_Opnd        m_scale;
+    R_Opnd          m_index;
+    R_Opnd          m_base;
+
+public:
+    //M_Opnd(Opnd_Size sz): RM_Opnd(Mem, K_M, sz), m_disp(0), m_scale(0), m_index(n_reg), m_base(n_reg) {}
+    M_Opnd(I_32 disp):
+        RM_Opnd(Mem), m_disp(disp), m_scale(0), m_index(n_reg), m_base(n_reg) {}
+    M_Opnd(Reg_No rbase, I_32 rdisp):
+        RM_Opnd(Mem), m_disp(rdisp), m_scale(0), m_index(n_reg), m_base(rbase) {}
+    M_Opnd(I_32 disp, Reg_No rbase, Reg_No rindex, unsigned scale):
+        RM_Opnd(Mem), m_disp(disp), m_scale(scale), m_index(rindex), m_base(rbase) {}
+    M_Opnd(const M_Opnd & that) : RM_Opnd(Mem),
+        m_disp((int)that.m_disp.get_value()), m_scale((int)that.m_scale.get_value()),
+        m_index(that.m_index.reg_no()), m_base(that.m_base.reg_no())
+        {}
+    //
+    inline const R_Opnd & base(void) const { return m_base; }
+    inline const R_Opnd & index(void) const { return m_index; }
+    inline const Imm_Opnd & scale(void) const { return m_scale; }
+    inline const Imm_Opnd & disp(void) const { return m_disp; }
+};
+
+//
+//  a memory operand with base register and displacement
+//
+class M_Base_Opnd: public M_Opnd {
+
+public:
+    M_Base_Opnd(Reg_No base, I_32 disp) : M_Opnd(disp, base, n_reg, 0) {}
+
+private:
+    // disallow copying - but it leads to ICC errors #734 in encoder.inl
+    // M_Base_Opnd(const M_Base_Opnd &): M_Opnd(0) { assert(false); }
+};
+
+//
+//  a memory operand with base register, scaled index register
+//  and displacement.
+//
+class M_Index_Opnd : public M_Opnd {
+
+public:
+    M_Index_Opnd(Reg_No base, Reg_No index, I_32 disp, unsigned scale):
+        M_Opnd(disp, base, index, scale) {}
+
+private:
+    // disallow copying - but it leads to ICC errors #734 in encoder.inl
+    // M_Index_Opnd(const M_Index_Opnd &): M_Opnd(0) { assert(false); }
+};
+
+class XMM_Opnd : public Opnd {
+
+protected:
+    unsigned        m_idx;
+
+public:
+    XMM_Opnd(unsigned _idx): Opnd(XMM), m_idx(_idx) {};
+    unsigned get_idx( void ) const { return m_idx; };
+
+private:
+    // disallow copying
+    XMM_Opnd(const XMM_Opnd &): Opnd(XMM) { assert(false); }
+};
+
+//
+// operand structures for ia32 registers
+//
+#ifdef _EM64T_
+
+extern R_Opnd rax_opnd;
+extern R_Opnd rcx_opnd;
+extern R_Opnd rdx_opnd;
+extern R_Opnd rbx_opnd;
+extern R_Opnd rdi_opnd;
+extern R_Opnd rsi_opnd;
+extern R_Opnd rsp_opnd;
+extern R_Opnd rbp_opnd;
+
+extern R_Opnd r8_opnd;
+extern R_Opnd r9_opnd;
+extern R_Opnd r10_opnd;
+extern R_Opnd r11_opnd;
+extern R_Opnd r12_opnd;
+extern R_Opnd r13_opnd;
+extern R_Opnd r14_opnd;
+extern R_Opnd r15_opnd;
+
+extern XMM_Opnd xmm8_opnd;
+extern XMM_Opnd xmm9_opnd;
+extern XMM_Opnd xmm10_opnd;
+extern XMM_Opnd xmm11_opnd;
+extern XMM_Opnd xmm12_opnd;
+extern XMM_Opnd xmm13_opnd;
+extern XMM_Opnd xmm14_opnd;
+extern XMM_Opnd xmm15_opnd;
+#else
+
+extern R_Opnd eax_opnd;
+extern R_Opnd ecx_opnd;
+extern R_Opnd edx_opnd;
+extern R_Opnd ebx_opnd;
+extern R_Opnd esp_opnd;
+extern R_Opnd ebp_opnd;
+extern R_Opnd esi_opnd;
+extern R_Opnd edi_opnd;
+
+#endif // _EM64T_
+
+extern XMM_Opnd xmm0_opnd;
+extern XMM_Opnd xmm1_opnd;
+extern XMM_Opnd xmm2_opnd;
+extern XMM_Opnd xmm3_opnd;
+extern XMM_Opnd xmm4_opnd;
+extern XMM_Opnd xmm5_opnd;
+extern XMM_Opnd xmm6_opnd;
+extern XMM_Opnd xmm7_opnd;
+
+#ifdef NO_ENCODER_INLINE
+    #define ENCODER_DECLARE_EXPORT
+#else
+    #define ENCODER_DECLARE_EXPORT inline
+    #include "encoder.inl"
+#endif
+
+// prefix
+ENCODER_DECLARE_EXPORT char * prefix(char * stream, InstrPrefix p);
+
+// stack push and pop instructions
+ENCODER_DECLARE_EXPORT char * push(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * push(char * stream, const Imm_Opnd & imm);
+ENCODER_DECLARE_EXPORT char * pop(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// cmpxchg or xchg
+ENCODER_DECLARE_EXPORT char * cmpxchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * xchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+
+// inc(rement), dec(rement), not, neg(ate) instructions
+ENCODER_DECLARE_EXPORT char * inc(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * dec(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * _not(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * neg(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * nop(char * stream);
+ENCODER_DECLARE_EXPORT char * int3(char * stream);
+
+// alu instructions: add, or, adc, sbb, and, sub, xor, cmp
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// test instruction
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+
+// shift instructions: shl, shr, sar, shld, shrd, ror
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+
+// multiply instructions: mul, imul
+ENCODER_DECLARE_EXPORT char * mul(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, const Imm_Opnd& imm, Opnd_Size sz = size_platf);
+
+// divide instructions: div, idiv
+ENCODER_DECLARE_EXPORT char * idiv(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// data movement: mov
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const M_Opnd & m,  const R_Opnd & r, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const R_Opnd & r,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+
+ENCODER_DECLARE_EXPORT char * movsx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * movzx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm);
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm);
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm);
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm);
+
+// sse mov
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const M_Opnd & mem, const XMM_Opnd & xmm, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+// sse add, sub, mul, div
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+// xor, compare
+ENCODER_DECLARE_EXPORT char * sse_xor(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);
+
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem, bool dbl);
+
+// sse conversions
+ENCODER_DECLARE_EXPORT char * sse_cvt_si(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const XMM_Opnd & xmm, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvt_fp2dq(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvt_dq2fp(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem64);
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem32);
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);
+
+// condition operations
+ENCODER_DECLARE_EXPORT char * cmov(char * stream, ConditionCode cc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * setcc(char * stream, ConditionCode cc, const RM_Opnd & rm8);
+
+// load effective address: lea
+ENCODER_DECLARE_EXPORT char * lea(char * stream, const R_Opnd & r, const M_Opnd & m, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * cdq(char * stream);
+ENCODER_DECLARE_EXPORT char * wait(char * stream);
+
+// control-flow instructions
+ENCODER_DECLARE_EXPORT char * loop(char * stream, const Imm_Opnd & imm);
+
+// jump with 8-bit relative
+ENCODER_DECLARE_EXPORT char * jump8(char * stream, const Imm_Opnd & imm);
+
+// jump with 32-bit relative
+ENCODER_DECLARE_EXPORT char * jump32(char * stream, const Imm_Opnd & imm);
+
+// register indirect jump
+ENCODER_DECLARE_EXPORT char * jump(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// jump to target address
+ENCODER_DECLARE_EXPORT char *jump(char * stream, char *target);
+
+// jump with displacement
+//char * jump(char * stream, I_32 disp);
+
+// conditional branch with 8-bit branch offset
+ENCODER_DECLARE_EXPORT char * branch8(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix);
+
+// conditional branch with 32-bit branch offset
+ENCODER_DECLARE_EXPORT char * branch32(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix);
+
+// conditional branch with target label address
+//char * branch(char * stream, ConditionCode cc, const char * target, InstrPrefix prefix = no_prefix);
+
+// conditional branch with displacement immediate
+ENCODER_DECLARE_EXPORT char * branch(char * stream, ConditionCode cc, I_32 disp, InstrPrefix prefix = no_prefix);
+
+// call with displacement
+ENCODER_DECLARE_EXPORT char * call(char * stream, const Imm_Opnd & imm);
+
+// indirect call through register or memory location
+ENCODER_DECLARE_EXPORT char * call(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// call target address
+ENCODER_DECLARE_EXPORT char * call(char * stream, const char * target);
+
+// return instruction
+ENCODER_DECLARE_EXPORT char * ret(char * stream);
+ENCODER_DECLARE_EXPORT char * ret(char * stream, unsigned short pop);
+ENCODER_DECLARE_EXPORT char * ret(char * stream, const Imm_Opnd & imm);
+
+// string operations
+ENCODER_DECLARE_EXPORT char * set_d(char * stream, bool set);
+ENCODER_DECLARE_EXPORT char * scas(char * stream, unsigned char prefix);
+ENCODER_DECLARE_EXPORT char * stos(char * stream, unsigned char prefix);
+
+// floating-point instructions
+
+// st(0) = st(0) fp_op m{32,64}real
+//!char * fp_op_mem(char * stream, FP_Opcode opc,const M_Opnd& mem,int is_double);
+
+// st(0) = st(0) fp_op st(i)
+//!char *fp_op(char * stream, FP_Opcode opc,unsigned i);
+
+// st(i) = st(i) fp_op st(0)    ; optionally pop stack
+//!char * fp_op(char * stream, FP_Opcode opc,unsigned i,unsigned pop_stk);
+
+// compare st(0),st(1) and pop stack twice
+//!char * fcompp(char * stream);
+ENCODER_DECLARE_EXPORT char * fldcw(char * stream, const M_Opnd & mem);
+ENCODER_DECLARE_EXPORT char * fnstcw(char * stream, const M_Opnd & mem);
+ENCODER_DECLARE_EXPORT char * fnstsw(char * stream);
+//!char * fchs(char * stream);
+//!char * frem(char * stream);
+//!char * fxch(char * stream,unsigned i);
+//!char * fcomip(char * stream, unsigned i);
+
+// load from memory (as fp) into fp register stack
+ENCODER_DECLARE_EXPORT char * fld(char * stream, const M_Opnd & m, bool is_double);
+//!char *fld80(char * stream,const M_Opnd& mem);
+
+// load from memory (as int) into fp register stack
+//!char * fild(char * stream,const M_Opnd& mem,int is_long);
+
+// push st(i) onto fp register stack
+//!char * fld(char * stream,unsigned i);
+
+// push the constants 0.0 and 1.0 onto the fp register stack
+//!char * fldz(char * stream);
+//!char * fld1(char * stream);
+
+// store stack to memory (as int), always popping the stack
+ENCODER_DECLARE_EXPORT char * fist(char * stream, const M_Opnd & mem, bool is_long, bool pop_stk);
+// store stack to to memory (as fp), optionally popping the stack
+ENCODER_DECLARE_EXPORT char * fst(char * stream, const M_Opnd & m, bool is_double, bool pop_stk);
+// store ST(0) to ST(i), optionally popping the stack. Takes 1 clock
+ENCODER_DECLARE_EXPORT char * fst(char * stream, unsigned i, bool pop_stk);
+
+//!char * pushad(char * stream);
+//!char * pushfd(char * stream);
+//!char * popad(char * stream);
+//!char * popfd(char * stream);
+
+// stack frame allocation instructions: enter & leave
+//
+//    enter frame_size
+//
+//    is equivalent to:
+//
+//    push    ebp
+//    mov     ebp,esp
+//    sub     esp,frame_size
+//
+//!char *enter(char * stream,const Imm_Opnd& imm);
+
+// leave
+// is equivalent to:
+//
+// mov        esp,ebp
+// pop        ebp
+//!char *leave(char * stream);
+
+// sahf  loads SF, ZF, AF, PF, and CF flags from eax
+//!char *sahf(char * stream);
+
+// Intrinsic FP math functions
+
+//!char *math_fsin(char * stream);
+//!char *math_fcos(char * stream);
+//!char *math_fabs(char * stream);
+//!char *math_fpatan(char * stream);
+ENCODER_DECLARE_EXPORT char * fprem(char * stream);
+ENCODER_DECLARE_EXPORT char * fprem1(char * stream);
+//!char *math_frndint(char * stream);
+//!char *math_fptan(char * stream);
+
+//
+// Add 1-7 bytes padding, with as few instructions as possible,
+// with no effect on the processor state (e.g., registers, flags)
+//
+//!char *padding(char * stream, unsigned num);
+
+// prolog and epilog code generation
+//- char *prolog(char * stream,unsigned frame_size,unsigned reg_save_mask);
+//- char *epilog(char * stream,unsigned reg_save_mask);
+
+//!extern R_Opnd reg_operand_array[];
+
+// fsave and frstor
+//!char *fsave(char * stream);
+//!char *frstor(char * stream);
+
+// lahf : Load Status Flags into AH Register
+//!char *lahf(char * stream);
+
+// mfence : Memory Fence
+//!char *mfence(char * stream);
+
+#endif // _VM_ENCODER_H_
diff --git a/vm/compiler/codegen/x86/libenc/encoder.inl b/vm/compiler/codegen/x86/libenc/encoder.inl
new file mode 100644
index 0000000..663d492
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/encoder.inl
@@ -0,0 +1,850 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+
+extern const RegName map_of_regno_2_regname[];
+extern const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[];
+extern const Mnemonic map_of_alu_opcode_2_mnemonic[];
+extern const Mnemonic map_of_shift_opcode_2_mnemonic[];
+
+// S_ stands for 'Signed'
+extern const Mnemonic S_map_of_condition_code_2_branch_mnemonic[];
+// U_ stands for 'Unsigned'
+extern const Mnemonic U_map_of_condition_code_2_branch_mnemonic[];
+
+inline static RegName map_reg(Reg_No r) {
+    assert(r >= 0 && r <= n_reg);
+    return map_of_regno_2_regname[r];
+}
+
+inline static OpndSize map_size(Opnd_Size o_size) {
+    assert(o_size >= 0 && o_size <= n_size);
+    return map_of_EncoderOpndSize_2_RealOpndSize[o_size];
+}
+
+inline static Mnemonic map_alu(ALU_Opcode alu) {
+    assert(alu >= 0 && alu < n_alu);
+    return map_of_alu_opcode_2_mnemonic[alu];
+}
+
+inline static Mnemonic map_shift(Shift_Opcode shc) {
+    assert(shc >= 0 && shc < n_shift);
+    return map_of_shift_opcode_2_mnemonic[shc];
+}
+
+inline bool fit8(int64 val) {
+    return (CHAR_MIN <= val) && (val <= CHAR_MAX);
+}
+
+inline bool fit32(int64 val) {
+    return (INT_MIN <= val) && (val <= INT_MAX);
+}
+
+inline static void add_r(EncoderBase::Operands & args, const R_Opnd & r, Opnd_Size sz, OpndExt ext = OpndExt_None) {
+    RegName reg = map_reg(r.reg_no());
+    if (sz != n_size) {
+        OpndSize size = map_size(sz);
+        if (size != getRegSize(reg)) {
+            reg = getAliasReg(reg, size);
+        }
+    }
+    args.add(EncoderBase::Operand(reg, ext));
+}
+
+inline static void add_m(EncoderBase::Operands & args, const M_Opnd & m, Opnd_Size sz, OpndExt ext = OpndExt_None) {
+        assert(n_size != sz);
+        args.add(EncoderBase::Operand(map_size(sz),
+            map_reg(m.base().reg_no()), map_reg(m.index().reg_no()),
+            (unsigned)m.scale().get_value(), (int)m.disp().get_value(), ext));
+}
+
+inline static void add_rm(EncoderBase::Operands & args, const RM_Opnd & rm, Opnd_Size sz, OpndExt ext = OpndExt_None) {
+    rm.is_reg() ? add_r(args, (R_Opnd &)rm, sz, ext) : add_m(args, (M_Opnd &)rm, sz, ext);
+}
+
+inline static void add_xmm(EncoderBase::Operands & args, const XMM_Opnd & xmm, bool dbl) {
+    // Gregory -
+    // XMM registers indexes in Reg_No enum are shifted by xmm0_reg, their indexes
+    // don't start with 0, so it is necessary to subtract xmm0_reg index from
+    // xmm.get_idx() value
+    assert(xmm.get_idx() >= xmm0_reg);
+    return args.add((RegName)( (dbl ? RegName_XMM0D : RegName_XMM0S) + xmm.get_idx() -
+            xmm0_reg));
+}
+
+inline static void add_fp(EncoderBase::Operands & args, unsigned i, bool dbl) {
+    return args.add((RegName)( (dbl ? RegName_FP0D : RegName_FP0S) + i));
+}
+
+inline static void add_imm(EncoderBase::Operands & args, const Imm_Opnd & imm) {
+    assert(n_size != imm.get_size());
+    args.add(EncoderBase::Operand(map_size(imm.get_size()), imm.get_value(),
+        imm.is_signed() ? OpndExt_Signed : OpndExt_Zero));
+}
+
+ENCODER_DECLARE_EXPORT char * prefix(char * stream, InstrPrefix p) {
+    *stream = (char)p;
+    return stream + 1;
+}
+
+// stack push and pop instructions
+ENCODER_DECLARE_EXPORT char * push(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_PUSH, args);
+}
+
+ENCODER_DECLARE_EXPORT char * push(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+#ifdef _EM64T_
+    add_imm(args, imm);
+#else
+    // we need this workaround to be compatible with the former ia32 encoder implementation
+    add_imm(args, Imm_Opnd(size_32, imm.get_value()));
+#endif
+    return EncoderBase::encode(stream, Mnemonic_PUSH, args);
+}
+
+ENCODER_DECLARE_EXPORT char * pop(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_POP, args);
+}
+
+// cmpxchg or xchg
+ENCODER_DECLARE_EXPORT char * cmpxchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    RegName implicitReg = getAliasReg(RegName_EAX, map_size(sz));
+    args.add(implicitReg);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CMPXCHG, args);
+}
+
+ENCODER_DECLARE_EXPORT char * xchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_XCHG, args);
+}
+
+// inc(rement), dec(rement), not, neg(ate) instructions
+ENCODER_DECLARE_EXPORT char * inc(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_INC, args);
+}
+
+ENCODER_DECLARE_EXPORT char * dec(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_DEC, args);
+}
+
+ENCODER_DECLARE_EXPORT char * _not(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_NOT, args);
+}
+
+ENCODER_DECLARE_EXPORT char * neg(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_NEG, args);
+}
+
+ENCODER_DECLARE_EXPORT char * nop(char * stream) {
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream, Mnemonic_NOP, args);
+}
+
+ENCODER_DECLARE_EXPORT char * int3(char * stream) {
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream, Mnemonic_INT3, args);
+}
+
+// alu instructions: add, or, adc, sbb, and, sub, xor, cmp
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, map_alu(opc), args);
+};
+
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, m, sz);
+    add_rm(args, r, sz);
+    return (char*)EncoderBase::encode(stream, map_alu(opc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, map_alu(opc), args);
+}
+
+// test instruction
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    assert(imm.get_size() <= sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_TEST, args);
+}
+
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_TEST, args);
+}
+
+// shift instructions: shl, shr, sar, shld, shrd
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    args.add(RegName_CL);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm,
+                            const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    assert(shc == shld_opc || shc == shrd_opc);
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm,
+                            const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    assert(shc == shld_opc || shc == shrd_opc);
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    args.add(RegName_CL);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+// multiply instructions: mul, imul
+ENCODER_DECLARE_EXPORT char * mul(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    args.add(RegName_EDX);
+    args.add(RegName_EAX);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MUL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm,
+                           const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args);
+}
+
+// divide instructions: div, idiv
+ENCODER_DECLARE_EXPORT char * idiv(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+#ifdef _EM64T_
+    add_r(args, rdx_opnd, sz);
+    add_r(args, rax_opnd, sz);
+#else
+    add_r(args, edx_opnd, sz);
+    add_r(args, eax_opnd, sz);
+#endif
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IDIV, args);
+}
+
+// data movement: mov
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_m(args, m, sz);
+    add_r(args, r, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args);
+}
+
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args);
+}
+
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, size_32);
+    add_xmm(args, xmm, false);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, false);
+    add_rm(args, rm, size_32);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, size_64);
+    add_xmm(args, xmm, true);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, true);
+    add_rm(args, rm, size_64);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movsx(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, n_size);
+    add_rm(args, rm, sz, OpndExt_Signed);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movzx(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, n_size);
+    // movzx r64, r/m32 is not available on em64t
+    // mov r32, r/m32 should zero out upper bytes
+    assert(sz <= size_16);
+    add_rm(args, rm, sz, OpndExt_Zero);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
+}
+
+// sse mov
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const M_Opnd &  mem, const XMM_Opnd & xmm, bool dbl) {
+    EncoderBase::Operands args;
+    add_m(args, mem, dbl ? size_64 : size_32);
+    add_xmm(args, xmm, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args );
+}
+
+// sse add, sub, mul, div
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_ADDSD : Mnemonic_ADDSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_ADDSD : Mnemonic_ADDSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_SUBSD : Mnemonic_SUBSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_SUBSD : Mnemonic_SUBSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mul( char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MULSD : Mnemonic_MULSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd& xmm0, const XMM_Opnd& xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args,  xmm0, dbl);
+    add_xmm(args,  xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MULSD : Mnemonic_MULSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_DIVSD : Mnemonic_DIVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_DIVSD : Mnemonic_DIVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_xor(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_xmm(args, xmm1, true);
+    return (char*)EncoderBase::encode(stream, Mnemonic_PXOR, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_xmm(args, xmm1, true);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_COMISD : Mnemonic_COMISS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_COMISD : Mnemonic_COMISS, args);
+}
+
+// sse conversions
+ENCODER_DECLARE_EXPORT char * sse_cvt_si(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTSI2SD : Mnemonic_CVTSI2SS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_rm(args, reg, size_32);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTTSD2SI : Mnemonic_CVTTSS2SI, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const XMM_Opnd & xmm, bool dbl) {
+    EncoderBase::Operands args;
+    add_rm(args, reg, size_32);
+    add_xmm(args, xmm, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTTSD2SI : Mnemonic_CVTTSS2SI, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvt_fp2dq(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ?  Mnemonic_CVTTPD2DQ : Mnemonic_CVTTPS2DQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvt_dq2fp(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ?  Mnemonic_CVTDQ2PD : Mnemonic_CVTDQ2PS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem64) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, false);
+    add_m(args, mem64, size_64);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSD2SS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, false);
+    add_xmm(args, xmm1, true);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSD2SS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem32) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_m(args, mem32, size_32);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSS2SD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_xmm(args, xmm1, false);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSS2SD, args);
+}
+
+// condition operations
+ENCODER_DECLARE_EXPORT char *cmov(char * stream, ConditionCode cc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, (Mnemonic)(Mnemonic_CMOVcc + cc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * setcc(char * stream, ConditionCode cc, const RM_Opnd & rm8) {
+    EncoderBase::Operands args;
+    add_rm(args, rm8, size_8);
+    return (char*)EncoderBase::encode(stream, (Mnemonic)(Mnemonic_SETcc + cc), args);
+}
+
+// load effective address: lea
+ENCODER_DECLARE_EXPORT char * lea(char * stream, const R_Opnd & r, const M_Opnd & m, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_m(args, m, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_LEA, args);
+}
+
+ENCODER_DECLARE_EXPORT char * cdq(char * stream) {
+    EncoderBase::Operands args;
+    args.add(RegName_EDX);
+    args.add(RegName_EAX);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CDQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * wait(char * stream) {
+    return (char*)EncoderBase::encode(stream, Mnemonic_WAIT, EncoderBase::Operands());
+}
+
+// control-flow instructions
+
+// loop
+ENCODER_DECLARE_EXPORT char * loop(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_8);
+    args.add(RegName_ECX);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_LOOP, args);
+}
+
+// jump
+ENCODER_DECLARE_EXPORT char * jump8(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_8);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args);
+}
+
+ENCODER_DECLARE_EXPORT char * jump32(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_32);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args);
+}
+
+ENCODER_DECLARE_EXPORT char * jump(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args);
+}
+
+/**
+ * @note On EM64T: if target lies beyond 2G (does not fit into 32 bit
+ *       offset) then generates indirect jump using RAX (whose content is
+ *       destroyed).
+ */
+ENCODER_DECLARE_EXPORT char * jump(char * stream, char * target) {
+#ifdef _EM64T_
+    int64 offset = target - stream;
+    // sub 2 bytes for the short version
+    offset -= 2;
+    if (fit8(offset)) {
+        // use 8-bit signed relative form
+        return jump8(stream, Imm_Opnd(size_8, offset));
+    } else if (fit32(offset)) {
+        // sub 5 (3 + 2)bytes for the long version
+        offset -= 3;
+        // use 32-bit signed relative form
+        return jump32(stream, Imm_Opnd(size_32, offset));
+    }
+    // need to use absolute indirect jump
+    stream = mov(stream, rax_opnd, Imm_Opnd(size_64, (int64)target), size_64);
+    return jump(stream, rax_opnd, size_64);
+#else
+    I_32 offset = target - stream;
+    // sub 2 bytes for the short version
+    offset -= 2;
+    if (fit8(offset)) {
+        // use 8-bit signed relative form
+        return jump8(stream, Imm_Opnd(size_8, offset));
+    }
+    // sub 5 (3 + 2) bytes for the long version
+    offset -= 3;
+    // use 32-bit signed relative form
+    return jump32(stream, Imm_Opnd(size_32, offset));
+#endif
+}
+
+// branch
+ENCODER_DECLARE_EXPORT char * branch8(char * stream, ConditionCode cond,
+                                      const Imm_Opnd & imm,
+                                      InstrPrefix pref)
+{
+    if (pref != no_prefix) {
+        assert(pref == hint_branch_taken_prefix || pref == hint_branch_taken_prefix);
+        stream = prefix(stream, pref);
+    }
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cond);
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_8);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, m, args);
+}
+
+ENCODER_DECLARE_EXPORT char * branch32(char * stream, ConditionCode cond,
+                                       const Imm_Opnd & imm,
+                                       InstrPrefix pref)
+{
+    if (pref != no_prefix) {
+        assert(pref == hint_branch_taken_prefix || pref == hint_branch_taken_prefix);
+        stream = prefix(stream, pref);
+    }
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cond);
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_32);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, m, args);
+}
+
+/*
+ENCODER_DECLARE_EXPORT char * branch(char * stream, ConditionCode cc, const char * target, InstrPrefix prefix) {
+// sub 2 bytes for the short version
+int64 offset = stream-target-2;
+if( fit8(offset) ) {
+return branch8(stream, cc, Imm_Opnd(size_8, (char)offset), is_signed);
+}
+return branch32(stream, cc, Imm_Opnd(size_32, (int)offset), is_signed);
+}
+*/
+
+// call
+ENCODER_DECLARE_EXPORT char * call(char * stream, const Imm_Opnd & imm)
+{
+    EncoderBase::Operands args;
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CALL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * call(char * stream, const RM_Opnd & rm,
+                                   Opnd_Size sz)
+{
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CALL, args);
+}
+
+/**
+* @note On EM64T: if target lies beyond 2G (does not fit into 32 bit
+*       offset) then generates indirect jump using RAX (whose content is
+*       destroyed).
+*/
+ENCODER_DECLARE_EXPORT char * call(char * stream, const char * target)
+{
+#ifdef _EM64T_
+    int64 offset = target - stream;
+    if (fit32(offset)) {
+        offset -= 5; // sub 5 bytes for this instruction
+        Imm_Opnd imm(size_32, offset);
+        return call(stream, imm);
+    }
+    // need to use absolute indirect call
+    stream = mov(stream, rax_opnd, Imm_Opnd(size_64, (int64)target), size_64);
+    return call(stream, rax_opnd, size_64);
+#else
+    I_32 offset = target - stream;
+    offset -= 5; // sub 5 bytes for this instruction
+    Imm_Opnd imm(size_32, offset);
+    return call(stream, imm);
+#endif
+}
+
+// return instruction
+ENCODER_DECLARE_EXPORT char * ret(char * stream)
+{
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream, Mnemonic_RET, args);
+}
+
+ENCODER_DECLARE_EXPORT char * ret(char * stream, const Imm_Opnd & imm)
+{
+    EncoderBase::Operands args;
+    // TheManual says imm can be 16-bit only
+    //assert(imm.get_size() <= size_16);
+    args.add(EncoderBase::Operand(map_size(size_16), imm.get_value()));
+    return (char*)EncoderBase::encode(stream, Mnemonic_RET, args);
+}
+
+ENCODER_DECLARE_EXPORT char * ret(char * stream, unsigned short pop)
+{
+    // TheManual says it can only be imm16
+    EncoderBase::Operands args(EncoderBase::Operand(OpndSize_16, pop, OpndExt_Zero));
+    return (char*)EncoderBase::encode(stream, Mnemonic_RET, args);
+}
+
+// floating-point instructions
+ENCODER_DECLARE_EXPORT char * fld(char * stream, const M_Opnd & m,
+                                  bool is_double) {
+    EncoderBase::Operands args;
+    // a fake FP register as operand
+    add_fp(args, 0, is_double);
+    add_m(args, m, is_double ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, Mnemonic_FLD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fist(char * stream, const M_Opnd & mem,
+                                   bool is_long, bool pop_stk)
+{
+    EncoderBase::Operands args;
+    if (pop_stk) {
+        add_m(args, mem, is_long ? size_64 : size_32);
+        // a fake FP register as operand
+        add_fp(args, 0, is_long);
+        return (char*)EncoderBase::encode(stream,  Mnemonic_FISTP, args);
+    }
+    // only 32-bit operands are supported
+    assert(is_long == false);
+    add_m(args, mem, size_32);
+    add_fp(args, 0, false);
+    return (char*)EncoderBase::encode(stream,  Mnemonic_FIST, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fst(char * stream, const M_Opnd & m,
+                                  bool is_double, bool pop_stk)
+{
+    EncoderBase::Operands args;
+    add_m(args, m, is_double ? size_64 : size_32);
+    // a fake FP register as operand
+    add_fp(args, 0, is_double);
+    return (char*)EncoderBase::encode(stream,
+                                    pop_stk ? Mnemonic_FSTP : Mnemonic_FST,
+                                    args);
+}
+
+ENCODER_DECLARE_EXPORT char * fst(char * stream, unsigned i, bool pop_stk)
+{
+    EncoderBase::Operands args;
+    add_fp(args, i, true);
+    return (char*)EncoderBase::encode(stream,
+                                    pop_stk ? Mnemonic_FSTP : Mnemonic_FST,
+                                    args);
+}
+
+ENCODER_DECLARE_EXPORT char * fldcw(char * stream, const M_Opnd & mem) {
+    EncoderBase::Operands args;
+    add_m(args, mem, size_16);
+    return (char*)EncoderBase::encode(stream, Mnemonic_FLDCW, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fnstcw(char * stream, const M_Opnd & mem) {
+    EncoderBase::Operands args;
+    add_m(args, mem, size_16);
+    return (char*)EncoderBase::encode(stream, Mnemonic_FNSTCW, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fnstsw(char * stream)
+{
+    return (char*)EncoderBase::encode(stream, Mnemonic_FNSTCW,
+                                      EncoderBase::Operands());
+}
+
+// string operations
+ENCODER_DECLARE_EXPORT char * set_d(char * stream, bool set) {
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream,
+                                      set ? Mnemonic_STD : Mnemonic_CLD,
+                                      args);
+}
+
+ENCODER_DECLARE_EXPORT char * scas(char * stream, unsigned char prefix)
+{
+	EncoderBase::Operands args;
+    if (prefix != no_prefix) {
+        assert(prefix == prefix_repnz || prefix == prefix_repz);
+        *stream = prefix;
+        ++stream;
+    }
+    return (char*)EncoderBase::encode(stream, Mnemonic_SCAS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * stos(char * stream, unsigned char prefix)
+{
+    if (prefix != no_prefix) {
+        assert(prefix == prefix_rep);
+        *stream = prefix;
+        ++stream;
+    }
+
+	EncoderBase::Operands args;
+	return (char*)EncoderBase::encode(stream, Mnemonic_STOS, args);
+}
+
+// Intrinsic FP math functions
+
+ENCODER_DECLARE_EXPORT char * fprem(char * stream) {
+    return (char*)EncoderBase::encode(stream, Mnemonic_FPREM,
+                                      EncoderBase::Operands());
+}
+
+ENCODER_DECLARE_EXPORT char * fprem1(char * stream) {
+    return (char*)EncoderBase::encode(stream, Mnemonic_FPREM1,
+                                      EncoderBase::Operands());
+}
diff --git a/vm/compiler/template/Makefile-template b/vm/compiler/template/Makefile-template
new file mode 100644
index 0000000..9203183
--- /dev/null
+++ b/vm/compiler/template/Makefile-template
@@ -0,0 +1,49 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Makefile for the Dalvik modular interpreter.  This is not currently
+# integrated into the build system.
+#
+
+SHELL := /bin/sh
+
+# Build system has TARGET_ARCH=arm, but we need the exact architecture.
+# The base assumption for an ARM platform is ARMv5TE, but we may want to
+# support older ARMv4 devices, or use special features from ARMv6 or VFP.
+# The simulator build is "desktop".
+#
+# To generate sources for all targets:
+# for arch in desktop armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+#
+#TARGET_ARCH_EXT := armv5te
+
+OUTPUT_DIR := out
+
+# Accumulate all possible dependencies for the generated files in a very
+# conservative fashion.  If it's not one of the generated files in "out",
+# assume it's a dependency.
+SOURCE_DEPS := \
+	$(shell find . -path ./$(OUTPUT_DIR) -prune -o -type f -print)
+
+# Source files generated by the script.  There's always one C and one
+# assembly file, though in practice one or the other could be empty.
+GEN_SOURCES := \
+	$(OUTPUT_DIR)/CompilerTemplateAsm-$(TARGET_ARCH_EXT).S
+
+target: $(GEN_SOURCES)
+
+$(GEN_SOURCES): $(SOURCE_DEPS)
+	@mkdir -p out
+	./gen-template.py $(TARGET_ARCH_EXT) $(OUTPUT_DIR)
diff --git a/vm/compiler/template/README.txt b/vm/compiler/template/README.txt
new file mode 100644
index 0000000..fced412
--- /dev/null
+++ b/vm/compiler/template/README.txt
@@ -0,0 +1 @@
+See README.txt under dalvik/vm/mterp for details.
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S
new file mode 100644
index 0000000..51693fa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"faddd   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S
new file mode 100644
index 0000000..ad1e122
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fadds   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S
new file mode 100644
index 0000000..992c894
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S
@@ -0,0 +1,33 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     *
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    fldd    d0, [r0]                    @ d0<- vBB
+    fldd    d1, [r1]                    @ d1<- vCC
+    fcmpd  d0, d1                       @ compare (vBB, vCC)
+    mov     r0, #1                      @ r0<- 1 (default)
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S
new file mode 100644
index 0000000..0510ef6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    flds    s0, [r0]                    @ d0<- vBB
+    flds    s1, [r1]                    @ d1<- vCC
+    fcmps  s0, s1                      @ compare (vBB, vCC)
+    mov     r0, #1                      @ r0<- 1 (default)
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S
new file mode 100644
index 0000000..7241af1
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    fldd    d0, [r0]                    @ d0<- vBB
+    fldd    d1, [r1]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    mvn     r0, #0                      @ r0<- -1 (default)
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r0<- 1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S
new file mode 100644
index 0000000..bdb42d6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    flds    s0, [r0]                    @ d0<- vBB
+    flds    s1, [r1]                    @ d1<- vCC
+    fcmps  s0, s1                      @ compare (vBB, vCC)
+    mvn     r0, #0                      @ r0<- -1 (default)
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r0<- 1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S
new file mode 100644
index 0000000..8fa58b8
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fdivd   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S
new file mode 100644
index 0000000..fc125ce
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fdivs   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..dba3b08
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopNarrower.S" {"instr":"fcvtsd  s0, d0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S
new file mode 100644
index 0000000..4d910aa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopNarrower.S" {"instr":"ftosizd  s0, d0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..a5157dd
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopWider.S" {"instr":"fcvtds  d0, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S
new file mode 100644
index 0000000..90900aa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funop.S" {"instr":"ftosizs s1, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..c9f4fd6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopWider.S" {"instr":"fsitod  d0, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..a8f57b5
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funop.S" {"instr":"fsitos  s1, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..8bee853
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,19 @@
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    vpush   {d0-d15}                    @ save out all fp registers
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S
new file mode 100644
index 0000000..459e796
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fmuld   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S
new file mode 100644
index 0000000..301fa84
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fmuls   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..196d082
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,11 @@
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    r0 - offset from rSELF to the 1st element of the coreRegs save array.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace.coreRegs[0]
+    add     r0, #64                     @ pointer to heapArgSpace.fpRegs[0]
+    vldmia  r0, {d0-d15}
+    sub     r0, #64                     @ pointer to heapArgSpace.coreRegs[0]
+    ldmia   r0, {r0-r12}
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..11f62b7
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,23 @@
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: r7 value to save
+     *    Top of stack + 0: r0 value to save
+     *    r0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    r7 - the value of regMap
+     *
+     * The handler must save regMap, r0-r12 and then return with r0-r12
+     * with their original values (note that this means r0 and r7 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace
+    stmia   r0!, {r7}                   @ save regMap
+    ldr     r7, [r13, #0]               @ recover r0 value
+    stmia   r0!, {r7}                   @ save r0
+    ldr     r7, [r13, #4]               @ recover r7 value
+    stmia   r0!, {r1-r12}
+    add     r0, #12                     @ move to start of FP save regio
+    vstmia  r0, {d0-d15}
+    pop     {r0, r7}                    @ recover r0, r7
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S
new file mode 100644
index 0000000..1c6bb46
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S
@@ -0,0 +1,23 @@
+%verify "executed"
+    /*
+     * 64-bit floating point vfp sqrt operation.
+     * If the result is a NaN, bail out to library code to do
+     * the right thing.
+     *
+     * On entry:
+     *     r2 src addr of op1
+     * On exit:
+     *     r0,r1 = res
+     */
+    fldd    d0, [r2]
+    fsqrtd  d1, d0
+    fcmpd   d1, d1
+    fmstat
+    fmrrd   r0, r1, d1
+    bxeq    lr   @ Result OK - return
+    ldr     r2, .Lsqrt
+    fmrrd   r0, r1, d0   @ reload orig operand
+    bx      r2   @ tail call to sqrt library routine
+
+.Lsqrt:
+    .word   sqrt
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S
new file mode 100644
index 0000000..8fa20a0
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fsubd   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S
new file mode 100644
index 0000000..5e17e51
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fsubs   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TemplateOpList.h b/vm/compiler/template/armv5te-vfp/TemplateOpList.h
new file mode 100644
index 0000000..0365ba4
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TemplateOpList.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/armv5te-vfp/fbinop.S b/vm/compiler/template/armv5te-vfp/fbinop.S
new file mode 100644
index 0000000..3bc4b52
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/fbinop.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 32-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     flds    s0,[r1]
+     flds    s1,[r2]
+     $instr
+     fsts    s2,[r0]
+     bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/fbinopWide.S b/vm/compiler/template/armv5te-vfp/fbinopWide.S
new file mode 100644
index 0000000..3774646
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/fbinopWide.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 64-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     fldd    d0,[r1]
+     fldd    d1,[r2]
+     $instr
+     fstd    d2,[r0]
+     bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/funop.S b/vm/compiler/template/armv5te-vfp/funop.S
new file mode 100644
index 0000000..8409c28
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funop.S
@@ -0,0 +1,15 @@
+    /*
+     * Generic 32bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s1 = op s0".
+     *
+     * For: float-to-int, int-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    $instr                              @ s1<- op s0
+    fsts    s1, [r0]                    @ vA<- s1
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/funopNarrower.S b/vm/compiler/template/armv5te-vfp/funopNarrower.S
new file mode 100644
index 0000000..8566fca
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funopNarrower.S
@@ -0,0 +1,15 @@
+    /*
+     * Generic 64bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    fldd    d0, [r1]                    @ d0<- vB
+    $instr                              @ s0<- op d0
+    fsts    s0, [r0]                    @ vA<- s0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/funopWider.S b/vm/compiler/template/armv5te-vfp/funopWider.S
new file mode 100644
index 0000000..dbe745c
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funopWider.S
@@ -0,0 +1,15 @@
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    $instr                              @ d0<- op s0
+    fstd    d0, [r0]                    @ vA<- d0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/platform.S b/vm/compiler/template/armv5te-vfp/platform.S
new file mode 100644
index 0000000..e0666a5
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/platform.S
@@ -0,0 +1,5 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S
new file mode 100644
index 0000000..f18f6d3
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S
@@ -0,0 +1 @@
+%include "armv5te/TEMPLATE_CMPL_DOUBLE.S" { "naninst":"mov     r0, #1" }
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S
new file mode 100644
index 0000000..02887e5
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S
@@ -0,0 +1 @@
+%include "armv5te/TEMPLATE_CMPL_FLOAT.S" { "naninst":"mov     r0, #1" }
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S
new file mode 100644
index 0000000..23614e9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S
@@ -0,0 +1,38 @@
+%default { "naninst":"mvn     r0, #0" }
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    push    {r0-r3}                     @ save operands
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cdcmple       @ PIC way of "bl __aeabi_cdcmple"
+    blx     ip
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0, trumps less than
+    add     sp, #16                     @ drop unused operands
+    bx      r11
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    pop     {r2-r3}                     @ restore operands in reverse order
+    pop     {r0-r1}                     @ restore operands in reverse order
+    ldr     ip, .L__aeabi_cdcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    $naninst                            @ r1<- 1 or -1 for NaN
+    bx      r11
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S
new file mode 100644
index 0000000..f9293e6
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S
@@ -0,0 +1,56 @@
+%default { "naninst":"mvn     r0, #0" }
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    mov     r9, r0                      @ Save copies - we may need to redo
+    mov     r10, r1
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cfcmple       @ cmp <=: C clear if <, Z set if eq
+    blx     ip
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0, trumps less than
+    bx      r11
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    mov     r0, r10                     @ restore in reverse order
+    mov     r1, r9
+    ldr     ip, .L__aeabi_cfcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    $naninst                            @ r1<- 1 or -1 for NaN
+    bx      r11
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S
new file mode 100644
index 0000000..e5e8196
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S
@@ -0,0 +1,33 @@
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .L${opcode}_less            @ signed compare on high part
+    bgt     .L${opcode}_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .L${opcode}_greater         @ unsigned compare on low part
+.L${opcode}_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.L${opcode}_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S b/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S
new file mode 100644
index 0000000..9f24887
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S
@@ -0,0 +1,30 @@
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC -3.
+     * On entry:
+     *    lr - if NULL:
+     *        r1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [lr, #3] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S
new file mode 100644
index 0000000..99a17ab
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S
@@ -0,0 +1,49 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+$chaintgt:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
new file mode 100644
index 0000000..d1be4fd
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
new file mode 100644
index 0000000..d8661d9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
@@ -0,0 +1,83 @@
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
new file mode 100644
index 0000000..816277a
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
new file mode 100644
index 0000000..b7015eb
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
@@ -0,0 +1,60 @@
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
new file mode 100644
index 0000000..bfea7d9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
new file mode 100644
index 0000000..9dd4ff8
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
@@ -0,0 +1,60 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     $chaintgt   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
new file mode 100644
index 0000000..6ca5bdd
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..03926b6
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,17 @@
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    bx      lr                          @ return to compiled code
+#endif
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S
new file mode 100644
index 0000000..1ed3fb1
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S
@@ -0,0 +1,21 @@
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S
new file mode 100644
index 0000000..2695483
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S
@@ -0,0 +1,28 @@
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S
new file mode 100644
index 0000000..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_PERIODIC_PROFILING.S b/vm/compiler/template/armv5te/TEMPLATE_PERIODIC_PROFILING.S
new file mode 100644
index 0000000..c0f7d6e
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_PERIODIC_PROFILING.S
@@ -0,0 +1,26 @@
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .L${opcode}_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.L${opcode}_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..25b4ffa
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,8 @@
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    r0 - offset from rSELF to the 1st element of the coreRegs save array.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace.coreRegs[0]
+    ldmia   r0, {r0-r12}
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RETURN.S b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S
new file mode 100644
index 0000000..b10afcf
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S
@@ -0,0 +1,57 @@
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RETURN_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_RETURN_PROF.S
new file mode 100644
index 0000000..d7af0bd
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RETURN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_RETURN.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..1c3aa4d
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,21 @@
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: r7 value to save
+     *    Top of stack + 0: r0 value to save
+     *    r0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    r7 - the value of regMap
+     *
+     * The handler must save regMap, r0-r12 and then return with r0-r12
+     * with their original values (note that this means r0 and r7 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace
+    stmia   r0!, {r7}                   @ save regMap
+    ldr     r7, [r13, #0]               @ recover r0 value
+    stmia   r0!, {r7}                   @ save r0
+    ldr     r7, [r13, #4]               @ recover r7 value
+    stmia   r0!, {r1-r12}
+    pop     {r0, r7}                    @ recover r0, r7
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S
new file mode 100644
index 0000000..532f8a4
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S
@@ -0,0 +1,15 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S
new file mode 100644
index 0000000..c737840
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S
@@ -0,0 +1,15 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
new file mode 100644
index 0000000..54bde47
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
@@ -0,0 +1,133 @@
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
diff --git a/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S b/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S
new file mode 100644
index 0000000..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..abfec4b
--- /dev/null
+++ b/vm/compiler/template/armv5te/TemplateOpList.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(CMPG_DOUBLE)
+JIT_TEMPLATE(CMPL_DOUBLE)
+JIT_TEMPLATE(CMPG_FLOAT)
+JIT_TEMPLATE(CMPL_FLOAT)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S
new file mode 100644
index 0000000..b567a90
--- /dev/null
+++ b/vm/compiler/template/armv5te/footer.S
@@ -0,0 +1,129 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global 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..6dcf5b9
--- /dev/null
+++ b/vm/compiler/template/armv5te/header.S
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
diff --git a/vm/compiler/template/armv5te/platform.S b/vm/compiler/template/armv5te/platform.S
new file mode 100644
index 0000000..e0666a5
--- /dev/null
+++ b/vm/compiler/template/armv5te/platform.S
@@ -0,0 +1,5 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
diff --git a/vm/compiler/template/armv7-a-neon/TemplateOpList.h b/vm/compiler/template/armv7-a-neon/TemplateOpList.h
new file mode 100644
index 0000000..0365ba4
--- /dev/null
+++ b/vm/compiler/template/armv7-a-neon/TemplateOpList.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/armv7-a/TemplateOpList.h b/vm/compiler/template/armv7-a/TemplateOpList.h
new file mode 100644
index 0000000..0365ba4
--- /dev/null
+++ b/vm/compiler/template/armv7-a/TemplateOpList.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/config-armv5te b/vm/compiler/template/config-armv5te
new file mode 100644
index 0000000..668df1b
--- /dev/null
+++ b/vm/compiler/template/config-armv5te
@@ -0,0 +1,45 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv5te-vfp b/vm/compiler/template/config-armv5te-vfp
new file mode 100644
index 0000000..774bd96
--- /dev/null
+++ b/vm/compiler/template/config-armv5te-vfp
@@ -0,0 +1,68 @@
+
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te-vfp/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te-vfp
+    op TEMPLATE_CMP_LONG armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN armv5te
+    op TEMPLATE_MUL_LONG armv5te
+    op TEMPLATE_RETURN armv5te
+    op TEMPLATE_SHL_LONG armv5te
+    op TEMPLATE_SHR_LONG armv5te
+    op TEMPLATE_USHR_LONG armv5te
+    op TEMPLATE_THROW_EXCEPTION_COMMON armv5te
+    op TEMPLATE_STRING_COMPARETO armv5te
+    op TEMPLATE_STRING_INDEXOF armv5te
+    op TEMPLATE_INTERPRET armv5te
+    op TEMPLATE_MONITOR_ENTER armv5te
+    op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+    op TEMPLATE_PERIODIC_PROFILING armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF armv5te
+    op TEMPLATE_RETURN_PROF armv5te
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv7-a b/vm/compiler/template/config-armv7-a
new file mode 100644
index 0000000..9d66e55
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a
@@ -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.
+
+#
+# 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 TEMPLATE_PERIODIC_PROFILING armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF armv5te
+    op TEMPLATE_RETURN_PROF armv5te
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv7-a-neon b/vm/compiler/template/config-armv7-a-neon
new file mode 100644
index 0000000..9d66e55
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a-neon
@@ -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.
+
+#
+# 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 TEMPLATE_PERIODIC_PROFILING armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF armv5te
+    op TEMPLATE_RETURN_PROF armv5te
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-ia32 b/vm/compiler/template/config-ia32
new file mode 100644
index 0000000..5709017
--- /dev/null
+++ b/vm/compiler/template/config-ia32
@@ -0,0 +1,45 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import ia32/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import ia32/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start ia32
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import ia32/footer.S
diff --git a/vm/compiler/template/config-mips b/vm/compiler/template/config-mips
new file mode 100644
index 0000000..f212150
--- /dev/null
+++ b/vm/compiler/template/config-mips
@@ -0,0 +1,93 @@
+
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import mips/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import mips/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start mips
+
+    op TEMPLATE_SHL_LONG mips
+    op TEMPLATE_SHR_LONG mips
+    op TEMPLATE_USHR_LONG mips
+    op TEMPLATE_INT_TO_DOUBLE_VFP mips
+    op TEMPLATE_FLOAT_TO_DOUBLE_VFP mips
+    op TEMPLATE_ADD_DOUBLE_VFP mips
+    op TEMPLATE_DIV_DOUBLE_VFP mips
+    op TEMPLATE_MUL_DOUBLE_VFP mips
+    op TEMPLATE_SUB_DOUBLE_VFP mips
+    op TEMPLATE_ADD_FLOAT_VFP mips
+    op TEMPLATE_DIV_FLOAT_VFP mips
+    op TEMPLATE_MUL_FLOAT_VFP mips
+    op TEMPLATE_SUB_FLOAT_VFP mips
+    op TEMPLATE_FLOAT_TO_INT_VFP mips
+    op TEMPLATE_INT_TO_FLOAT_VFP mips
+    op TEMPLATE_DOUBLE_TO_FLOAT_VFP mips
+    op TEMPLATE_DOUBLE_TO_INT_VFP mips
+    op TEMPLATE_CMP_LONG mips
+    op TEMPLATE_CMPL_FLOAT_VFP mips
+    op TEMPLATE_CMPL_DOUBLE_VFP mips
+    op TEMPLATE_CMPG_FLOAT_VFP mips
+    op TEMPLATE_CMPG_DOUBLE_VFP mips
+    op TEMPLATE_MUL_LONG mips
+    op TEMPLATE_INTERPRET mips
+    op TEMPLATE_THROW_EXCEPTION_COMMON mips
+    op TEMPLATE_SQRT_DOUBLE_VFP mips
+    op TEMPLATE_SAVE_STATE mips
+    op TEMPLATE_RESTORE_STATE mips
+    op TEMPLATE_RETURN mips
+    op TEMPLATE_STRING_COMPARETO mips
+    op TEMPLATE_STRING_INDEXOF mips
+    op TEMPLATE_MEM_OP_DECODE mips
+    op TEMPLATE_MONITOR_ENTER mips
+    op TEMPLATE_MONITOR_ENTER_DEBUG mips
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN mips
+    op TEMPLATE_INVOKE_METHOD_CHAIN mips
+    op TEMPLATE_INVOKE_METHOD_NATIVE mips
+    op TEMPLATE_INVOKE_METHOD_NO_OPT mips
+
+    # New templates for ICS
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF mips
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF mips
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF mips
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF mips
+    op TEMPLATE_PERIODIC_PROFILING mips
+    op TEMPLATE_RETURN_PROF mips
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import mips/footer.S
diff --git a/vm/compiler/template/gen-template.py b/vm/compiler/template/gen-template.py
new file mode 100755
index 0000000..9122fd5
--- /dev/null
+++ b/vm/compiler/template/gen-template.py
@@ -0,0 +1,423 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Using instructions from an architecture-specific config file, generate C
+# and assembly source files for the Dalvik JIT.
+#
+
+import sys, string, re, time
+from string import Template
+
+interp_defs_file = "TemplateOpList.h" # need opcode list
+
+handler_size_bits = -1000
+handler_size_bytes = -1000
+in_op_start = 0             # 0=not started, 1=started, 2=ended
+default_op_dir = None
+opcode_locations = {}
+asm_stub_text = []
+label_prefix = ".L"         # use ".L" to hide labels from gdb
+
+
+# Exception class.
+class DataParseError(SyntaxError):
+    "Failure when parsing data file"
+
+#
+# Set any omnipresent substitution values.
+#
+def getGlobalSubDict():
+    return { "handler_size_bits":handler_size_bits,
+             "handler_size_bytes":handler_size_bytes }
+
+#
+# Parse arch config file --
+# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
+# log2(handler_size_bytes).  Throws an exception if "bytes" is not a power
+# of two.
+#
+def setHandlerSize(tokens):
+    global handler_size_bits, handler_size_bytes
+    if len(tokens) != 2:
+        raise DataParseError("handler-size requires one argument")
+    if handler_size_bits != -1000:
+        raise DataParseError("handler-size may only be set once")
+
+    # compute log2(n), and make sure n is a power of 2
+    handler_size_bytes = bytes = int(tokens[1])
+    bits = -1
+    while bytes > 0:
+        bytes //= 2     # halve with truncating division
+        bits += 1
+
+    if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
+        raise DataParseError("handler-size (%d) must be power of 2 and > 0" \
+                % orig_bytes)
+    handler_size_bits = bits
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def importFile(tokens):
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    source = tokens[1]
+    if source.endswith(".S"):
+        appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
+    else:
+        raise DataParseError("don't know how to import %s (expecting .c/.S)"
+                % source)
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def setAsmStub(tokens):
+    global asm_stub_text
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    try:
+        stub_fp = open(tokens[1])
+        asm_stub_text = stub_fp.readlines()
+    except IOError, err:
+        stub_fp.close()
+        raise DataParseError("unable to load asm-stub: %s" % str(err))
+    stub_fp.close()
+
+#
+# Parse arch config file --
+# Start of opcode list.
+#
+def opStart(tokens):
+    global in_op_start
+    global default_op_dir
+    if len(tokens) != 2:
+        raise DataParseError("opStart takes a directory name argument")
+    if in_op_start != 0:
+        raise DataParseError("opStart can only be specified once")
+    default_op_dir = tokens[1]
+    in_op_start = 1
+
+#
+# Parse arch config file --
+# Set location of a single opcode's source file.
+#
+def opEntry(tokens):
+    #global opcode_locations
+    if len(tokens) != 3:
+        raise DataParseError("op requires exactly two arguments")
+    if in_op_start != 1:
+        raise DataParseError("op statements must be between opStart/opEnd")
+    try:
+        index = opcodes.index(tokens[1])
+    except ValueError:
+        raise DataParseError("unknown opcode %s" % tokens[1])
+    opcode_locations[tokens[1]] = tokens[2]
+
+#
+# Parse arch config file --
+# End of opcode list; emit instruction blocks.
+#
+def opEnd(tokens):
+    global in_op_start
+    if len(tokens) != 1:
+        raise DataParseError("opEnd takes no arguments")
+    if in_op_start != 1:
+        raise DataParseError("opEnd must follow opStart, and only appear once")
+    in_op_start = 2
+
+    loadAndEmitOpcodes()
+
+
+#
+# Extract an ordered list of instructions from the VM sources.  We use the
+# "goto table" definition macro, which has exactly kNumPackedOpcodes
+# entries.
+#
+def getOpcodeList():
+    opcodes = []
+    opcode_fp = open("%s/%s" % (target_arch, interp_defs_file))
+    opcode_re = re.compile(r"^JIT_TEMPLATE\((\w+)\)", re.DOTALL)
+    for line in opcode_fp:
+        match = opcode_re.match(line)
+        if not match:
+            continue
+        opcodes.append("TEMPLATE_" + match.group(1))
+    opcode_fp.close()
+
+    return opcodes
+
+
+#
+# Load and emit opcodes for all kNumPackedOpcodes instructions.
+#
+def loadAndEmitOpcodes():
+    sister_list = []
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global dvmCompilerTemplateStart\n")
+    asm_fp.write("    .type   dvmCompilerTemplateStart, %function\n")
+    asm_fp.write("    .section .data.rel.ro\n\n")
+    asm_fp.write("dvmCompilerTemplateStart:\n\n")
+
+    for i in xrange(len(opcodes)):
+        op = opcodes[i]
+
+        if opcode_locations.has_key(op):
+            location = opcode_locations[op]
+        else:
+            location = default_op_dir
+
+        loadAndEmitAsm(location, i, sister_list)
+
+    # Use variable sized handlers now
+    # asm_fp.write("\n    .balign %d\n" % handler_size_bytes)
+    asm_fp.write("    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart\n")
+
+#
+# Load an assembly fragment and emit it.
+#
+def loadAndEmitAsm(location, opindex, sister_list):
+    op = opcodes[opindex]
+    source = "%s/%s.S" % (location, op)
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+    print " emit %s --> asm" % source
+
+    emitAsmHeader(asm_fp, dict)
+    appendSourceFile(source, dict, asm_fp, sister_list)
+
+#
+# Output the alignment directive and label for an assembly piece.
+#
+def emitAsmHeader(outfp, dict):
+    outfp.write("/* ------------------------------ */\n")
+    # The alignment directive ensures that the handler occupies
+    # at least the correct amount of space.  We don't try to deal
+    # with overflow here.
+    outfp.write("    .balign 4\n")
+    # Emit a label so that gdb will say the right thing.  We prepend an
+    # underscore so the symbol name doesn't clash with the Opcode enum.
+    template_name = "dvmCompiler_%(opcode)s" % dict
+    outfp.write("    .global %s\n" % template_name);
+    outfp.write("%s:\n" % template_name);
+
+#
+# Output a generic instruction stub that updates the "glue" struct and
+# calls the C implementation.
+#
+def emitAsmStub(outfp, dict):
+    emitAsmHeader(outfp, dict)
+    for line in asm_stub_text:
+        templ = Template(line)
+        outfp.write(templ.substitute(dict))
+
+#
+# Append the file specified by "source" to the open "outfp".  Each line will
+# be template-replaced using the substitution dictionary "dict".
+#
+# If the first line of the file starts with "%" it is taken as a directive.
+# A "%include" line contains a filename and, optionally, a Python-style
+# dictionary declaration with substitution strings.  (This is implemented
+# with recursion.)
+#
+# If "sister_list" is provided, and we find a line that contains only "&",
+# all subsequent lines from the file will be appended to sister_list instead
+# of copied to the output.
+#
+# This may modify "dict".
+#
+def appendSourceFile(source, dict, outfp, sister_list):
+    outfp.write("/* File: %s */\n" % source)
+    infp = open(source, "r")
+    in_sister = False
+    for line in infp:
+        if line.startswith("%include"):
+            # Parse the "include" line
+            tokens = line.strip().split(' ', 2)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%include in %s" % source)
+
+            alt_source = tokens[1].strip("\"")
+            if alt_source == source:
+                raise DataParseError("self-referential %%include in %s"
+                        % source)
+
+            new_dict = dict.copy()
+            if len(tokens) == 3:
+                new_dict.update(eval(tokens[2]))
+            #print " including src=%s dict=%s" % (alt_source, new_dict)
+            appendSourceFile(alt_source, new_dict, outfp, sister_list)
+            continue
+
+        elif line.startswith("%default"):
+            # copy keywords into dictionary
+            tokens = line.strip().split(' ', 1)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%default in %s" % source)
+            defaultValues = eval(tokens[1])
+            for entry in defaultValues:
+                dict.setdefault(entry, defaultValues[entry])
+            continue
+
+        elif line.startswith("%verify"):
+            # more to come, someday
+            continue
+
+        elif line.startswith("%break") and sister_list != None:
+            # allow more than one %break, ignoring all following the first
+            if not in_sister:
+                in_sister = True
+                sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
+            continue
+
+        # perform keyword substitution if a dictionary was provided
+        if dict != None:
+            templ = Template(line)
+            try:
+                subline = templ.substitute(dict)
+            except KeyError, err:
+                raise DataParseError("keyword substitution failed in %s: %s"
+                        % (source, str(err)))
+            except:
+                print "ERROR: substitution failed: " + line
+                raise
+        else:
+            subline = line
+
+        # write output to appropriate file
+        if in_sister:
+            sister_list.append(subline)
+        else:
+            outfp.write(subline)
+    outfp.write("\n")
+    infp.close()
+
+#
+# Emit a C-style section header comment.
+#
+def emitSectionComment(str, fp):
+    equals = "========================================" \
+             "==================================="
+
+    fp.write("\n/*\n * %s\n *  %s\n * %s\n */\n" %
+        (equals, str, equals))
+
+
+#
+# ===========================================================================
+# "main" code
+#
+
+#
+# Check args.
+#
+if len(sys.argv) != 3:
+    print "Usage: %s target-arch output-dir" % sys.argv[0]
+    sys.exit(2)
+
+target_arch = sys.argv[1]
+output_dir = sys.argv[2]
+
+#
+# Extract opcode list.
+#
+opcodes = getOpcodeList()
+#for op in opcodes:
+#    print "  %s" % op
+
+#
+# Open config file.
+#
+try:
+    config_fp = open("config-%s" % target_arch)
+except:
+    print "Unable to open config file 'config-%s'" % target_arch
+    sys.exit(1)
+
+#
+# Open and prepare output files.
+#
+try:
+    asm_fp = open("%s/CompilerTemplateAsm-%s.S" % (output_dir, target_arch), "w")
+except:
+    print "Unable to open output files"
+    print "Make sure directory '%s' exists and existing files are writable" \
+            % output_dir
+    # Ideally we'd remove the files to avoid confusing "make", but if they
+    # failed to open we probably won't be able to remove them either.
+    sys.exit(1)
+
+print "Generating %s" % (asm_fp.name)
+
+file_header = """/*
+ * This file was generated automatically by gen-template.py for '%s'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+""" % (target_arch)
+
+asm_fp.write(file_header)
+
+#
+# Process the config file.
+#
+failed = False
+try:
+    for line in config_fp:
+        line = line.strip()         # remove CRLF, leading spaces
+        tokens = line.split(' ')    # tokenize
+        #print "%d: %s" % (len(tokens), tokens)
+        if len(tokens[0]) == 0:
+            #print "  blank"
+            pass
+        elif tokens[0][0] == '#':
+            #print "  comment"
+            pass
+        else:
+            if tokens[0] == "handler-size":
+                setHandlerSize(tokens)
+            elif tokens[0] == "import":
+                importFile(tokens)
+            elif tokens[0] == "asm-stub":
+                setAsmStub(tokens)
+            elif tokens[0] == "op-start":
+                opStart(tokens)
+            elif tokens[0] == "op-end":
+                opEnd(tokens)
+            elif tokens[0] == "op":
+                opEntry(tokens)
+            else:
+                raise DataParseError, "unrecognized command '%s'" % tokens[0]
+except DataParseError, err:
+    print "Failed: " + str(err)
+    # TODO: remove output files so "make" doesn't get confused
+    failed = True
+    asm_fp.close()
+    c_fp = asm_fp = None
+
+config_fp.close()
+
+#
+# Done!
+#
+if asm_fp:
+    asm_fp.close()
+
+sys.exit(failed)
diff --git a/vm/compiler/template/ia32/TEMPLATE_INTERPRET.S b/vm/compiler/template/ia32/TEMPLATE_INTERPRET.S
new file mode 100644
index 0000000..5c7bf7c
--- /dev/null
+++ b/vm/compiler/template/ia32/TEMPLATE_INTERPRET.S
@@ -0,0 +1,38 @@
+    /*
+     * This handler is a bit odd - it may be called via chaining or
+     * from static code and is expected to cause control to flow
+     * to the interpreter.  The problem is where to find the Dalvik
+     * PC of the next instruction.  When called via chaining, the dPC
+     * will be located at *rp.  When called from static code, rPC is
+     * valid and rp is a real return pointer (that should be ignored).
+     * The Arm target deals with this by using the link register as
+     * a flag.  If it is zero, we know we were called from static code.
+     * If non-zero, it points to the chain cell containing dPC.
+     * For x86, we'll infer the source by looking where rp points.
+     * If it points to anywhere within the code cache, we'll assume
+     * we got here via chaining.  Otherwise, we'll assume rPC is valid.
+     *
+     * On entry:
+     *    (TOS)<- return pointer or pointer to dPC
+     */
+
+/*
+ * FIXME - this won't work as-is.  The cache boundaries are not
+ * set up until later.  Perhaps rething this whole thing.  Do we
+ * really need an interpret teplate?
+ */
+
+
+     movl   rSELF,%ecx
+     movl   $$.LinterpPunt,%edx
+     pop    %eax
+     /*cmpl   %eax,offThread_jitCacheEnd(%ecx)*/
+     ja     1f
+     /*cmpl   %eax,offThread_jitCacheStart(%ecx)*/
+     jb     1f
+     movl   %eax,rPC
+1:
+     jmp    *(%edx)
+
+.LinterpPunt:
+    .long   dvmJitToInterpPunt
diff --git a/vm/compiler/template/ia32/TemplateOpList.h b/vm/compiler/template/ia32/TemplateOpList.h
new file mode 100644
index 0000000..a5000da
--- /dev/null
+++ b/vm/compiler/template/ia32/TemplateOpList.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(INTERPRET)
diff --git a/vm/compiler/template/ia32/footer.S b/vm/compiler/template/ia32/footer.S
new file mode 100644
index 0000000..226e928
--- /dev/null
+++ b/vm/compiler/template/ia32/footer.S
@@ -0,0 +1,13 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
diff --git a/vm/compiler/template/ia32/header.S b/vm/compiler/template/ia32/header.S
new file mode 100644
index 0000000..ea2cc0f
--- /dev/null
+++ b/vm/compiler/template/ia32/header.S
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/* Subset of defines from mterp/x86/header.S */
+#define rSELF (%ebp)
+#define rPC   %esi
+#define rFP   %edi
+#define rINST %ebx
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
diff --git a/vm/compiler/template/ia32/platform.S b/vm/compiler/template/ia32/platform.S
new file mode 100644
index 0000000..a84e62d
--- /dev/null
+++ b/vm/compiler/template/ia32/platform.S
@@ -0,0 +1,7 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
diff --git a/vm/compiler/template/mips/TEMPLATE_ADD_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_ADD_DOUBLE_VFP.S
new file mode 100644
index 0000000..56a02e3
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_ADD_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__adddf3)","instr_f":"add.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_ADD_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_ADD_FLOAT_VFP.S
new file mode 100644
index 0000000..b0cbb31
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_ADD_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__addsf3)", "instr_f":"add.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPG_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPG_DOUBLE_VFP.S
new file mode 100644
index 0000000..f5fa114
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPG_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/TEMPLATE_CMPL_DOUBLE_VFP.S" { "naninst":"li            rTEMP, 1" }
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPG_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPG_FLOAT_VFP.S
new file mode 100644
index 0000000..c239a75
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPG_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/TEMPLATE_CMPL_FLOAT_VFP.S" { "naninst":"li     rTEMP, 1" }
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPL_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPL_DOUBLE_VFP.S
new file mode 100644
index 0000000..0a1dd68
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPL_DOUBLE_VFP.S
@@ -0,0 +1,68 @@
+%default { "naninst":"li     rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two double precision floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    move rOBJ, a0                       # save a0
+    move rBIX, a1                       # save a1
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__eqdf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, ${opcode}_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__ltdf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, ${opcode}_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__gtdf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, ${opcode}_finish
+#else
+    LOAD64_F(fs0, fs0f, a0)             # fs0<- vBB
+    LOAD64_F(fs1, fs1f, a1)             # fs1<- vCC
+    c.olt.d     fcc0, fs0, fs1          # Is fs0 < fs1
+    li          rTEMP, -1
+    bc1t        fcc0, ${opcode}_finish
+    c.olt.d     fcc0, fs1, fs0
+    li          rTEMP, 1
+    bc1t        fcc0, ${opcode}_finish
+    c.eq.d      fcc0, fs0, fs1
+    li          rTEMP, 0
+    bc1t        fcc0, ${opcode}_finish
+#endif
+
+    $naninst
+
+${opcode}_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPL_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPL_FLOAT_VFP.S
new file mode 100644
index 0000000..7ef7723
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPL_FLOAT_VFP.S
@@ -0,0 +1,68 @@
+%default { "naninst":"li     rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    LOAD(rOBJ, a0)                      # rOBJ<- vBB
+    LOAD(rBIX, a1)                      # rBIX<- vCC
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__eqsf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, ${opcode}_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__ltsf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, ${opcode}_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__gtsf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, ${opcode}_finish
+#else
+    LOAD_F(fs0, a0)                     # fs0<- vBB
+    LOAD_F(fs1, a1)                     # fs1<- vCC
+    c.olt.s     fcc0, fs0, fs1          #Is fs0 < fs1
+    li          rTEMP, -1
+    bc1t        fcc0, ${opcode}_finish
+    c.olt.s     fcc0, fs1, fs0
+    li          rTEMP, 1
+    bc1t        fcc0, ${opcode}_finish
+    c.eq.s      fcc0, fs0, fs1
+    li          rTEMP, 0
+    bc1t        fcc0, ${opcode}_finish
+#endif
+
+    $naninst
+
+${opcode}_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_CMP_LONG.S b/vm/compiler/template/mips/TEMPLATE_CMP_LONG.S
new file mode 100644
index 0000000..9ecb069
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMP_LONG.S
@@ -0,0 +1,27 @@
+%verify "endianess"
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;        # (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;        # (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     *
+     * This code assumes the register pair ordering will depend on endianess (a1:a0 or a0:a1).
+     *    a1:a0 => vBB
+     *    a3:a2 => vCC
+     */
+    /* cmp-long vAA, vBB, vCC */
+    slt    t0, rARG1, rARG3             # compare hi
+    sgt    t1, rARG1, rARG3
+    subu   v0, t1, t0                   # v0<- (-1,1,0)
+    bnez   v0, .L${opcode}_finish
+                                        # at this point x.hi==y.hi
+    sltu   t0, rARG0, rARG2             # compare lo
+    sgtu   t1, rARG0, rARG2
+    subu   v0, t1, t0                   # v0<- (-1,1,0) for [< > =]
+.L${opcode}_finish:
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_DIV_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_DIV_DOUBLE_VFP.S
new file mode 100644
index 0000000..a951f93
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DIV_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__divdf3)","instr_f":"div.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_DIV_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_DIV_FLOAT_VFP.S
new file mode 100644
index 0000000..11b3da6
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DIV_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__divsf3)", "instr_f":"div.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..51b1e96
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funopNarrower.S" {"instr":"JAL(__truncdfsf2)","instr_f":"cvt.s.d  fv0,fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_INT_VFP.S b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_INT_VFP.S
new file mode 100644
index 0000000..4774bb1
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_INT_VFP.S
@@ -0,0 +1,79 @@
+%verify "executed"
+%include "mips/funopNarrower.S" {"instr":"b    d2i_doconv","instr_f":"b    d2i_doconv"}
+
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rOBJ as global to hold arguments (they are not bound to a global var)
+ */
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move        rBIX, rARG0                       # save a0
+    move        rOBJ, rARG1                       #  and a1
+    JAL(__gedf2)                               # is arg >= maxint?
+
+    move        t0, v0
+    li          v0, ~0x80000000                # return maxint (7fffffff)
+    bgez        t0, .L${opcode}_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                       # recover arg
+    move        rARG1, rOBJ
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                               # is arg <= minint?
+
+    move        t0, v0
+    li          v0, 0x80000000                 # return minint (80000000)
+    blez        t0, .L${opcode}_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    move        rARG2, rBIX                  # compare against self
+    move        rARG3, rOBJ
+    JAL(__nedf2)                        # is arg == self?
+
+    move        t0, v0                  # zero == no
+    li          v0, 0
+    bnez        t0, .L${opcode}_set_vreg        # return zero for NaN
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    JAL(__fixdfsi)                      # convert double to int
+    b           .L${opcode}_set_vreg
+#else
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa1, fa0
+    l.s         fv0, .LDOUBLE_TO_INT_maxret
+    bc1t        .L${opcode}_set_vreg_f
+
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa0, fa1
+    l.s         fv0, .LDOUBLE_TO_INT_minret
+    bc1t        .L${opcode}_set_vreg_f
+
+    mov.d       fa1, fa0
+    c.un.d      fcc0, fa0, fa1
+    li.s        fv0, 0
+    bc1t        .L${opcode}_set_vreg_f
+
+    trunc.w.d   fv0, fa0
+    b           .L${opcode}_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword   0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword   0xc1e0000000000000                  # minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word   0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word   0x80000000
diff --git a/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..66d14ce
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funopWider.S" {"instr":"JAL(__extendsfdf2)","instr_f":"cvt.d.s fv0, fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_INT_VFP.S b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_INT_VFP.S
new file mode 100644
index 0000000..6eaaab9
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_INT_VFP.S
@@ -0,0 +1,62 @@
+%verify "executed"
+%include "mips/funop.S" {"instr":"b    f2i_doconv","instr_f":"b        f2i_doconv"}
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+        li      a1, 0x4f000000  # (float)maxint
+        move    rBIX, a0
+        JAL(__gesf2)            # is arg >= maxint?
+        move    t0, v0
+        li      v0, ~0x80000000 # return maxint (7fffffff)
+        bgez    t0, .L${opcode}_set_vreg
+
+        move    a0, rBIX                # recover arg
+        li      a1, 0xcf000000  # (float)minint
+        JAL(__lesf2)
+
+        move    t0, v0
+        li      v0, 0x80000000  # return minint (80000000)
+        blez    t0, .L${opcode}_set_vreg
+        move    a0, rBIX
+        move    a1, rBIX
+        JAL(__nesf2)
+
+        move    t0, v0
+        li      v0, 0           # return zero for NaN
+        bnez    t0, .L${opcode}_set_vreg
+
+        move    a0, rBIX
+        JAL(__fixsfsi)
+        b .L${opcode}_set_vreg
+#else
+        l.s             fa1, .LFLOAT_TO_INT_max
+        c.ole.s         fcc0, fa1, fa0
+        l.s             fv0, .LFLOAT_TO_INT_ret_max
+        bc1t            .L${opcode}_set_vreg_f
+
+        l.s             fa1, .LFLOAT_TO_INT_min
+        c.ole.s         fcc0, fa0, fa1
+        l.s             fv0, .LFLOAT_TO_INT_ret_min
+        bc1t            .L${opcode}_set_vreg_f
+
+        mov.s           fa1, fa0
+        c.un.s          fcc0, fa0, fa1
+        li.s            fv0, 0
+        bc1t            .L${opcode}_set_vreg_f
+
+        trunc.w.s       fv0, fa0
+        b .L${opcode}_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+        .word   0x4f000000
+.LFLOAT_TO_INT_min:
+        .word   0xcf000000
+.LFLOAT_TO_INT_ret_max:
+        .word   0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+        .word   0x80000000
+
diff --git a/vm/compiler/template/mips/TEMPLATE_INTERPRET.S b/vm/compiler/template/mips/TEMPLATE_INTERPRET.S
new file mode 100644
index 0000000..1284621
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INTERPRET.S
@@ -0,0 +1,21 @@
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC.
+     * On entry:
+     *    ra - if NULL:
+     *        a1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [ra] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    la      t0, dvmJitToInterpPunt
+    move    a0, a1
+    beq     ra, zero, 1f
+    lw      a0, 0(ra)
+1:
+    jr      t0
+    # doesn't return
diff --git a/vm/compiler/template/mips/TEMPLATE_INT_TO_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_INT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..0dca600
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funopWider.S" {"instr":"JAL(__floatsidf)","instr_f":"cvt.d.w    fv0, fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_INT_TO_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_INT_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..384c207
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INT_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funop.S" {"instr":"JAL(__floatsisf)","instr_f":"cvt.s.w fv0, fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN.S
new file mode 100644
index 0000000..c1e03ce
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN.S
@@ -0,0 +1,67 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    # methodToCall is guaranteed to be non-native
+$chaintgt:
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    add    t2, ra, 8                              # setup the punt-to-interp address
+                                                  # 8 bytes skips branch and delay slot
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    jr     t2                                     # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    jr     t2                                     # bail to the interpreter
+
+2:
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- methodToCall->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    move   a1, rSELF
+    # a0=methodToCall, a1=rSELF
+    la     t9, dvmFastMethodTraceEnter
+    jalr   t9
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    RETURN                                        # return to the callee-chaining cell
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
new file mode 100644
index 0000000..797ff03
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE.S
new file mode 100644
index 0000000..7bba88c
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE.S
@@ -0,0 +1,106 @@
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    lw     rTEMP, offMethod_nativeFunc(a0)        # t9<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+2:
+#else
+    RETURN                                        # bail to the interpreter unconditionally
+#endif
+
+    # go ahead and transfer control to the native code
+    lw     t6, offThread_jniLocal_topCookie(rSELF)  # t6<- thread->localRef->...
+    sw     a1, offThread_curFrame(rSELF)          # self->curFrame = newFp
+    sw     zero, offThread_inJitCodeCache(rSELF)  # not in the jit code cache
+    sw     t6, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                  # newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(rBIX, a1)                    # rBIX<- new stack save area
+    move   a2, a0                                 # a2<- methodToCall
+    move   a0, a1                                 # a0<- newFp
+    add    a1, rSELF, offThread_retval            # a1<- &retval
+    move   a3, rSELF                              # a3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # a2: methodToCall
+    # preserve rTEMP,a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(rTEMP, 16)
+
+    move   a0, a2
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)                                      # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a0-a3
+    SCRATCH_LOAD(rTEMP, 16)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    move   rOBJ, a2                               # save a2
+#endif
+    move   t9, rTEMP
+    JALR(t9)                                   # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    move   a0, rOBJ
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+#endif
+
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC (advance 3 dalvik instr)
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
new file mode 100644
index 0000000..e167996
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_NATIVE.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S
new file mode 100644
index 0000000..06935d7
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S
@@ -0,0 +1,80 @@
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    lw     t0, offMethod_accessFlags(a0)          # t0<- methodToCall->accessFlags
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+
+2:
+    and    t6, t0, ACC_NATIVE
+    beqz   t6, 3f
+#if !defined(WITH_SELF_VERIFICATION)
+    j      .LinvokeNative
+#else
+    RETURN                                        # bail to the interpreter
+#endif
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     t0, .LdvmJitToInterpTraceSelectNoChain # defined in footer.S
+    lw     rTEMP, (t0)
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- method->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve rTEMP,a1-a3
+    SCRATCH_STORE(rTEMP, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    # a0=methodToCall, a1=rSELF
+    move   a1, rSELF
+    la     t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a1-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(rTEMP, 0)
+#endif
+
+    # Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    li     a0, kInlineCacheMiss
+#endif
+    jr     rTEMP                                  # dvmJitToInterpTraceSelectNoChain
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
new file mode 100644
index 0000000..386ce63
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
new file mode 100644
index 0000000..e95ab32
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
@@ -0,0 +1,59 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      u4 delay_slot;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr   : to branch to the chaining cell
+     *    - lr+8 : to punt to the interpreter
+     *    - lr+16: to fully resolve the callee and may rechain.
+     *             a3 <- class
+     */
+    # a0 = this, a1 = returnCell, a2 = predictedChainCell, rPC = dalvikCallsite
+    lw      a3, offObject_clazz(a0)     # a3 <- this->class
+    lw      rIBASE, 8(a2)                   # t0 <- predictedChainCell->clazz
+    lw      a0, 12(a2)                  # a0 <- predictedChainCell->method
+    lw      t1, offThread_icRechainCount(rSELF)    # t1 <- shared rechainCount
+
+#if defined(WITH_JIT_TUNING)
+    la      rINST, .LdvmICHitCount
+    #add     t2, t2, 1
+    bne    a3, rIBASE, 1f
+    nop
+    lw      t2, 0(rINST)
+    add     t2, t2, 1
+    sw      t2, 0(rINST)
+1:
+    #add     t2, t2, 1
+#endif
+    beq     a3, rIBASE, $chaintgt       # branch if predicted chain is valid
+    lw      rINST, offClassObject_vtable(a3)     # rINST <- this->class->vtable
+    beqz    rIBASE, 2f                      # initialized class or not
+    sub     a1, t1, 1                   # count--
+    sw      a1, offThread_icRechainCount(rSELF)   # write back to InterpState
+    b       3f
+2:
+    move    a1, zero
+3:
+    add     ra, ra, 16                  # return to fully-resolve landing pad
+    /*
+     * a1 <- count
+     * a2 <- &predictedChainCell
+     * a3 <- this->class
+     * rPC <- dPC
+     * rINST <- this->class->vtable
+     */
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
new file mode 100644
index 0000000..39d6452
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/mips/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..038ccfd
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,165 @@
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+#ifdef HARD_FLOAT
+    /* push f0-f31 onto stack */
+    sw      f0, fr0*-4(sp)              # push f0
+    sw      f1, fr1*-4(sp)              # push f1
+    sw      f2, fr2*-4(sp)              # push f2
+    sw      f3, fr3*-4(sp)              # push f3
+    sw      f4, fr4*-4(sp)              # push f4
+    sw      f5, fr5*-4(sp)              # push f5
+    sw      f6, fr6*-4(sp)              # push f6
+    sw      f7, fr7*-4(sp)              # push f7
+    sw      f8, fr8*-4(sp)              # push f8
+    sw      f9, fr9*-4(sp)              # push f9
+    sw      f10, fr10*-4(sp)            # push f10
+    sw      f11, fr11*-4(sp)            # push f11
+    sw      f12, fr12*-4(sp)            # push f12
+    sw      f13, fr13*-4(sp)            # push f13
+    sw      f14, fr14*-4(sp)            # push f14
+    sw      f15, fr15*-4(sp)            # push f15
+    sw      f16, fr16*-4(sp)            # push f16
+    sw      f17, fr17*-4(sp)            # push f17
+    sw      f18, fr18*-4(sp)            # push f18
+    sw      f19, fr19*-4(sp)            # push f19
+    sw      f20, fr20*-4(sp)            # push f20
+    sw      f21, fr21*-4(sp)            # push f21
+    sw      f22, fr22*-4(sp)            # push f22
+    sw      f23, fr23*-4(sp)            # push f23
+    sw      f24, fr24*-4(sp)            # push f24
+    sw      f25, fr25*-4(sp)            # push f25
+    sw      f26, fr26*-4(sp)            # push f26
+    sw      f27, fr27*-4(sp)            # push f27
+    sw      f28, fr28*-4(sp)            # push f28
+    sw      f29, fr29*-4(sp)            # push f29
+    sw      f30, fr30*-4(sp)            # push f30
+    sw      f31, fr31*-4(sp)            # push f31
+
+    sub     sp, (32-0)*4                # adjust stack pointer
+#endif
+
+    /* push gp registers (except zero, gp, sp, and fp) */
+    .set noat
+    sw      AT, r_AT*-4(sp)             # push at
+    .set at
+    sw      v0, r_V0*-4(sp)             # push v0
+    sw      v1, r_V1*-4(sp)             # push v1
+    sw      a0, r_A0*-4(sp)             # push a0
+    sw      a1, r_A1*-4(sp)             # push a1
+    sw      a2, r_A2*-4(sp)             # push a2
+    sw      a3, r_A3*-4(sp)             # push a3
+    sw      t0, r_T0*-4(sp)             # push t0
+    sw      t1, r_T1*-4(sp)             # push t1
+    sw      t2, r_T2*-4(sp)             # push t2
+    sw      t3, r_T3*-4(sp)             # push t3
+    sw      t4, r_T4*-4(sp)             # push t4
+    sw      t5, r_T5*-4(sp)             # push t5
+    sw      t6, r_T6*-4(sp)             # push t6
+    sw      t7, r_T7*-4(sp)             # push t7
+    sw      s0, r_S0*-4(sp)             # push s0
+    sw      s1, r_S1*-4(sp)             # push s1
+    sw      s2, r_S2*-4(sp)             # push s2
+    sw      s3, r_S3*-4(sp)             # push s3
+    sw      s4, r_S4*-4(sp)             # push s4
+    sw      s5, r_S5*-4(sp)             # push s5
+    sw      s6, r_S6*-4(sp)             # push s6
+    sw      s7, r_S7*-4(sp)             # push s7
+    sw      t8, r_T8*-4(sp)             # push t8
+    sw      t9, r_T9*-4(sp)             # push t9
+    sw      k0, r_K0*-4(sp)             # push k0
+    sw      k1, r_K1*-4(sp)             # push k1
+    sw      ra, r_RA*-4(sp)             # push RA
+
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    sub     sp, (32-0)*4                # adjust stack pointer
+
+    la     a2, .LdvmSelfVerificationMemOpDecode  # defined in footer.S
+    lw     a2, (a2)
+    move   a0, ra                       # a0<- link register
+    move   a1, sp                       # a1<- stack pointer
+    JALR(a2)
+
+    /* pop gp registers (except zero, gp, sp, and fp) */
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    add     sp, (32-0)*4                # adjust stack pointer
+    .set noat
+    lw      AT, r_AT*-4(sp)             # pop at
+    .set at
+    lw      v0, r_V0*-4(sp)             # pop v0
+    lw      v1, r_V1*-4(sp)             # pop v1
+    lw      a0, r_A0*-4(sp)             # pop a0
+    lw      a1, r_A1*-4(sp)             # pop a1
+    lw      a2, r_A2*-4(sp)             # pop a2
+    lw      a3, r_A3*-4(sp)             # pop a3
+    lw      t0, r_T0*-4(sp)             # pop t0
+    lw      t1, r_T1*-4(sp)             # pop t1
+    lw      t2, r_T2*-4(sp)             # pop t2
+    lw      t3, r_T3*-4(sp)             # pop t3
+    lw      t4, r_T4*-4(sp)             # pop t4
+    lw      t5, r_T5*-4(sp)             # pop t5
+    lw      t6, r_T6*-4(sp)             # pop t6
+    lw      t7, r_T7*-4(sp)             # pop t7
+    lw      s0, r_S0*-4(sp)             # pop s0
+    lw      s1, r_S1*-4(sp)             # pop s1
+    lw      s2, r_S2*-4(sp)             # pop s2
+    lw      s3, r_S3*-4(sp)             # pop s3
+    lw      s4, r_S4*-4(sp)             # pop s4
+    lw      s5, r_S5*-4(sp)             # pop s5
+    lw      s6, r_S6*-4(sp)             # pop s6
+    lw      s7, r_S7*-4(sp)             # pop s7
+    lw      t8, r_T8*-4(sp)             # pop t8
+    lw      t9, r_T9*-4(sp)             # pop t9
+    lw      k0, r_K0*-4(sp)             # pop k0
+    lw      k1, r_K1*-4(sp)             # pop k1
+    lw      ra, r_RA*-4(sp)             # pop RA
+
+#ifdef HARD_FLOAT
+    /* pop f0-f31 from stack */
+    add     sp, (32-0)*4                # adjust stack pointer
+    lw      f0, fr0*-4(sp)              # pop f0
+    lw      f1, fr1*-4(sp)              # pop f1
+    lw      f2, fr2*-4(sp)              # pop f2
+    lw      f3, fr3*-4(sp)              # pop f3
+    lw      f4, fr4*-4(sp)              # pop f4
+    lw      f5, fr5*-4(sp)              # pop f5
+    lw      f6, fr6*-4(sp)              # pop f6
+    lw      f7, fr7*-4(sp)              # pop f7
+    lw      f8, fr8*-4(sp)              # pop f8
+    lw      f9, fr9*-4(sp)              # pop f9
+    lw      f10, fr10*-4(sp)            # pop f10
+    lw      f11, fr11*-4(sp)            # pop f11
+    lw      f12, fr12*-4(sp)            # pop f12
+    lw      f13, fr13*-4(sp)            # pop f13
+    lw      f14, fr14*-4(sp)            # pop f14
+    lw      f15, fr15*-4(sp)            # pop f15
+    lw      f16, fr16*-4(sp)            # pop f16
+    lw      f17, fr17*-4(sp)            # pop f17
+    lw      f18, fr18*-4(sp)            # pop f18
+    lw      f19, fr19*-4(sp)            # pop f19
+    lw      f20, fr20*-4(sp)            # pop f20
+    lw      f21, fr21*-4(sp)            # pop f21
+    lw      f22, fr22*-4(sp)            # pop f22
+    lw      f23, fr23*-4(sp)            # pop f23
+    lw      f24, fr24*-4(sp)            # pop f24
+    lw      f25, fr25*-4(sp)            # pop f25
+    lw      f26, fr26*-4(sp)            # pop f26
+    lw      f27, fr27*-4(sp)            # pop f27
+    lw      f28, fr28*-4(sp)            # pop f28
+    lw      f29, fr29*-4(sp)            # pop f29
+    lw      f30, fr30*-4(sp)            # pop f30
+    lw      f31, fr31*-4(sp)            # pop f31
+#endif
+
+    RETURN
+#endif
diff --git a/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER.S b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER.S
new file mode 100644
index 0000000..902cdb7
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER.S
@@ -0,0 +1,25 @@
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li      a0, kHeavyweightMonitor
+#endif
+    jr      a2
diff --git a/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER_DEBUG.S b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER_DEBUG.S
new file mode 100644
index 0000000..23bf661
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER_DEBUG.S
@@ -0,0 +1,30 @@
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     *
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # test for exception
+    lw     a1, offThread_exception(rSELF)
+    beqz   a1, 1f
+    sub    a0, rPC, 2                           # roll dPC back to this monitor instruction
+    j      .LhandleException
+1:
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li     a0, kHeavyweightMonitor
+#endif
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+    jr     a2
diff --git a/vm/compiler/template/mips/TEMPLATE_MUL_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_MUL_DOUBLE_VFP.S
new file mode 100644
index 0000000..9254d76
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MUL_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__muldf3)","instr_f":"mul.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_MUL_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_MUL_FLOAT_VFP.S
new file mode 100644
index 0000000..c1517b3
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MUL_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__mulsf3)","instr_f":"mul.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_MUL_LONG.S b/vm/compiler/template/mips/TEMPLATE_MUL_LONG.S
new file mode 100644
index 0000000..d91dcb8
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MUL_LONG.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in a0/a1, op2 in a2/a3, return in v0/v1
+     *
+     * Consider WXxYZ (a1a0 x a3a2) with a long multiply:
+     *
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     *
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     rRESULT1,rARG3,rARG0              #  v1= a3a0
+    multu   rARG2,rARG0
+    mfhi    t1
+    mflo    rRESULT0                          #  v0= a2a0
+    mul     t0,rARG2,rARG1                    #  t0= a2a1
+    addu    rRESULT1,rRESULT1,t1              #  v1= a3a0 + hi(a2a0)
+    addu    rRESULT1,rRESULT1,t0              #  v1= a3a0 + hi(a2a0) + a2a1;
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_PERIODIC_PROFILING.S b/vm/compiler/template/mips/TEMPLATE_PERIODIC_PROFILING.S
new file mode 100644
index 0000000..4df771d
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_PERIODIC_PROFILING.S
@@ -0,0 +1,28 @@
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (ra-16) is address of pointer to counter.  Note: the counter
+     *    actually exists 16 bytes before the return target for mips.
+     *     - 4 bytes for prof count addr.
+     *     - 4 bytes for chain cell offset (2bytes 32 bit aligned).
+     *     - 4 bytes for call TEMPLATE_PERIODIC_PROFILING.
+     *     - 4 bytes for call delay slot.
+     */
+     lw     a0, -16(ra)
+     lw     a1, offThread_pProfileCountdown(rSELF)
+     lw     a2, 0(a0)                   # get counter
+     lw     a3, 0(a1)                   # get countdown timer
+     addu   a2, 1
+     sub    a3, 1                       # FIXME - bug in ARM code???
+     bltz   a3, .L${opcode}_disable_profiling
+     sw     a2, 0(a0)
+     sw     a3, 0(a1)
+     RETURN
+.L${opcode}_disable_profiling:
+     la     a0, dvmJitTraceProfilingOff
+     JALR(a0)
+     # The ra register is preserved by the JALR macro.
+     jr     ra
diff --git a/vm/compiler/template/mips/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/mips/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..a4c505b
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,91 @@
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    a0 - offset from rSELF to the 1st element of the coreRegs save array.
+     * Note: the following registers are not restored
+     *       zero, AT, gp, sp, fp, ra
+     */
+
+    add     a0, a0, rSELF               # pointer to heapArgSpace.coreRegs[0]
+#if 0
+    lw      zero, r_ZERO*4(a0)          # restore zero
+#endif
+    .set noat
+    lw      AT, r_AT*4(a0)              # restore at
+    .set at
+    lw      v0, r_V0*4(a0)              # restore v0
+    lw      v1, r_V1*4(a0)              # restore v1
+
+    lw      a1, r_A1*4(a0)              # restore a1
+    lw      a2, r_A2*4(a0)              # restore a2
+    lw      a3, r_A3*4(a0)              # restore a3
+
+    lw      t0, r_T0*4(a0)              # restore t0
+    lw      t1, r_T1*4(a0)              # restore t1
+    lw      t2, r_T2*4(a0)              # restore t2
+    lw      t3, r_T3*4(a0)              # restore t3
+    lw      t4, r_T4*4(a0)              # restore t4
+    lw      t5, r_T5*4(a0)              # restore t5
+    lw      t6, r_T6*4(a0)              # restore t6
+    lw      t7, r_T7*4(a0)              # restore t7
+
+    lw      s0, r_S0*4(a0)              # restore s0
+    lw      s1, r_S1*4(a0)              # restore s1
+    lw      s2, r_S2*4(a0)              # restore s2
+    lw      s3, r_S3*4(a0)              # restore s3
+    lw      s4, r_S4*4(a0)              # restore s4
+    lw      s5, r_S5*4(a0)              # restore s5
+    lw      s6, r_S6*4(a0)              # restore s6
+    lw      s7, r_S7*4(a0)              # restore s7
+
+    lw      t8, r_T8*4(a0)              # restore t8
+    lw      t9, r_T9*4(a0)              # restore t9
+
+    lw      k0, r_K0*4(a0)              # restore k0
+    lw      k1, r_K1*4(a0)              # restore k1
+
+#if 0
+    lw      gp, r_GP*4(a0)              # restore gp
+    lw      sp, r_SP*4(a0)              # restore sp
+    lw      fp, r_FP*4(a0)              # restore fp
+    lw      ra, r_RA*4(a0)              # restore ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    lw      f0, fr0*4(a0)               # restore f0
+    lw      f1, fr1*4(a0)               # restore f1
+    lw      f2, fr2*4(a0)               # restore f2
+    lw      f3, fr3*4(a0)               # restore f3
+    lw      f4, fr4*4(a0)               # restore f4
+    lw      f5, fr5*4(a0)               # restore f5
+    lw      f6, fr6*4(a0)               # restore f6
+    lw      f7, fr7*4(a0)               # restore f7
+    lw      f8, fr8*4(a0)               # restore f8
+    lw      f9, fr9*4(a0)               # restore f9
+    lw      f10, fr10*4(a0)             # restore f10
+    lw      f11, fr11*4(a0)             # restore f11
+    lw      f12, fr12*4(a0)             # restore f12
+    lw      f13, fr13*4(a0)             # restore f13
+    lw      f14, fr14*4(a0)             # restore f14
+    lw      f15, fr15*4(a0)             # restore f15
+    lw      f16, fr16*4(a0)             # restore f16
+    lw      f17, fr17*4(a0)             # restore f17
+    lw      f18, fr18*4(a0)             # restore f18
+    lw      f19, fr19*4(a0)             # restore f19
+    lw      f20, fr20*4(a0)             # restore f20
+    lw      f21, fr21*4(a0)             # restore f21
+    lw      f22, fr22*4(a0)             # restore f22
+    lw      f23, fr23*4(a0)             # restore f23
+    lw      f24, fr24*4(a0)             # restore f24
+    lw      f25, fr25*4(a0)             # restore f25
+    lw      f26, fr26*4(a0)             # restore f26
+    lw      f27, fr27*4(a0)             # restore f27
+    lw      f28, fr28*4(a0)             # restore f28
+    lw      f29, fr29*4(a0)             # restore f29
+    lw      f30, fr30*4(a0)             # restore f30
+    lw      f31, fr31*4(a0)             # restore f31
+#endif
+
+    lw      a0, r_A1*4(a0)              # restore a0
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_RETURN.S b/vm/compiler/template/mips/TEMPLATE_RETURN.S
new file mode 100644
index 0000000..e9cee05
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_RETURN.S
@@ -0,0 +1,77 @@
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    # a0=rSELF
+    move    a0, rSELF
+    la      t9, dvmFastMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    SAVEAREA_FROM_FP(a0, rFP)           # a0<- saveArea (old)
+    lw      t0, offStackSaveArea_prevFrame(a0)     # t0<- saveArea->prevFrame
+    lbu     t1, offThread_breakFlags(rSELF)        # t1<- breakFlags
+    lw      rPC, offStackSaveArea_savedPc(a0)      # rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    lw      t2,  offStackSaveArea_returnAddr(a0)   # t2<- chaining cell ret
+#else
+    move    t2, zero                               # disable chaining
+#endif
+    lw      a2, offStackSaveArea_method - sizeofStackSaveArea(t0)
+                                                   # a2<- method we're returning to
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     a2, zero, 1f                           # bail to interpreter
+#else
+    bne     a2, zero, 2f
+    JALR(ra)                                       # punt to interpreter and compare state
+    # DOUG: assume this does not return ???
+2:
+#endif
+    la      t4, .LdvmJitToInterpNoChainNoProfile   # defined in footer.S
+    lw      a1, (t4)
+    move    rFP, t0                                # publish new FP
+    beq     a2, zero, 4f
+    lw      t0, offMethod_clazz(a2)                # t0<- method->clazz
+4:
+
+    sw      a2, offThread_method(rSELF)            # self->method = newSave->method
+    lw      a0, offClassObject_pDvmDex(t0)         # a0<- method->clazz->pDvmDex
+    sw      rFP, offThread_curFrame(rSELF)         # self->curFrame = fp
+    add     rPC, rPC, 3*2                          # publish new rPC
+    sw      a0, offThread_methodClassDex(rSELF)
+    movn    t2, zero, t1                           # check the breadFlags and
+                                                   # clear the chaining cell address
+    sw      t2, offThread_inJitCodeCache(rSELF)    # in code cache or not
+    beq     t2, zero, 3f                           # chaining cell exists?
+    JALR(t2)                                       # jump to the chaining cell
+    # DOUG: assume this does not return ???
+3:
+#if defined(WITH_JIT_TUNING)
+    li      a0, kCallsiteInterpreted
+#endif
+    j       a1                                     # callsite is interpreted
+1:
+    sw      zero, offThread_inJitCodeCache(rSELF)  # reset inJitCodeCache
+    SAVE_PC_TO_SELF()                              # SAVE_PC_FP_TO_SELF()
+    SAVE_FP_TO_SELF()
+    la      t4, .LdvmMterpStdBail                  # defined in footer.S
+    lw      a2, (t4)
+    move    a0, rSELF                              # Expecting rSELF in a0
+    JALR(a2)                                       # exit the interpreter
+    # DOUG: assume this does not return ???
diff --git a/vm/compiler/template/mips/TEMPLATE_RETURN_PROF.S b/vm/compiler/template/mips/TEMPLATE_RETURN_PROF.S
new file mode 100644
index 0000000..b4e0754
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_RETURN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_RETURN.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/mips/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..2e74481
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,105 @@
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: a1 value to save
+     *    Top of stack + 0: a0 value to save
+     *    a0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    a1 - the value of regMap
+     *
+     * The handler must save regMap, r0-r31, f0-f31 if FPU, and then return with
+     * r0-r31 with their original values (note that this means a0 and a1 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     * Note: the following registers are not saved
+     *       zero, AT, gp, sp, fp, ra
+     */
+    add     a0, a0, rSELF               # pointer to heapArgSpace
+    sw      a1, 0(a0)                   # save regMap
+    add     a0, a0, 4                   # pointer to coreRegs
+#if 0
+    sw      zero, r_ZERO*4(a0)          # save zero
+#endif
+    .set noat
+    sw      AT, r_AT*4(a0)              # save at
+    .set at
+    sw      v0, r_V0*4(a0)              # save v0
+    sw      v1, r_V1*4(a0)              # save v1
+
+    lw      a1, 0(sp)                   # recover a0 value
+    sw      a1, r_A0*4(a0)              # save a0
+    lw      a1, 4(sp)                   # recover a1 value
+    sw      a1, r_A1*4(a0)              # save a1
+    sw      a2, r_A2*4(a0)              # save a2
+    sw      a3, r_A3*4(a0)              # save a3
+
+    sw      t0, r_T0*4(a0)              # save t0
+    sw      t1, r_T1*4(a0)              # save t1
+    sw      t2, r_T2*4(a0)              # save t2
+    sw      t3, r_T3*4(a0)              # save t3
+    sw      t4, r_T4*4(a0)              # save t4
+    sw      t5, r_T5*4(a0)              # save t5
+    sw      t6, r_T6*4(a0)              # save t6
+    sw      t7, r_T7*4(a0)              # save t7
+
+    sw      s0, r_S0*4(a0)              # save s0
+    sw      s1, r_S1*4(a0)              # save s1
+    sw      s2, r_S2*4(a0)              # save s2
+    sw      s3, r_S3*4(a0)              # save s3
+    sw      s4, r_S4*4(a0)              # save s4
+    sw      s5, r_S5*4(a0)              # save s5
+    sw      s6, r_S6*4(a0)              # save s6
+    sw      s7, r_S7*4(a0)              # save s7
+
+    sw      t8, r_T8*4(a0)              # save t8
+    sw      t9, r_T9*4(a0)              # save t9
+
+    sw      k0, r_K0*4(a0)              # save k0
+    sw      k1, r_K1*4(a0)              # save k1
+
+#if 0
+    sw      gp, r_GP*4(a0)              # save gp
+    sw      sp, r_SP*4(a0)              # save sp (need to adjust??? )
+    sw      fp, r_FP*4(a0)              # save fp
+    sw      ra, r_RA*4(a0)              # save ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    sw      f0, fr0*4(a0)               # save f0
+    sw      f1, fr1*4(a0)               # save f1
+    sw      f2, fr2*4(a0)               # save f2
+    sw      f3, fr3*4(a0)               # save f3
+    sw      f4, fr4*4(a0)               # save f4
+    sw      f5, fr5*4(a0)               # save f5
+    sw      f6, fr6*4(a0)               # save f6
+    sw      f7, fr7*4(a0)               # save f7
+    sw      f8, fr8*4(a0)               # save f8
+    sw      f9, fr9*4(a0)               # save f9
+    sw      f10, fr10*4(a0)             # save f10
+    sw      f11, fr11*4(a0)             # save f11
+    sw      f12, fr12*4(a0)             # save f12
+    sw      f13, fr13*4(a0)             # save f13
+    sw      f14, fr14*4(a0)             # save f14
+    sw      f15, fr15*4(a0)             # save f15
+    sw      f16, fr16*4(a0)             # save f16
+    sw      f17, fr17*4(a0)             # save f17
+    sw      f18, fr18*4(a0)             # save f18
+    sw      f19, fr19*4(a0)             # save f19
+    sw      f20, fr20*4(a0)             # save f20
+    sw      f21, fr21*4(a0)             # save f21
+    sw      f22, fr22*4(a0)             # save f22
+    sw      f23, fr23*4(a0)             # save f23
+    sw      f24, fr24*4(a0)             # save f24
+    sw      f25, fr25*4(a0)             # save f25
+    sw      f26, fr26*4(a0)             # save f26
+    sw      f27, fr27*4(a0)             # save f27
+    sw      f28, fr28*4(a0)             # save f28
+    sw      f29, fr29*4(a0)             # save f29
+    sw      f30, fr30*4(a0)             # save f30
+    sw      f31, fr31*4(a0)             # save f31
+#endif
+
+    lw      a1, 0(sp)                   # recover a0 value
+    lw      a1, 4(sp)                   # recover a1 value
+    sub     sp, sp, 8                   # adjust stack ptr
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SHL_LONG.S b/vm/compiler/template/mips/TEMPLATE_SHL_LONG.S
new file mode 100644
index 0000000..b15f0c3
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SHL_LONG.S
@@ -0,0 +1,17 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sll     rRESULT0, rARG0, a2		#  rlo<- alo << (shift&31)
+    not     rRESULT1, a2		#  rhi<- 31-shift  (shift is 5b)
+    srl     rARG0, 1
+    srl     rARG0, rRESULT1		#  alo<- alo >> (32-(shift&31))
+    sll     rRESULT1, rARG1, a2		#  rhi<- ahi << (shift&31)
+    or      rRESULT1, rARG0		#  rhi<- rhi | alo
+    andi    a2, 0x20			#  shift< shift & 0x20
+    movn    rRESULT1, rRESULT0, a2	#  rhi<- rlo (if shift&0x20)
+    movn    rRESULT0, zero, a2		#  rlo<- 0  (if shift&0x20)
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SHR_LONG.S b/vm/compiler/template/mips/TEMPLATE_SHR_LONG.S
new file mode 100644
index 0000000..e59686d
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SHR_LONG.S
@@ -0,0 +1,18 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sra     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    sra     a3, rARG1, 31		#  a3<- sign(ah)
+    not     rARG0, a2			#  alo<- 31-shift (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, a3, a2		#  rhi<- sign(ahi) (if shift&0x20)
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SQRT_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_SQRT_DOUBLE_VFP.S
new file mode 100644
index 0000000..253a4a4
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SQRT_DOUBLE_VFP.S
@@ -0,0 +1,23 @@
+%verify "executed"
+
+    /*
+     * 64-bit floating point sqrt operation.
+     * If the result is a NaN, bail out to library code to do
+     * the right thing.
+     *
+     * On entry:
+     *     a2 src addr of op1
+     * On exit:
+     *     v0,v1/fv0 = res
+     */
+#ifdef  SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)        # a0/a1<- vBB/vBB+1
+#else
+    LOAD64_F(fa0, fa0f, a2)         # fa0/fa0f<- vBB/vBB+1
+    sqrt.d	fv0, fa0
+    c.eq.d	fv0, fv0
+    bc1t	1f
+#endif
+    JAL(sqrt)
+1:
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_STRING_COMPARETO.S b/vm/compiler/template/mips/TEMPLATE_STRING_COMPARETO.S
new file mode 100644
index 0000000..a514351
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_STRING_COMPARETO.S
@@ -0,0 +1,146 @@
+    /*
+     * String's compareTo.
+     *
+     * Requires a0/a1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   this object pointer
+     *    a1:   comp object pointer
+     *
+     */
+
+     subu  v0, a0, a1                # Same?
+     bnez  v0, 1f
+     RETURN
+1:
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_OFFSET(a1)
+     lw    t2, STRING_FIELDOFF_COUNT(a0)
+     lw    a2, STRING_FIELDOFF_COUNT(a1)
+     lw    a0, STRING_FIELDOFF_VALUE(a0)
+     lw    a1, STRING_FIELDOFF_VALUE(a1)
+
+    /*
+     * At this point, we have this/comp:
+     *    offset: t0/t1
+     *    count:  t2/a2
+     *    value:  a0/a1
+     * We're going to compute
+     *    a3 <- countDiff
+     *    a2 <- minCount
+     */
+     subu  a3, t2, a2                # a3<- countDiff
+     sleu  t7, t2, a2
+     movn  a2, t2, t7                # a2<- minCount
+
+     /*
+      * Note: data pointers point to first element.
+      */
+     addu  a0, 16                    # point to contents[0]
+     addu  a1, 16                    # point to contents[0]
+
+     /* Now, build pointers to the string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  a0, a0, t7
+     sll   t7, t1, 1                 # multiply offset by 2
+     addu  a1, a1, t7
+
+     /*
+      * At this point we have:
+      *   a0: *this string data
+      *   a1: *comp string data
+      *   a2: iteration count for comparison
+      *   a3: value to return if the first part of the string is equal
+      *   v0: reserved for result
+      *   t0-t5 available for loading string data
+      */
+
+     subu  a2, 2
+     bltz  a2, do_remainder2
+
+     /*
+      * Unroll the first two checks so we can quickly catch early mismatch
+      * on long strings (but preserve incoming alignment)
+      */
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     addu  a0, 4                     # offset to contents[2]
+     addu  a1, 4                     # offset to contents[2]
+     li    t7, 28
+     bgt   a2, t7, do_memcmp16
+     subu  a2, 3
+     bltz  a2, do_remainder
+
+loopback_triple:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     lhu   t4, 4(a0)
+     lhu   t5, 4(a1)
+     subu  v0, t4, t5
+     beqz  v0, 3f
+     RETURN
+3:
+     addu  a0, 6                     # offset to contents[i+3]
+     addu  a1, 6                     # offset to contents[i+3]
+     subu  a2, 3
+     bgez  a2, loopback_triple
+
+do_remainder:
+     addu  a2, 3
+     beqz  a2, returnDiff
+
+loopback_single:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     bnez  v0, 1f
+     addu  a0, 2                     # offset to contents[i+1]
+     addu  a1, 2                     # offset to contents[i+1]
+     subu  a2, 1
+     bnez  a2, loopback_single
+
+returnDiff:
+     move  v0, a3
+1:
+     RETURN
+
+do_remainder2:
+     addu  a2, 2
+     bnez  a2, loopback_single
+     move  v0, a3
+     RETURN
+
+    /* Long string case */
+do_memcmp16:
+     move  rOBJ, a3                  # save return value if strings are equal
+     JAL(__memcmp16)
+     seq   t0, v0, zero
+     movn  v0, rOBJ, t0              # overwrite return value if strings are equal
+     RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_STRING_INDEXOF.S b/vm/compiler/template/mips/TEMPLATE_STRING_INDEXOF.S
new file mode 100644
index 0000000..9d9cd60
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_STRING_INDEXOF.S
@@ -0,0 +1,111 @@
+    /*
+     * String's indexOf.
+     *
+     * Requires a0 to have been previously checked for null.  Will
+     * return index of match of a1 in v0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   string object pointer
+     *    a1:   char to match
+     *    a2:   Starting offset in string data
+     */
+
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_COUNT(a0)
+     lw    v0, STRING_FIELDOFF_VALUE(a0)
+
+    /*
+     * At this point, we have:
+     *    v0: object pointer
+     *    a1: char to match
+     *    a2: starting offset
+     *    t0: offset
+     *    t1: string length
+     */
+
+    /* Point to first element */
+     addu  v0, 16                    # point to contents[0]
+
+    /* Build pointer to start of string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Save a copy of starting data in v1 */
+     move  v1, v0
+
+    /* Clamp start to [0..count] */
+     slt   t7, a2, zero
+     movn  a2, zero, t7
+     sgt   t7, a2, t1
+     movn  a2, t1, t7
+
+    /* Build pointer to start of data to compare */
+     sll   t7, a2, 1                # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Compute iteration count */
+     subu  a3, t1, a2
+
+    /*
+     * At this point we have:
+     *   v0: start of data to test
+     *   a1: char to compare
+     *   a3: iteration count
+     *   v1: original start of string
+     *   t0-t7 available for loading string data
+     */
+     subu  a3, 4
+     bltz  a3, indexof_remainder
+
+indexof_loop4:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     lhu   t0, 2(v0)
+     beq   t0, a1, match_1
+     lhu   t0, 4(v0)
+     beq   t0, a1, match_2
+     lhu   t0, 6(v0)
+     beq   t0, a1, match_3
+     addu  v0, 8                     # offset to contents[i+4]
+     subu  a3, 4
+     bgez  a3, indexof_loop4
+
+indexof_remainder:
+     addu  a3, 4
+     beqz  a3, indexof_nomatch
+
+indexof_loop1:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     addu  v0, 2                     # offset to contents[i+1]
+     subu  a3, 1
+     bnez  a3, indexof_loop1
+
+indexof_nomatch:
+     li    v0, -1
+     RETURN
+
+match_0:
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_1:
+     addu  v0, 2
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_2:
+     addu  v0, 4
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_3:
+     addu  v0, 6
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SUB_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_SUB_DOUBLE_VFP.S
new file mode 100644
index 0000000..b07bf44
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SUB_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__subdf3)","instr_f":"sub.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_SUB_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_SUB_FLOAT_VFP.S
new file mode 100644
index 0000000..b0333cd
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SUB_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__subsf3)","instr_f":"sub.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_THROW_EXCEPTION_COMMON.S b/vm/compiler/template/mips/TEMPLATE_THROW_EXCEPTION_COMMON.S
new file mode 100644
index 0000000..d6e8b2e
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_THROW_EXCEPTION_COMMON.S
@@ -0,0 +1,6 @@
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    a0    Dalvik PC that raises the exception
+     */
+    j      .LhandleException
diff --git a/vm/compiler/template/mips/TEMPLATE_USHR_LONG.S b/vm/compiler/template/mips/TEMPLATE_USHR_LONG.S
new file mode 100644
index 0000000..f2a9ddb
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_USHR_LONG.S
@@ -0,0 +1,17 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    srl     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    not     rARG0, a2			#  alo<- 31-n  (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, zero, a2		#  rhi<- 0 (if shift&0x20)
+    RETURN
diff --git a/vm/compiler/template/mips/TemplateOpList.h b/vm/compiler/template/mips/TemplateOpList.h
new file mode 100644
index 0000000..6aabdba
--- /dev/null
+++ b/vm/compiler/template/mips/TemplateOpList.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(RESTORE_STATE)
+JIT_TEMPLATE(SAVE_STATE)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/mips/fbinop.S b/vm/compiler/template/mips/fbinop.S
new file mode 100644
index 0000000..e9ccf0a
--- /dev/null
+++ b/vm/compiler/template/mips/fbinop.S
@@ -0,0 +1,38 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if $chkzero
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+    $preinstr                           # optional op
+    $instr                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if $chkzero
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+    $preinstr                           # optional op
+    $instr_f                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/fbinopWide.S b/vm/compiler/template/mips/fbinopWide.S
new file mode 100644
index 0000000..0e31b87
--- /dev/null
+++ b/vm/compiler/template/mips/fbinopWide.S
@@ -0,0 +1,45 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if $chkzero
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+    $preinstr                           # optional op
+    $instr                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if $chkzero
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+    $preinstr                           # optional op
+    $instr_f
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/footer.S b/vm/compiler/template/mips/footer.S
new file mode 100644
index 0000000..91442dd
--- /dev/null
+++ b/vm/compiler/template/mips/footer.S
@@ -0,0 +1,140 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+.LinvokeNative:
+    # Prep for the native call
+    # a1 = newFP, a0 = methodToCall
+    lw     t9, offThread_jniLocal_topCookie(rSELF)  # t9<- thread->localRef->...
+    sw     zero, offThread_inJitCodeCache(rSELF)    # not in jit code cache
+    sw     a1, offThread_curFrame(rSELF)            # self->curFrame = newFp
+    sw     t9, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                 # newFp->localRefCookie=top
+    lhu     ra, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(rBIX, a1)                   # rBIX<- new stack save area
+
+    move    a2, a0                               # a2<- methodToCall
+    move    a0, a1                               # a0<- newFp
+    add     a1, rSELF, offThread_retval          # a1<- &retval
+    move    a3, rSELF                            # a3<- self
+    andi    ra, kSubModeMethodTrace
+    beqz    ra, 121f
+    # a2: methodToCall
+    # preserve a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                      # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a2 again
+    SCRATCH_LOAD(a2, 8)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+    b       212f
+
+121:
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                     # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+212:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, offStackSaveArea_savedPc(rBIX)        # reload rPC
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+
+/*
+ * On entry:
+ * a0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    la     t0, .LdeadFood
+    lw     t0, (t0)                  # should not see this under self-verification mode
+    jr     t0
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    sw     zero, offThread_inJitCodeCache(rSELF)  # in interpreter land
+    la     a1, .LdvmMterpCommonExceptionThrown  # PIC way of getting &func
+    lw     a1, (a1)
+    la     rIBASE, .LdvmAsmInstructionStart     # PIC way of getting &func
+    lw     rIBASE, (rIBASE)
+    move   rPC, a0                              # reload the faulting Dalvid address
+    jr     a1                                   # branch to dvmMterpCommonExeceptionThrown
+
+    .align  4
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
diff --git a/vm/compiler/template/mips/funop.S b/vm/compiler/template/mips/funop.S
new file mode 100644
index 0000000..0a984d7
--- /dev/null
+++ b/vm/compiler/template/mips/funop.S
@@ -0,0 +1,30 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    $preinstr                           # optional op
+    $instr                              # v0<- op, a0-a3 changed
+.L${opcode}_set_vreg:
+    STORE(v0, rOBJ)                     # vAA<- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    $preinstr                           # optional op
+    $instr_f                            # fv0 = result
+.L${opcode}_set_vreg_f:
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/funopNarrower.S b/vm/compiler/template/mips/funopNarrower.S
new file mode 100644
index 0000000..7fbaf7b
--- /dev/null
+++ b/vm/compiler/template/mips/funopNarrower.S
@@ -0,0 +1,33 @@
+%default {"preinstr":"", "load":"LOAD64_F(fa0, fa0f, a1)"}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     */
+    move rINST, a0                      # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vB/vB+1
+    $preinstr                           # optional op
+    $instr                              # v0<- op, a0-a3 changed
+.L${opcode}_set_vreg:
+    STORE(v0, rINST)                    # vA<- v0
+#else
+    $load
+    $preinstr                           # optional op
+    $instr_f                            # fv0 = result
+.L${opcode}_set_vreg_f:
+    STORE_F(fv0, rINST)                 # vA<- fv0
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/funopWider.S b/vm/compiler/template/mips/funopWider.S
new file mode 100644
index 0000000..887e171
--- /dev/null
+++ b/vm/compiler/template/mips/funopWider.S
@@ -0,0 +1,29 @@
+%default {"preinstr":"", "st_result":"STORE64_F(fv0, fv0f, rOBJ)"}
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vB
+    $preinstr                           # optional op
+    $instr                              # result<- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)   # vA/vA+1<- v0/v1
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vB
+    $preinstr                           # optional op
+    $instr_f
+
+.L${opcode}_set_vreg:
+    $st_result                          # vA/vA+1<- fv0/fv0f
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/header.S b/vm/compiler/template/mips/header.S
new file mode 100644
index 0000000..de4a051
--- /dev/null
+++ b/vm/compiler/template/mips/header.S
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+#include "../../../mterp/common/mips-defines.h"
+#include "../../../mterp/common/jit-config.h"
+#include <asm/regdef.h>
+#include <asm/fpregdef.h>
+
+#ifdef	__mips_hard_float
+#define		HARD_FLOAT
+#else
+#define		SOFT_FLOAT
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		pointer to thread
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+/* register offsets */
+#define r_ZERO      0
+#define r_AT        1
+#define r_V0        2
+#define r_V1        3
+#define r_A0        4
+#define r_A1        5
+#define r_A2        6
+#define r_A3        7
+#define r_T0        8
+#define r_T1        9
+#define r_T2        10
+#define r_T3        11
+#define r_T4        12
+#define r_T5        13
+#define r_T6        14
+#define r_T7        15
+#define r_S0        16
+#define r_S1        17
+#define r_S2        18
+#define r_S3        19
+#define r_S4        20
+#define r_S5        21
+#define r_S6        22
+#define r_S7        23
+#define r_T8        24
+#define r_T9        25
+#define r_K0        26
+#define r_K1        27
+#define r_GP        28
+#define r_SP        29
+#define r_FP        30
+#define r_RA        31
+#define r_F0        32
+#define r_F1        33
+#define r_F2        34
+#define r_F3        35
+#define r_F4        36
+#define r_F5        37
+#define r_F6        38
+#define r_F7        39
+#define r_F8        40
+#define r_F9        41
+#define r_F10       42
+#define r_F11       43
+#define r_F12       44
+#define r_F13       45
+#define r_F14       46
+#define r_F15       47
+#define r_F16       48
+#define r_F17       49
+#define r_F18       50
+#define r_F19       51
+#define r_F20       52
+#define r_F21       53
+#define r_F22       54
+#define r_F23       55
+#define r_F24       56
+#define r_F25       57
+#define r_F26       58
+#define r_F27       59
+#define r_F28       60
+#define r_F29       61
+#define r_F30       62
+#define r_F31       63
+
+/* single-purpose registers, given names for clarity */
+#define rPC	s0
+#define rFP	s1
+#define rSELF	s2
+#define rIBASE	s3
+#define rINST	s4
+#define rOBJ	s5
+#define rBIX	s6
+#define rTEMP	s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0     a0
+#define rARG1     a1
+#define rARG2     a2
+#define rARG3     a3
+#define rRESULT0  v0
+#define rRESULT1  v1
+#else
+#define rARG0     a1
+#define rARG1     a0
+#define rARG2     a3
+#define rARG3     a2
+#define rRESULT0  v1
+#define rRESULT1  v0
+#endif
+
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()	lw	rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF()	sw	rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF()	lw	rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF()	sw	rFP, offThread_curFrame(rSELF)
+
+#define EXPORT_PC() \
+	sw	rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+	subu	rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST()			lhu	rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count)	lhu     rINST, (_count*2)(rPC); \
+					addu	rPC, rPC, (_count * 2)
+
+#define FETCH_ADVANCE_INST_RB(rd)	addu	rPC, rPC, rd;	\
+					lhu     rINST, (rPC)
+
+#define FETCH(rd, _count)		lhu	rd, (_count * 2)(rPC)
+#define FETCH_S(rd, _count)		lh	rd, (_count * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd)		and	rd, rINST, 0xFF
+
+#define GOTO_OPCODE(rd)			sll  rd, rd, ${handler_size_bits};	\
+					addu rd, rIBASE, rd;	\
+					jr  rd
+
+
+#define LOAD(rd, rbase)			lw  rd, 0(rbase)
+#define LOAD_F(rd, rbase)		l.s rd, (rbase)
+#define STORE(rd, rbase)		sw  rd, 0(rbase)
+#define STORE_F(rd, rbase)		s.s rd, (rbase)
+
+#define GET_VREG(rd, rix)		LOAD_eas2(rd,rFP,rix)
+
+#define GET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix)		STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst)	.set noreorder;		\
+					sll  dst, dst, ${handler_size_bits};	\
+					addu dst, rIBASE, dst;			\
+					sll  t8, rix, 2;	\
+					addu t8, t8, rFP;	\
+					jr  dst;		\
+					sw  rd, 0(t8);		\
+					.set reorder
+
+#define SET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  s.s	rd, (AT); .set at
+
+
+#define GET_OPA(rd)			srl     rd, rINST, 8
+#ifndef		MIPS32R2
+#define GET_OPA4(rd)			GET_OPA(rd);  and  rd, 0xf
+#else
+#define GET_OPA4(rd)			ext	rd, rd, 8, 4
+#endif
+#define GET_OPB(rd)			srl     rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd,off)		lw    rd, offThread_##off##(rSELF)
+
+#define LOAD_rSELF_method(rd)		LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd)	LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd)	LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd)		LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd)	LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd)		LOAD_rSELF_OFF(rd, bailPtr)
+
+#define GET_JIT_PROF_TABLE(rd)		LOAD_rSELF_OFF(rd,pJitProfTable)
+#define GET_JIT_THRESHOLD(rd)		LOAD_rSELF_OFF(rd,jitThreshold)
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd,rbase,roff,rshift)	.set noat;		\
+					sll  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define EAS1(rd,rbase,roff)		EASN(rd,rbase,roff,1)
+#define EAS2(rd,rbase,roff)		EASN(rd,rbase,roff,2)
+#define EAS3(rd,rbase,roff)		EASN(rd,rbase,roff,3)
+#define EAS4(rd,rbase,roff)		EASN(rd,rbase,roff,4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd,rbase,roff,rshift)	.set noat;		\
+					srl  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define LOAD_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  lw  rd, 0(AT); .set at
+
+#define STORE_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  sw  rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd,rbase,off)	lw	rd, off(rbase)
+#define LOADu2_RB_OFF(rd,rbase,off)	lhu	rd, off(rbase)
+#define STORE_RB_OFF(rd,rbase,off)	sw	rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, off(rbase);	\
+					        sw	rhi, (off+4)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, off(rbase);	\
+					        lw	rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, off(rbase);	\
+						s.s	rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, off(rbase);	\
+						l.s	rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, (off+4)(rbase);	\
+					        sw	rhi, (off)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, (off+4)(rbase);	\
+					        lw	rhi, (off)(rbase)
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, (off+4)(rbase);	\
+						s.s	rhi, (off)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, (off+4)(rbase);	\
+						l.s	rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo,rhi,rbase)		STORE64_off(rlo,rhi,rbase,0)
+#define LOAD64(rlo,rhi,rbase)		LOAD64_off(rlo,rhi,rbase,0)
+
+#define STORE64_F(rlo,rhi,rbase)	STORE64_off_F(rlo,rhi,rbase,0)
+#define LOAD64_F(rlo,rhi,rbase)		LOAD64_off_F(rlo,rhi,rbase,0)
+
+#define STORE64_lo(rd,rbase)		sw	rd, 0(rbase)
+#define STORE64_hi(rd,rbase)		sw	rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd,rbase)		LOAD_RB_OFF(rd,rbase,offThread_exception)
+#define LOAD_base_offArrayObject_length(rd,rbase)	LOAD_RB_OFF(rd,rbase,offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd,rbase)   LOAD_RB_OFF(rd,rbase,offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd,rbase)	LOAD_RB_OFF(rd,rbase,offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd,rbase)	LOAD_RB_OFF(rd,rbase,offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_name)
+#define LOAD_base_offObject_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd,rbase)		LOADu2_RB_OFF(rd,rbase,offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd,rbase)		STORE_RB_OFF(rd,rbase,offThread_exception)
+
+
+#define	STACK_STORE(rd,off)	sw   rd, off(sp)
+#define	STACK_LOAD(rd,off)	lw   rd, off(sp)
+#define CREATE_STACK(n)	 	subu sp, sp, n
+#define DELETE_STACK(n)	 	addu sp, sp, n
+
+#define SAVE_RA(offset)	 	STACK_STORE(ra, offset)
+#define LOAD_RA(offset)	 	STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest,addr)	la   dest, addr
+#define LOAD_IMM(dest, imm)	li   dest, imm
+#define MOVE_REG(dest,src)	move dest, src
+#define	RETURN			jr   ra
+#define	STACK_SIZE		128
+
+#define STACK_OFFSET_ARG04	16
+#define STACK_OFFSET_GP		84
+#define STACK_OFFSET_rFP	112
+
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+        .cprestore STACK_OFFSET_GP
+
+#define JAL(func)		move rTEMP, ra;				\
+				jal  func;				\
+				move ra, rTEMP
+
+#define JALR(reg)		move rTEMP, ra;				\
+				jalr ra, reg;				\
+				move ra, rTEMP
+
+#define BAL(n)			bal  n
+
+#define	STACK_STORE_RA()  	CREATE_STACK(STACK_SIZE);		\
+				STACK_STORE(gp, STACK_OFFSET_GP);	\
+				STACK_STORE(ra, 124)
+
+#define	STACK_STORE_S0()  	STACK_STORE_RA();			\
+				STACK_STORE(s0, 116)
+
+#define	STACK_STORE_S0S1()  	STACK_STORE_S0();			\
+				STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define	STACK_LOAD_RA()		STACK_LOAD(ra, 124);			\
+				STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				DELETE_STACK(STACK_SIZE)
+
+#define	STACK_LOAD_S0()  	STACK_LOAD(s0, 116);			\
+				STACK_LOAD_RA()
+
+#define	STACK_LOAD_S0S1()  	STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD_S0()
+
+#define STACK_STORE_FULL()	CREATE_STACK(STACK_SIZE);	\
+				STACK_STORE(ra, 124);		\
+				STACK_STORE(fp, 120);		\
+				STACK_STORE(s0, 116);		\
+				STACK_STORE(s1, STACK_OFFSET_rFP);	\
+				STACK_STORE(s2, 108);		\
+				STACK_STORE(s3, 104);		\
+				STACK_STORE(s4, 100);		\
+				STACK_STORE(s5, 96);		\
+				STACK_STORE(s6, 92);		\
+				STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL()	STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				STACK_LOAD(s7, 88);	\
+				STACK_LOAD(s6, 92);	\
+				STACK_LOAD(s5, 96);	\
+				STACK_LOAD(s4, 100);	\
+				STACK_LOAD(s3, 104);	\
+				STACK_LOAD(s2, 108);	\
+				STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD(s0, 116);	\
+				STACK_LOAD(fp, 120);	\
+				STACK_LOAD(ra, 124);	\
+				DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define STACK_OFFSET_SCR   32
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
diff --git a/vm/compiler/template/mips/platform.S b/vm/compiler/template/mips/platform.S
new file mode 100644
index 0000000..8b0d23e
--- /dev/null
+++ b/vm/compiler/template/mips/platform.S
@@ -0,0 +1,6 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
new file mode 100644
index 0000000..27319e7
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
@@ -0,0 +1,1981 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    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
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    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
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global 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..68f6441
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
@@ -0,0 +1,1712 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_DOUBLE
+dvmCompiler_TEMPLATE_CMPG_DOUBLE:
+/* File: armv5te/TEMPLATE_CMPG_DOUBLE.S */
+/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    push    {r0-r3}                     @ save operands
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cdcmple       @ PIC way of "bl __aeabi_cdcmple"
+    blx     ip
+    bhi     .LTEMPLATE_CMPG_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0, trumps less than
+    add     sp, #16                     @ drop unused operands
+    bx      r11
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPG_DOUBLE_gt_or_nan:
+    pop     {r2-r3}                     @ restore operands in reverse order
+    pop     {r0-r1}                     @ restore operands in reverse order
+    ldr     ip, .L__aeabi_cdcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mov     r0, #1                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_DOUBLE
+dvmCompiler_TEMPLATE_CMPL_DOUBLE:
+/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    push    {r0-r3}                     @ save operands
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cdcmple       @ PIC way of "bl __aeabi_cdcmple"
+    blx     ip
+    bhi     .LTEMPLATE_CMPL_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0, trumps less than
+    add     sp, #16                     @ drop unused operands
+    bx      r11
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPL_DOUBLE_gt_or_nan:
+    pop     {r2-r3}                     @ restore operands in reverse order
+    pop     {r0-r1}                     @ restore operands in reverse order
+    ldr     ip, .L__aeabi_cdcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mvn     r0, #0                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_FLOAT
+dvmCompiler_TEMPLATE_CMPG_FLOAT:
+/* File: armv5te/TEMPLATE_CMPG_FLOAT.S */
+/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    mov     r9, r0                      @ Save copies - we may need to redo
+    mov     r10, r1
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cfcmple       @ cmp <=: C clear if <, Z set if eq
+    blx     ip
+    bhi     .LTEMPLATE_CMPG_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0, trumps less than
+    bx      r11
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPG_FLOAT_gt_or_nan:
+    mov     r0, r10                     @ restore in reverse order
+    mov     r1, r9
+    ldr     ip, .L__aeabi_cfcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mov     r0, #1                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_FLOAT
+dvmCompiler_TEMPLATE_CMPL_FLOAT:
+/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    mov     r9, r0                      @ Save copies - we may need to redo
+    mov     r10, r1
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cfcmple       @ cmp <=: C clear if <, Z set if eq
+    blx     ip
+    bhi     .LTEMPLATE_CMPL_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0, trumps less than
+    bx      r11
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPL_FLOAT_gt_or_nan:
+    mov     r0, r10                     @ restore in reverse order
+    mov     r1, r9
+    ldr     ip, .L__aeabi_cfcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mvn     r0, #0                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    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
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    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
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global 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..7573bd8
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S
@@ -0,0 +1,1981 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    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
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    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
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global 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..fd21a0e
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -0,0 +1,1981 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    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
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    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
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#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
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-ia32.S b/vm/compiler/template/out/CompilerTemplateAsm-ia32.S
new file mode 100644
index 0000000..eef9d65
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-ia32.S
@@ -0,0 +1,113 @@
+/*
+ * This file was generated automatically by gen-template.py for 'ia32'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: ia32/header.S */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/* Subset of defines from mterp/x86/header.S */
+#define rSELF (%ebp)
+#define rPC   %esi
+#define rFP   %edi
+#define rINST %ebx
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: ia32/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: ia32/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler is a bit odd - it may be called via chaining or
+     * from static code and is expected to cause control to flow
+     * to the interpreter.  The problem is where to find the Dalvik
+     * PC of the next instruction.  When called via chaining, the dPC
+     * will be located at *rp.  When called from static code, rPC is
+     * valid and rp is a real return pointer (that should be ignored).
+     * The Arm target deals with this by using the link register as
+     * a flag.  If it is zero, we know we were called from static code.
+     * If non-zero, it points to the chain cell containing dPC.
+     * For x86, we'll infer the source by looking where rp points.
+     * If it points to anywhere within the code cache, we'll assume
+     * we got here via chaining.  Otherwise, we'll assume rPC is valid.
+     *
+     * On entry:
+     *    (TOS)<- return pointer or pointer to dPC
+     */
+
+/*
+ * FIXME - this won't work as-is.  The cache boundaries are not
+ * set up until later.  Perhaps rething this whole thing.  Do we
+ * really need an interpret teplate?
+ */
+
+
+     movl   rSELF,%ecx
+     movl   $.LinterpPunt,%edx
+     pop    %eax
+     /*cmpl   %eax,offThread_jitCacheEnd(%ecx)*/
+     ja     1f
+     /*cmpl   %eax,offThread_jitCacheStart(%ecx)*/
+     jb     1f
+     movl   %eax,rPC
+1:
+     jmp    *(%edx)
+
+.LinterpPunt:
+    .long   dvmJitToInterpPunt
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: ia32/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-mips.S b/vm/compiler/template/out/CompilerTemplateAsm-mips.S
new file mode 100644
index 0000000..fb8402e
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-mips.S
@@ -0,0 +1,3407 @@
+/*
+ * This file was generated automatically by gen-template.py for 'mips'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: mips/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+#include "../../../mterp/common/mips-defines.h"
+#include "../../../mterp/common/jit-config.h"
+#include <asm/regdef.h>
+#include <asm/fpregdef.h>
+
+#ifdef	__mips_hard_float
+#define		HARD_FLOAT
+#else
+#define		SOFT_FLOAT
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		pointer to thread
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+/* register offsets */
+#define r_ZERO      0
+#define r_AT        1
+#define r_V0        2
+#define r_V1        3
+#define r_A0        4
+#define r_A1        5
+#define r_A2        6
+#define r_A3        7
+#define r_T0        8
+#define r_T1        9
+#define r_T2        10
+#define r_T3        11
+#define r_T4        12
+#define r_T5        13
+#define r_T6        14
+#define r_T7        15
+#define r_S0        16
+#define r_S1        17
+#define r_S2        18
+#define r_S3        19
+#define r_S4        20
+#define r_S5        21
+#define r_S6        22
+#define r_S7        23
+#define r_T8        24
+#define r_T9        25
+#define r_K0        26
+#define r_K1        27
+#define r_GP        28
+#define r_SP        29
+#define r_FP        30
+#define r_RA        31
+#define r_F0        32
+#define r_F1        33
+#define r_F2        34
+#define r_F3        35
+#define r_F4        36
+#define r_F5        37
+#define r_F6        38
+#define r_F7        39
+#define r_F8        40
+#define r_F9        41
+#define r_F10       42
+#define r_F11       43
+#define r_F12       44
+#define r_F13       45
+#define r_F14       46
+#define r_F15       47
+#define r_F16       48
+#define r_F17       49
+#define r_F18       50
+#define r_F19       51
+#define r_F20       52
+#define r_F21       53
+#define r_F22       54
+#define r_F23       55
+#define r_F24       56
+#define r_F25       57
+#define r_F26       58
+#define r_F27       59
+#define r_F28       60
+#define r_F29       61
+#define r_F30       62
+#define r_F31       63
+
+/* single-purpose registers, given names for clarity */
+#define rPC	s0
+#define rFP	s1
+#define rSELF	s2
+#define rIBASE	s3
+#define rINST	s4
+#define rOBJ	s5
+#define rBIX	s6
+#define rTEMP	s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0     a0
+#define rARG1     a1
+#define rARG2     a2
+#define rARG3     a3
+#define rRESULT0  v0
+#define rRESULT1  v1
+#else
+#define rARG0     a1
+#define rARG1     a0
+#define rARG2     a3
+#define rARG3     a2
+#define rRESULT0  v1
+#define rRESULT1  v0
+#endif
+
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()	lw	rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF()	sw	rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF()	lw	rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF()	sw	rFP, offThread_curFrame(rSELF)
+
+#define EXPORT_PC() \
+	sw	rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+	subu	rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST()			lhu	rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count)	lhu     rINST, (_count*2)(rPC); \
+					addu	rPC, rPC, (_count * 2)
+
+#define FETCH_ADVANCE_INST_RB(rd)	addu	rPC, rPC, rd;	\
+					lhu     rINST, (rPC)
+
+#define FETCH(rd, _count)		lhu	rd, (_count * 2)(rPC)
+#define FETCH_S(rd, _count)		lh	rd, (_count * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd)		and	rd, rINST, 0xFF
+
+#define GOTO_OPCODE(rd)			sll  rd, rd, -1000;	\
+					addu rd, rIBASE, rd;	\
+					jr  rd
+
+
+#define LOAD(rd, rbase)			lw  rd, 0(rbase)
+#define LOAD_F(rd, rbase)		l.s rd, (rbase)
+#define STORE(rd, rbase)		sw  rd, 0(rbase)
+#define STORE_F(rd, rbase)		s.s rd, (rbase)
+
+#define GET_VREG(rd, rix)		LOAD_eas2(rd,rFP,rix)
+
+#define GET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix)		STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst)	.set noreorder;		\
+					sll  dst, dst, -1000;	\
+					addu dst, rIBASE, dst;			\
+					sll  t8, rix, 2;	\
+					addu t8, t8, rFP;	\
+					jr  dst;		\
+					sw  rd, 0(t8);		\
+					.set reorder
+
+#define SET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  s.s	rd, (AT); .set at
+
+
+#define GET_OPA(rd)			srl     rd, rINST, 8
+#ifndef		MIPS32R2
+#define GET_OPA4(rd)			GET_OPA(rd);  and  rd, 0xf
+#else
+#define GET_OPA4(rd)			ext	rd, rd, 8, 4
+#endif
+#define GET_OPB(rd)			srl     rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd,off)		lw    rd, offThread_##off##(rSELF)
+
+#define LOAD_rSELF_method(rd)		LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd)	LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd)	LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd)		LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd)	LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd)		LOAD_rSELF_OFF(rd, bailPtr)
+
+#define GET_JIT_PROF_TABLE(rd)		LOAD_rSELF_OFF(rd,pJitProfTable)
+#define GET_JIT_THRESHOLD(rd)		LOAD_rSELF_OFF(rd,jitThreshold)
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd,rbase,roff,rshift)	.set noat;		\
+					sll  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define EAS1(rd,rbase,roff)		EASN(rd,rbase,roff,1)
+#define EAS2(rd,rbase,roff)		EASN(rd,rbase,roff,2)
+#define EAS3(rd,rbase,roff)		EASN(rd,rbase,roff,3)
+#define EAS4(rd,rbase,roff)		EASN(rd,rbase,roff,4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd,rbase,roff,rshift)	.set noat;		\
+					srl  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define LOAD_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  lw  rd, 0(AT); .set at
+
+#define STORE_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  sw  rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd,rbase,off)	lw	rd, off(rbase)
+#define LOADu2_RB_OFF(rd,rbase,off)	lhu	rd, off(rbase)
+#define STORE_RB_OFF(rd,rbase,off)	sw	rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, off(rbase);	\
+					        sw	rhi, (off+4)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, off(rbase);	\
+					        lw	rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, off(rbase);	\
+						s.s	rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, off(rbase);	\
+						l.s	rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, (off+4)(rbase);	\
+					        sw	rhi, (off)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, (off+4)(rbase);	\
+					        lw	rhi, (off)(rbase)
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, (off+4)(rbase);	\
+						s.s	rhi, (off)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, (off+4)(rbase);	\
+						l.s	rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo,rhi,rbase)		STORE64_off(rlo,rhi,rbase,0)
+#define LOAD64(rlo,rhi,rbase)		LOAD64_off(rlo,rhi,rbase,0)
+
+#define STORE64_F(rlo,rhi,rbase)	STORE64_off_F(rlo,rhi,rbase,0)
+#define LOAD64_F(rlo,rhi,rbase)		LOAD64_off_F(rlo,rhi,rbase,0)
+
+#define STORE64_lo(rd,rbase)		sw	rd, 0(rbase)
+#define STORE64_hi(rd,rbase)		sw	rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd,rbase)		LOAD_RB_OFF(rd,rbase,offThread_exception)
+#define LOAD_base_offArrayObject_length(rd,rbase)	LOAD_RB_OFF(rd,rbase,offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd,rbase)   LOAD_RB_OFF(rd,rbase,offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd,rbase)	LOAD_RB_OFF(rd,rbase,offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd,rbase)	LOAD_RB_OFF(rd,rbase,offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_name)
+#define LOAD_base_offObject_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd,rbase)		LOADu2_RB_OFF(rd,rbase,offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd,rbase)		STORE_RB_OFF(rd,rbase,offThread_exception)
+
+
+#define	STACK_STORE(rd,off)	sw   rd, off(sp)
+#define	STACK_LOAD(rd,off)	lw   rd, off(sp)
+#define CREATE_STACK(n)	 	subu sp, sp, n
+#define DELETE_STACK(n)	 	addu sp, sp, n
+
+#define SAVE_RA(offset)	 	STACK_STORE(ra, offset)
+#define LOAD_RA(offset)	 	STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest,addr)	la   dest, addr
+#define LOAD_IMM(dest, imm)	li   dest, imm
+#define MOVE_REG(dest,src)	move dest, src
+#define	RETURN			jr   ra
+#define	STACK_SIZE		128
+
+#define STACK_OFFSET_ARG04	16
+#define STACK_OFFSET_GP		84
+#define STACK_OFFSET_rFP	112
+
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+        .cprestore STACK_OFFSET_GP
+
+#define JAL(func)		move rTEMP, ra;				\
+				jal  func;				\
+				move ra, rTEMP
+
+#define JALR(reg)		move rTEMP, ra;				\
+				jalr ra, reg;				\
+				move ra, rTEMP
+
+#define BAL(n)			bal  n
+
+#define	STACK_STORE_RA()  	CREATE_STACK(STACK_SIZE);		\
+				STACK_STORE(gp, STACK_OFFSET_GP);	\
+				STACK_STORE(ra, 124)
+
+#define	STACK_STORE_S0()  	STACK_STORE_RA();			\
+				STACK_STORE(s0, 116)
+
+#define	STACK_STORE_S0S1()  	STACK_STORE_S0();			\
+				STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define	STACK_LOAD_RA()		STACK_LOAD(ra, 124);			\
+				STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				DELETE_STACK(STACK_SIZE)
+
+#define	STACK_LOAD_S0()  	STACK_LOAD(s0, 116);			\
+				STACK_LOAD_RA()
+
+#define	STACK_LOAD_S0S1()  	STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD_S0()
+
+#define STACK_STORE_FULL()	CREATE_STACK(STACK_SIZE);	\
+				STACK_STORE(ra, 124);		\
+				STACK_STORE(fp, 120);		\
+				STACK_STORE(s0, 116);		\
+				STACK_STORE(s1, STACK_OFFSET_rFP);	\
+				STACK_STORE(s2, 108);		\
+				STACK_STORE(s3, 104);		\
+				STACK_STORE(s4, 100);		\
+				STACK_STORE(s5, 96);		\
+				STACK_STORE(s6, 92);		\
+				STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL()	STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				STACK_LOAD(s7, 88);	\
+				STACK_LOAD(s6, 92);	\
+				STACK_LOAD(s5, 96);	\
+				STACK_LOAD(s4, 100);	\
+				STACK_LOAD(s3, 104);	\
+				STACK_LOAD(s2, 108);	\
+				STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD(s0, 116);	\
+				STACK_LOAD(fp, 120);	\
+				STACK_LOAD(ra, 124);	\
+				DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define STACK_OFFSET_SCR   32
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
+
+/* File: mips/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: mips/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;        # (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;        # (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     *
+     * This code assumes the register pair ordering will depend on endianess (a1:a0 or a0:a1).
+     *    a1:a0 => vBB
+     *    a3:a2 => vCC
+     */
+    /* cmp-long vAA, vBB, vCC */
+    slt    t0, rARG1, rARG3             # compare hi
+    sgt    t1, rARG1, rARG3
+    subu   v0, t1, t0                   # v0<- (-1,1,0)
+    bnez   v0, .LTEMPLATE_CMP_LONG_finish
+                                        # at this point x.hi==y.hi
+    sltu   t0, rARG0, rARG2             # compare lo
+    sgtu   t1, rARG0, rARG2
+    subu   v0, t1, t0                   # v0<- (-1,1,0) for [< > =]
+.LTEMPLATE_CMP_LONG_finish:
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: mips/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    # a0=rSELF
+    move    a0, rSELF
+    la      t9, dvmFastMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    SAVEAREA_FROM_FP(a0, rFP)           # a0<- saveArea (old)
+    lw      t0, offStackSaveArea_prevFrame(a0)     # t0<- saveArea->prevFrame
+    lbu     t1, offThread_breakFlags(rSELF)        # t1<- breakFlags
+    lw      rPC, offStackSaveArea_savedPc(a0)      # rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    lw      t2,  offStackSaveArea_returnAddr(a0)   # t2<- chaining cell ret
+#else
+    move    t2, zero                               # disable chaining
+#endif
+    lw      a2, offStackSaveArea_method - sizeofStackSaveArea(t0)
+                                                   # a2<- method we're returning to
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     a2, zero, 1f                           # bail to interpreter
+#else
+    bne     a2, zero, 2f
+    JALR(ra)                                       # punt to interpreter and compare state
+    # DOUG: assume this does not return ???
+2:
+#endif
+    la      t4, .LdvmJitToInterpNoChainNoProfile   # defined in footer.S
+    lw      a1, (t4)
+    move    rFP, t0                                # publish new FP
+    beq     a2, zero, 4f
+    lw      t0, offMethod_clazz(a2)                # t0<- method->clazz
+4:
+
+    sw      a2, offThread_method(rSELF)            # self->method = newSave->method
+    lw      a0, offClassObject_pDvmDex(t0)         # a0<- method->clazz->pDvmDex
+    sw      rFP, offThread_curFrame(rSELF)         # self->curFrame = fp
+    add     rPC, rPC, 3*2                          # publish new rPC
+    sw      a0, offThread_methodClassDex(rSELF)
+    movn    t2, zero, t1                           # check the breadFlags and
+                                                   # clear the chaining cell address
+    sw      t2, offThread_inJitCodeCache(rSELF)    # in code cache or not
+    beq     t2, zero, 3f                           # chaining cell exists?
+    JALR(t2)                                       # jump to the chaining cell
+    # DOUG: assume this does not return ???
+3:
+#if defined(WITH_JIT_TUNING)
+    li      a0, kCallsiteInterpreted
+#endif
+    j       a1                                     # callsite is interpreted
+1:
+    sw      zero, offThread_inJitCodeCache(rSELF)  # reset inJitCodeCache
+    SAVE_PC_TO_SELF()                              # SAVE_PC_FP_TO_SELF()
+    SAVE_FP_TO_SELF()
+    la      t4, .LdvmMterpStdBail                  # defined in footer.S
+    lw      a2, (t4)
+    move    a0, rSELF                              # Expecting rSELF in a0
+    JALR(a2)                                       # exit the interpreter
+    # DOUG: assume this does not return ???
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    lw     t0, offMethod_accessFlags(a0)          # t0<- methodToCall->accessFlags
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+
+2:
+    and    t6, t0, ACC_NATIVE
+    beqz   t6, 3f
+#if !defined(WITH_SELF_VERIFICATION)
+    j      .LinvokeNative
+#else
+    RETURN                                        # bail to the interpreter
+#endif
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     t0, .LdvmJitToInterpTraceSelectNoChain # defined in footer.S
+    lw     rTEMP, (t0)
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- method->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve rTEMP,a1-a3
+    SCRATCH_STORE(rTEMP, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    # a0=methodToCall, a1=rSELF
+    move   a1, rSELF
+    la     t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a1-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(rTEMP, 0)
+#endif
+
+    # Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    li     a0, kInlineCacheMiss
+#endif
+    jr     rTEMP                                  # dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: mips/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    # methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    add    t2, ra, 8                              # setup the punt-to-interp address
+                                                  # 8 bytes skips branch and delay slot
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    jr     t2                                     # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    jr     t2                                     # bail to the interpreter
+
+2:
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- methodToCall->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    move   a1, rSELF
+    # a0=methodToCall, a1=rSELF
+    la     t9, dvmFastMethodTraceEnter
+    jalr   t9
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    RETURN                                        # return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      u4 delay_slot;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr   : to branch to the chaining cell
+     *    - lr+8 : to punt to the interpreter
+     *    - lr+16: to fully resolve the callee and may rechain.
+     *             a3 <- class
+     */
+    # a0 = this, a1 = returnCell, a2 = predictedChainCell, rPC = dalvikCallsite
+    lw      a3, offObject_clazz(a0)     # a3 <- this->class
+    lw      rIBASE, 8(a2)                   # t0 <- predictedChainCell->clazz
+    lw      a0, 12(a2)                  # a0 <- predictedChainCell->method
+    lw      t1, offThread_icRechainCount(rSELF)    # t1 <- shared rechainCount
+
+#if defined(WITH_JIT_TUNING)
+    la      rINST, .LdvmICHitCount
+    #add     t2, t2, 1
+    bne    a3, rIBASE, 1f
+    nop
+    lw      t2, 0(rINST)
+    add     t2, t2, 1
+    sw      t2, 0(rINST)
+1:
+    #add     t2, t2, 1
+#endif
+    beq     a3, rIBASE, .LinvokeChain       # branch if predicted chain is valid
+    lw      rINST, offClassObject_vtable(a3)     # rINST <- this->class->vtable
+    beqz    rIBASE, 2f                      # initialized class or not
+    sub     a1, t1, 1                   # count--
+    sw      a1, offThread_icRechainCount(rSELF)   # write back to InterpState
+    b       3f
+2:
+    move    a1, zero
+3:
+    add     ra, ra, 16                  # return to fully-resolve landing pad
+    /*
+     * a1 <- count
+     * a2 <- &predictedChainCell
+     * a3 <- this->class
+     * rPC <- dPC
+     * rINST <- this->class->vtable
+     */
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    lw     rTEMP, offMethod_nativeFunc(a0)        # t9<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+2:
+#else
+    RETURN                                        # bail to the interpreter unconditionally
+#endif
+
+    # go ahead and transfer control to the native code
+    lw     t6, offThread_jniLocal_topCookie(rSELF)  # t6<- thread->localRef->...
+    sw     a1, offThread_curFrame(rSELF)          # self->curFrame = newFp
+    sw     zero, offThread_inJitCodeCache(rSELF)  # not in the jit code cache
+    sw     t6, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                  # newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(rBIX, a1)                    # rBIX<- new stack save area
+    move   a2, a0                                 # a2<- methodToCall
+    move   a0, a1                                 # a0<- newFp
+    add    a1, rSELF, offThread_retval            # a1<- &retval
+    move   a3, rSELF                              # a3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # a2: methodToCall
+    # preserve rTEMP,a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(rTEMP, 16)
+
+    move   a0, a2
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)                                      # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a0-a3
+    SCRATCH_LOAD(rTEMP, 16)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    move   rOBJ, a2                               # save a2
+#endif
+    move   t9, rTEMP
+    JALR(t9)                                   # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    move   a0, rOBJ
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+#endif
+
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC (advance 3 dalvik instr)
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: mips/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in a0/a1, op2 in a2/a3, return in v0/v1
+     *
+     * Consider WXxYZ (a1a0 x a3a2) with a long multiply:
+     *
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     *
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     rRESULT1,rARG3,rARG0              #  v1= a3a0
+    multu   rARG2,rARG0
+    mfhi    t1
+    mflo    rRESULT0                          #  v0= a2a0
+    mul     t0,rARG2,rARG1                    #  t0= a2a1
+    addu    rRESULT1,rRESULT1,t1              #  v1= a3a0 + hi(a2a0)
+    addu    rRESULT1,rRESULT1,t0              #  v1= a3a0 + hi(a2a0) + a2a1;
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: mips/TEMPLATE_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sll     rRESULT0, rARG0, a2		#  rlo<- alo << (shift&31)
+    not     rRESULT1, a2		#  rhi<- 31-shift  (shift is 5b)
+    srl     rARG0, 1
+    srl     rARG0, rRESULT1		#  alo<- alo >> (32-(shift&31))
+    sll     rRESULT1, rARG1, a2		#  rhi<- ahi << (shift&31)
+    or      rRESULT1, rARG0		#  rhi<- rhi | alo
+    andi    a2, 0x20			#  shift< shift & 0x20
+    movn    rRESULT1, rRESULT0, a2	#  rhi<- rlo (if shift&0x20)
+    movn    rRESULT0, zero, a2		#  rlo<- 0  (if shift&0x20)
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: mips/TEMPLATE_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sra     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    sra     a3, rARG1, 31		#  a3<- sign(ah)
+    not     rARG0, a2			#  alo<- 31-shift (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, a3, a2		#  rhi<- sign(ahi) (if shift&0x20)
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: mips/TEMPLATE_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    srl     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    not     rARG0, a2			#  alo<- 31-n  (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, zero, a2		#  rhi<- 0 (if shift&0x20)
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_ADD_FLOAT_VFP
+dvmCompiler_TEMPLATE_ADD_FLOAT_VFP:
+/* File: mips/TEMPLATE_ADD_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__addsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    add.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SUB_FLOAT_VFP
+dvmCompiler_TEMPLATE_SUB_FLOAT_VFP:
+/* File: mips/TEMPLATE_SUB_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__subsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    sub.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_FLOAT_VFP
+dvmCompiler_TEMPLATE_MUL_FLOAT_VFP:
+/* File: mips/TEMPLATE_MUL_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__mulsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    mul.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DIV_FLOAT_VFP
+dvmCompiler_TEMPLATE_DIV_FLOAT_VFP:
+/* File: mips/TEMPLATE_DIV_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__divsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    div.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP
+dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP:
+/* File: mips/TEMPLATE_ADD_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__adddf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    add.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP:
+/* File: mips/TEMPLATE_SUB_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__subdf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    sub.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP:
+/* File: mips/TEMPLATE_MUL_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__muldf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    mul.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP
+dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP:
+/* File: mips/TEMPLATE_DIV_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__divdf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    div.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP:
+/* File: mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S */
+/* File: mips/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     */
+    move rINST, a0                      # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vB/vB+1
+                               # optional op
+    JAL(__truncdfsf2)                              # v0<- op, a0-a3 changed
+.LTEMPLATE_DOUBLE_TO_FLOAT_VFP_set_vreg:
+    STORE(v0, rINST)                    # vA<- v0
+#else
+    LOAD64_F(fa0, fa0f, a1)
+                               # optional op
+    cvt.s.d  fv0,fa0                            # fv0 = result
+.LTEMPLATE_DOUBLE_TO_FLOAT_VFP_set_vreg_f:
+    STORE_F(fv0, rINST)                 # vA<- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP:
+/* File: mips/TEMPLATE_DOUBLE_TO_INT_VFP.S */
+/* File: mips/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     */
+    move rINST, a0                      # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vB/vB+1
+                               # optional op
+    b    d2i_doconv                              # v0<- op, a0-a3 changed
+.LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg:
+    STORE(v0, rINST)                    # vA<- v0
+#else
+    LOAD64_F(fa0, fa0f, a1)
+                               # optional op
+    b    d2i_doconv                            # fv0 = result
+.LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f:
+    STORE_F(fv0, rINST)                 # vA<- fv0
+#endif
+    RETURN
+
+
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rOBJ as global to hold arguments (they are not bound to a global var)
+ */
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move        rBIX, rARG0                       # save a0
+    move        rOBJ, rARG1                       #  and a1
+    JAL(__gedf2)                               # is arg >= maxint?
+
+    move        t0, v0
+    li          v0, ~0x80000000                # return maxint (7fffffff)
+    bgez        t0, .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                       # recover arg
+    move        rARG1, rOBJ
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                               # is arg <= minint?
+
+    move        t0, v0
+    li          v0, 0x80000000                 # return minint (80000000)
+    blez        t0, .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    move        rARG2, rBIX                  # compare against self
+    move        rARG3, rOBJ
+    JAL(__nedf2)                        # is arg == self?
+
+    move        t0, v0                  # zero == no
+    li          v0, 0
+    bnez        t0, .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg        # return zero for NaN
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    JAL(__fixdfsi)                      # convert double to int
+    b           .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg
+#else
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa1, fa0
+    l.s         fv0, .LDOUBLE_TO_INT_maxret
+    bc1t        .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa0, fa1
+    l.s         fv0, .LDOUBLE_TO_INT_minret
+    bc1t        .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+
+    mov.d       fa1, fa0
+    c.un.d      fcc0, fa0, fa1
+    li.s        fv0, 0
+    bc1t        .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+
+    trunc.w.d   fv0, fa0
+    b           .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword   0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword   0xc1e0000000000000                  # minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word   0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word   0x80000000
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP:
+/* File: mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S */
+/* File: mips/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vB
+                               # optional op
+    JAL(__extendsfdf2)                              # result<- op, a0-a3 changed
+
+.LTEMPLATE_FLOAT_TO_DOUBLE_VFP_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)   # vA/vA+1<- v0/v1
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vB
+                               # optional op
+    cvt.d.s fv0, fa0
+
+.LTEMPLATE_FLOAT_TO_DOUBLE_VFP_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                          # vA/vA+1<- fv0/fv0f
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP:
+/* File: mips/TEMPLATE_FLOAT_TO_INT_VFP.S */
+/* File: mips/funop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+                               # optional op
+    b    f2i_doconv                              # v0<- op, a0-a3 changed
+.LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg:
+    STORE(v0, rOBJ)                     # vAA<- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+                               # optional op
+    b        f2i_doconv                            # fv0 = result
+.LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f:
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+        li      a1, 0x4f000000  # (float)maxint
+        move    rBIX, a0
+        JAL(__gesf2)            # is arg >= maxint?
+        move    t0, v0
+        li      v0, ~0x80000000 # return maxint (7fffffff)
+        bgez    t0, .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+
+        move    a0, rBIX                # recover arg
+        li      a1, 0xcf000000  # (float)minint
+        JAL(__lesf2)
+
+        move    t0, v0
+        li      v0, 0x80000000  # return minint (80000000)
+        blez    t0, .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+        move    a0, rBIX
+        move    a1, rBIX
+        JAL(__nesf2)
+
+        move    t0, v0
+        li      v0, 0           # return zero for NaN
+        bnez    t0, .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+
+        move    a0, rBIX
+        JAL(__fixsfsi)
+        b .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+#else
+        l.s             fa1, .LFLOAT_TO_INT_max
+        c.ole.s         fcc0, fa1, fa0
+        l.s             fv0, .LFLOAT_TO_INT_ret_max
+        bc1t            .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+
+        l.s             fa1, .LFLOAT_TO_INT_min
+        c.ole.s         fcc0, fa0, fa1
+        l.s             fv0, .LFLOAT_TO_INT_ret_min
+        bc1t            .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+
+        mov.s           fa1, fa0
+        c.un.s          fcc0, fa0, fa1
+        li.s            fv0, 0
+        bc1t            .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+
+        trunc.w.s       fv0, fa0
+        b .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+        .word   0x4f000000
+.LFLOAT_TO_INT_min:
+        .word   0xcf000000
+.LFLOAT_TO_INT_ret_max:
+        .word   0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+        .word   0x80000000
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP:
+/* File: mips/TEMPLATE_INT_TO_DOUBLE_VFP.S */
+/* File: mips/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vB
+                               # optional op
+    JAL(__floatsidf)                              # result<- op, a0-a3 changed
+
+.LTEMPLATE_INT_TO_DOUBLE_VFP_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)   # vA/vA+1<- v0/v1
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vB
+                               # optional op
+    cvt.d.w    fv0, fa0
+
+.LTEMPLATE_INT_TO_DOUBLE_VFP_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                          # vA/vA+1<- fv0/fv0f
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP:
+/* File: mips/TEMPLATE_INT_TO_FLOAT_VFP.S */
+/* File: mips/funop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+                               # optional op
+    JAL(__floatsisf)                              # v0<- op, a0-a3 changed
+.LTEMPLATE_INT_TO_FLOAT_VFP_set_vreg:
+    STORE(v0, rOBJ)                     # vAA<- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+                               # optional op
+    cvt.s.w fv0, fa0                            # fv0 = result
+.LTEMPLATE_INT_TO_FLOAT_VFP_set_vreg_f:
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP:
+/* File: mips/TEMPLATE_CMPG_DOUBLE_VFP.S */
+/* File: mips/TEMPLATE_CMPL_DOUBLE_VFP.S */
+    /*
+     * Compare two double precision floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    move rOBJ, a0                       # save a0
+    move rBIX, a1                       # save a1
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__eqdf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__ltdf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__gtdf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+#else
+    LOAD64_F(fs0, fs0f, a0)             # fs0<- vBB
+    LOAD64_F(fs1, fs1f, a1)             # fs1<- vCC
+    c.olt.d     fcc0, fs0, fs1          # Is fs0 < fs1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    c.olt.d     fcc0, fs1, fs0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    c.eq.d      fcc0, fs0, fs1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+#endif
+
+    li            rTEMP, 1
+
+TEMPLATE_CMPG_DOUBLE_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP:
+/* File: mips/TEMPLATE_CMPL_DOUBLE_VFP.S */
+    /*
+     * Compare two double precision floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    move rOBJ, a0                       # save a0
+    move rBIX, a1                       # save a1
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__eqdf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__ltdf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__gtdf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+#else
+    LOAD64_F(fs0, fs0f, a0)             # fs0<- vBB
+    LOAD64_F(fs1, fs1f, a1)             # fs1<- vCC
+    c.olt.d     fcc0, fs0, fs1          # Is fs0 < fs1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    c.olt.d     fcc0, fs1, fs0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    c.eq.d      fcc0, fs0, fs1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+#endif
+
+    li     rTEMP, -1
+
+TEMPLATE_CMPL_DOUBLE_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP:
+/* File: mips/TEMPLATE_CMPG_FLOAT_VFP.S */
+/* File: mips/TEMPLATE_CMPL_FLOAT_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    LOAD(rOBJ, a0)                      # rOBJ<- vBB
+    LOAD(rBIX, a1)                      # rBIX<- vCC
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__eqsf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__ltsf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__gtsf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPG_FLOAT_VFP_finish
+#else
+    LOAD_F(fs0, a0)                     # fs0<- vBB
+    LOAD_F(fs1, a1)                     # fs1<- vCC
+    c.olt.s     fcc0, fs0, fs1          #Is fs0 < fs1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    c.olt.s     fcc0, fs1, fs0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    c.eq.s      fcc0, fs0, fs1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPG_FLOAT_VFP_finish
+#endif
+
+    li     rTEMP, 1
+
+TEMPLATE_CMPG_FLOAT_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP:
+/* File: mips/TEMPLATE_CMPL_FLOAT_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    LOAD(rOBJ, a0)                      # rOBJ<- vBB
+    LOAD(rBIX, a1)                      # rBIX<- vCC
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__eqsf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__ltsf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__gtsf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPL_FLOAT_VFP_finish
+#else
+    LOAD_F(fs0, a0)                     # fs0<- vBB
+    LOAD_F(fs1, a1)                     # fs1<- vCC
+    c.olt.s     fcc0, fs0, fs1          #Is fs0 < fs1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    c.olt.s     fcc0, fs1, fs0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    c.eq.s      fcc0, fs0, fs1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPL_FLOAT_VFP_finish
+#endif
+
+    li     rTEMP, -1
+
+TEMPLATE_CMPL_FLOAT_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP:
+/* File: mips/TEMPLATE_SQRT_DOUBLE_VFP.S */
+
+    /*
+     * 64-bit floating point sqrt operation.
+     * If the result is a NaN, bail out to library code to do
+     * the right thing.
+     *
+     * On entry:
+     *     a2 src addr of op1
+     * On exit:
+     *     v0,v1/fv0 = res
+     */
+#ifdef  SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)        # a0/a1<- vBB/vBB+1
+#else
+    LOAD64_F(fa0, fa0f, a2)         # fa0/fa0f<- vBB/vBB+1
+    sqrt.d	fv0, fa0
+    c.eq.d	fv0, fv0
+    bc1t	1f
+#endif
+    JAL(sqrt)
+1:
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: mips/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    a0    Dalvik PC that raises the exception
+     */
+    j      .LhandleException
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: mips/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+#ifdef HARD_FLOAT
+    /* push f0-f31 onto stack */
+    sw      f0, fr0*-4(sp)              # push f0
+    sw      f1, fr1*-4(sp)              # push f1
+    sw      f2, fr2*-4(sp)              # push f2
+    sw      f3, fr3*-4(sp)              # push f3
+    sw      f4, fr4*-4(sp)              # push f4
+    sw      f5, fr5*-4(sp)              # push f5
+    sw      f6, fr6*-4(sp)              # push f6
+    sw      f7, fr7*-4(sp)              # push f7
+    sw      f8, fr8*-4(sp)              # push f8
+    sw      f9, fr9*-4(sp)              # push f9
+    sw      f10, fr10*-4(sp)            # push f10
+    sw      f11, fr11*-4(sp)            # push f11
+    sw      f12, fr12*-4(sp)            # push f12
+    sw      f13, fr13*-4(sp)            # push f13
+    sw      f14, fr14*-4(sp)            # push f14
+    sw      f15, fr15*-4(sp)            # push f15
+    sw      f16, fr16*-4(sp)            # push f16
+    sw      f17, fr17*-4(sp)            # push f17
+    sw      f18, fr18*-4(sp)            # push f18
+    sw      f19, fr19*-4(sp)            # push f19
+    sw      f20, fr20*-4(sp)            # push f20
+    sw      f21, fr21*-4(sp)            # push f21
+    sw      f22, fr22*-4(sp)            # push f22
+    sw      f23, fr23*-4(sp)            # push f23
+    sw      f24, fr24*-4(sp)            # push f24
+    sw      f25, fr25*-4(sp)            # push f25
+    sw      f26, fr26*-4(sp)            # push f26
+    sw      f27, fr27*-4(sp)            # push f27
+    sw      f28, fr28*-4(sp)            # push f28
+    sw      f29, fr29*-4(sp)            # push f29
+    sw      f30, fr30*-4(sp)            # push f30
+    sw      f31, fr31*-4(sp)            # push f31
+
+    sub     sp, (32-0)*4                # adjust stack pointer
+#endif
+
+    /* push gp registers (except zero, gp, sp, and fp) */
+    .set noat
+    sw      AT, r_AT*-4(sp)             # push at
+    .set at
+    sw      v0, r_V0*-4(sp)             # push v0
+    sw      v1, r_V1*-4(sp)             # push v1
+    sw      a0, r_A0*-4(sp)             # push a0
+    sw      a1, r_A1*-4(sp)             # push a1
+    sw      a2, r_A2*-4(sp)             # push a2
+    sw      a3, r_A3*-4(sp)             # push a3
+    sw      t0, r_T0*-4(sp)             # push t0
+    sw      t1, r_T1*-4(sp)             # push t1
+    sw      t2, r_T2*-4(sp)             # push t2
+    sw      t3, r_T3*-4(sp)             # push t3
+    sw      t4, r_T4*-4(sp)             # push t4
+    sw      t5, r_T5*-4(sp)             # push t5
+    sw      t6, r_T6*-4(sp)             # push t6
+    sw      t7, r_T7*-4(sp)             # push t7
+    sw      s0, r_S0*-4(sp)             # push s0
+    sw      s1, r_S1*-4(sp)             # push s1
+    sw      s2, r_S2*-4(sp)             # push s2
+    sw      s3, r_S3*-4(sp)             # push s3
+    sw      s4, r_S4*-4(sp)             # push s4
+    sw      s5, r_S5*-4(sp)             # push s5
+    sw      s6, r_S6*-4(sp)             # push s6
+    sw      s7, r_S7*-4(sp)             # push s7
+    sw      t8, r_T8*-4(sp)             # push t8
+    sw      t9, r_T9*-4(sp)             # push t9
+    sw      k0, r_K0*-4(sp)             # push k0
+    sw      k1, r_K1*-4(sp)             # push k1
+    sw      ra, r_RA*-4(sp)             # push RA
+
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    sub     sp, (32-0)*4                # adjust stack pointer
+
+    la     a2, .LdvmSelfVerificationMemOpDecode  # defined in footer.S
+    lw     a2, (a2)
+    move   a0, ra                       # a0<- link register
+    move   a1, sp                       # a1<- stack pointer
+    JALR(a2)
+
+    /* pop gp registers (except zero, gp, sp, and fp) */
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    add     sp, (32-0)*4                # adjust stack pointer
+    .set noat
+    lw      AT, r_AT*-4(sp)             # pop at
+    .set at
+    lw      v0, r_V0*-4(sp)             # pop v0
+    lw      v1, r_V1*-4(sp)             # pop v1
+    lw      a0, r_A0*-4(sp)             # pop a0
+    lw      a1, r_A1*-4(sp)             # pop a1
+    lw      a2, r_A2*-4(sp)             # pop a2
+    lw      a3, r_A3*-4(sp)             # pop a3
+    lw      t0, r_T0*-4(sp)             # pop t0
+    lw      t1, r_T1*-4(sp)             # pop t1
+    lw      t2, r_T2*-4(sp)             # pop t2
+    lw      t3, r_T3*-4(sp)             # pop t3
+    lw      t4, r_T4*-4(sp)             # pop t4
+    lw      t5, r_T5*-4(sp)             # pop t5
+    lw      t6, r_T6*-4(sp)             # pop t6
+    lw      t7, r_T7*-4(sp)             # pop t7
+    lw      s0, r_S0*-4(sp)             # pop s0
+    lw      s1, r_S1*-4(sp)             # pop s1
+    lw      s2, r_S2*-4(sp)             # pop s2
+    lw      s3, r_S3*-4(sp)             # pop s3
+    lw      s4, r_S4*-4(sp)             # pop s4
+    lw      s5, r_S5*-4(sp)             # pop s5
+    lw      s6, r_S6*-4(sp)             # pop s6
+    lw      s7, r_S7*-4(sp)             # pop s7
+    lw      t8, r_T8*-4(sp)             # pop t8
+    lw      t9, r_T9*-4(sp)             # pop t9
+    lw      k0, r_K0*-4(sp)             # pop k0
+    lw      k1, r_K1*-4(sp)             # pop k1
+    lw      ra, r_RA*-4(sp)             # pop RA
+
+#ifdef HARD_FLOAT
+    /* pop f0-f31 from stack */
+    add     sp, (32-0)*4                # adjust stack pointer
+    lw      f0, fr0*-4(sp)              # pop f0
+    lw      f1, fr1*-4(sp)              # pop f1
+    lw      f2, fr2*-4(sp)              # pop f2
+    lw      f3, fr3*-4(sp)              # pop f3
+    lw      f4, fr4*-4(sp)              # pop f4
+    lw      f5, fr5*-4(sp)              # pop f5
+    lw      f6, fr6*-4(sp)              # pop f6
+    lw      f7, fr7*-4(sp)              # pop f7
+    lw      f8, fr8*-4(sp)              # pop f8
+    lw      f9, fr9*-4(sp)              # pop f9
+    lw      f10, fr10*-4(sp)            # pop f10
+    lw      f11, fr11*-4(sp)            # pop f11
+    lw      f12, fr12*-4(sp)            # pop f12
+    lw      f13, fr13*-4(sp)            # pop f13
+    lw      f14, fr14*-4(sp)            # pop f14
+    lw      f15, fr15*-4(sp)            # pop f15
+    lw      f16, fr16*-4(sp)            # pop f16
+    lw      f17, fr17*-4(sp)            # pop f17
+    lw      f18, fr18*-4(sp)            # pop f18
+    lw      f19, fr19*-4(sp)            # pop f19
+    lw      f20, fr20*-4(sp)            # pop f20
+    lw      f21, fr21*-4(sp)            # pop f21
+    lw      f22, fr22*-4(sp)            # pop f22
+    lw      f23, fr23*-4(sp)            # pop f23
+    lw      f24, fr24*-4(sp)            # pop f24
+    lw      f25, fr25*-4(sp)            # pop f25
+    lw      f26, fr26*-4(sp)            # pop f26
+    lw      f27, fr27*-4(sp)            # pop f27
+    lw      f28, fr28*-4(sp)            # pop f28
+    lw      f29, fr29*-4(sp)            # pop f29
+    lw      f30, fr30*-4(sp)            # pop f30
+    lw      f31, fr31*-4(sp)            # pop f31
+#endif
+
+    RETURN
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: mips/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires a0/a1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   this object pointer
+     *    a1:   comp object pointer
+     *
+     */
+
+     subu  v0, a0, a1                # Same?
+     bnez  v0, 1f
+     RETURN
+1:
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_OFFSET(a1)
+     lw    t2, STRING_FIELDOFF_COUNT(a0)
+     lw    a2, STRING_FIELDOFF_COUNT(a1)
+     lw    a0, STRING_FIELDOFF_VALUE(a0)
+     lw    a1, STRING_FIELDOFF_VALUE(a1)
+
+    /*
+     * At this point, we have this/comp:
+     *    offset: t0/t1
+     *    count:  t2/a2
+     *    value:  a0/a1
+     * We're going to compute
+     *    a3 <- countDiff
+     *    a2 <- minCount
+     */
+     subu  a3, t2, a2                # a3<- countDiff
+     sleu  t7, t2, a2
+     movn  a2, t2, t7                # a2<- minCount
+
+     /*
+      * Note: data pointers point to first element.
+      */
+     addu  a0, 16                    # point to contents[0]
+     addu  a1, 16                    # point to contents[0]
+
+     /* Now, build pointers to the string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  a0, a0, t7
+     sll   t7, t1, 1                 # multiply offset by 2
+     addu  a1, a1, t7
+
+     /*
+      * At this point we have:
+      *   a0: *this string data
+      *   a1: *comp string data
+      *   a2: iteration count for comparison
+      *   a3: value to return if the first part of the string is equal
+      *   v0: reserved for result
+      *   t0-t5 available for loading string data
+      */
+
+     subu  a2, 2
+     bltz  a2, do_remainder2
+
+     /*
+      * Unroll the first two checks so we can quickly catch early mismatch
+      * on long strings (but preserve incoming alignment)
+      */
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     addu  a0, 4                     # offset to contents[2]
+     addu  a1, 4                     # offset to contents[2]
+     li    t7, 28
+     bgt   a2, t7, do_memcmp16
+     subu  a2, 3
+     bltz  a2, do_remainder
+
+loopback_triple:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     lhu   t4, 4(a0)
+     lhu   t5, 4(a1)
+     subu  v0, t4, t5
+     beqz  v0, 3f
+     RETURN
+3:
+     addu  a0, 6                     # offset to contents[i+3]
+     addu  a1, 6                     # offset to contents[i+3]
+     subu  a2, 3
+     bgez  a2, loopback_triple
+
+do_remainder:
+     addu  a2, 3
+     beqz  a2, returnDiff
+
+loopback_single:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     bnez  v0, 1f
+     addu  a0, 2                     # offset to contents[i+1]
+     addu  a1, 2                     # offset to contents[i+1]
+     subu  a2, 1
+     bnez  a2, loopback_single
+
+returnDiff:
+     move  v0, a3
+1:
+     RETURN
+
+do_remainder2:
+     addu  a2, 2
+     bnez  a2, loopback_single
+     move  v0, a3
+     RETURN
+
+    /* Long string case */
+do_memcmp16:
+     move  rOBJ, a3                  # save return value if strings are equal
+     JAL(__memcmp16)
+     seq   t0, v0, zero
+     movn  v0, rOBJ, t0              # overwrite return value if strings are equal
+     RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: mips/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires a0 to have been previously checked for null.  Will
+     * return index of match of a1 in v0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   string object pointer
+     *    a1:   char to match
+     *    a2:   Starting offset in string data
+     */
+
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_COUNT(a0)
+     lw    v0, STRING_FIELDOFF_VALUE(a0)
+
+    /*
+     * At this point, we have:
+     *    v0: object pointer
+     *    a1: char to match
+     *    a2: starting offset
+     *    t0: offset
+     *    t1: string length
+     */
+
+    /* Point to first element */
+     addu  v0, 16                    # point to contents[0]
+
+    /* Build pointer to start of string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Save a copy of starting data in v1 */
+     move  v1, v0
+
+    /* Clamp start to [0..count] */
+     slt   t7, a2, zero
+     movn  a2, zero, t7
+     sgt   t7, a2, t1
+     movn  a2, t1, t7
+
+    /* Build pointer to start of data to compare */
+     sll   t7, a2, 1                # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Compute iteration count */
+     subu  a3, t1, a2
+
+    /*
+     * At this point we have:
+     *   v0: start of data to test
+     *   a1: char to compare
+     *   a3: iteration count
+     *   v1: original start of string
+     *   t0-t7 available for loading string data
+     */
+     subu  a3, 4
+     bltz  a3, indexof_remainder
+
+indexof_loop4:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     lhu   t0, 2(v0)
+     beq   t0, a1, match_1
+     lhu   t0, 4(v0)
+     beq   t0, a1, match_2
+     lhu   t0, 6(v0)
+     beq   t0, a1, match_3
+     addu  v0, 8                     # offset to contents[i+4]
+     subu  a3, 4
+     bgez  a3, indexof_loop4
+
+indexof_remainder:
+     addu  a3, 4
+     beqz  a3, indexof_nomatch
+
+indexof_loop1:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     addu  v0, 2                     # offset to contents[i+1]
+     subu  a3, 1
+     bnez  a3, indexof_loop1
+
+indexof_nomatch:
+     li    v0, -1
+     RETURN
+
+match_0:
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_1:
+     addu  v0, 2
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_2:
+     addu  v0, 4
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_3:
+     addu  v0, 6
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: mips/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC.
+     * On entry:
+     *    ra - if NULL:
+     *        a1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [ra] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    la      t0, dvmJitToInterpPunt
+    move    a0, a1
+    beq     ra, zero, 1f
+    lw      a0, 0(ra)
+1:
+    jr      t0
+    # doesn't return
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: mips/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li      a0, kHeavyweightMonitor
+#endif
+    jr      a2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: mips/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     *
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # test for exception
+    lw     a1, offThread_exception(rSELF)
+    beqz   a1, 1f
+    sub    a0, rPC, 2                           # roll dPC back to this monitor instruction
+    j      .LhandleException
+1:
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li     a0, kHeavyweightMonitor
+#endif
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+    jr     a2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RESTORE_STATE
+dvmCompiler_TEMPLATE_RESTORE_STATE:
+/* File: mips/TEMPLATE_RESTORE_STATE.S */
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    a0 - offset from rSELF to the 1st element of the coreRegs save array.
+     * Note: the following registers are not restored
+     *       zero, AT, gp, sp, fp, ra
+     */
+
+    add     a0, a0, rSELF               # pointer to heapArgSpace.coreRegs[0]
+#if 0
+    lw      zero, r_ZERO*4(a0)          # restore zero
+#endif
+    .set noat
+    lw      AT, r_AT*4(a0)              # restore at
+    .set at
+    lw      v0, r_V0*4(a0)              # restore v0
+    lw      v1, r_V1*4(a0)              # restore v1
+
+    lw      a1, r_A1*4(a0)              # restore a1
+    lw      a2, r_A2*4(a0)              # restore a2
+    lw      a3, r_A3*4(a0)              # restore a3
+
+    lw      t0, r_T0*4(a0)              # restore t0
+    lw      t1, r_T1*4(a0)              # restore t1
+    lw      t2, r_T2*4(a0)              # restore t2
+    lw      t3, r_T3*4(a0)              # restore t3
+    lw      t4, r_T4*4(a0)              # restore t4
+    lw      t5, r_T5*4(a0)              # restore t5
+    lw      t6, r_T6*4(a0)              # restore t6
+    lw      t7, r_T7*4(a0)              # restore t7
+
+    lw      s0, r_S0*4(a0)              # restore s0
+    lw      s1, r_S1*4(a0)              # restore s1
+    lw      s2, r_S2*4(a0)              # restore s2
+    lw      s3, r_S3*4(a0)              # restore s3
+    lw      s4, r_S4*4(a0)              # restore s4
+    lw      s5, r_S5*4(a0)              # restore s5
+    lw      s6, r_S6*4(a0)              # restore s6
+    lw      s7, r_S7*4(a0)              # restore s7
+
+    lw      t8, r_T8*4(a0)              # restore t8
+    lw      t9, r_T9*4(a0)              # restore t9
+
+    lw      k0, r_K0*4(a0)              # restore k0
+    lw      k1, r_K1*4(a0)              # restore k1
+
+#if 0
+    lw      gp, r_GP*4(a0)              # restore gp
+    lw      sp, r_SP*4(a0)              # restore sp
+    lw      fp, r_FP*4(a0)              # restore fp
+    lw      ra, r_RA*4(a0)              # restore ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    lw      f0, fr0*4(a0)               # restore f0
+    lw      f1, fr1*4(a0)               # restore f1
+    lw      f2, fr2*4(a0)               # restore f2
+    lw      f3, fr3*4(a0)               # restore f3
+    lw      f4, fr4*4(a0)               # restore f4
+    lw      f5, fr5*4(a0)               # restore f5
+    lw      f6, fr6*4(a0)               # restore f6
+    lw      f7, fr7*4(a0)               # restore f7
+    lw      f8, fr8*4(a0)               # restore f8
+    lw      f9, fr9*4(a0)               # restore f9
+    lw      f10, fr10*4(a0)             # restore f10
+    lw      f11, fr11*4(a0)             # restore f11
+    lw      f12, fr12*4(a0)             # restore f12
+    lw      f13, fr13*4(a0)             # restore f13
+    lw      f14, fr14*4(a0)             # restore f14
+    lw      f15, fr15*4(a0)             # restore f15
+    lw      f16, fr16*4(a0)             # restore f16
+    lw      f17, fr17*4(a0)             # restore f17
+    lw      f18, fr18*4(a0)             # restore f18
+    lw      f19, fr19*4(a0)             # restore f19
+    lw      f20, fr20*4(a0)             # restore f20
+    lw      f21, fr21*4(a0)             # restore f21
+    lw      f22, fr22*4(a0)             # restore f22
+    lw      f23, fr23*4(a0)             # restore f23
+    lw      f24, fr24*4(a0)             # restore f24
+    lw      f25, fr25*4(a0)             # restore f25
+    lw      f26, fr26*4(a0)             # restore f26
+    lw      f27, fr27*4(a0)             # restore f27
+    lw      f28, fr28*4(a0)             # restore f28
+    lw      f29, fr29*4(a0)             # restore f29
+    lw      f30, fr30*4(a0)             # restore f30
+    lw      f31, fr31*4(a0)             # restore f31
+#endif
+
+    lw      a0, r_A1*4(a0)              # restore a0
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SAVE_STATE
+dvmCompiler_TEMPLATE_SAVE_STATE:
+/* File: mips/TEMPLATE_SAVE_STATE.S */
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: a1 value to save
+     *    Top of stack + 0: a0 value to save
+     *    a0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    a1 - the value of regMap
+     *
+     * The handler must save regMap, r0-r31, f0-f31 if FPU, and then return with
+     * r0-r31 with their original values (note that this means a0 and a1 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     * Note: the following registers are not saved
+     *       zero, AT, gp, sp, fp, ra
+     */
+    add     a0, a0, rSELF               # pointer to heapArgSpace
+    sw      a1, 0(a0)                   # save regMap
+    add     a0, a0, 4                   # pointer to coreRegs
+#if 0
+    sw      zero, r_ZERO*4(a0)          # save zero
+#endif
+    .set noat
+    sw      AT, r_AT*4(a0)              # save at
+    .set at
+    sw      v0, r_V0*4(a0)              # save v0
+    sw      v1, r_V1*4(a0)              # save v1
+
+    lw      a1, 0(sp)                   # recover a0 value
+    sw      a1, r_A0*4(a0)              # save a0
+    lw      a1, 4(sp)                   # recover a1 value
+    sw      a1, r_A1*4(a0)              # save a1
+    sw      a2, r_A2*4(a0)              # save a2
+    sw      a3, r_A3*4(a0)              # save a3
+
+    sw      t0, r_T0*4(a0)              # save t0
+    sw      t1, r_T1*4(a0)              # save t1
+    sw      t2, r_T2*4(a0)              # save t2
+    sw      t3, r_T3*4(a0)              # save t3
+    sw      t4, r_T4*4(a0)              # save t4
+    sw      t5, r_T5*4(a0)              # save t5
+    sw      t6, r_T6*4(a0)              # save t6
+    sw      t7, r_T7*4(a0)              # save t7
+
+    sw      s0, r_S0*4(a0)              # save s0
+    sw      s1, r_S1*4(a0)              # save s1
+    sw      s2, r_S2*4(a0)              # save s2
+    sw      s3, r_S3*4(a0)              # save s3
+    sw      s4, r_S4*4(a0)              # save s4
+    sw      s5, r_S5*4(a0)              # save s5
+    sw      s6, r_S6*4(a0)              # save s6
+    sw      s7, r_S7*4(a0)              # save s7
+
+    sw      t8, r_T8*4(a0)              # save t8
+    sw      t9, r_T9*4(a0)              # save t9
+
+    sw      k0, r_K0*4(a0)              # save k0
+    sw      k1, r_K1*4(a0)              # save k1
+
+#if 0
+    sw      gp, r_GP*4(a0)              # save gp
+    sw      sp, r_SP*4(a0)              # save sp (need to adjust??? )
+    sw      fp, r_FP*4(a0)              # save fp
+    sw      ra, r_RA*4(a0)              # save ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    sw      f0, fr0*4(a0)               # save f0
+    sw      f1, fr1*4(a0)               # save f1
+    sw      f2, fr2*4(a0)               # save f2
+    sw      f3, fr3*4(a0)               # save f3
+    sw      f4, fr4*4(a0)               # save f4
+    sw      f5, fr5*4(a0)               # save f5
+    sw      f6, fr6*4(a0)               # save f6
+    sw      f7, fr7*4(a0)               # save f7
+    sw      f8, fr8*4(a0)               # save f8
+    sw      f9, fr9*4(a0)               # save f9
+    sw      f10, fr10*4(a0)             # save f10
+    sw      f11, fr11*4(a0)             # save f11
+    sw      f12, fr12*4(a0)             # save f12
+    sw      f13, fr13*4(a0)             # save f13
+    sw      f14, fr14*4(a0)             # save f14
+    sw      f15, fr15*4(a0)             # save f15
+    sw      f16, fr16*4(a0)             # save f16
+    sw      f17, fr17*4(a0)             # save f17
+    sw      f18, fr18*4(a0)             # save f18
+    sw      f19, fr19*4(a0)             # save f19
+    sw      f20, fr20*4(a0)             # save f20
+    sw      f21, fr21*4(a0)             # save f21
+    sw      f22, fr22*4(a0)             # save f22
+    sw      f23, fr23*4(a0)             # save f23
+    sw      f24, fr24*4(a0)             # save f24
+    sw      f25, fr25*4(a0)             # save f25
+    sw      f26, fr26*4(a0)             # save f26
+    sw      f27, fr27*4(a0)             # save f27
+    sw      f28, fr28*4(a0)             # save f28
+    sw      f29, fr29*4(a0)             # save f29
+    sw      f30, fr30*4(a0)             # save f30
+    sw      f31, fr31*4(a0)             # save f31
+#endif
+
+    lw      a1, 0(sp)                   # recover a0 value
+    lw      a1, 4(sp)                   # recover a1 value
+    sub     sp, sp, 8                   # adjust stack ptr
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: mips/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (ra-16) is address of pointer to counter.  Note: the counter
+     *    actually exists 16 bytes before the return target for mips.
+     *     - 4 bytes for prof count addr.
+     *     - 4 bytes for chain cell offset (2bytes 32 bit aligned).
+     *     - 4 bytes for call TEMPLATE_PERIODIC_PROFILING.
+     *     - 4 bytes for call delay slot.
+     */
+     lw     a0, -16(ra)
+     lw     a1, offThread_pProfileCountdown(rSELF)
+     lw     a2, 0(a0)                   # get counter
+     lw     a3, 0(a1)                   # get countdown timer
+     addu   a2, 1
+     sub    a3, 1                       # FIXME - bug in ARM code???
+     bltz   a3, .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     sw     a2, 0(a0)
+     sw     a3, 0(a1)
+     RETURN
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     la     a0, dvmJitTraceProfilingOff
+     JALR(a0)
+     # The ra register is preserved by the JALR macro.
+     jr     ra
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: mips/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    # a0=rSELF
+    move    a0, rSELF
+    la      t9, dvmFastMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    SAVEAREA_FROM_FP(a0, rFP)           # a0<- saveArea (old)
+    lw      t0, offStackSaveArea_prevFrame(a0)     # t0<- saveArea->prevFrame
+    lbu     t1, offThread_breakFlags(rSELF)        # t1<- breakFlags
+    lw      rPC, offStackSaveArea_savedPc(a0)      # rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    lw      t2,  offStackSaveArea_returnAddr(a0)   # t2<- chaining cell ret
+#else
+    move    t2, zero                               # disable chaining
+#endif
+    lw      a2, offStackSaveArea_method - sizeofStackSaveArea(t0)
+                                                   # a2<- method we're returning to
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     a2, zero, 1f                           # bail to interpreter
+#else
+    bne     a2, zero, 2f
+    JALR(ra)                                       # punt to interpreter and compare state
+    # DOUG: assume this does not return ???
+2:
+#endif
+    la      t4, .LdvmJitToInterpNoChainNoProfile   # defined in footer.S
+    lw      a1, (t4)
+    move    rFP, t0                                # publish new FP
+    beq     a2, zero, 4f
+    lw      t0, offMethod_clazz(a2)                # t0<- method->clazz
+4:
+
+    sw      a2, offThread_method(rSELF)            # self->method = newSave->method
+    lw      a0, offClassObject_pDvmDex(t0)         # a0<- method->clazz->pDvmDex
+    sw      rFP, offThread_curFrame(rSELF)         # self->curFrame = fp
+    add     rPC, rPC, 3*2                          # publish new rPC
+    sw      a0, offThread_methodClassDex(rSELF)
+    movn    t2, zero, t1                           # check the breadFlags and
+                                                   # clear the chaining cell address
+    sw      t2, offThread_inJitCodeCache(rSELF)    # in code cache or not
+    beq     t2, zero, 3f                           # chaining cell exists?
+    JALR(t2)                                       # jump to the chaining cell
+    # DOUG: assume this does not return ???
+3:
+#if defined(WITH_JIT_TUNING)
+    li      a0, kCallsiteInterpreted
+#endif
+    j       a1                                     # callsite is interpreted
+1:
+    sw      zero, offThread_inJitCodeCache(rSELF)  # reset inJitCodeCache
+    SAVE_PC_TO_SELF()                              # SAVE_PC_FP_TO_SELF()
+    SAVE_FP_TO_SELF()
+    la      t4, .LdvmMterpStdBail                  # defined in footer.S
+    lw      a2, (t4)
+    move    a0, rSELF                              # Expecting rSELF in a0
+    JALR(a2)                                       # exit the interpreter
+    # DOUG: assume this does not return ???
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    lw     t0, offMethod_accessFlags(a0)          # t0<- methodToCall->accessFlags
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+
+2:
+    and    t6, t0, ACC_NATIVE
+    beqz   t6, 3f
+#if !defined(WITH_SELF_VERIFICATION)
+    j      .LinvokeNative
+#else
+    RETURN                                        # bail to the interpreter
+#endif
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     t0, .LdvmJitToInterpTraceSelectNoChain # defined in footer.S
+    lw     rTEMP, (t0)
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- method->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve rTEMP,a1-a3
+    SCRATCH_STORE(rTEMP, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    # a0=methodToCall, a1=rSELF
+    move   a1, rSELF
+    la     t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a1-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(rTEMP, 0)
+#endif
+
+    # Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    li     a0, kInlineCacheMiss
+#endif
+    jr     rTEMP                                  # dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    # methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    add    t2, ra, 8                              # setup the punt-to-interp address
+                                                  # 8 bytes skips branch and delay slot
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    jr     t2                                     # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    jr     t2                                     # bail to the interpreter
+
+2:
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- methodToCall->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    move   a1, rSELF
+    # a0=methodToCall, a1=rSELF
+    la     t9, dvmFastMethodTraceEnter
+    jalr   t9
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    RETURN                                        # return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      u4 delay_slot;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr   : to branch to the chaining cell
+     *    - lr+8 : to punt to the interpreter
+     *    - lr+16: to fully resolve the callee and may rechain.
+     *             a3 <- class
+     */
+    # a0 = this, a1 = returnCell, a2 = predictedChainCell, rPC = dalvikCallsite
+    lw      a3, offObject_clazz(a0)     # a3 <- this->class
+    lw      rIBASE, 8(a2)                   # t0 <- predictedChainCell->clazz
+    lw      a0, 12(a2)                  # a0 <- predictedChainCell->method
+    lw      t1, offThread_icRechainCount(rSELF)    # t1 <- shared rechainCount
+
+#if defined(WITH_JIT_TUNING)
+    la      rINST, .LdvmICHitCount
+    #add     t2, t2, 1
+    bne    a3, rIBASE, 1f
+    nop
+    lw      t2, 0(rINST)
+    add     t2, t2, 1
+    sw      t2, 0(rINST)
+1:
+    #add     t2, t2, 1
+#endif
+    beq     a3, rIBASE, .LinvokeChainProf       # branch if predicted chain is valid
+    lw      rINST, offClassObject_vtable(a3)     # rINST <- this->class->vtable
+    beqz    rIBASE, 2f                      # initialized class or not
+    sub     a1, t1, 1                   # count--
+    sw      a1, offThread_icRechainCount(rSELF)   # write back to InterpState
+    b       3f
+2:
+    move    a1, zero
+3:
+    add     ra, ra, 16                  # return to fully-resolve landing pad
+    /*
+     * a1 <- count
+     * a2 <- &predictedChainCell
+     * a3 <- this->class
+     * rPC <- dPC
+     * rINST <- this->class->vtable
+     */
+    RETURN
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    lw     rTEMP, offMethod_nativeFunc(a0)        # t9<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+2:
+#else
+    RETURN                                        # bail to the interpreter unconditionally
+#endif
+
+    # go ahead and transfer control to the native code
+    lw     t6, offThread_jniLocal_topCookie(rSELF)  # t6<- thread->localRef->...
+    sw     a1, offThread_curFrame(rSELF)          # self->curFrame = newFp
+    sw     zero, offThread_inJitCodeCache(rSELF)  # not in the jit code cache
+    sw     t6, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                  # newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(rBIX, a1)                    # rBIX<- new stack save area
+    move   a2, a0                                 # a2<- methodToCall
+    move   a0, a1                                 # a0<- newFp
+    add    a1, rSELF, offThread_retval            # a1<- &retval
+    move   a3, rSELF                              # a3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # a2: methodToCall
+    # preserve rTEMP,a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(rTEMP, 16)
+
+    move   a0, a2
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)                                      # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a0-a3
+    SCRATCH_LOAD(rTEMP, 16)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    move   rOBJ, a2                               # save a2
+#endif
+    move   t9, rTEMP
+    JALR(t9)                                   # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    move   a0, rOBJ
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+#endif
+
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC (advance 3 dalvik instr)
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: mips/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+.LinvokeNative:
+    # Prep for the native call
+    # a1 = newFP, a0 = methodToCall
+    lw     t9, offThread_jniLocal_topCookie(rSELF)  # t9<- thread->localRef->...
+    sw     zero, offThread_inJitCodeCache(rSELF)    # not in jit code cache
+    sw     a1, offThread_curFrame(rSELF)            # self->curFrame = newFp
+    sw     t9, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                 # newFp->localRefCookie=top
+    lhu     ra, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(rBIX, a1)                   # rBIX<- new stack save area
+
+    move    a2, a0                               # a2<- methodToCall
+    move    a0, a1                               # a0<- newFp
+    add     a1, rSELF, offThread_retval          # a1<- &retval
+    move    a3, rSELF                            # a3<- self
+    andi    ra, kSubModeMethodTrace
+    beqz    ra, 121f
+    # a2: methodToCall
+    # preserve a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                      # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a2 again
+    SCRATCH_LOAD(a2, 8)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+    b       212f
+
+121:
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                     # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+212:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, offStackSaveArea_savedPc(rBIX)        # reload rPC
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+
+/*
+ * On entry:
+ * a0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    la     t0, .LdeadFood
+    lw     t0, (t0)                  # should not see this under self-verification mode
+    jr     t0
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    sw     zero, offThread_inJitCodeCache(rSELF)  # in interpreter land
+    la     a1, .LdvmMterpCommonExceptionThrown  # PIC way of getting &func
+    lw     a1, (a1)
+    la     rIBASE, .LdvmAsmInstructionStart     # PIC way of getting &func
+    lw     rIBASE, (rIBASE)
+    move   rPC, a0                              # reload the faulting Dalvid address
+    jr     a1                                   # branch to dvmMterpCommonExeceptionThrown
+
+    .align  4
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+
+    .global 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..60f45be
--- /dev/null
+++ b/vm/compiler/template/rebuild.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Rebuild for all known targets.  Necessary until the stuff in "out" gets
+# generated as part of the build.
+#
+set -e
+for arch in ia32 armv5te armv5te-vfp armv7-a armv7-a-neon mips; do TARGET_ARCH_EXT=$arch make -f Makefile-template; done
diff --git a/vm/dalvik b/vm/dalvik
new file mode 100644
index 0000000..cb46775
--- /dev/null
+++ b/vm/dalvik
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+mkdir -p /tmp/android-data/dalvik-cache
+ANDROID_PRINTF_LOG=tag \
+ANDROID_LOG_TAGS="" \
+ANDROID_DATA=/tmp/android-data \
+ANDROID_ROOT=$ANDROID_BUILD_TOP/out/host/linux-x86 \
+LD_LIBRARY_PATH=$ANDROID_BUILD_TOP/out/host/linux-x86/lib \
+exec $ANDROID_BUILD_TOP/out/host/linux-x86/bin/dalvikvm \
+-Xbootclasspath\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/core-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/bouncycastle-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/apache-xml-hostdex.jar \
+$*
+
diff --git a/vm/hprof/Hprof.cpp b/vm/hprof/Hprof.cpp
new file mode 100644
index 0000000..d1dd499
--- /dev/null
+++ b/vm/hprof/Hprof.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Preparation and completion of hprof data generation.  The output is
+ * written into two files and then combined.  This is necessary because
+ * we generate some of the data (strings and classes) while we dump the
+ * heap, and some analysis tools require that the class and string data
+ * appear first.
+ */
+
+#include "Hprof.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/Visit.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+
+#define kHeadSuffix "-hptemp"
+
+hprof_context_t* hprofStartup(const char *outputFileName, int fd,
+                              bool directToDdms)
+{
+    hprofStartup_String();
+    hprofStartup_Class();
+
+    hprof_context_t *ctx = (hprof_context_t *)calloc(1, sizeof(*ctx));
+    if (ctx == NULL) {
+        ALOGE("hprof: can't allocate context.");
+        return NULL;
+    }
+
+    /* pass in name or descriptor of the output file */
+    hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms);
+
+    assert(ctx->memFp != NULL);
+
+    return ctx;
+}
+
+/*
+ * Finish up the hprof dump.  Returns true on success.
+ */
+bool hprofShutdown(hprof_context_t *tailCtx)
+{
+    /* flush the "tail" portion of the output */
+    hprofFlushCurrentRecord(tailCtx);
+
+    /*
+     * Create a new context struct for the start of the file.  We
+     * heap-allocate it so we can share the "free" function.
+     */
+    hprof_context_t *headCtx = (hprof_context_t *)calloc(1, sizeof(*headCtx));
+    if (headCtx == NULL) {
+        ALOGE("hprof: can't allocate context.");
+        hprofFreeContext(tailCtx);
+        return false;
+    }
+    hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
+        tailCtx->directToDdms);
+
+    ALOGI("hprof: dumping heap strings to \"%s\".", tailCtx->fileName);
+    hprofDumpStrings(headCtx);
+    hprofDumpClasses(headCtx);
+
+    /* Write a dummy stack trace record so the analysis
+     * tools don't freak out.
+     */
+    hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
+    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
+    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
+    hprofAddU4ToRecord(&headCtx->curRec, 0);    // no frames
+
+    hprofFlushCurrentRecord(headCtx);
+
+    hprofShutdown_Class();
+    hprofShutdown_String();
+
+    /* flush to ensure memstream pointer and size are updated */
+    fflush(headCtx->memFp);
+    fflush(tailCtx->memFp);
+
+    if (tailCtx->directToDdms) {
+        /* send the data off to DDMS */
+        struct iovec iov[2];
+        iov[0].iov_base = headCtx->fileDataPtr;
+        iov[0].iov_len = headCtx->fileDataSize;
+        iov[1].iov_base = tailCtx->fileDataPtr;
+        iov[1].iov_len = tailCtx->fileDataSize;
+        dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+    } else {
+        /*
+         * Open the output file, and copy the head and tail to it.
+         */
+        assert(headCtx->fd == tailCtx->fd);
+
+        int outFd;
+        if (headCtx->fd >= 0) {
+            outFd = dup(headCtx->fd);
+            if (outFd < 0) {
+                ALOGE("dup(%d) failed: %s", headCtx->fd, strerror(errno));
+                /* continue to fail-handler below */
+            }
+        } else {
+            outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+            if (outFd < 0) {
+                ALOGE("can't open %s: %s", headCtx->fileName, strerror(errno));
+                /* continue to fail-handler below */
+            }
+        }
+        if (outFd < 0) {
+            hprofFreeContext(headCtx);
+            hprofFreeContext(tailCtx);
+            return false;
+        }
+
+        int result;
+        result = sysWriteFully(outFd, headCtx->fileDataPtr,
+            headCtx->fileDataSize, "hprof-head");
+        result |= sysWriteFully(outFd, tailCtx->fileDataPtr,
+            tailCtx->fileDataSize, "hprof-tail");
+        close(outFd);
+        if (result != 0) {
+            hprofFreeContext(headCtx);
+            hprofFreeContext(tailCtx);
+            return false;
+        }
+    }
+
+    /* throw out a log message for the benefit of "runhat" */
+    ALOGI("hprof: heap dump completed (%dKB)",
+        (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024);
+
+    hprofFreeContext(headCtx);
+    hprofFreeContext(tailCtx);
+
+    return true;
+}
+
+/*
+ * Free any heap-allocated items in "ctx", and then free "ctx" itself.
+ */
+void hprofFreeContext(hprof_context_t *ctx)
+{
+    assert(ctx != NULL);
+
+    /* we don't own ctx->fd, do not close */
+
+    if (ctx->memFp != NULL)
+        fclose(ctx->memFp);
+    free(ctx->curRec.body);
+    free(ctx->fileName);
+    free(ctx->fileDataPtr);
+    free(ctx);
+}
+
+/*
+ * Visitor invoked on every root reference.
+ */
+static void hprofRootVisitor(void *addr, u4 threadId, RootType type, void *arg)
+{
+    static const hprof_heap_tag_t xlate[] = {
+        HPROF_ROOT_UNKNOWN,
+        HPROF_ROOT_JNI_GLOBAL,
+        HPROF_ROOT_JNI_LOCAL,
+        HPROF_ROOT_JAVA_FRAME,
+        HPROF_ROOT_NATIVE_STACK,
+        HPROF_ROOT_STICKY_CLASS,
+        HPROF_ROOT_THREAD_BLOCK,
+        HPROF_ROOT_MONITOR_USED,
+        HPROF_ROOT_THREAD_OBJECT,
+        HPROF_ROOT_INTERNED_STRING,
+        HPROF_ROOT_FINALIZING,
+        HPROF_ROOT_DEBUGGER,
+        HPROF_ROOT_REFERENCE_CLEANUP,
+        HPROF_ROOT_VM_INTERNAL,
+        HPROF_ROOT_JNI_MONITOR,
+    };
+    hprof_context_t *ctx;
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    assert(type < NELEM(xlate));
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        return;
+    }
+    ctx = (hprof_context_t *)arg;
+    ctx->gcScanState = xlate[type];
+    ctx->gcThreadSerialNumber = threadId;
+    hprofMarkRootObject(ctx, obj, 0);
+    ctx->gcScanState = 0;
+    ctx->gcThreadSerialNumber = 0;
+}
+
+/*
+ * Visitor invoked on every heap object.
+ */
+static void hprofBitmapCallback(Object *obj, void *arg)
+{
+    assert(obj != NULL);
+    assert(arg != NULL);
+    hprof_context_t *ctx = (hprof_context_t *)arg;
+    hprofDumpHeapObject(ctx, obj);
+}
+
+/*
+ * Walk the roots and heap writing heap information to the specified
+ * file.
+ *
+ * If "fd" is >= 0, the output will be written to that file descriptor.
+ * Otherwise, "fileName" is used to create an output file.
+ *
+ * If "directToDdms" is set, the other arguments are ignored, and data is
+ * sent directly to DDMS.
+ *
+ * Returns 0 on success, or an error code on failure.
+ */
+int hprofDumpHeap(const char* fileName, int fd, bool directToDdms)
+{
+    hprof_context_t *ctx;
+    int success;
+
+    assert(fileName != NULL);
+    dvmLockHeap();
+    dvmSuspendAllThreads(SUSPEND_FOR_HPROF);
+    ctx = hprofStartup(fileName, fd, directToDdms);
+    if (ctx == NULL) {
+        return -1;
+    }
+    // first record
+    hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+    dvmVisitRoots(hprofRootVisitor, ctx);
+    dvmHeapBitmapWalk(dvmHeapSourceGetLiveBits(), hprofBitmapCallback, ctx);
+    hprofFinishHeapDump(ctx);
+//TODO: write a HEAP_SUMMARY record
+    success = hprofShutdown(ctx) ? 0 : -1;
+    dvmResumeAllThreads(SUSPEND_FOR_HPROF);
+    dvmUnlockHeap();
+    return success;
+}
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
new file mode 100644
index 0000000..3ee8c61
--- /dev/null
+++ b/vm/hprof/Hprof.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DALVIK_HPROF_HPROF_H_
+#define DALVIK_HPROF_HPROF_H_
+
+#include "Dalvik.h"
+
+#define HPROF_ID_SIZE (sizeof (u4))
+
+#define UNIQUE_ERROR() \
+    -((((uintptr_t)__func__) << 16 | __LINE__) & (0x7fffffff))
+
+#define HPROF_TIME 0
+#define HPROF_NULL_STACK_TRACE   0
+#define HPROF_NULL_THREAD        0
+
+typedef u4 hprof_id;
+typedef hprof_id hprof_string_id;
+typedef hprof_id hprof_object_id;
+typedef hprof_id hprof_class_object_id;
+
+enum hprof_basic_type {
+    hprof_basic_object = 2,
+    hprof_basic_boolean = 4,
+    hprof_basic_char = 5,
+    hprof_basic_float = 6,
+    hprof_basic_double = 7,
+    hprof_basic_byte = 8,
+    hprof_basic_short = 9,
+    hprof_basic_int = 10,
+    hprof_basic_long = 11,
+};
+
+enum hprof_tag_t {
+    HPROF_TAG_STRING = 0x01,
+    HPROF_TAG_LOAD_CLASS = 0x02,
+    HPROF_TAG_UNLOAD_CLASS = 0x03,
+    HPROF_TAG_STACK_FRAME = 0x04,
+    HPROF_TAG_STACK_TRACE = 0x05,
+    HPROF_TAG_ALLOC_SITES = 0x06,
+    HPROF_TAG_HEAP_SUMMARY = 0x07,
+    HPROF_TAG_START_THREAD = 0x0A,
+    HPROF_TAG_END_THREAD = 0x0B,
+    HPROF_TAG_HEAP_DUMP = 0x0C,
+    HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1C,
+    HPROF_TAG_HEAP_DUMP_END = 0x2C,
+    HPROF_TAG_CPU_SAMPLES = 0x0D,
+    HPROF_TAG_CONTROL_SETTINGS = 0x0E,
+};
+
+/* Values for the first byte of
+ * HEAP_DUMP and HEAP_DUMP_SEGMENT
+ * records:
+ */
+enum hprof_heap_tag_t {
+    /* standard */
+    HPROF_ROOT_UNKNOWN = 0xFF,
+    HPROF_ROOT_JNI_GLOBAL = 0x01,
+    HPROF_ROOT_JNI_LOCAL = 0x02,
+    HPROF_ROOT_JAVA_FRAME = 0x03,
+    HPROF_ROOT_NATIVE_STACK = 0x04,
+    HPROF_ROOT_STICKY_CLASS = 0x05,
+    HPROF_ROOT_THREAD_BLOCK = 0x06,
+    HPROF_ROOT_MONITOR_USED = 0x07,
+    HPROF_ROOT_THREAD_OBJECT = 0x08,
+    HPROF_CLASS_DUMP = 0x20,
+    HPROF_INSTANCE_DUMP = 0x21,
+    HPROF_OBJECT_ARRAY_DUMP = 0x22,
+    HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,
+
+    /* Android */
+    HPROF_HEAP_DUMP_INFO = 0xfe,
+    HPROF_ROOT_INTERNED_STRING = 0x89,
+    HPROF_ROOT_FINALIZING = 0x8a,  /* obsolete */
+    HPROF_ROOT_DEBUGGER = 0x8b,
+    HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,  /* obsolete */
+    HPROF_ROOT_VM_INTERNAL = 0x8d,
+    HPROF_ROOT_JNI_MONITOR = 0x8e,
+    HPROF_UNREACHABLE = 0x90,  /* obsolete */
+    HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
+};
+
+/* Represents a top-level hprof record, whose serialized
+ * format is:
+ *
+ *     u1     TAG: denoting the type of the record
+ *     u4     TIME: number of microseconds since the time stamp in the header
+ *     u4     LENGTH: number of bytes that follow this u4 field
+ *                    and belong to this record
+ *     [u1]*  BODY: as many bytes as specified in the above u4 field
+ */
+struct hprof_record_t {
+    unsigned char *body;
+    u4 time;
+    u4 length;
+    size_t allocLen;
+    u1 tag;
+    bool dirty;
+};
+
+enum HprofHeapId {
+    HPROF_HEAP_DEFAULT = 0,
+    HPROF_HEAP_ZYGOTE = 'Z',
+    HPROF_HEAP_APP = 'A'
+};
+
+struct hprof_context_t {
+    /* curRec *must* be first so that we
+     * can cast from a context to a record.
+     */
+    hprof_record_t curRec;
+
+    u4 gcThreadSerialNumber;
+    u1 gcScanState;
+    HprofHeapId currentHeap;    // which heap we're currently emitting
+    u4 stackTraceSerialNumber;
+    size_t objectsInSegment;
+
+    /*
+     * If directToDdms is set, "fileName" and "fd" will be ignored.
+     * Otherwise, "fileName" must be valid, though if "fd" >= 0 it will
+     * only be used for debug messages.
+     */
+    bool directToDdms;
+    char *fileName;
+    char *fileDataPtr;          // for open_memstream
+    size_t fileDataSize;        // for open_memstream
+    FILE *memFp;
+    int fd;
+};
+
+
+/*
+ * HprofString.cpp functions
+ */
+
+hprof_string_id hprofLookupStringId(const char *str);
+
+int hprofDumpStrings(hprof_context_t *ctx);
+
+int hprofStartup_String(void);
+int hprofShutdown_String(void);
+
+
+/*
+ * HprofClass.cpp functions
+ */
+
+hprof_class_object_id hprofLookupClassId(const ClassObject *clazz);
+
+int hprofDumpClasses(hprof_context_t *ctx);
+
+int hprofStartup_Class(void);
+int hprofShutdown_Class(void);
+
+
+/*
+ * HprofHeap.cpp functions
+ */
+
+int hprofStartHeapDump(hprof_context_t *ctx);
+int hprofFinishHeapDump(hprof_context_t *ctx);
+
+int hprofSetGcScanState(hprof_context_t *ctx,
+                        hprof_heap_tag_t state, u4 threadSerialNumber);
+int hprofMarkRootObject(hprof_context_t *ctx,
+                        const Object *obj, jobject jniObj);
+
+int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj);
+
+/*
+ * HprofOutput.cpp functions
+ */
+
+void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
+                      bool writeHeader, bool directToDdms);
+
+int hprofFlushRecord(hprof_record_t *rec, FILE *fp);
+int hprofFlushCurrentRecord(hprof_context_t *ctx);
+int hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time);
+
+int hprofAddU1ToRecord(hprof_record_t *rec, u1 value);
+int hprofAddU1ListToRecord(hprof_record_t *rec,
+                           const u1 *values, size_t numValues);
+
+int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str);
+
+int hprofAddU2ToRecord(hprof_record_t *rec, u2 value);
+int hprofAddU2ListToRecord(hprof_record_t *rec,
+                           const u2 *values, size_t numValues);
+
+int hprofAddU4ToRecord(hprof_record_t *rec, u4 value);
+int hprofAddU4ListToRecord(hprof_record_t *rec,
+                           const u4 *values, size_t numValues);
+
+int hprofAddU8ToRecord(hprof_record_t *rec, u8 value);
+int hprofAddU8ListToRecord(hprof_record_t *rec,
+                           const u8 *values, size_t numValues);
+
+#define hprofAddIdToRecord(rec, id) hprofAddU4ToRecord((rec), (u4)(id))
+#define hprofAddIdListToRecord(rec, values, numValues) \
+            hprofAddU4ListToRecord((rec), (const u4 *)(values), (numValues))
+
+/*
+ * Hprof.cpp functions
+ */
+
+hprof_context_t* hprofStartup(const char *outputFileName, int fd,
+    bool directToDdms);
+bool hprofShutdown(hprof_context_t *ctx);
+void hprofFreeContext(hprof_context_t *ctx);
+int hprofDumpHeap(const char* fileName, int fd, bool directToDdms);
+
+#endif  // DALVIK_HPROF_HPROF_H_
diff --git a/vm/hprof/HprofClass.cpp b/vm/hprof/HprofClass.cpp
new file mode 100644
index 0000000..023efdb
--- /dev/null
+++ b/vm/hprof/HprofClass.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Class object pool
+ */
+
+#include "Hprof.h"
+
+static HashTable *gClassHashTable;
+
+int hprofStartup_Class()
+{
+    gClassHashTable = dvmHashTableCreate(128, NULL);
+    if (gClassHashTable == NULL) {
+        return UNIQUE_ERROR();
+    }
+    return 0;
+}
+
+int hprofShutdown_Class()
+{
+    dvmHashTableFree(gClassHashTable);
+
+    return 0;
+}
+
+static u4 computeClassHash(const ClassObject *clazz)
+{
+    u4 hash;
+    const char *cp;
+    char c;
+
+    cp = clazz->descriptor;
+    hash = (u4)clazz->classLoader;
+    while ((c = *cp++) != '\0') {
+        hash = hash * 31 + c;
+    }
+
+    return hash;
+}
+
+static int classCmp(const void *v1, const void *v2)
+{
+    const ClassObject *c1 = (const ClassObject *)v1;
+    const ClassObject *c2 = (const ClassObject *)v2;
+    intptr_t diff;
+
+    diff = (uintptr_t)c1->classLoader - (uintptr_t)c2->classLoader;
+    if (diff == 0) {
+        return strcmp(c1->descriptor, c2->descriptor);
+    }
+    return diff;
+}
+
+static int getPrettyClassNameId(const char *descriptor) {
+    std::string name(dvmHumanReadableDescriptor(descriptor));
+    return hprofLookupStringId(name.c_str());
+}
+
+hprof_class_object_id hprofLookupClassId(const ClassObject *clazz)
+{
+    void *val;
+
+    if (clazz == NULL) {
+        /* Someone's probably looking up the superclass
+         * of java.lang.Object or of a primitive class.
+         */
+        return (hprof_class_object_id)0;
+    }
+
+    dvmHashTableLock(gClassHashTable);
+
+    /* We're using the hash table as a list.
+     * TODO: replace the hash table with a more suitable structure
+     */
+    val = dvmHashTableLookup(gClassHashTable, computeClassHash(clazz),
+            (void *)clazz, classCmp, true);
+    assert(val != NULL);
+
+    dvmHashTableUnlock(gClassHashTable);
+
+    /* Make sure that the class's name is in the string table.
+     * This is a bunch of extra work that we only have to do
+     * because of the order of tables in the output file
+     * (strings need to be dumped before classes).
+     */
+    getPrettyClassNameId(clazz->descriptor);
+
+    return (hprof_class_object_id)clazz;
+}
+
+int hprofDumpClasses(hprof_context_t *ctx)
+{
+    HashIter iter;
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+
+    dvmHashTableLock(gClassHashTable);
+
+    for (err = 0, dvmHashIterBegin(gClassHashTable, &iter);
+         err == 0 && !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter))
+    {
+        err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
+        if (err == 0) {
+            const ClassObject *clazz;
+
+            clazz = (const ClassObject *)dvmHashIterData(&iter);
+            assert(clazz != NULL);
+
+            /* LOAD CLASS format:
+             *
+             * u4:     class serial number (always > 0)
+             * ID:     class object ID
+             * u4:     stack trace serial number
+             * ID:     class name string ID
+             *
+             * We use the address of the class object structure as its ID.
+             */
+            hprofAddU4ToRecord(rec, clazz->serialNumber);
+            hprofAddIdToRecord(rec, (hprof_class_object_id)clazz);
+            hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE);
+            hprofAddIdToRecord(rec, getPrettyClassNameId(clazz->descriptor));
+        }
+    }
+
+    dvmHashTableUnlock(gClassHashTable);
+
+    return err;
+}
diff --git a/vm/hprof/HprofHeap.cpp b/vm/hprof/HprofHeap.cpp
new file mode 100644
index 0000000..40e773b
--- /dev/null
+++ b/vm/hprof/HprofHeap.cpp
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Heap object dump
+ */
+#include "Hprof.h"
+
+/* Set DUMP_PRIM_DATA to 1 if you want to include the contents
+ * of primitive arrays (byte arrays, character arrays, etc.)
+ * in heap dumps.  This can be a large amount of data.
+ */
+#define DUMP_PRIM_DATA 1
+
+#define OBJECTS_PER_SEGMENT     ((size_t)128)
+#define BYTES_PER_SEGMENT       ((size_t)4096)
+
+/* The static field-name for the synthetic object generated to account
+ * for class Static overhead.
+ */
+#define STATIC_OVERHEAD_NAME    "$staticOverhead"
+/* The ID for the synthetic object generated to account for class
+ * Static overhead.
+ */
+#define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1))
+
+int hprofStartHeapDump(hprof_context_t *ctx)
+{
+    UNUSED_PARAMETER(ctx);
+
+    ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
+    ctx->currentHeap = HPROF_HEAP_DEFAULT;
+    return 0;
+}
+
+int hprofFinishHeapDump(hprof_context_t *ctx)
+{
+    return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
+}
+
+int hprofSetGcScanState(hprof_context_t *ctx,
+                        hprof_heap_tag_t state,
+                        u4 threadSerialNumber)
+{
+    /* Used by hprofMarkRootObject()
+     */
+    ctx->gcScanState = state;
+    ctx->gcThreadSerialNumber = threadSerialNumber;
+    return 0;
+}
+
+static hprof_basic_type signatureToBasicTypeAndSize(const char *sig,
+                                                    size_t *sizeOut)
+{
+    char c = sig[0];
+    hprof_basic_type ret;
+    size_t size;
+
+    switch (c) {
+    case '[':
+    case 'L': ret = hprof_basic_object;  size = 4; break;
+    case 'Z': ret = hprof_basic_boolean; size = 1; break;
+    case 'C': ret = hprof_basic_char;    size = 2; break;
+    case 'F': ret = hprof_basic_float;   size = 4; break;
+    case 'D': ret = hprof_basic_double;  size = 8; break;
+    case 'B': ret = hprof_basic_byte;    size = 1; break;
+    case 'S': ret = hprof_basic_short;   size = 2; break;
+    default: assert(false);
+    case 'I': ret = hprof_basic_int;     size = 4; break;
+    case 'J': ret = hprof_basic_long;    size = 8; break;
+    }
+
+    if (sizeOut != NULL) {
+        *sizeOut = size;
+    }
+
+    return ret;
+}
+
+static hprof_basic_type primitiveToBasicTypeAndSize(PrimitiveType prim,
+                                                    size_t *sizeOut)
+{
+    hprof_basic_type ret;
+    size_t size;
+
+    switch (prim) {
+    case PRIM_BOOLEAN: ret = hprof_basic_boolean; size = 1; break;
+    case PRIM_CHAR:    ret = hprof_basic_char;    size = 2; break;
+    case PRIM_FLOAT:   ret = hprof_basic_float;   size = 4; break;
+    case PRIM_DOUBLE:  ret = hprof_basic_double;  size = 8; break;
+    case PRIM_BYTE:    ret = hprof_basic_byte;    size = 1; break;
+    case PRIM_SHORT:   ret = hprof_basic_short;   size = 2; break;
+    default: assert(false);
+    case PRIM_INT:     ret = hprof_basic_int;     size = 4; break;
+    case PRIM_LONG:    ret = hprof_basic_long;    size = 8; break;
+    }
+
+    if (sizeOut != NULL) {
+        *sizeOut = size;
+    }
+
+    return ret;
+}
+
+/* Always called when marking objects, but only does
+ * something when ctx->gcScanState is non-zero, which is usually
+ * only true when marking the root set or unreachable
+ * objects.  Used to add rootset references to obj.
+ */
+int hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
+{
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+    hprof_heap_tag_t heapTag = (hprof_heap_tag_t)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)
+{
+    return HPROF_NULL_STACK_TRACE;
+}
+
+int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
+{
+    const ClassObject *clazz;
+    hprof_record_t *rec = &ctx->curRec;
+    HprofHeapId desiredHeap;
+
+    desiredHeap = dvmIsZygoteObject(obj) ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
+
+    if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
+        rec->length >= BYTES_PER_SEGMENT)
+    {
+        /* This flushes the old segment and starts a new one.
+         */
+        hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+        ctx->objectsInSegment = 0;
+
+        /* Starting a new HEAP_DUMP resets the heap to default.
+         */
+        ctx->currentHeap = HPROF_HEAP_DEFAULT;
+    }
+
+    if (desiredHeap != ctx->currentHeap) {
+        hprof_string_id nameId;
+
+        /* This object is in a different heap than the current one.
+         * Emit a HEAP_DUMP_INFO tag to change heaps.
+         */
+        hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
+        hprofAddU4ToRecord(rec, (u4)desiredHeap);   // u4: heap id
+        switch (desiredHeap) {
+        case HPROF_HEAP_APP:
+            nameId = hprofLookupStringId("app");
+            break;
+        case HPROF_HEAP_ZYGOTE:
+            nameId = hprofLookupStringId("zygote");
+            break;
+        default:
+            /* Internal error. */
+            assert(!"Unexpected desiredHeap");
+            nameId = hprofLookupStringId("<ILLEGAL>");
+            break;
+        }
+        hprofAddIdToRecord(rec, nameId);
+        ctx->currentHeap = desiredHeap;
+    }
+
+    clazz = obj->clazz;
+
+    if (clazz == NULL) {
+        /* This object will bother HprofReader, because it has a NULL
+         * class, so just don't dump it. It could be
+         * gDvm.unlinkedJavaLangClass or it could be an object just
+         * allocated which hasn't been initialized yet.
+         */
+    } else {
+        if (dvmIsClassObject(obj)) {
+            const ClassObject *thisClass = (const ClassObject *)obj;
+            /* obj is a ClassObject.
+             */
+            int sFieldCount = thisClass->sfieldCount;
+            if (sFieldCount != 0) {
+                int byteLength = sFieldCount*sizeof(StaticField);
+                /* Create a byte array to reflect the allocation of the
+                 * StaticField array at the end of this class.
+                 */
+                hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+                hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+                hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+                hprofAddU4ToRecord(rec, byteLength);
+                hprofAddU1ToRecord(rec, hprof_basic_byte);
+                for (int i = 0; i < byteLength; i++) {
+                    hprofAddU1ToRecord(rec, 0);
+                }
+            }
+
+            hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
+            hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
+            hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
+            hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
+            hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
+            hprofAddIdToRecord(rec, (hprof_object_id)0);    // no signer
+            hprofAddIdToRecord(rec, (hprof_object_id)0);    // no prot domain
+            hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
+            hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
+            if (obj == (Object *)gDvm.classJavaLangClass) {
+                // ClassObjects have their static fields appended, so
+                // aren't all the same size. But they're at least this
+                // size.
+                hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
+            } else {
+                hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
+            }
+
+            hprofAddU2ToRecord(rec, 0);                     // empty const pool
+
+            /* Static fields
+             */
+            if (sFieldCount == 0) {
+                hprofAddU2ToRecord(rec, (u2)0);
+            } else {
+                hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
+                hprofAddIdToRecord(rec,
+                                   hprofLookupStringId(STATIC_OVERHEAD_NAME));
+                hprofAddU1ToRecord(rec, hprof_basic_object);
+                hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+                for (int i = 0; i < sFieldCount; i++) {
+                    hprof_basic_type t;
+                    size_t size;
+                    const StaticField *f = &thisClass->sfields[i];
+
+                    t = signatureToBasicTypeAndSize(f->signature, &size);
+                    hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
+                    hprofAddU1ToRecord(rec, t);
+                    if (size == 1) {
+                        hprofAddU1ToRecord(rec, (u1)f->value.b);
+                    } else if (size == 2) {
+                        hprofAddU2ToRecord(rec, (u2)f->value.c);
+                    } else if (size == 4) {
+                        hprofAddU4ToRecord(rec, (u4)f->value.i);
+                    } else if (size == 8) {
+                        hprofAddU8ToRecord(rec, (u8)f->value.j);
+                    } else {
+                        assert(false);
+                    }
+                }
+            }
+
+            /* Instance fields for this class (no superclass fields)
+             */
+            int iFieldCount = thisClass->ifieldCount;
+            hprofAddU2ToRecord(rec, (u2)iFieldCount);
+            for (int i = 0; i < iFieldCount; i++) {
+                const InstField *f = &thisClass->ifields[i];
+                hprof_basic_type t;
+
+                t = signatureToBasicTypeAndSize(f->signature, NULL);
+                hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
+                hprofAddU1ToRecord(rec, t);
+            }
+        } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+            const ArrayObject *aobj = (const ArrayObject *)obj;
+            u4 length = aobj->length;
+
+            if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
+                /* obj is an object array.
+                 */
+                hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
+
+                hprofAddIdToRecord(rec, (hprof_object_id)obj);
+                hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+                hprofAddU4ToRecord(rec, length);
+                hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+
+                /* Dump the elements, which are always objects or NULL.
+                 */
+                hprofAddIdListToRecord(rec,
+                        (const hprof_object_id *)(void *)aobj->contents, length);
+            } else {
+                hprof_basic_type t;
+                size_t size;
+
+                t = primitiveToBasicTypeAndSize(clazz->elementClass->
+                                                primitiveType, &size);
+
+                /* obj is a primitive array.
+                 */
+#if DUMP_PRIM_DATA
+                hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+#else
+                hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
+#endif
+
+                hprofAddIdToRecord(rec, (hprof_object_id)obj);
+                hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+                hprofAddU4ToRecord(rec, length);
+                hprofAddU1ToRecord(rec, t);
+
+#if DUMP_PRIM_DATA
+                /* Dump the raw, packed element values.
+                 */
+                if (size == 1) {
+                    hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
+                            length);
+                } else if (size == 2) {
+                    hprofAddU2ListToRecord(rec, (const u2 *)(void *)aobj->contents,
+                            length);
+                } else if (size == 4) {
+                    hprofAddU4ListToRecord(rec, (const u4 *)(void *)aobj->contents,
+                            length);
+                } else if (size == 8) {
+                    hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
+                            length);
+                }
+#endif
+            }
+        } else {
+            const ClassObject *sclass;
+            size_t sizePatchOffset, savedLen;
+
+            /* obj is an instance object.
+             */
+            hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
+            hprofAddIdToRecord(rec, (hprof_object_id)obj);
+            hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+            hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+
+            /* Reserve some space for the length of the instance
+             * data, which we won't know until we're done writing
+             * it.
+             */
+            sizePatchOffset = rec->length;
+            hprofAddU4ToRecord(rec, 0x77777777);
+
+            /* Write the instance data;  fields for this
+             * class, followed by super class fields, and so on.
+             */
+            sclass = clazz;
+            while (sclass != NULL) {
+                int ifieldCount = sclass->ifieldCount;
+                for (int i = 0; i < ifieldCount; i++) {
+                    const InstField *f = &sclass->ifields[i];
+                    size_t size;
+
+                    (void) signatureToBasicTypeAndSize(f->signature, &size);
+                    if (size == 1) {
+                        hprofAddU1ToRecord(rec,
+                                (u1)dvmGetFieldByte(obj, f->byteOffset));
+                    } else if (size == 2) {
+                        hprofAddU2ToRecord(rec,
+                                (u2)dvmGetFieldChar(obj, f->byteOffset));
+                    } else if (size == 4) {
+                        hprofAddU4ToRecord(rec,
+                                (u4)dvmGetFieldInt(obj, f->byteOffset));
+                    } else if (size == 8) {
+                        hprofAddU8ToRecord(rec,
+                                (u8)dvmGetFieldLong(obj, f->byteOffset));
+                    } else {
+                        assert(false);
+                    }
+                }
+
+                sclass = sclass->super;
+            }
+
+            /* Patch the instance field length.
+             */
+            savedLen = rec->length;
+            rec->length = sizePatchOffset;
+            hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
+            rec->length = savedLen;
+        }
+    }
+
+    ctx->objectsInSegment++;
+
+    return 0;
+}
diff --git a/vm/hprof/HprofOutput.cpp b/vm/hprof/HprofOutput.cpp
new file mode 100644
index 0000000..3a6b9d8
--- /dev/null
+++ b/vm/hprof/HprofOutput.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <sys/time.h>
+#include <cutils/open_memstream.h>
+#include <time.h>
+#include <errno.h>
+#include "Hprof.h"
+
+#define HPROF_MAGIC_STRING  "JAVA PROFILE 1.0.3"
+
+#define U2_TO_BUF_BE(buf, offset, value) \
+    do { \
+        unsigned char *buf_ = (unsigned char *)(buf); \
+        int offset_ = (int)(offset); \
+        u2 value_ = (u2)(value); \
+        buf_[offset_ + 0] = (unsigned char)(value_ >>  8); \
+        buf_[offset_ + 1] = (unsigned char)(value_      ); \
+    } while (0)
+
+#define U4_TO_BUF_BE(buf, offset, value) \
+    do { \
+        unsigned char *buf_ = (unsigned char *)(buf); \
+        int offset_ = (int)(offset); \
+        u4 value_ = (u4)(value); \
+        buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
+        buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
+        buf_[offset_ + 2] = (unsigned char)(value_ >>  8); \
+        buf_[offset_ + 3] = (unsigned char)(value_      ); \
+    } while (0)
+
+#define U8_TO_BUF_BE(buf, offset, value) \
+    do { \
+        unsigned char *buf_ = (unsigned char *)(buf); \
+        int offset_ = (int)(offset); \
+        u8 value_ = (u8)(value); \
+        buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
+        buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
+        buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
+        buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
+        buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
+        buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
+        buf_[offset_ + 6] = (unsigned char)(value_ >>  8); \
+        buf_[offset_ + 7] = (unsigned char)(value_      ); \
+    } while (0)
+
+/*
+ * Initialize an hprof context struct.
+ *
+ * This will take ownership of "fileName".
+ *
+ * NOTE: ctx is expected to have been zeroed out prior to calling this
+ * function.
+ */
+void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
+                      bool writeHeader, bool directToDdms)
+{
+    /*
+     * Have to do this here, because it must happen after we
+     * memset the struct (want to treat fileDataPtr/fileDataSize
+     * as read-only while the file is open).
+     */
+    FILE* fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize);
+    if (fp == NULL) {
+        /* not expected */
+        ALOGE("hprof: open_memstream failed: %s", strerror(errno));
+        dvmAbort();
+    }
+
+    ctx->directToDdms = directToDdms;
+    ctx->fileName = fileName;
+    ctx->memFp = fp;
+    ctx->fd = fd;
+
+    ctx->curRec.allocLen = 128;
+    ctx->curRec.body = (unsigned char *)malloc(ctx->curRec.allocLen);
+//xxx check for/return an error
+
+    if (writeHeader) {
+        char magic[] = HPROF_MAGIC_STRING;
+        unsigned char buf[4];
+        struct timeval now;
+        u8 nowMs;
+
+        /* Write the file header.
+         *
+         * [u1]*: NUL-terminated magic string.
+         */
+        fwrite(magic, 1, sizeof(magic), fp);
+
+        /* u4: size of identifiers.  We're using addresses
+         *     as IDs, so make sure a pointer fits.
+         */
+        U4_TO_BUF_BE(buf, 0, sizeof(void *));
+        fwrite(buf, 1, sizeof(u4), fp);
+
+        /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
+         */
+        if (gettimeofday(&now, NULL) < 0) {
+            nowMs = 0;
+        } else {
+            nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
+        }
+
+        /* u4: high word of the 64-bit time.
+         */
+        U4_TO_BUF_BE(buf, 0, (u4)(nowMs >> 32));
+        fwrite(buf, 1, sizeof(u4), fp);
+
+        /* u4: low word of the 64-bit time.
+         */
+        U4_TO_BUF_BE(buf, 0, (u4)(nowMs & 0xffffffffULL));
+        fwrite(buf, 1, sizeof(u4), fp); //xxx fix the time
+    }
+}
+
+int hprofFlushRecord(hprof_record_t *rec, FILE *fp)
+{
+    if (rec->dirty) {
+        unsigned char headBuf[sizeof (u1) + 2 * sizeof (u4)];
+        int nb;
+
+        headBuf[0] = rec->tag;
+        U4_TO_BUF_BE(headBuf, 1, rec->time);
+        U4_TO_BUF_BE(headBuf, 5, rec->length);
+
+        nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
+        if (nb != sizeof(headBuf)) {
+            return UNIQUE_ERROR();
+        }
+        nb = fwrite(rec->body, 1, rec->length, fp);
+        if (nb != (int)rec->length) {
+            return UNIQUE_ERROR();
+        }
+
+        rec->dirty = false;
+    }
+//xxx if we used less than half (or whatever) of allocLen, shrink the buffer.
+
+    return 0;
+}
+
+int hprofFlushCurrentRecord(hprof_context_t *ctx)
+{
+    return hprofFlushRecord(&ctx->curRec, ctx->memFp);
+}
+
+int hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time)
+{
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+
+    err = hprofFlushRecord(rec, ctx->memFp);
+    if (err != 0) {
+        return err;
+    } else if (rec->dirty) {
+        return UNIQUE_ERROR();
+    }
+
+    rec->dirty = true;
+    rec->tag = tag;
+    rec->time = time;
+    rec->length = 0;
+
+    return 0;
+}
+
+static inline int guaranteeRecordAppend(hprof_record_t *rec, size_t nmore)
+{
+    size_t minSize;
+
+    minSize = rec->length + nmore;
+    if (minSize > rec->allocLen) {
+        unsigned char *newBody;
+        size_t newAllocLen;
+
+        newAllocLen = rec->allocLen * 2;
+        if (newAllocLen < minSize) {
+            newAllocLen = rec->allocLen + nmore + nmore/2;
+        }
+        newBody = (unsigned char *)realloc(rec->body, newAllocLen);
+        if (newBody != NULL) {
+            rec->body = newBody;
+            rec->allocLen = newAllocLen;
+        } else {
+//TODO: set an error flag so future ops will fail
+            return UNIQUE_ERROR();
+        }
+    }
+
+    assert(rec->length + nmore <= rec->allocLen);
+    return 0;
+}
+
+int hprofAddU1ListToRecord(hprof_record_t *rec, const u1 *values,
+                           size_t numValues)
+{
+    int err;
+
+    err = guaranteeRecordAppend(rec, numValues);
+    if (err != 0) {
+        return err;
+    }
+
+    memcpy(rec->body + rec->length, values, numValues);
+    rec->length += numValues;
+
+    return 0;
+}
+
+int hprofAddU1ToRecord(hprof_record_t *rec, u1 value)
+{
+    int err;
+
+    err = guaranteeRecordAppend(rec, 1);
+    if (err != 0) {
+        return err;
+    }
+
+    rec->body[rec->length++] = value;
+
+    return 0;
+}
+
+int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str)
+{
+    /* The terminating NUL character is NOT written.
+     */
+//xxx don't do a strlen;  add and grow as necessary, until NUL
+    return hprofAddU1ListToRecord(rec, (const u1 *)str, strlen(str));
+}
+
+int hprofAddU2ListToRecord(hprof_record_t *rec, const u2 *values,
+                           size_t numValues)
+{
+    int err = guaranteeRecordAppend(rec, numValues * 2);
+    if (err != 0) {
+        return err;
+    }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+    unsigned char *insert = rec->body + rec->length;
+    for (size_t i = 0; i < numValues; i++) {
+        U2_TO_BUF_BE(insert, 0, *values++);
+        insert += sizeof(*values);
+    }
+    rec->length += numValues * 2;
+
+    return 0;
+}
+
+int hprofAddU2ToRecord(hprof_record_t *rec, u2 value)
+{
+    return hprofAddU2ListToRecord(rec, &value, 1);
+}
+
+int hprofAddU4ListToRecord(hprof_record_t *rec, const u4 *values,
+                           size_t numValues)
+{
+    int err = guaranteeRecordAppend(rec, numValues * 4);
+    if (err != 0) {
+        return err;
+    }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+    unsigned char *insert = rec->body + rec->length;
+    for (size_t i = 0; i < numValues; i++) {
+        U4_TO_BUF_BE(insert, 0, *values++);
+        insert += sizeof(*values);
+    }
+    rec->length += numValues * 4;
+
+    return 0;
+}
+
+int hprofAddU4ToRecord(hprof_record_t *rec, u4 value)
+{
+    return hprofAddU4ListToRecord(rec, &value, 1);
+}
+
+int hprofAddU8ListToRecord(hprof_record_t *rec, const u8 *values,
+                           size_t numValues)
+{
+    int err = guaranteeRecordAppend(rec, numValues * 8);
+    if (err != 0) {
+        return err;
+    }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+    unsigned char *insert = rec->body + rec->length;
+    for (size_t i = 0; i < numValues; i++) {
+        U8_TO_BUF_BE(insert, 0, *values++);
+        insert += sizeof(*values);
+    }
+    rec->length += numValues * 8;
+
+    return 0;
+}
+
+int hprofAddU8ToRecord(hprof_record_t *rec, u8 value)
+{
+    return hprofAddU8ListToRecord(rec, &value, 1);
+}
diff --git a/vm/hprof/HprofString.cpp b/vm/hprof/HprofString.cpp
new file mode 100644
index 0000000..3534f07
--- /dev/null
+++ b/vm/hprof/HprofString.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Common string pool for the profiler
+ */
+#include "Hprof.h"
+
+static HashTable *gStringHashTable;
+
+int hprofStartup_String()
+{
+    gStringHashTable = dvmHashTableCreate(512, free);
+    if (gStringHashTable == NULL) {
+        return UNIQUE_ERROR();
+    }
+    return 0;
+}
+
+int hprofShutdown_String()
+{
+    dvmHashTableFree(gStringHashTable);
+    return 0;
+}
+
+static u4 computeUtf8Hash(const char *str)
+{
+    u4 hash = 0;
+    const char *cp;
+    char c;
+
+    cp = str;
+    while ((c = *cp++) != '\0') {
+        hash = hash * 31 + c;
+    }
+
+    return hash;
+}
+
+hprof_string_id hprofLookupStringId(const char *str)
+{
+    void *val;
+    u4 hashValue;
+
+    dvmHashTableLock(gStringHashTable);
+
+    hashValue = computeUtf8Hash(str);
+    val = dvmHashTableLookup(gStringHashTable, hashValue, (void *)str,
+            (HashCompareFunc)strcmp, false);
+    if (val == NULL) {
+        const char *newStr;
+
+        newStr = strdup(str);
+        val = dvmHashTableLookup(gStringHashTable, hashValue, (void *)newStr,
+                (HashCompareFunc)strcmp, true);
+        assert(val != NULL);
+    }
+
+    dvmHashTableUnlock(gStringHashTable);
+
+    return (hprof_string_id)val;
+}
+
+int hprofDumpStrings(hprof_context_t *ctx)
+{
+    HashIter iter;
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+
+    dvmHashTableLock(gStringHashTable);
+
+    for (err = 0, dvmHashIterBegin(gStringHashTable, &iter);
+         err == 0 && !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter))
+    {
+        err = hprofStartNewRecord(ctx, HPROF_TAG_STRING, HPROF_TIME);
+        if (err == 0) {
+            const char *str;
+
+            str = (const char *)dvmHashIterData(&iter);
+            assert(str != NULL);
+
+            /* STRING format:
+             *
+             * ID:     ID for this string
+             * [u1]*:  UTF8 characters for string (NOT NULL terminated)
+             *         (the record format encodes the length)
+             *
+             * We use the address of the string data as its ID.
+             */
+            err = hprofAddU4ToRecord(rec, (u4)str);
+            if (err == 0) {
+                err = hprofAddUtf8StringToRecord(rec, str);
+            }
+        }
+    }
+
+    dvmHashTableUnlock(gStringHashTable);
+
+    return err;
+}
diff --git a/vm/interp/Interp.cpp b/vm/interp/Interp.cpp
new file mode 100644
index 0000000..a37e134
--- /dev/null
+++ b/vm/interp/Interp.cpp
@@ -0,0 +1,1965 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Main interpreter entry point and support functions.
+ *
+ * The entry point selects the "standard" or "debug" interpreter and
+ * facilitates switching between them.  The standard interpreter may
+ * use the "fast" or "portable" implementation.
+ *
+ * Some debugger support functions are included here.
+ */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#if defined(WITH_JIT)
+#include "interp/Jit.h"
+#endif
+
+
+/*
+ * ===========================================================================
+ *      Debugger support
+ * ===========================================================================
+ */
+
+// fwd
+static BreakpointSet* dvmBreakpointSetAlloc();
+static void dvmBreakpointSetFree(BreakpointSet* pSet);
+
+#if defined(WITH_JIT)
+/* Target-specific save/restore */
+extern "C" void dvmJitCalleeSave(double *saveArea);
+extern "C" void dvmJitCalleeRestore(double *saveArea);
+/* Interpreter entry points from compiled code */
+extern "C" void dvmJitToInterpNormal();
+extern "C" void dvmJitToInterpNoChain();
+extern "C" void dvmJitToInterpPunt();
+extern "C" void dvmJitToInterpSingleStep();
+extern "C" void dvmJitToInterpTraceSelect();
+#if defined(WITH_SELF_VERIFICATION)
+extern "C" void dvmJitToInterpBackwardBranch();
+#endif
+#endif
+
+/*
+ * Initialize global breakpoint structures.
+ */
+bool dvmBreakpointStartup()
+{
+    gDvm.breakpointSet = dvmBreakpointSetAlloc();
+    return (gDvm.breakpointSet != NULL);
+}
+
+/*
+ * Free resources.
+ */
+void dvmBreakpointShutdown()
+{
+    dvmBreakpointSetFree(gDvm.breakpointSet);
+}
+
+
+/*
+ * This represents a breakpoint inserted in the instruction stream.
+ *
+ * The debugger may ask us to create the same breakpoint multiple times.
+ * We only remove the breakpoint when the last instance is cleared.
+ */
+struct Breakpoint {
+    Method*     method;                 /* method we're associated with */
+    u2*         addr;                   /* absolute memory address */
+    u1          originalOpcode;         /* original 8-bit opcode value */
+    int         setCount;               /* #of times this breakpoint was set */
+};
+
+/*
+ * Set of breakpoints.
+ */
+struct BreakpointSet {
+    /* grab lock before reading or writing anything else in here */
+    pthread_mutex_t lock;
+
+    /* vector of breakpoint structures */
+    int         alloc;
+    int         count;
+    Breakpoint* breakpoints;
+};
+
+/*
+ * Initialize a BreakpointSet.  Initially empty.
+ */
+static BreakpointSet* dvmBreakpointSetAlloc()
+{
+    BreakpointSet* pSet = (BreakpointSet*) calloc(1, sizeof(*pSet));
+
+    dvmInitMutex(&pSet->lock);
+    /* leave the rest zeroed -- will alloc on first use */
+
+    return pSet;
+}
+
+/*
+ * Free storage associated with a BreakpointSet.
+ */
+static void dvmBreakpointSetFree(BreakpointSet* pSet)
+{
+    if (pSet == NULL)
+        return;
+
+    free(pSet->breakpoints);
+    free(pSet);
+}
+
+/*
+ * Lock the breakpoint set.
+ *
+ * It's not currently necessary to switch to VMWAIT in the event of
+ * contention, because nothing in here can block.  However, it's possible
+ * that the bytecode-updater code could become fancier in the future, so
+ * we do the trylock dance as a bit of future-proofing.
+ */
+static void dvmBreakpointSetLock(BreakpointSet* pSet)
+{
+    if (dvmTryLockMutex(&pSet->lock) != 0) {
+        Thread* self = dvmThreadSelf();
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmLockMutex(&pSet->lock);
+        dvmChangeStatus(self, oldStatus);
+    }
+}
+
+/*
+ * Unlock the breakpoint set.
+ */
+static void dvmBreakpointSetUnlock(BreakpointSet* pSet)
+{
+    dvmUnlockMutex(&pSet->lock);
+}
+
+/*
+ * Return the #of breakpoints.
+ */
+static int dvmBreakpointSetCount(const BreakpointSet* pSet)
+{
+    return pSet->count;
+}
+
+/*
+ * See if we already have an entry for this address.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns the index of the breakpoint entry, or -1 if not found.
+ */
+static int dvmBreakpointSetFind(const BreakpointSet* pSet, const u2* addr)
+{
+    int i;
+
+    for (i = 0; i < pSet->count; i++) {
+        Breakpoint* pBreak = &pSet->breakpoints[i];
+        if (pBreak->addr == addr)
+            return i;
+    }
+
+    return -1;
+}
+
+/*
+ * Retrieve the opcode that was originally at the specified location.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" with the opcode in *pOrig on success.
+ */
+static bool dvmBreakpointSetOriginalOpcode(const BreakpointSet* pSet,
+    const u2* addr, u1* pOrig)
+{
+    int idx = dvmBreakpointSetFind(pSet, addr);
+    if (idx < 0)
+        return false;
+
+    *pOrig = pSet->breakpoints[idx].originalOpcode;
+    return true;
+}
+
+/*
+ * Check the opcode.  If it's a "magic" NOP, indicating the start of
+ * switch or array data in the instruction stream, we don't want to set
+ * a breakpoint.
+ *
+ * This can happen because the line number information dx generates
+ * associates the switch data with the switch statement's line number,
+ * and some debuggers put breakpoints at every address associated with
+ * a given line.  The result is that the breakpoint stomps on the NOP
+ * instruction that doubles as a data table magic number, and an explicit
+ * check in the interpreter results in an exception being thrown.
+ *
+ * We don't want to simply refuse to add the breakpoint to the table,
+ * because that confuses the housekeeping.  We don't want to reject the
+ * debugger's event request, and we want to be sure that there's exactly
+ * one un-set operation for every set op.
+ */
+static bool instructionIsMagicNop(const u2* addr)
+{
+    u2 curVal = *addr;
+    return ((GET_OPCODE(curVal)) == OP_NOP && (curVal >> 8) != 0);
+}
+
+/*
+ * Add a breakpoint at a specific address.  If the address is already
+ * present in the table, this just increments the count.
+ *
+ * For a new entry, this will extract and preserve the current opcode from
+ * the instruction stream, and replace it with a breakpoint opcode.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" on success.
+ */
+static bool dvmBreakpointSetAdd(BreakpointSet* pSet, Method* method,
+    unsigned int instrOffset)
+{
+    const int kBreakpointGrowth = 10;
+    const u2* addr = method->insns + instrOffset;
+    int idx = dvmBreakpointSetFind(pSet, addr);
+    Breakpoint* pBreak;
+
+    if (idx < 0) {
+        if (pSet->count == pSet->alloc) {
+            int newSize = pSet->alloc + kBreakpointGrowth;
+            Breakpoint* newVec;
+
+            ALOGV("+++ increasing breakpoint set size to %d", newSize);
+
+            /* pSet->breakpoints will be NULL on first entry */
+            newVec = (Breakpoint*)realloc(pSet->breakpoints, newSize * sizeof(Breakpoint));
+            if (newVec == NULL)
+                return false;
+
+            pSet->breakpoints = newVec;
+            pSet->alloc = newSize;
+        }
+
+        pBreak = &pSet->breakpoints[pSet->count++];
+        pBreak->method = method;
+        pBreak->addr = (u2*)addr;
+        pBreak->originalOpcode = *(u1*)addr;
+        pBreak->setCount = 1;
+
+        /*
+         * Change the opcode.  We must ensure that the BreakpointSet
+         * updates happen before we change the opcode.
+         *
+         * If the method has not been verified, we do NOT insert the
+         * breakpoint yet, since that will screw up the verifier.  The
+         * debugger is allowed to insert breakpoints in unverified code,
+         * but since we don't execute unverified code we don't need to
+         * alter the bytecode yet.
+         *
+         * The class init code will "flush" all pending opcode writes
+         * before verification completes.
+         */
+        assert(*(u1*)addr != OP_BREAKPOINT);
+        if (dvmIsClassVerified(method->clazz)) {
+            ALOGV("Class %s verified, adding breakpoint at %p",
+                method->clazz->descriptor, addr);
+            if (instructionIsMagicNop(addr)) {
+                ALOGV("Refusing to set breakpoint on %04x at %s.%s + %#x",
+                    *addr, method->clazz->descriptor, method->name,
+                    instrOffset);
+            } else {
+                ANDROID_MEMBAR_FULL();
+                dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+                    OP_BREAKPOINT);
+            }
+        } else {
+            ALOGV("Class %s NOT verified, deferring breakpoint at %p",
+                method->clazz->descriptor, addr);
+        }
+    } else {
+        /*
+         * Breakpoint already exists, just increase the count.
+         */
+        pBreak = &pSet->breakpoints[idx];
+        pBreak->setCount++;
+    }
+
+    return true;
+}
+
+/*
+ * Remove one instance of the specified breakpoint.  When the count
+ * reaches zero, the entry is removed from the table, and the original
+ * opcode is restored.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetRemove(BreakpointSet* pSet, Method* method,
+    unsigned int instrOffset)
+{
+    const u2* addr = method->insns + instrOffset;
+    int idx = dvmBreakpointSetFind(pSet, addr);
+
+    if (idx < 0) {
+        /* breakpoint not found in set -- unexpected */
+        if (*(u1*)addr == OP_BREAKPOINT) {
+            ALOGE("Unable to restore breakpoint opcode (%s.%s +%#x)",
+                method->clazz->descriptor, method->name, instrOffset);
+            dvmAbort();
+        } else {
+            ALOGW("Breakpoint was already restored? (%s.%s +%#x)",
+                method->clazz->descriptor, method->name, instrOffset);
+        }
+    } else {
+        Breakpoint* pBreak = &pSet->breakpoints[idx];
+        if (pBreak->setCount == 1) {
+            /*
+             * Must restore opcode before removing set entry.
+             *
+             * If the breakpoint was never flushed, we could be ovewriting
+             * a value with the same value.  Not a problem, though we
+             * could end up causing a copy-on-write here when we didn't
+             * need to.  (Not worth worrying about.)
+             */
+            dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+                pBreak->originalOpcode);
+            ANDROID_MEMBAR_FULL();
+
+            if (idx != pSet->count-1) {
+                /* shift down */
+                memmove(&pSet->breakpoints[idx], &pSet->breakpoints[idx+1],
+                    (pSet->count-1 - idx) * sizeof(pSet->breakpoints[0]));
+            }
+            pSet->count--;
+            pSet->breakpoints[pSet->count].addr = (u2*) 0xdecadead; // debug
+        } else {
+            pBreak->setCount--;
+            assert(pBreak->setCount > 0);
+        }
+    }
+}
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".  We want to
+ * change the opcode, which might not have happened when the breakpoint
+ * was initially set because the class was in the process of being
+ * verified.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetFlush(BreakpointSet* pSet, ClassObject* clazz)
+{
+    int i;
+    for (i = 0; i < pSet->count; i++) {
+        Breakpoint* pBreak = &pSet->breakpoints[i];
+        if (pBreak->method->clazz == clazz) {
+            /*
+             * The breakpoint is associated with a method in this class.
+             * It might already be there or it might not; either way,
+             * flush it out.
+             */
+            ALOGV("Flushing breakpoint at %p for %s",
+                pBreak->addr, clazz->descriptor);
+            if (instructionIsMagicNop(pBreak->addr)) {
+                ALOGV("Refusing to flush breakpoint on %04x at %s.%s + %#x",
+                    *pBreak->addr, pBreak->method->clazz->descriptor,
+                    pBreak->method->name, pBreak->addr - pBreak->method->insns);
+            } else {
+                dvmDexChangeDex1(clazz->pDvmDex, (u1*)pBreak->addr,
+                    OP_BREAKPOINT);
+            }
+        }
+    }
+}
+
+
+/*
+ * Do any debugger-attach-time initialization.
+ */
+void dvmInitBreakpoints()
+{
+    /* quick sanity check */
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    dvmBreakpointSetLock(pSet);
+    if (dvmBreakpointSetCount(pSet) != 0) {
+        ALOGW("WARNING: %d leftover breakpoints", dvmBreakpointSetCount(pSet));
+        /* generally not good, but we can keep going */
+    }
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Add an address to the list, putting it in the first non-empty slot.
+ *
+ * Sometimes the debugger likes to add two entries for one breakpoint.
+ * We add two entries here, so that we get the right behavior when it's
+ * removed twice.
+ *
+ * This will only be run from the JDWP thread, and it will happen while
+ * we are updating the event list, which is synchronized.  We're guaranteed
+ * to be the only one adding entries, and the lock ensures that nobody
+ * will be trying to remove them while we're in here.
+ *
+ * "addr" is the absolute address of the breakpoint bytecode.
+ */
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    dvmBreakpointSetLock(pSet);
+    dvmBreakpointSetAdd(pSet, method, instrOffset);
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Remove an address from the list by setting the entry to NULL.
+ *
+ * This can be called from the JDWP thread (because the debugger has
+ * cancelled the breakpoint) or from an event thread (because it's a
+ * single-shot breakpoint, e.g. "run to line").  We only get here as
+ * the result of removing an entry from the event list, which is
+ * synchronized, so it should not be possible for two threads to be
+ * updating breakpoints at the same time.
+ */
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    dvmBreakpointSetLock(pSet);
+    dvmBreakpointSetRemove(pSet, method, instrOffset);
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Get the original opcode from under a breakpoint.
+ *
+ * On SMP hardware it's possible one core might try to execute a breakpoint
+ * after another core has cleared it.  We need to handle the case where
+ * there's no entry in the breakpoint set.  (The memory barriers in the
+ * locks and in the breakpoint update code should ensure that, once we've
+ * observed the absence of a breakpoint entry, we will also now observe
+ * the restoration of the original opcode.  The fact that we're holding
+ * the lock prevents other threads from confusing things further.)
+ */
+u1 dvmGetOriginalOpcode(const u2* addr)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    u1 orig = 0;
+
+    dvmBreakpointSetLock(pSet);
+    if (!dvmBreakpointSetOriginalOpcode(pSet, addr, &orig)) {
+        orig = *(u1*)addr;
+        if (orig == OP_BREAKPOINT) {
+            ALOGE("GLITCH: can't find breakpoint, opcode is still set");
+            dvmAbort();
+        }
+    }
+    dvmBreakpointSetUnlock(pSet);
+
+    return orig;
+}
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".
+ *
+ * We don't want to modify the bytecode of a method before the verifier
+ * gets a chance to look at it, so we postpone opcode replacement until
+ * after verification completes.
+ */
+void dvmFlushBreakpoints(ClassObject* clazz)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+
+    if (pSet == NULL)
+        return;
+
+    assert(dvmIsClassVerified(clazz));
+    dvmBreakpointSetLock(pSet);
+    dvmBreakpointSetFlush(pSet, clazz);
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Add a single step event.  Currently this is a global item.
+ *
+ * We set up some initial values based on the thread's current state.  This
+ * won't work well if the thread is running, so it's up to the caller to
+ * verify that it's suspended.
+ *
+ * This is only called from the JDWP thread.
+ */
+bool dvmAddSingleStep(Thread* thread, int size, int depth)
+{
+    StepControl* pCtrl = &gDvm.stepControl;
+
+    if (pCtrl->active && thread != pCtrl->thread) {
+        ALOGW("WARNING: single-step active for %p; adding %p",
+            pCtrl->thread, thread);
+
+        /*
+         * Keep going, overwriting previous.  This can happen if you
+         * suspend a thread in Object.wait, hit the single-step key, then
+         * switch to another thread and do the same thing again.
+         * The first thread's step is still pending.
+         *
+         * TODO: consider making single-step per-thread.  Adds to the
+         * overhead, but could be useful in rare situations.
+         */
+    }
+
+    pCtrl->size = static_cast<JdwpStepSize>(size);
+    pCtrl->depth = static_cast<JdwpStepDepth>(depth);
+    pCtrl->thread = thread;
+
+    /*
+     * We may be stepping into or over method calls, or running until we
+     * return from the current method.  To make this work we need to track
+     * the current line, current method, and current stack depth.  We need
+     * to be checking these after most instructions, notably those that
+     * call methods, return from methods, or are on a different line from the
+     * previous instruction.
+     *
+     * We have to start with a snapshot of the current state.  If we're in
+     * an interpreted method, everything we need is in the current frame.  If
+     * we're in a native method, possibly with some extra JNI frames pushed
+     * on by PushLocalFrame, we want to use the topmost native method.
+     */
+    const StackSaveArea* saveArea;
+    u4* fp;
+    u4* prevFp = NULL;
+
+    for (fp = thread->interpSave.curFrame; fp != NULL;
+         fp = saveArea->prevFrame) {
+        const Method* method;
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+        method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*)fp) && !dvmIsNativeMethod(method))
+            break;
+        prevFp = fp;
+    }
+    if (fp == NULL) {
+        ALOGW("Unexpected: step req in native-only threadid=%d",
+            thread->threadId);
+        return false;
+    }
+    if (prevFp != NULL) {
+        /*
+         * First interpreted frame wasn't the one at the bottom.  Break
+         * frames are only inserted when calling from native->interp, so we
+         * don't need to worry about one being here.
+         */
+        ALOGV("##### init step while in native method");
+        fp = prevFp;
+        assert(!dvmIsBreakFrame((u4*)fp));
+        assert(dvmIsNativeMethod(SAVEAREA_FROM_FP(fp)->method));
+        saveArea = SAVEAREA_FROM_FP(fp);
+    }
+
+    /*
+     * Pull the goodies out.  "xtra.currentPc" should be accurate since
+     * we update it on every instruction while the debugger is connected.
+     */
+    pCtrl->method = saveArea->method;
+    // Clear out any old address set
+    if (pCtrl->pAddressSet != NULL) {
+        // (discard const)
+        free((void *)pCtrl->pAddressSet);
+        pCtrl->pAddressSet = NULL;
+    }
+    if (dvmIsNativeMethod(pCtrl->method)) {
+        pCtrl->line = -1;
+    } else {
+        pCtrl->line = dvmLineNumFromPC(saveArea->method,
+                        saveArea->xtra.currentPc - saveArea->method->insns);
+        pCtrl->pAddressSet
+                = dvmAddressSetForLine(saveArea->method, pCtrl->line);
+    }
+    pCtrl->frameDepth =
+        dvmComputeVagueFrameDepth(thread, thread->interpSave.curFrame);
+    pCtrl->active = true;
+
+    ALOGV("##### step init: thread=%p meth=%p '%s' line=%d frameDepth=%d depth=%s size=%s",
+        pCtrl->thread, pCtrl->method, pCtrl->method->name,
+        pCtrl->line, pCtrl->frameDepth,
+        dvmJdwpStepDepthStr(pCtrl->depth),
+        dvmJdwpStepSizeStr(pCtrl->size));
+
+    return true;
+}
+
+/*
+ * Disable a single step event.
+ */
+void dvmClearSingleStep(Thread* thread)
+{
+    UNUSED_PARAMETER(thread);
+
+    gDvm.stepControl.active = false;
+}
+
+/*
+ * The interpreter just threw.  Handle any special subMode requirements.
+ * All interpSave state must be valid on entry.
+ */
+void dvmReportExceptionThrow(Thread* self, Object* exception)
+{
+    const Method* curMethod = self->interpSave.method;
+#if defined(WITH_JIT)
+    if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
+        dvmJitEndTraceSelect(self, self->interpSave.pc);
+    }
+    if (self->interpBreak.ctl.breakFlags & kInterpSingleStep) {
+        /* Discard any single-step native returns to translation */
+        self->jitResumeNPC = NULL;
+    }
+#endif
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        void *catchFrame;
+        int offset = self->interpSave.pc - curMethod->insns;
+        int catchRelPc = dvmFindCatchBlock(self, offset, exception,
+                                           true, &catchFrame);
+        dvmDbgPostException(self->interpSave.curFrame, offset, catchFrame,
+                            catchRelPc, exception);
+    }
+}
+
+/*
+ * The interpreter is preparing to do an invoke (both native & normal).
+ * Handle any special subMode requirements.  All interpSave state
+ * must be valid on entry.
+ */
+void dvmReportInvoke(Thread* self, const Method* methodToCall)
+{
+    TRACE_METHOD_ENTER(self, methodToCall);
+}
+
+/*
+ * The interpreter is preparing to do a native invoke. Handle any
+ * special subMode requirements.  NOTE: for a native invoke,
+ * dvmReportInvoke() and dvmReportPreNativeInvoke() will both
+ * be called prior to the invoke.  fp is the Dalvik FP of the calling
+ * method.
+ */
+void dvmReportPreNativeInvoke(const Method* methodToCall, Thread* self, u4* fp)
+{
+#if defined(WITH_JIT)
+    /*
+     * Actively building a trace?  If so, end it now.   The trace
+     * builder can't follow into or through a native method.
+     */
+    if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
+        dvmCheckJit(self->interpSave.pc, self);
+    }
+#endif
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        Object* thisPtr = dvmGetThisPtr(self->interpSave.method, fp);
+        assert(thisPtr == NULL || dvmIsHeapAddress(thisPtr));
+        dvmDbgPostLocationEvent(methodToCall, -1, thisPtr, DBG_METHOD_ENTRY);
+    }
+}
+
+/*
+ * The interpreter has returned from a native invoke. Handle any
+ * special subMode requirements.  fp is the Dalvik FP of the calling
+ * method.
+ */
+void dvmReportPostNativeInvoke(const Method* methodToCall, Thread* self, u4* fp)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        Object* thisPtr = dvmGetThisPtr(self->interpSave.method, fp);
+        assert(thisPtr == NULL || dvmIsHeapAddress(thisPtr));
+        dvmDbgPostLocationEvent(methodToCall, -1, thisPtr, DBG_METHOD_EXIT);
+    }
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        dvmFastNativeMethodTraceExit(methodToCall, self);
+    }
+}
+
+/*
+ * The interpreter has returned from a normal method.  Handle any special
+ * subMode requirements.  All interpSave state must be valid on entry.
+ */
+void dvmReportReturn(Thread* self)
+{
+    TRACE_METHOD_EXIT(self, self->interpSave.method);
+#if defined(WITH_JIT)
+    if (dvmIsBreakFrame(self->interpSave.curFrame) &&
+        (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild)) {
+        dvmCheckJit(self->interpSave.pc, self);
+    }
+#endif
+}
+
+/*
+ * Update the debugger on interesting events, such as hitting a breakpoint
+ * or a single-step point.  This is called from the top of the interpreter
+ * loop, before the current instruction is processed.
+ *
+ * Set "methodEntry" if we've just entered the method.  This detects
+ * method exit by checking to see if the next instruction is "return".
+ *
+ * This can't catch native method entry/exit, so we have to handle that
+ * at the point of invocation.  We also need to catch it in dvmCallMethod
+ * if we want to capture native->native calls made through JNI.
+ *
+ * Notes to self:
+ * - Don't want to switch to VMWAIT while posting events to the debugger.
+ *   Let the debugger code decide if we need to change state.
+ * - We may want to check for debugger-induced thread suspensions on
+ *   every instruction.  That would make a "suspend all" more responsive
+ *   and reduce the chances of multiple simultaneous events occurring.
+ *   However, it could change the behavior some.
+ *
+ * TODO: method entry/exit events are probably less common than location
+ * breakpoints.  We may be able to speed things up a bit if we don't query
+ * the event list unless we know there's at least one lurking within.
+ */
+static void updateDebugger(const Method* method, const u2* pc, const u4* fp,
+                           Thread* self)
+{
+    int eventFlags = 0;
+
+    /*
+     * Update xtra.currentPc on every instruction.  We need to do this if
+     * there's a chance that we could get suspended.  This can happen if
+     * eventFlags != 0 here, or somebody manually requests a suspend
+     * (which gets handled at PERIOD_CHECKS time).  One place where this
+     * needs to be correct is in dvmAddSingleStep().
+     */
+    dvmExportPC(pc, fp);
+
+    if (self->debugIsMethodEntry) {
+        eventFlags |= DBG_METHOD_ENTRY;
+        self->debugIsMethodEntry = false;
+    }
+
+    /*
+     * See if we have a breakpoint here.
+     *
+     * Depending on the "mods" associated with event(s) on this address,
+     * we may or may not actually send a message to the debugger.
+     */
+    if (GET_OPCODE(*pc) == OP_BREAKPOINT) {
+        ALOGV("+++ breakpoint hit at %p", pc);
+        eventFlags |= DBG_BREAKPOINT;
+    }
+
+    /*
+     * If the debugger is single-stepping one of our threads, check to
+     * see if we're that thread and we've reached a step point.
+     */
+    const StepControl* pCtrl = &gDvm.stepControl;
+    if (pCtrl->active && pCtrl->thread == self) {
+        int frameDepth;
+        bool doStop = false;
+        const char* msg = NULL;
+
+        assert(!dvmIsNativeMethod(method));
+
+        if (pCtrl->depth == SD_INTO) {
+            /*
+             * Step into method calls.  We break when the line number
+             * or method pointer changes.  If we're in SS_MIN mode, we
+             * always stop.
+             */
+            if (pCtrl->method != method) {
+                doStop = true;
+                msg = "new method";
+            } else if (pCtrl->size == SS_MIN) {
+                doStop = true;
+                msg = "new instruction";
+            } else if (!dvmAddressSetGet(
+                    pCtrl->pAddressSet, pc - method->insns)) {
+                doStop = true;
+                msg = "new line";
+            }
+        } else if (pCtrl->depth == SD_OVER) {
+            /*
+             * Step over method calls.  We break when the line number is
+             * different and the frame depth is <= the original frame
+             * depth.  (We can't just compare on the method, because we
+             * might get unrolled past it by an exception, and it's tricky
+             * to identify recursion.)
+             */
+            frameDepth = dvmComputeVagueFrameDepth(self, fp);
+            if (frameDepth < pCtrl->frameDepth) {
+                /* popped up one or more frames, always trigger */
+                doStop = true;
+                msg = "method pop";
+            } else if (frameDepth == pCtrl->frameDepth) {
+                /* same depth, see if we moved */
+                if (pCtrl->size == SS_MIN) {
+                    doStop = true;
+                    msg = "new instruction";
+                } else if (!dvmAddressSetGet(pCtrl->pAddressSet,
+                            pc - method->insns)) {
+                    doStop = true;
+                    msg = "new line";
+                }
+            }
+        } else {
+            assert(pCtrl->depth == SD_OUT);
+            /*
+             * Return from the current method.  We break when the frame
+             * depth pops up.
+             *
+             * This differs from the "method exit" break in that it stops
+             * with the PC at the next instruction in the returned-to
+             * function, rather than the end of the returning function.
+             */
+            frameDepth = dvmComputeVagueFrameDepth(self, fp);
+            if (frameDepth < pCtrl->frameDepth) {
+                doStop = true;
+                msg = "method pop";
+            }
+        }
+
+        if (doStop) {
+            ALOGV("#####S %s", msg);
+            eventFlags |= DBG_SINGLE_STEP;
+        }
+    }
+
+    /*
+     * Check to see if this is a "return" instruction.  JDWP says we should
+     * send the event *after* the code has been executed, but it also says
+     * the location we provide is the last instruction.  Since the "return"
+     * instruction has no interesting side effects, we should be safe.
+     * (We can't just move this down to the returnFromMethod label because
+     * we potentially need to combine it with other events.)
+     *
+     * We're also not supposed to generate a method exit event if the method
+     * terminates "with a thrown exception".
+     */
+    u2 opcode = GET_OPCODE(*pc);
+    if (opcode == OP_RETURN_VOID || opcode == OP_RETURN || opcode == OP_RETURN_VOID_BARRIER ||
+        opcode == OP_RETURN_OBJECT || opcode == OP_RETURN_WIDE)
+    {
+        eventFlags |= DBG_METHOD_EXIT;
+    }
+
+    /*
+     * If there's something interesting going on, see if it matches one
+     * of the debugger filters.
+     */
+    if (eventFlags != 0) {
+        Object* thisPtr = dvmGetThisPtr(method, fp);
+        if (thisPtr != NULL && !dvmIsHeapAddress(thisPtr)) {
+            /*
+             * TODO: remove this check if we're confident that the "this"
+             * pointer is where it should be -- slows us down, especially
+             * during single-step.
+             */
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            ALOGE("HEY: invalid 'this' ptr %p (%s.%s %s)", thisPtr,
+                method->clazz->descriptor, method->name, desc);
+            free(desc);
+            dvmAbort();
+        }
+        dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
+            eventFlags);
+    }
+}
+
+/*
+ * Recover the "this" pointer from the current interpreted method.  "this"
+ * is always in "in0" for non-static methods.
+ *
+ * The "ins" start at (#of registers - #of ins).  Note in0 != v0.
+ *
+ * This works because "dx" guarantees that it will work.  It's probably
+ * fairly common to have a virtual method that doesn't use its "this"
+ * pointer, in which case we're potentially wasting a register.  However,
+ * the debugger doesn't treat "this" as just another argument.  For
+ * example, events (such as breakpoints) can be enabled for specific
+ * values of "this".  There is also a separate StackFrame.ThisObject call
+ * in JDWP that is expected to work for any non-native non-static method.
+ *
+ * Because we need it when setting up debugger event filters, we want to
+ * be able to do this quickly.
+ */
+Object* dvmGetThisPtr(const Method* method, const u4* fp)
+{
+    if (dvmIsStaticMethod(method))
+        return NULL;
+    return (Object*)fp[method->registersSize - method->insSize];
+}
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+/*
+ * Verify that all internally-tracked references have been released.  If
+ * they haven't, print them and abort the VM.
+ *
+ * "debugTrackedRefStart" indicates how many refs were on the list when
+ * we were first invoked.
+ */
+void dvmInterpCheckTrackedRefs(Thread* self, const Method* method,
+    int debugTrackedRefStart)
+{
+    if (dvmReferenceTableEntries(&self->internalLocalRefTable)
+        != (size_t) debugTrackedRefStart)
+    {
+        char* desc;
+        Object** top;
+        int count;
+
+        count = dvmReferenceTableEntries(&self->internalLocalRefTable);
+
+        ALOGE("TRACK: unreleased internal reference (prev=%d total=%d)",
+            debugTrackedRefStart, count);
+        desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGE("       current method is %s.%s %s", method->clazz->descriptor,
+            method->name, desc);
+        free(desc);
+        top = self->internalLocalRefTable.table + debugTrackedRefStart;
+        while (top < self->internalLocalRefTable.nextEntry) {
+            ALOGE("  %p (%s)",
+                 *top,
+                 ((*top)->clazz != NULL) ? (*top)->clazz->descriptor : "");
+            top++;
+        }
+        dvmDumpThread(self, false);
+
+        dvmAbort();
+    }
+    //ALOGI("TRACK OK");
+}
+#endif
+
+
+#ifdef LOG_INSTR
+/*
+ * Dump the v-registers.  Sent to the ILOG log tag.
+ */
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly)
+{
+    int i, localCount;
+
+    localCount = method->registersSize - method->insSize;
+
+    ALOG(LOG_VERBOSE, LOG_TAG"i", "Registers (fp=%p):", framePtr);
+    for (i = method->registersSize-1; i >= 0; i--) {
+        if (i >= localCount) {
+            ALOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d in%-2d : 0x%08x",
+                i, i-localCount, framePtr[i]);
+        } else {
+            if (inOnly) {
+                ALOG(LOG_VERBOSE, LOG_TAG"i", "  [...]");
+                break;
+            }
+            const char* name = "";
+#if 0   // "locals" structure has changed -- need to rewrite this
+            int j;
+            DexFile* pDexFile = method->clazz->pDexFile;
+            const DexCode* pDexCode = dvmGetMethodCode(method);
+            int localsSize = dexGetLocalsSize(pDexFile, pDexCode);
+            const DexLocal* locals = dvmDexGetLocals(pDexFile, pDexCode);
+            for (j = 0; j < localsSize, j++) {
+                if (locals[j].registerNum == (u4) i) {
+                    name = dvmDexStringStr(locals[j].pName);
+                    break;
+                }
+            }
+#endif
+            ALOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d      : 0x%08x %s",
+                i, framePtr[i], name);
+        }
+    }
+}
+#endif
+
+
+/*
+ * ===========================================================================
+ *      Entry point and general support functions
+ * ===========================================================================
+ */
+
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the packed-switch
+ * instruction).
+ */
+s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal)
+{
+    const int kInstrLen = 3;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    if (*switchData++ != kPackedSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError("bad packed switch magic");
+        return kInstrLen;
+    }
+
+    u2 size = *switchData++;
+    assert(size > 0);
+
+    s4 firstKey = *switchData++;
+    firstKey |= (*switchData++) << 16;
+
+    int index = testVal - firstKey;
+    if (index < 0 || index >= size) {
+        LOGVV("Value %d not found in switch (%d-%d)",
+            testVal, firstKey, firstKey+size-1);
+        return kInstrLen;
+    }
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    const s4* entries = (const s4*) switchData;
+    assert(((u4)entries & 0x3) == 0);
+
+    assert(index >= 0 && index < size);
+    LOGVV("Value %d found in slot %d (goto 0x%02x)",
+        testVal, index,
+        s4FromSwitchData(&entries[index]));
+    return s4FromSwitchData(&entries[index]);
+}
+
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the sparse-switch
+ * instruction).
+ */
+s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal)
+{
+    const int kInstrLen = 3;
+    u2 size;
+    const s4* keys;
+    const s4* entries;
+
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+
+    if (*switchData++ != kSparseSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError("bad sparse switch magic");
+        return kInstrLen;
+    }
+
+    size = *switchData++;
+    assert(size > 0);
+
+    /* The keys are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    keys = (const s4*) switchData;
+    assert(((u4)keys & 0x3) == 0);
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = keys + size;
+    assert(((u4)entries & 0x3) == 0);
+
+    /*
+     * Binary-search through the array of keys, which are guaranteed to
+     * be sorted low-to-high.
+     */
+    int lo = 0;
+    int hi = size - 1;
+    while (lo <= hi) {
+        int mid = (lo + hi) >> 1;
+
+        s4 foundVal = s4FromSwitchData(&keys[mid]);
+        if (testVal < foundVal) {
+            hi = mid - 1;
+        } else if (testVal > foundVal) {
+            lo = mid + 1;
+        } else {
+            LOGVV("Value %d found in entry %d (goto 0x%02x)",
+                testVal, mid, s4FromSwitchData(&entries[mid]));
+            return s4FromSwitchData(&entries[mid]);
+        }
+    }
+
+    LOGVV("Value %d not found in switch", testVal);
+    return kInstrLen;
+}
+
+/*
+ * Copy data for a fill-array-data instruction.  On a little-endian machine
+ * we can just do a memcpy(), on a big-endian system we have work to do.
+ *
+ * The trick here is that dexopt has byte-swapped each code unit, which is
+ * exactly what we want for short/char data.  For byte data we need to undo
+ * the swap, and for 4- or 8-byte values we need to swap pieces within
+ * each word.
+ */
+static void copySwappedArrayData(void* dest, const u2* src, u4 size, u2 width)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    memcpy(dest, src, size*width);
+#else
+    int i;
+
+    switch (width) {
+    case 1:
+        /* un-swap pairs of bytes as we go */
+        for (i = (size-1) & ~1; i >= 0; i -= 2) {
+            ((u1*)dest)[i] = ((u1*)src)[i+1];
+            ((u1*)dest)[i+1] = ((u1*)src)[i];
+        }
+        /*
+         * "src" is padded to end on a two-byte boundary, but we don't want to
+         * assume "dest" is, so we handle odd length specially.
+         */
+        if ((size & 1) != 0) {
+            ((u1*)dest)[size-1] = ((u1*)src)[size];
+        }
+        break;
+    case 2:
+        /* already swapped correctly */
+        memcpy(dest, src, size*width);
+        break;
+    case 4:
+        /* swap word halves */
+        for (i = 0; i < (int) size; i++) {
+            ((u4*)dest)[i] = (src[(i << 1) + 1] << 16) | src[i << 1];
+        }
+        break;
+    case 8:
+        /* swap word halves and words */
+        for (i = 0; i < (int) (size << 1); i += 2) {
+            ((int*)dest)[i] = (src[(i << 1) + 3] << 16) | src[(i << 1) + 2];
+            ((int*)dest)[i+1] = (src[(i << 1) + 1] << 16) | src[i << 1];
+        }
+        break;
+    default:
+        ALOGE("Unexpected width %d in copySwappedArrayData", width);
+        dvmAbort();
+        break;
+    }
+#endif
+}
+
+/*
+ * Fill the array with predefined constant values.
+ *
+ * Returns true if job is completed, otherwise false to indicate that
+ * an exception has been thrown.
+ */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObj, const u2* arrayData)
+{
+    u2 width;
+    u4 size;
+
+    if (arrayObj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+    assert (!IS_CLASS_FLAG_SET(((Object *)arrayObj)->clazz,
+                               CLASS_ISOBJECTARRAY));
+
+    /*
+     * Array data table format:
+     *  ushort ident = 0x0300   magic value
+     *  ushort width            width of each element in the table
+     *  uint   size             number of elements in the table
+     *  ubyte  data[size*width] table of data values (may contain a single-byte
+     *                          padding at the end)
+     *
+     * Total size is 4+(width * size + 1)/2 16-bit code units.
+     */
+    if (arrayData[0] != kArrayDataSignature) {
+        dvmThrowInternalError("bad array data magic");
+        return false;
+    }
+
+    width = arrayData[1];
+    size = arrayData[2] | (((u4)arrayData[3]) << 16);
+
+    if (size > arrayObj->length) {
+        dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, size);
+        return false;
+    }
+    copySwappedArrayData(arrayObj->contents, &arrayData[4], size, width);
+    return true;
+}
+
+/*
+ * Find the concrete method that corresponds to "methodIdx".  The code in
+ * "method" is executing invoke-method with "thisClass" as its first argument.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+Method* dvmInterpFindInterfaceMethod(ClassObject* thisClass, u4 methodIdx,
+    const Method* method, DvmDex* methodClassDex)
+{
+    Method* absMethod;
+    Method* methodToCall;
+    int i, vtableIndex;
+
+    /*
+     * Resolve the method.  This gives us the abstract method from the
+     * interface class declaration.
+     */
+    absMethod = dvmDexGetResolvedMethod(methodClassDex, methodIdx);
+    if (absMethod == NULL) {
+        absMethod = dvmResolveInterfaceMethod(method->clazz, methodIdx);
+        if (absMethod == NULL) {
+            ALOGV("+ unknown method");
+            return NULL;
+        }
+    }
+
+    /* make sure absMethod->methodIndex means what we think it means */
+    assert(dvmIsAbstractMethod(absMethod));
+
+    /*
+     * Run through the "this" object's iftable.  Find the entry for
+     * absMethod's class, then use absMethod->methodIndex to find
+     * the method's entry.  The value there is the offset into our
+     * vtable of the actual method to execute.
+     *
+     * The verifier does not guarantee that objects stored into
+     * interface references actually implement the interface, so this
+     * check cannot be eliminated.
+     */
+    for (i = 0; i < thisClass->iftableCount; i++) {
+        if (thisClass->iftable[i].clazz == absMethod->clazz)
+            break;
+    }
+    if (i == thisClass->iftableCount) {
+        /* impossible in verified DEX, need to check for it in unverified */
+        dvmThrowIncompatibleClassChangeError("interface not implemented");
+        return NULL;
+    }
+
+    assert(absMethod->methodIndex <
+        thisClass->iftable[i].clazz->virtualMethodCount);
+
+    vtableIndex =
+        thisClass->iftable[i].methodIndexArray[absMethod->methodIndex];
+    assert(vtableIndex >= 0 && vtableIndex < thisClass->vtableCount);
+    methodToCall = thisClass->vtable[vtableIndex];
+
+#if 0
+    /* this can happen when there's a stale class file */
+    if (dvmIsAbstractMethod(methodToCall)) {
+        dvmThrowAbstractMethodError("interface method not implemented");
+        return NULL;
+    }
+#else
+    assert(!dvmIsAbstractMethod(methodToCall) ||
+        methodToCall->nativeFunc != NULL);
+#endif
+
+    LOGVV("+++ interface=%s.%s concrete=%s.%s",
+        absMethod->clazz->descriptor, absMethod->name,
+        methodToCall->clazz->descriptor, methodToCall->name);
+    assert(methodToCall != NULL);
+
+    return methodToCall;
+}
+
+
+
+/*
+ * Helpers for dvmThrowVerificationError().
+ *
+ * Each returns a newly-allocated string.
+ */
+#define kThrowShow_accessFromClass     1
+static std::string classNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    if (refType == VERIFY_ERROR_REF_FIELD) {
+        /* get class ID from field ID */
+        const DexFieldId* pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+        ref = pFieldId->classIdx;
+    } else if (refType == VERIFY_ERROR_REF_METHOD) {
+        /* get class ID from method ID */
+        const DexMethodId* pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+        ref = pMethodId->classIdx;
+    }
+
+    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, ref);
+    std::string dotClassName(dvmHumanReadableDescriptor(className));
+    if (flags == 0) {
+        return dotClassName;
+    }
+
+    std::string result;
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        result += "tried to access class " + dotClassName;
+        result += " from class " + dvmHumanReadableDescriptor(method->clazz->descriptor);
+    } else {
+        assert(false);      // should've been caught above
+    }
+
+    return result;
+}
+static std::string fieldNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    if (refType != VERIFY_ERROR_REF_FIELD) {
+        ALOGW("Expected ref type %d, got %d", VERIFY_ERROR_REF_FIELD, refType);
+        return NULL;    /* no message */
+    }
+
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexFieldId* pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->classIdx);
+    const char* fieldName = dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);
+
+    std::string dotName(dvmHumanReadableDescriptor(className));
+
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        std::string result;
+        result += "tried to access field ";
+        result += dotName + "." + fieldName;
+        result += " from class ";
+        result += dvmHumanReadableDescriptor(method->clazz->descriptor);
+        return result;
+    }
+    return dotName + "." + fieldName;
+}
+static std::string methodNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    if (refType != VERIFY_ERROR_REF_METHOD) {
+        ALOGW("Expected ref type %d, got %d", VERIFY_ERROR_REF_METHOD,refType);
+        return NULL;    /* no message */
+    }
+
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexMethodId* pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, pMethodId->classIdx);
+    const char* methodName = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+    std::string dotName(dvmHumanReadableDescriptor(className));
+
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        std::string result;
+        result += "tried to access method ";
+        result += dotName + "." + methodName + ":" + desc;
+        result += " from class " + dvmHumanReadableDescriptor(method->clazz->descriptor);
+        free(desc);
+        return result;
+    }
+    return dotName + "." + methodName;
+}
+
+/*
+ * Throw an exception for a problem identified by the verifier.
+ *
+ * This is used by the invoke-verification-error instruction.  It always
+ * throws an exception.
+ *
+ * "kind" indicates the kind of failure encountered by the verifier.  It
+ * has two parts, an error code and an indication of the reference type.
+ */
+void dvmThrowVerificationError(const Method* method, int kind, int ref)
+{
+    int errorPart = kind & ~(0xff << kVerifyErrorRefTypeShift);
+    int errorRefPart = kind >> kVerifyErrorRefTypeShift;
+    VerifyError errorKind = static_cast<VerifyError>(errorPart);
+    VerifyErrorRefType refType = static_cast<VerifyErrorRefType>(errorRefPart);
+    ClassObject* exceptionClass = gDvm.exVerifyError;
+    std::string msg;
+
+    switch ((VerifyError) errorKind) {
+    case VERIFY_ERROR_NO_CLASS:
+        exceptionClass = gDvm.exNoClassDefFoundError;
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_NO_FIELD:
+        exceptionClass = gDvm.exNoSuchFieldError;
+        msg = fieldNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_NO_METHOD:
+        exceptionClass = gDvm.exNoSuchMethodError;
+        msg = methodNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_ACCESS_CLASS:
+        exceptionClass = gDvm.exIllegalAccessError;
+        msg = classNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_ACCESS_FIELD:
+        exceptionClass = gDvm.exIllegalAccessError;
+        msg = fieldNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_ACCESS_METHOD:
+        exceptionClass = gDvm.exIllegalAccessError;
+        msg = methodNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_CLASS_CHANGE:
+        exceptionClass = gDvm.exIncompatibleClassChangeError;
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_INSTANTIATION:
+        exceptionClass = gDvm.exInstantiationError;
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+
+    case VERIFY_ERROR_GENERIC:
+        /* generic VerifyError; use default exception, no message */
+        break;
+    case VERIFY_ERROR_NONE:
+        /* should never happen; use default exception */
+        assert(false);
+        msg = "weird - no error specified";
+        break;
+
+    /* no default clause -- want warning if enum updated */
+    }
+
+    dvmThrowException(exceptionClass, msg.c_str());
+}
+
+/*
+ * Update interpBreak for a single thread.
+ */
+void updateInterpBreak(Thread* thread, ExecutionSubModes subMode, bool enable)
+{
+    InterpBreak oldValue, newValue;
+    do {
+        oldValue = newValue = thread->interpBreak;
+        newValue.ctl.breakFlags = kInterpNoBreak;  // Assume full reset
+        if (enable)
+            newValue.ctl.subMode |= subMode;
+        else
+            newValue.ctl.subMode &= ~subMode;
+        if (newValue.ctl.subMode & SINGLESTEP_BREAK_MASK)
+            newValue.ctl.breakFlags |= kInterpSingleStep;
+        if (newValue.ctl.subMode & SAFEPOINT_BREAK_MASK)
+            newValue.ctl.breakFlags |= kInterpSafePoint;
+#ifndef DVM_NO_ASM_INTERP
+        newValue.ctl.curHandlerTable = (newValue.ctl.breakFlags) ?
+            thread->altHandlerTable : thread->mainHandlerTable;
+#endif
+    } while (dvmQuasiAtomicCas64(oldValue.all, newValue.all,
+             &thread->interpBreak.all) != 0);
+}
+
+/*
+ * Update interpBreak for all threads.
+ */
+void updateAllInterpBreak(ExecutionSubModes subMode, bool enable)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    dvmLockThreadList(self);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        updateInterpBreak(thread, subMode, enable);
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Update the normal and debugger suspend counts for a thread.
+ * threadSuspendCount must be acquired before calling this to
+ * ensure a clean update of suspendCount, dbgSuspendCount and
+ * sumThreadSuspendCount.
+ *
+ * CLEANUP TODO: Currently only the JIT is using sumThreadSuspendCount.
+ * Move under WITH_JIT ifdefs.
+*/
+void dvmAddToSuspendCounts(Thread* thread, int delta, int dbgDelta)
+{
+    thread->suspendCount += delta;
+    thread->dbgSuspendCount += dbgDelta;
+    updateInterpBreak(thread, kSubModeSuspendPending,
+                      (thread->suspendCount != 0));
+    // Update the global suspend count total
+    gDvm.sumThreadSuspendCount += delta;
+}
+
+
+void dvmDisableSubMode(Thread* thread, ExecutionSubModes subMode)
+{
+    updateInterpBreak(thread, subMode, false);
+}
+
+void dvmEnableSubMode(Thread* thread, ExecutionSubModes subMode)
+{
+    updateInterpBreak(thread, subMode, true);
+}
+
+void dvmEnableAllSubMode(ExecutionSubModes subMode)
+{
+    updateAllInterpBreak(subMode, true);
+}
+
+void dvmDisableAllSubMode(ExecutionSubModes subMode)
+{
+    updateAllInterpBreak(subMode, false);
+}
+
+/*
+ * Do a sanity check on interpreter state saved to Thread.
+ * A failure here doesn't necessarily mean that something is wrong,
+ * so this code should only be used during development to suggest
+ * a possible problem.
+ */
+void dvmCheckInterpStateConsistency()
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    uint8_t breakFlags;
+    uint8_t subMode;
+#ifndef DVM_NO_ASM_INTERP
+    void* handlerTable;
+#endif
+
+    dvmLockThreadList(self);
+    breakFlags = self->interpBreak.ctl.breakFlags;
+    subMode = self->interpBreak.ctl.subMode;
+#ifndef DVM_NO_ASM_INTERP
+    handlerTable = self->interpBreak.ctl.curHandlerTable;
+#endif
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (subMode != thread->interpBreak.ctl.subMode) {
+            ALOGD("Warning: subMode mismatch - %#x:%#x, tid[%d]",
+                subMode,thread->interpBreak.ctl.subMode,thread->threadId);
+         }
+        if (breakFlags != thread->interpBreak.ctl.breakFlags) {
+            ALOGD("Warning: breakFlags mismatch - %#x:%#x, tid[%d]",
+                breakFlags,thread->interpBreak.ctl.breakFlags,thread->threadId);
+         }
+#ifndef DVM_NO_ASM_INTERP
+        if (handlerTable != thread->interpBreak.ctl.curHandlerTable) {
+            ALOGD("Warning: curHandlerTable mismatch - %#x:%#x, tid[%d]",
+                (int)handlerTable,(int)thread->interpBreak.ctl.curHandlerTable,
+                thread->threadId);
+         }
+#endif
+#if defined(WITH_JIT)
+         if (thread->pJitProfTable != gDvmJit.pProfTable) {
+             ALOGD("Warning: pJitProfTable mismatch - %#x:%#x, tid[%d]",
+                  (int)thread->pJitProfTable,(int)gDvmJit.pProfTable,
+                  thread->threadId);
+         }
+         if (thread->jitThreshold != gDvmJit.threshold) {
+             ALOGD("Warning: jitThreshold mismatch - %#x:%#x, tid[%d]",
+                  (int)thread->jitThreshold,(int)gDvmJit.threshold,
+                  thread->threadId);
+         }
+#endif
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Arm a safepoint callback for a thread.  If funct is null,
+ * clear any pending callback.
+ * TODO: only gc is currently using this feature, and will have
+ * at most a single outstanding callback request.  Until we need
+ * something more capable and flexible, enforce this limit.
+ */
+void dvmArmSafePointCallback(Thread* thread, SafePointCallback funct,
+                             void* arg)
+{
+    dvmLockMutex(&thread->callbackMutex);
+    if ((funct == NULL) || (thread->callback == NULL)) {
+        thread->callback = funct;
+        thread->callbackArg = arg;
+        if (funct != NULL) {
+            dvmEnableSubMode(thread, kSubModeCallbackPending);
+        } else {
+            dvmDisableSubMode(thread, kSubModeCallbackPending);
+        }
+    } else {
+        // Already armed.  Different?
+        if ((funct != thread->callback) ||
+            (arg != thread->callbackArg)) {
+            // Yes - report failure and die
+            ALOGE("ArmSafePointCallback failed, thread %d", thread->threadId);
+            dvmUnlockMutex(&thread->callbackMutex);
+            dvmAbort();
+        }
+    }
+    dvmUnlockMutex(&thread->callbackMutex);
+}
+
+/*
+ * One-time initialization at thread creation.  Here we initialize
+ * useful constants.
+ */
+void dvmInitInterpreterState(Thread* self)
+{
+#if defined(WITH_JIT)
+    /*
+     * Reserve a static entity here to quickly setup runtime contents as
+     * gcc will issue block copy instructions.
+     */
+    static struct JitToInterpEntries jitToInterpEntries = {
+        dvmJitToInterpNormal,
+        dvmJitToInterpNoChain,
+        dvmJitToInterpPunt,
+        dvmJitToInterpSingleStep,
+        dvmJitToInterpTraceSelect,
+#if defined(WITH_SELF_VERIFICATION)
+        dvmJitToInterpBackwardBranch,
+#else
+        NULL,
+#endif
+    };
+#endif
+
+    // Begin initialization
+    self->cardTable = gDvm.biasedCardTableBase;
+#if defined(WITH_JIT)
+    // One-time initializations
+    self->jitToInterpEntries = jitToInterpEntries;
+    self->icRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+    self->pProfileCountdown = &gDvmJit.profileCountdown;
+    // Jit state that can change
+    dvmJitUpdateThreadStateSingle(self);
+#endif
+    dvmInitializeInterpBreak(self);
+}
+
+/*
+ * For a newly-created thread, we need to start off with interpBreak
+ * set to any existing global modes.  The caller must hold the
+ * thread list lock.
+ */
+void dvmInitializeInterpBreak(Thread* thread)
+{
+    if (gDvm.instructionCountEnableCount > 0) {
+        dvmEnableSubMode(thread, kSubModeInstCounting);
+    }
+    if (dvmIsMethodTraceActive()) {
+        dvmEnableSubMode(thread, kSubModeMethodTrace);
+    }
+    if (gDvm.emulatorTraceEnableCount > 0) {
+        dvmEnableSubMode(thread, kSubModeEmulatorTrace);
+    }
+    if (gDvm.debuggerActive) {
+        dvmEnableSubMode(thread, kSubModeDebuggerActive);
+    }
+#if 0
+    // Debugging stress mode - force checkBefore
+    dvmEnableSubMode(thread, kSubModeCheckAlways);
+#endif
+}
+
+/*
+ * Inter-instruction handler invoked in between instruction interpretations
+ * to handle exceptional events such as debugging housekeeping, instruction
+ * count profiling, JIT trace building, etc.  Dalvik PC has been exported
+ * prior to call, but Thread copy of dPC & fp are not current.
+ */
+void dvmCheckBefore(const u2 *pc, u4 *fp, Thread* self)
+{
+    const Method* method = self->interpSave.method;
+    assert(pc >= method->insns && pc <
+           method->insns + dvmGetMethodInsnsSize(method));
+
+#if 0
+    /*
+     * When we hit a specific method, enable verbose instruction logging.
+     * Sometimes it's helpful to use the debugger attach as a trigger too.
+     */
+    if (*pIsMethodEntry) {
+        static const char* cd = "Landroid/test/Arithmetic;";
+        static const char* mn = "shiftTest2";
+        static const char* sg = "()V";
+
+        if (/*self->interpBreak.ctl.subMode & kSubModeDebuggerActive &&*/
+            strcmp(method->clazz->descriptor, cd) == 0 &&
+            strcmp(method->name, mn) == 0 &&
+            strcmp(method->shorty, sg) == 0)
+        {
+            ALOGW("Reached %s.%s, enabling verbose mode",
+                method->clazz->descriptor, method->name);
+            android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
+            dumpRegs(method, fp, true);
+        }
+
+        if (!gDvm.debuggerActive)
+            *pIsMethodEntry = false;
+    }
+#endif
+
+    /* Safe point handling */
+    if (self->suspendCount ||
+        (self->interpBreak.ctl.subMode & kSubModeCallbackPending)) {
+        // Are we are a safe point?
+        int flags;
+        flags = dexGetFlagsFromOpcode(dexOpcodeFromCodeUnit(*pc));
+        if (flags & (VERIFY_GC_INST_MASK & ~kInstrCanThrow)) {
+            // Yes, at a safe point.  Pending callback?
+            if (self->interpBreak.ctl.subMode & kSubModeCallbackPending) {
+                SafePointCallback callback;
+                void* arg;
+                // Get consistent funct/arg pair
+                dvmLockMutex(&self->callbackMutex);
+                callback = self->callback;
+                arg = self->callbackArg;
+                dvmUnlockMutex(&self->callbackMutex);
+                // Update Thread structure
+                self->interpSave.pc = pc;
+                self->interpSave.curFrame = fp;
+                if (callback != NULL) {
+                    // Do the callback
+                    if (!callback(self,arg)) {
+                        // disarm
+                        dvmArmSafePointCallback(self, NULL, NULL);
+                    }
+                }
+            }
+            // Need to suspend?
+            if (self->suspendCount) {
+                dvmExportPC(pc, fp);
+                dvmCheckSuspendPending(self);
+            }
+        }
+    }
+
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        updateDebugger(method, pc, fp, self);
+    }
+    if (gDvm.instructionCountEnableCount != 0) {
+        /*
+         * Count up the #of executed instructions.  This isn't synchronized
+         * for thread-safety; if we need that we should make this
+         * thread-local and merge counts into the global area when threads
+         * exit (perhaps suspending all other threads GC-style and pulling
+         * the data out of them).
+         */
+        gDvm.executedInstrCounts[GET_OPCODE(*pc)]++;
+    }
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+    dvmInterpCheckTrackedRefs(self, method,
+                              self->interpSave.debugTrackedRefStart);
+#endif
+
+#if defined(WITH_JIT)
+    // Does the JIT need anything done now?
+    if (self->interpBreak.ctl.subMode &
+            (kSubModeJitTraceBuild | kSubModeJitSV)) {
+        // Are we building a trace?
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
+            dvmCheckJit(pc, self);
+        }
+
+#if defined(WITH_SELF_VERIFICATION)
+        // Are we replaying a trace?
+        if (self->interpBreak.ctl.subMode & kSubModeJitSV) {
+            dvmCheckSelfVerification(pc, self);
+        }
+#endif
+    }
+#endif
+
+    /*
+     * CountedStep processing.  NOTE: must be the last here to allow
+     * preceeding special case handler to manipulate single-step count.
+     */
+    if (self->interpBreak.ctl.subMode & kSubModeCountedStep) {
+        if (self->singleStepCount == 0) {
+            // We've exhausted our single step count
+            dvmDisableSubMode(self, kSubModeCountedStep);
+#if defined(WITH_JIT)
+#if 0
+            /*
+             * For debugging.  If jitResumeDPC is non-zero, then
+             * we expect to return to a trace in progress.   There
+             * are valid reasons why we wouldn't (such as an exception
+             * throw), but here we can keep track.
+             */
+            if (self->jitResumeDPC != NULL) {
+                if (self->jitResumeDPC == pc) {
+                    if (self->jitResumeNPC != NULL) {
+                        ALOGD("SS return to trace - pc:%#x to 0x:%x",
+                             (int)pc, (int)self->jitResumeNPC);
+                    } else {
+                        ALOGD("SS return to interp - pc:%#x",(int)pc);
+                    }
+                } else {
+                    ALOGD("SS failed to return.  Expected %#x, now at %#x",
+                         (int)self->jitResumeDPC, (int)pc);
+                }
+            }
+#endif
+#if 0
+            // TODO - fix JIT single-stepping resume mode (b/5551114)
+            // self->jitResumeNPC needs to be cleared in callPrep
+
+            // If we've got a native return and no other reasons to
+            // remain in singlestep/break mode, do a long jump
+            if (self->jitResumeNPC != NULL &&
+                self->interpBreak.ctl.breakFlags == 0) {
+                assert(self->jitResumeDPC == pc);
+                self->jitResumeDPC = NULL;
+                dvmJitResumeTranslation(self, pc, fp);
+                // Doesn't return
+                dvmAbort();
+            }
+            // In case resume is blocked by non-zero breakFlags, clear
+            // jitResumeNPC here.
+            self->jitResumeNPC = NULL;
+            self->jitResumeDPC = NULL;
+            self->inJitCodeCache = NULL;
+#endif
+#endif
+        } else {
+            self->singleStepCount--;
+#if defined(WITH_JIT)
+            if ((self->singleStepCount > 0) && (self->jitResumeNPC != NULL)) {
+                /*
+                 * Direct return to an existing translation following a
+                 * single step is valid only if we step once.  If we're
+                 * here, an additional step was added so we need to invalidate
+                 * the return to translation.
+                 */
+                self->jitResumeNPC = NULL;
+                self->inJitCodeCache = NULL;
+            }
+#endif
+        }
+    }
+}
+
+/*
+ * Main interpreter loop entry point.
+ *
+ * This begins executing code at the start of "method".  On exit, "pResult"
+ * holds the return value of the method (or, if "method" returns NULL, it
+ * holds an undefined value).
+ *
+ * The interpreted stack frame, which holds the method arguments, has
+ * already been set up.
+ */
+void dvmInterpret(Thread* self, const Method* method, JValue* pResult)
+{
+    InterpSaveState interpSaveState;
+    ExecutionSubModes savedSubModes;
+
+#if defined(WITH_JIT)
+    /* Target-specific save/restore */
+    double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];
+    /*
+     * If the previous VM left the code cache through single-stepping the
+     * inJitCodeCache flag will be set when the VM is re-entered (for example,
+     * in self-verification mode we single-step NEW_INSTANCE which may re-enter
+     * the VM through findClassFromLoaderNoInit). Because of that, we cannot
+     * assert that self->inJitCodeCache is NULL here.
+     */
+#endif
+
+    /*
+     * Save interpreter state from previous activation, linking
+     * new to last.
+     */
+    interpSaveState = self->interpSave;
+    self->interpSave.prev = &interpSaveState;
+    /*
+     * Strip out and save any flags that should not be inherited by
+     * nested interpreter activation.
+     */
+    savedSubModes = (ExecutionSubModes)(
+              self->interpBreak.ctl.subMode & LOCAL_SUBMODE);
+    if (savedSubModes != kSubModeNormal) {
+        dvmDisableSubMode(self, savedSubModes);
+    }
+#if defined(WITH_JIT)
+    dvmJitCalleeSave(calleeSave);
+#endif
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+    self->interpSave.debugTrackedRefStart =
+        dvmReferenceTableEntries(&self->internalLocalRefTable);
+#endif
+    self->debugIsMethodEntry = true;
+#if defined(WITH_JIT)
+    /* Initialize the state to kJitNot */
+    self->jitState = kJitNot;
+#endif
+
+    /*
+     * Initialize working state.
+     *
+     * No need to initialize "retval".
+     */
+    self->interpSave.method = method;
+    self->interpSave.curFrame = (u4*) self->interpSave.curFrame;
+    self->interpSave.pc = method->insns;
+
+    assert(!dvmIsNativeMethod(method));
+
+    /*
+     * Make sure the class is ready to go.  Shouldn't be possible to get
+     * here otherwise.
+     */
+    if (method->clazz->status < CLASS_INITIALIZING ||
+        method->clazz->status == CLASS_ERROR)
+    {
+        ALOGE("ERROR: tried to execute code in unprepared class '%s' (%d)",
+            method->clazz->descriptor, method->clazz->status);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    typedef void (*Interpreter)(Thread*);
+    Interpreter stdInterp;
+    if (gDvm.executionMode == kExecutionModeInterpFast)
+        stdInterp = dvmMterpStd;
+#if defined(WITH_JIT)
+    else if (gDvm.executionMode == kExecutionModeJit ||
+             gDvm.executionMode == kExecutionModeNcgO0 ||
+             gDvm.executionMode == kExecutionModeNcgO1)
+        stdInterp = dvmMterpStd;
+#endif
+    else
+        stdInterp = dvmInterpretPortable;
+
+    // Call the interpreter
+    (*stdInterp)(self);
+
+    *pResult = self->interpSave.retval;
+
+    /* Restore interpreter state from previous activation */
+    self->interpSave = interpSaveState;
+#if defined(WITH_JIT)
+    dvmJitCalleeRestore(calleeSave);
+#endif
+    if (savedSubModes != kSubModeNormal) {
+        dvmEnableSubMode(self, savedSubModes);
+    }
+}
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
new file mode 100644
index 0000000..e54ec61
--- /dev/null
+++ b/vm/interp/Interp.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik interpreter public definitions.
+ */
+#ifndef DALVIK_INTERP_INTERP_H_
+#define DALVIK_INTERP_INTERP_H_
+
+/*
+ * Stash the dalvik PC in the frame.  Called  during interpretation.
+ */
+INLINE void dvmExportPC(const u2* pc, const u4* fp)
+{
+    SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc;
+}
+
+/*
+ * Extract the Dalvik opcode
+ */
+#define GET_OPCODE(_inst) (_inst & 0xff)
+
+/*
+ * Interpreter entry point.  Call here after setting up the interpreted
+ * stack (most code will want to get here via dvmCallMethod().)
+ */
+void dvmInterpret(Thread* thread, const Method* method, JValue* pResult);
+
+/*
+ * Throw an exception for a problem detected by the verifier.
+ *
+ * This is called from the handler for the throw-verification-error
+ * instruction.  "method" is the method currently being executed.
+ */
+extern "C" void dvmThrowVerificationError(const Method* method,
+                                          int kind, int ref);
+
+/*
+ * One-time initialization and shutdown.
+ */
+bool dvmBreakpointStartup(void);
+void dvmBreakpointShutdown(void);
+void dvmInitInterpreterState(Thread* self);
+
+/*
+ * Breakpoint implementation.
+ */
+void dvmInitBreakpoints();
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset);
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset);
+bool dvmAddSingleStep(Thread* thread, int size, int depth);
+void dvmClearSingleStep(Thread* thread);
+
+/*
+ * Recover the opcode that was replaced by a breakpoint.
+ */
+extern "C" u1 dvmGetOriginalOpcode(const u2* addr);
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".
+ */
+void dvmFlushBreakpoints(ClassObject* clazz);
+
+/*
+ * Debugger support
+ */
+extern "C" void dvmCheckBefore(const u2 *dPC, u4 *fp, Thread* self);
+extern "C" void dvmReportExceptionThrow(Thread* self, Object* exception);
+extern "C" void dvmReportPreNativeInvoke(const Method* methodToCall, Thread* self, u4* fp);
+extern "C" void dvmReportPostNativeInvoke(const Method* methodToCall, Thread* self, u4* fp);
+extern "C" void dvmReportInvoke(Thread* self, const Method* methodToCall);
+extern "C" void dvmReportReturn(Thread* self);
+
+/*
+ * InterpBreak & subMode control
+ */
+void dvmDisableSubMode(Thread* thread, ExecutionSubModes subMode);
+extern "C" void dvmEnableSubMode(Thread* thread, ExecutionSubModes subMode);
+void dvmDisableAllSubMode(ExecutionSubModes subMode);
+void dvmEnableAllSubMode(ExecutionSubModes subMode);
+void dvmAddToSuspendCounts(Thread* thread, int delta, int dbgDelta);
+void dvmCheckInterpStateConsistency();
+void dvmInitializeInterpBreak(Thread* thread);
+
+/*
+ * Register a callback to occur at the next safe point for a single thread.
+ * If funct is NULL, the previous registration is cancelled.
+ *
+ * The callback prototype is:
+ *        bool funct(Thread* thread, void* arg)
+ *
+ *  If funct returns false, the callback will be disarmed.  If true,
+ *  it will stay in effect.
+ */
+void dvmArmSafePointCallback(Thread* thread, SafePointCallback funct,
+                             void* arg);
+
+
+#ifndef DVM_NO_ASM_INTERP
+extern void* dvmAsmInstructionStart[];
+extern void* dvmAsmAltInstructionStart[];
+#endif
+
+#endif  // DALVIK_INTERP_INTERP_H_
diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h
new file mode 100644
index 0000000..a053f6d
--- /dev/null
+++ b/vm/interp/InterpDefs.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Dalvik interpreter definitions.  These are internal to the interpreter.
+ *
+ * This includes defines, types, function declarations, and inline functions
+ * that are common to all interpreter implementations.
+ *
+ * Functions and globals declared here are defined in Interp.c.
+ */
+#ifndef DALVIK_INTERP_DEFS_H_
+#define DALVIK_INTERP_DEFS_H_
+
+#if defined(WITH_JIT)
+/*
+ * Size of save area for callee-save FP regs, which are not automatically
+ * saved by interpreter main because it doesn't use them (but Jit'd code
+ * may). Save/restore routine is defined by target, and size should
+ * be >= max needed by any target.
+ */
+#define JIT_CALLEE_SAVE_DOUBLE_COUNT 8
+
+#endif
+
+/*
+ * Portable interpreter.
+ */
+extern void dvmInterpretPortable(Thread* self);
+
+/*
+ * "mterp" interpreter.
+ */
+extern void dvmMterpStd(Thread* self);
+
+/*
+ * Get the "this" pointer from the current frame.
+ */
+Object* dvmGetThisPtr(const Method* method, const u4* fp);
+
+/*
+ * Verify that our tracked local references are valid.
+ */
+void dvmInterpCheckTrackedRefs(Thread* self, const Method* method,
+    int debugTrackedRefStart);
+
+/*
+ * Process switch statement.
+ */
+extern "C" s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal);
+extern "C" s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal);
+
+/*
+ * Process fill-array-data.
+ */
+extern "C" bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,
+                                  const u2* arrayData);
+
+/*
+ * Find an interface method.
+ */
+Method* dvmInterpFindInterfaceMethod(ClassObject* thisClass, u4 methodIdx,
+    const Method* method, DvmDex* methodClassDex);
+
+/*
+ * Determine if the debugger or profiler is currently active.
+ */
+static inline bool dvmDebuggerOrProfilerActive()
+{
+    return gDvm.debuggerActive || gDvm.activeProfilers != 0;
+}
+
+#if defined(WITH_JIT)
+/*
+ * Determine if the jit, debugger or profiler is currently active.  Used when
+ * selecting which interpreter to switch to.
+ */
+static inline bool dvmJitDebuggerOrProfilerActive()
+{
+    return (gDvmJit.pProfTable != NULL) || dvmDebuggerOrProfilerActive();
+}
+
+/*
+ * Hide the translations and stick with the interpreter as long as one of the
+ * following conditions is true.
+ */
+static inline bool dvmJitHideTranslation()
+{
+    return (gDvm.sumThreadSuspendCount != 0) ||
+           (gDvmJit.codeCacheFull == true) ||
+           (gDvmJit.pProfTable == NULL);
+}
+
+#endif
+
+/*
+ * Construct an s4 from two consecutive half-words of switch data.
+ * This needs to check endianness because the DEX optimizer only swaps
+ * half-words in instruction stream.
+ *
+ * "switchData" must be 32-bit aligned.
+ */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline s4 s4FromSwitchData(const void* switchData) {
+    return *(s4*) switchData;
+}
+#else
+static inline s4 s4FromSwitchData(const void* switchData) {
+    u2* data = switchData;
+    return data[0] | (((s4) data[1]) << 16);
+}
+#endif
+
+#endif  // DALVIK_INTERP_DEFS_H_
diff --git a/vm/interp/InterpState.h b/vm/interp/InterpState.h
new file mode 100644
index 0000000..cc0a13f
--- /dev/null
+++ b/vm/interp/InterpState.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Dalvik interpreter definitions.  These are internal to the interpreter.
+ *
+ * This includes defines, types, function declarations, and inline functions
+ * that are common to all interpreter implementations.
+ *
+ * Functions and globals declared here are defined in Interp.c.
+ */
+#ifndef DALVIK_INTERP_STATE_H_
+#define DALVIK_INTERP_STATE_H_
+
+/*
+ * For x86 JIT. In the lowered code sequences for bytecodes, at most 10
+ * temporary variables may be live at the same time. Therefore, at most
+ * 10 temporary variables can be spilled at the same time.
+*/
+#define MAX_SPILL_JIT_IA 10
+
+/*
+ * Execution mode, e.g. interpreter vs. JIT.
+ */
+enum ExecutionMode {
+    kExecutionModeUnknown = 0,
+    kExecutionModeInterpPortable,
+    kExecutionModeInterpFast,
+#if defined(WITH_JIT)
+    kExecutionModeJit,
+#endif
+#if defined(WITH_JIT)  /* IA only */
+    kExecutionModeNcgO0,
+    kExecutionModeNcgO1,
+#endif
+};
+
+/*
+ * Execution sub modes, e.g. debugging, profiling, etc.
+ * Treated as bit flags for fast access.  These values are used directly
+ * by assembly code in the mterp interpeter and may also be used by
+ * code generated by the JIT.  Take care when changing.
+ */
+enum ExecutionSubModes {
+    kSubModeNormal            = 0x0000,   /* No active subMode */
+    kSubModeMethodTrace       = 0x0001,
+    kSubModeEmulatorTrace     = 0x0002,
+    kSubModeInstCounting      = 0x0004,
+    kSubModeDebuggerActive    = 0x0008,
+    kSubModeSuspendPending    = 0x0010,
+    kSubModeCallbackPending   = 0x0020,
+    kSubModeCountedStep       = 0x0040,
+    kSubModeCheckAlways       = 0x0080,
+    kSubModeJitTraceBuild     = 0x4000,
+    kSubModeJitSV             = 0x8000,
+    kSubModeDebugProfile   = (kSubModeMethodTrace |
+                              kSubModeEmulatorTrace |
+                              kSubModeInstCounting |
+                              kSubModeDebuggerActive)
+};
+
+/*
+ * Interpreter break flags.  When set, causes the interpreter to
+ * break from normal execution and invoke the associated callback
+ * handler.
+ */
+
+enum InterpBreakFlags {
+    kInterpNoBreak            = 0x00,    /* Don't check */
+    kInterpSingleStep         = 0x01,    /* Check between each inst */
+    kInterpSafePoint          = 0x02,    /* Check at safe points */
+};
+
+/*
+ * Mapping between subModes and required check intervals.  Note: in
+ * the future we might want to make this mapping target-dependent.
+ */
+#define SINGLESTEP_BREAK_MASK ( kSubModeInstCounting | \
+                                kSubModeDebuggerActive | \
+                                kSubModeCountedStep | \
+                                kSubModeCheckAlways | \
+                                kSubModeJitSV | \
+                                kSubModeJitTraceBuild )
+
+#define SAFEPOINT_BREAK_MASK  ( kSubModeSuspendPending | \
+                                kSubModeCallbackPending )
+
+typedef bool (*SafePointCallback)(struct Thread* thread, void* arg);
+
+/*
+ * Identify which break and submode flags should be local
+ * to an interpreter activation.
+ */
+#define LOCAL_SUBMODE (kSubModeJitTraceBuild)
+
+struct InterpSaveState {
+    const u2*       pc;         // Dalvik PC
+    u4*             curFrame;   // Dalvik frame pointer
+    const Method    *method;    // Method being executed
+    DvmDex*         methodClassDex;
+    JValue          retval;
+    void*           bailPtr;
+#if defined(WITH_TRACKREF_CHECKS)
+    int             debugTrackedRefStart;
+#else
+    int             unused;        // Keep struct size constant
+#endif
+    struct InterpSaveState* prev;  // To follow nested activations
+} __attribute__ ((__packed__));
+
+#ifdef WITH_JIT
+/*
+ * NOTE: Only entry points dispatched via [self + #offset] are put
+ * in this struct, and there are six of them:
+ * 1) dvmJitToInterpNormal: find if there is a corresponding compilation for
+ *    the new dalvik PC. If so, chain the originating compilation with the
+ *    target then jump to it. If the destination trace doesn't exist, update
+ *    the profile count for that Dalvik PC.
+ * 2) dvmJitToInterpNoChain: similar to dvmJitToInterpNormal but chaining is
+ *    not performed.
+ * 3) dvmJitToInterpPunt: use the fast interpreter to execute the next
+ *    instruction(s) and stay there as long as it is appropriate to return
+ *    to the compiled land. This is used when the jit'ed code is about to
+ *    throw an exception.
+ * 4) dvmJitToInterpSingleStep: use the portable interpreter to execute the
+ *    next instruction only and return to pre-specified location in the
+ *    compiled code to resume execution. This is mainly used as debugging
+ *    feature to bypass problematic opcode implementations without
+ *    disturbing the trace formation.
+ * 5) dvmJitToTraceSelect: Similar to dvmJitToInterpNormal except for the
+ *    profiling operation. If the new Dalvik PC is dominated by an already
+ *    translated trace, directly request a new translation if the destinaion
+ *    trace doesn't exist.
+ * 6) dvmJitToBackwardBranch: special case for SELF_VERIFICATION when the
+ *    destination Dalvik PC is included by the trace itself.
+ */
+struct JitToInterpEntries {
+    void (*dvmJitToInterpNormal)(void);
+    void (*dvmJitToInterpNoChain)(void);
+    void (*dvmJitToInterpPunt)(void);
+    void (*dvmJitToInterpSingleStep)(void);
+    void (*dvmJitToInterpTraceSelect)(void);
+#if defined(WITH_SELF_VERIFICATION)
+    void (*dvmJitToInterpBackwardBranch)(void);
+#else
+    void (*unused)(void);  // Keep structure size constant
+#endif
+};
+
+/* States of the interpreter when serving a JIT-related request */
+enum JitState {
+    /* Entering states in the debug interpreter */
+    kJitNot = 0,               // Non-JIT related reasons */
+    kJitTSelectRequest = 1,    // Request a trace (subject to filtering)
+    kJitTSelectRequestHot = 2, // Request a hot trace (bypass the filter)
+    kJitSelfVerification = 3,  // Self Verification Mode
+
+    /* Operational states in the debug interpreter */
+    kJitTSelect = 4,           // Actively selecting a trace
+    kJitTSelectEnd = 5,        // Done with the trace - wrap it up
+    kJitDone = 6,              // No further JIT actions for interpBreak
+};
+
+#if defined(WITH_SELF_VERIFICATION)
+enum SelfVerificationState {
+    kSVSIdle = 0,           // Idle
+    kSVSStart = 1,          // Shadow space set up, running compiled code
+    kSVSPunt = 2,           // Exiting compiled code by punting
+    kSVSSingleStep = 3,     // Exiting compiled code by single stepping
+    kSVSNoProfile = 4,      // Exiting compiled code and don't collect profiles
+    kSVSTraceSelect = 5,    // Exiting compiled code and compile the next pc
+    kSVSNormal = 6,         // Exiting compiled code normally
+    kSVSNoChain = 7,        // Exiting compiled code by no chain
+    kSVSBackwardBranch = 8, // Exiting compiled code with backward branch trace
+    kSVSDebugInterp = 9,    // Normal state restored, running debug interpreter
+};
+#endif
+
+/* Number of entries in the 2nd level JIT profiler filter cache */
+#define JIT_TRACE_THRESH_FILTER_SIZE 32
+/* Number of low dalvik pc address bits to include in 2nd level filter key */
+#define JIT_TRACE_THRESH_FILTER_PC_BITS 4
+#define MAX_JIT_RUN_LEN 64
+
+enum JitHint {
+   kJitHintNone = 0,
+   kJitHintTaken = 1,         // Last inst in run was taken branch
+   kJitHintNotTaken = 2,      // Last inst in run was not taken branch
+   kJitHintNoBias = 3,        // Last inst in run was unbiased branch
+};
+
+/*
+ * Element of a Jit trace description. If the isCode bit is set, it describes
+ * a contiguous sequence of Dalvik byte codes.
+ */
+struct JitCodeDesc {
+    unsigned numInsts:8;     // Number of Byte codes in run
+    unsigned runEnd:1;       // Run ends with last byte code
+    JitHint hint:7;          // Hint to apply to final code of run
+    u2 startOffset;          // Starting offset for trace run
+};
+
+/*
+ * A complete list of trace runs passed to the compiler looks like the
+ * following:
+ *   frag1
+ *   frag2
+ *   frag3
+ *   meta1
+ *     :
+ *   metan
+ *   frag4
+ *
+ * frags 1-4 have the "isCode" field set and describe the location/length of
+ * real code traces, while metas 1-n are misc information.
+ * The meaning of the meta content is loosely defined. It is usually the code
+ * fragment right before the first meta field (frag3 in this case) to
+ * understand and parse them. Frag4 could be a dummy one with 0 "numInsts" but
+ * the "runEnd" field set.
+ *
+ * For example, if a trace run contains a method inlining target, the class
+ * descriptor/loader of "this" and the currently resolved method pointer are
+ * three instances of meta information stored there.
+ */
+struct JitTraceRun {
+    union {
+        JitCodeDesc frag;
+        void*       meta;
+    } info;
+    u4 isCode:1;
+    u4 unused:31;
+};
+
+#if defined(ARCH_IA32)
+/*
+ * JIT code genarator optimization level
+ */
+enum JitOptLevel {
+    kJitOptLevelO0 = 0,
+    kJitOptLevelO1 = 1,
+};
+#endif  // #if defined(ARCH_IA32)
+#endif
+
+#endif  // DALVIK_INTERP_STATE_H_
diff --git a/vm/interp/Jit.cpp b/vm/interp/Jit.cpp
new file mode 100644
index 0000000..6d53954
--- /dev/null
+++ b/vm/interp/Jit.cpp
@@ -0,0 +1,1519 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifdef WITH_JIT
+
+/*
+ * Target independent portion of Android's Jit
+ */
+
+#include "Dalvik.h"
+#include "Jit.h"
+
+#include "libdex/DexOpcodes.h"
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "compiler/Compiler.h"
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include <errno.h>
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Allocate space for per-thread ShadowSpace data structures */
+void* dvmSelfVerificationShadowSpaceAlloc(Thread* self)
+{
+    self->shadowSpace = (ShadowSpace*) calloc(1, sizeof(ShadowSpace));
+    if (self->shadowSpace == NULL)
+        return NULL;
+
+    self->shadowSpace->registerSpaceSize = REG_SPACE;
+    self->shadowSpace->registerSpace =
+        (int*) calloc(self->shadowSpace->registerSpaceSize, sizeof(int));
+
+    return self->shadowSpace->registerSpace;
+}
+
+/* Free per-thread ShadowSpace data structures */
+void dvmSelfVerificationShadowSpaceFree(Thread* self)
+{
+    free(self->shadowSpace->registerSpace);
+    free(self->shadowSpace);
+}
+
+/*
+ * Save out PC, FP, thread state, and registers to shadow space.
+ * Return a pointer to the shadow space for JIT to use.
+ *
+ * The set of saved state from the Thread structure is:
+ *     pc  (Dalvik PC)
+ *     fp  (Dalvik FP)
+ *     retval
+ *     method
+ *     methodClassDex
+ *     interpStackEnd
+ */
+void* dvmSelfVerificationSaveState(const u2* pc, u4* fp,
+                                   Thread* self, int targetTrace)
+{
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    unsigned preBytes = self->interpSave.method->outsSize*4 +
+        sizeof(StackSaveArea);
+    unsigned postBytes = self->interpSave.method->registersSize*4;
+
+    //ALOGD("### selfVerificationSaveState(%d) pc: %#x fp: %#x",
+    //    self->threadId, (int)pc, (int)fp);
+
+    if (shadowSpace->selfVerificationState != kSVSIdle) {
+        ALOGD("~~~ Save: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, shadowSpace->selfVerificationState);
+        ALOGD("********** SHADOW STATE DUMP **********");
+        ALOGD("PC: %#x FP: %#x", (int)pc, (int)fp);
+    }
+    shadowSpace->selfVerificationState = kSVSStart;
+
+    // Dynamically grow shadow register space if necessary
+    if (preBytes + postBytes > shadowSpace->registerSpaceSize * sizeof(u4)) {
+        free(shadowSpace->registerSpace);
+        shadowSpace->registerSpaceSize = (preBytes + postBytes) / sizeof(u4);
+        shadowSpace->registerSpace =
+            (int*) calloc(shadowSpace->registerSpaceSize, sizeof(u4));
+    }
+
+    // Remember original state
+    shadowSpace->startPC = pc;
+    shadowSpace->fp = fp;
+    shadowSpace->retval = self->interpSave.retval;
+    shadowSpace->interpStackEnd = self->interpStackEnd;
+
+    /*
+     * Store the original method here in case the trace ends with a
+     * return/invoke, the last method.
+     */
+    shadowSpace->method = self->interpSave.method;
+    shadowSpace->methodClassDex = self->interpSave.methodClassDex;
+
+    shadowSpace->shadowFP = shadowSpace->registerSpace +
+                            shadowSpace->registerSpaceSize - postBytes/4;
+
+    self->interpSave.curFrame = (u4*)shadowSpace->shadowFP;
+    self->interpStackEnd = (u1*)shadowSpace->registerSpace;
+
+    // Create a copy of the stack
+    memcpy(((char*)shadowSpace->shadowFP)-preBytes, ((char*)fp)-preBytes,
+        preBytes+postBytes);
+
+    // Setup the shadowed heap space
+    shadowSpace->heapSpaceTail = shadowSpace->heapSpace;
+
+    // Reset trace length
+    shadowSpace->traceLength = 0;
+
+    return shadowSpace;
+}
+
+/*
+ * Save ending PC, FP and compiled code exit point to shadow space.
+ * Return a pointer to the shadow space for JIT to restore state.
+ */
+void* dvmSelfVerificationRestoreState(const u2* pc, u4* fp,
+                                      SelfVerificationState exitState,
+                                      Thread* self)
+{
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    shadowSpace->endPC = pc;
+    shadowSpace->endShadowFP = fp;
+    shadowSpace->jitExitState = exitState;
+
+    //ALOGD("### selfVerificationRestoreState(%d) pc: %#x fp: %#x endPC: %#x",
+    //    self->threadId, (int)shadowSpace->startPC, (int)shadowSpace->fp,
+    //    (int)pc);
+
+    if (shadowSpace->selfVerificationState != kSVSStart) {
+        ALOGD("~~~ Restore: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, shadowSpace->selfVerificationState);
+        ALOGD("********** SHADOW STATE DUMP **********");
+        ALOGD("Dalvik PC: %#x endPC: %#x", (int)shadowSpace->startPC,
+            (int)shadowSpace->endPC);
+        ALOGD("Interp FP: %#x", (int)shadowSpace->fp);
+        ALOGD("Shadow FP: %#x endFP: %#x", (int)shadowSpace->shadowFP,
+            (int)shadowSpace->endShadowFP);
+    }
+
+    // Special case when punting after a single instruction
+    if (exitState == kSVSPunt && pc == shadowSpace->startPC) {
+        shadowSpace->selfVerificationState = kSVSIdle;
+    } else {
+        shadowSpace->selfVerificationState = exitState;
+    }
+
+    /* Restore state before returning */
+    self->interpSave.pc = shadowSpace->startPC;
+    self->interpSave.curFrame = shadowSpace->fp;
+    self->interpSave.method = shadowSpace->method;
+    self->interpSave.methodClassDex = shadowSpace->methodClassDex;
+    self->interpSave.retval = shadowSpace->retval;
+    self->interpStackEnd = shadowSpace->interpStackEnd;
+
+    return shadowSpace;
+}
+
+/* Print contents of virtual registers */
+static void selfVerificationPrintRegisters(int* addr, int* addrRef,
+                                           int numWords)
+{
+    int i;
+    for (i = 0; i < numWords; i++) {
+        ALOGD("(v%d) 0x%8x%s", i, addr[i], addr[i] != addrRef[i] ? " X" : "");
+    }
+}
+
+/* Print values maintained in shadowSpace */
+static void selfVerificationDumpState(const u2* pc, Thread* self)
+{
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    int frameBytes = (int) shadowSpace->registerSpace +
+                     shadowSpace->registerSpaceSize*4 -
+                     (int) shadowSpace->shadowFP;
+    int localRegs = 0;
+    int frameBytes2 = 0;
+    if ((uintptr_t)self->interpSave.curFrame < (uintptr_t)shadowSpace->fp) {
+        localRegs = (stackSave->method->registersSize -
+                     stackSave->method->insSize)*4;
+        frameBytes2 = (int) shadowSpace->fp -
+                      (int)self->interpSave.curFrame - localRegs;
+    }
+    ALOGD("********** SHADOW STATE DUMP **********");
+    ALOGD("CurrentPC: %#x, Offset: 0x%04x", (int)pc,
+        (int)(pc - stackSave->method->insns));
+    ALOGD("Class: %s", shadowSpace->method->clazz->descriptor);
+    ALOGD("Method: %s", shadowSpace->method->name);
+    ALOGD("Dalvik PC: %#x endPC: %#x", (int)shadowSpace->startPC,
+        (int)shadowSpace->endPC);
+    ALOGD("Interp FP: %#x endFP: %#x", (int)shadowSpace->fp,
+        (int)self->interpSave.curFrame);
+    ALOGD("Shadow FP: %#x endFP: %#x", (int)shadowSpace->shadowFP,
+        (int)shadowSpace->endShadowFP);
+    ALOGD("Frame1 Bytes: %d Frame2 Local: %d Bytes: %d", frameBytes,
+        localRegs, frameBytes2);
+    ALOGD("Trace length: %d State: %d", shadowSpace->traceLength,
+        shadowSpace->selfVerificationState);
+}
+
+/* Print decoded instructions in the current trace */
+static void selfVerificationDumpTrace(const u2* pc, Thread* self)
+{
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    int i, addr, offset;
+    DecodedInstruction *decInsn;
+
+    ALOGD("********** SHADOW TRACE DUMP **********");
+    for (i = 0; i < shadowSpace->traceLength; i++) {
+        addr = shadowSpace->trace[i].addr;
+        offset =  (int)((u2*)addr - stackSave->method->insns);
+        decInsn = &(shadowSpace->trace[i].decInsn);
+        /* Not properly decoding instruction, some registers may be garbage */
+        ALOGD("%#x: (0x%04x) %s",
+            addr, offset, dexGetOpcodeName(decInsn->opcode));
+    }
+}
+
+/* Code is forced into this spin loop when a divergence is detected */
+static void selfVerificationSpinLoop(ShadowSpace *shadowSpace)
+{
+    const u2 *startPC = shadowSpace->startPC;
+    JitTraceDescription* desc = dvmCopyTraceDescriptor(startPC, NULL);
+    if (desc) {
+        dvmCompilerWorkEnqueue(startPC, kWorkOrderTraceDebug, desc);
+        /*
+         * This function effectively terminates the VM right here, so not
+         * freeing the desc pointer when the enqueuing fails is acceptable.
+         */
+    }
+    gDvmJit.selfVerificationSpin = true;
+    while(gDvmJit.selfVerificationSpin) sleep(10);
+}
+
+/*
+ * If here, we're re-interpreting an instruction that was included
+ * in a trace that was just executed.  This routine is called for
+ * each instruction in the original trace, and compares state
+ * when it reaches the end point.
+ *
+ * TUNING: the interpretation mechanism now supports a counted
+ * single-step mechanism.  If we were to associate an instruction
+ * count with each trace exit, we could just single-step the right
+ * number of cycles and then compare.  This would improve detection
+ * of control divergences, as well as (slightly) simplify this code.
+ */
+void dvmCheckSelfVerification(const u2* pc, Thread* self)
+{
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    SelfVerificationState state = shadowSpace->selfVerificationState;
+
+    DecodedInstruction decInsn;
+    dexDecodeInstruction(pc, &decInsn);
+
+    //ALOGD("### DbgIntp(%d): PC: %#x endPC: %#x state: %d len: %d %s",
+    //    self->threadId, (int)pc, (int)shadowSpace->endPC, state,
+    //    shadowSpace->traceLength, dexGetOpcodeName(decInsn.opcode));
+
+    if (state == kSVSIdle || state == kSVSStart) {
+        ALOGD("~~~ DbgIntrp: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, state);
+        selfVerificationDumpState(pc, self);
+        selfVerificationDumpTrace(pc, self);
+    }
+
+    /*
+     * Generalize the self verification state to kSVSDebugInterp unless the
+     * entry reason is kSVSBackwardBranch or kSVSSingleStep.
+     */
+    if (state != kSVSBackwardBranch && state != kSVSSingleStep) {
+        shadowSpace->selfVerificationState = kSVSDebugInterp;
+    }
+
+    /*
+     * Check if the current pc matches the endPC. Only check for non-zero
+     * trace length when backward branches are involved.
+     */
+    if (pc == shadowSpace->endPC &&
+        (state == kSVSDebugInterp || state == kSVSSingleStep ||
+         (state == kSVSBackwardBranch && shadowSpace->traceLength != 0))) {
+
+        shadowSpace->selfVerificationState = kSVSIdle;
+
+        /* Check register space */
+        int frameBytes = (int) shadowSpace->registerSpace +
+                         shadowSpace->registerSpaceSize*4 -
+                         (int) shadowSpace->shadowFP;
+        if (memcmp(shadowSpace->fp, shadowSpace->shadowFP, frameBytes)) {
+            if (state == kSVSBackwardBranch) {
+                /* State mismatch on backward branch - try one more iteration */
+                shadowSpace->selfVerificationState = kSVSDebugInterp;
+                goto log_and_continue;
+            }
+            ALOGD("~~~ DbgIntp(%d): REGISTERS DIVERGENCE!", self->threadId);
+            selfVerificationDumpState(pc, self);
+            selfVerificationDumpTrace(pc, self);
+            ALOGD("*** Interp Registers: addr: %#x bytes: %d",
+                (int)shadowSpace->fp, frameBytes);
+            selfVerificationPrintRegisters((int*)shadowSpace->fp,
+                                           (int*)shadowSpace->shadowFP,
+                                           frameBytes/4);
+            ALOGD("*** Shadow Registers: addr: %#x bytes: %d",
+                (int)shadowSpace->shadowFP, frameBytes);
+            selfVerificationPrintRegisters((int*)shadowSpace->shadowFP,
+                                           (int*)shadowSpace->fp,
+                                           frameBytes/4);
+            selfVerificationSpinLoop(shadowSpace);
+        }
+        /* Check new frame if it exists (invokes only) */
+        if ((uintptr_t)self->interpSave.curFrame < (uintptr_t)shadowSpace->fp) {
+            StackSaveArea* stackSave =
+                SAVEAREA_FROM_FP(self->interpSave.curFrame);
+            int localRegs = (stackSave->method->registersSize -
+                             stackSave->method->insSize)*4;
+            int frameBytes2 = (int) shadowSpace->fp -
+                              (int) self->interpSave.curFrame - localRegs;
+            if (memcmp(((char*)self->interpSave.curFrame)+localRegs,
+                ((char*)shadowSpace->endShadowFP)+localRegs, frameBytes2)) {
+                if (state == kSVSBackwardBranch) {
+                    /*
+                     * State mismatch on backward branch - try one more
+                     * iteration.
+                     */
+                    shadowSpace->selfVerificationState = kSVSDebugInterp;
+                    goto log_and_continue;
+                }
+                ALOGD("~~~ DbgIntp(%d): REGISTERS (FRAME2) DIVERGENCE!",
+                    self->threadId);
+                selfVerificationDumpState(pc, self);
+                selfVerificationDumpTrace(pc, self);
+                ALOGD("*** Interp Registers: addr: %#x l: %d bytes: %d",
+                    (int)self->interpSave.curFrame, localRegs, frameBytes2);
+                selfVerificationPrintRegisters((int*)self->interpSave.curFrame,
+                                               (int*)shadowSpace->endShadowFP,
+                                               (frameBytes2+localRegs)/4);
+                ALOGD("*** Shadow Registers: addr: %#x l: %d bytes: %d",
+                    (int)shadowSpace->endShadowFP, localRegs, frameBytes2);
+                selfVerificationPrintRegisters((int*)shadowSpace->endShadowFP,
+                                               (int*)self->interpSave.curFrame,
+                                               (frameBytes2+localRegs)/4);
+                selfVerificationSpinLoop(shadowSpace);
+            }
+        }
+
+        /* Check memory space */
+        bool memDiff = false;
+        ShadowHeap* heapSpacePtr;
+        for (heapSpacePtr = shadowSpace->heapSpace;
+             heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+            int memData = *((unsigned int*) heapSpacePtr->addr);
+            if (heapSpacePtr->data != memData) {
+                if (state == kSVSBackwardBranch) {
+                    /*
+                     * State mismatch on backward branch - try one more
+                     * iteration.
+                     */
+                    shadowSpace->selfVerificationState = kSVSDebugInterp;
+                    goto log_and_continue;
+                }
+                ALOGD("~~~ DbgIntp(%d): MEMORY DIVERGENCE!", self->threadId);
+                ALOGD("Addr: %#x Intrp Data: %#x Jit Data: %#x",
+                    heapSpacePtr->addr, memData, heapSpacePtr->data);
+                selfVerificationDumpState(pc, self);
+                selfVerificationDumpTrace(pc, self);
+                memDiff = true;
+            }
+        }
+        if (memDiff) selfVerificationSpinLoop(shadowSpace);
+
+
+        /*
+         * Success.  If this shadowed trace included a single-stepped
+         * instruction, we need to stay in the interpreter for one
+         * more interpretation before resuming.
+         */
+        if (state == kSVSSingleStep) {
+            assert(self->jitResumeNPC != NULL);
+            assert(self->singleStepCount == 0);
+            self->singleStepCount = 1;
+            dvmEnableSubMode(self, kSubModeCountedStep);
+        }
+
+        /*
+         * Switch off shadow replay mode.  The next shadowed trace
+         * execution will turn it back on.
+         */
+        dvmDisableSubMode(self, kSubModeJitSV);
+
+        self->jitState = kJitDone;
+        return;
+    }
+log_and_continue:
+    /* If end not been reached, make sure max length not exceeded */
+    if (shadowSpace->traceLength >= JIT_MAX_TRACE_LEN) {
+        ALOGD("~~~ DbgIntp(%d): CONTROL DIVERGENCE!", self->threadId);
+        ALOGD("startPC: %#x endPC: %#x currPC: %#x",
+            (int)shadowSpace->startPC, (int)shadowSpace->endPC, (int)pc);
+        selfVerificationDumpState(pc, self);
+        selfVerificationDumpTrace(pc, self);
+        selfVerificationSpinLoop(shadowSpace);
+        return;
+    }
+    /* Log the instruction address and decoded instruction for debug */
+    shadowSpace->trace[shadowSpace->traceLength].addr = (int)pc;
+    shadowSpace->trace[shadowSpace->traceLength].decInsn = decInsn;
+    shadowSpace->traceLength++;
+}
+#endif
+
+/*
+ * If one of our fixed tables or the translation buffer fills up,
+ * call this routine to avoid wasting cycles on future translation requests.
+ */
+void dvmJitStopTranslationRequests()
+{
+    /*
+     * Note 1: This won't necessarily stop all translation requests, and
+     * operates on a delayed mechanism.  Running threads look to the copy
+     * of this value in their private thread structures and won't see
+     * this change until it is refreshed (which happens on interpreter
+     * entry).
+     * Note 2: This is a one-shot memory leak on this table. Because this is a
+     * permanent off switch for Jit profiling, it is a one-time leak of 1K
+     * bytes, and no further attempt will be made to re-allocate it.  Can't
+     * free it because some thread may be holding a reference.
+     */
+    gDvmJit.pProfTable = NULL;
+    dvmJitUpdateThreadStateAll();
+}
+
+#if defined(WITH_JIT_TUNING)
+/* Convenience function to increment counter from assembly code */
+void dvmBumpNoChain(int from)
+{
+    gDvmJit.noChainExit[from]++;
+}
+
+/* Convenience function to increment counter from assembly code */
+void dvmBumpNormal()
+{
+    gDvmJit.normalExit++;
+}
+
+/* Convenience function to increment counter from assembly code */
+void dvmBumpPunt(int from)
+{
+    gDvmJit.puntExit++;
+}
+#endif
+
+/* Dumps debugging & tuning stats to the log */
+void dvmJitStats()
+{
+    int i;
+    int hit;
+    int not_hit;
+    int chains;
+    int stubs;
+    if (gDvmJit.pJitEntryTable) {
+        for (i=0, stubs=chains=hit=not_hit=0;
+             i < (int) gDvmJit.jitTableSize;
+             i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC != 0) {
+                hit++;
+                if (gDvmJit.pJitEntryTable[i].codeAddress ==
+                      dvmCompilerGetInterpretTemplate())
+                    stubs++;
+            } else
+                not_hit++;
+            if (gDvmJit.pJitEntryTable[i].u.info.chain != gDvmJit.jitTableSize)
+                chains++;
+        }
+        ALOGD("JIT: table size is %d, entries used is %d",
+             gDvmJit.jitTableSize,  gDvmJit.jitTableEntriesUsed);
+        ALOGD("JIT: %d traces, %d slots, %d chains, %d thresh, %s",
+             hit, not_hit + hit, chains, gDvmJit.threshold,
+             gDvmJit.blockingMode ? "Blocking" : "Non-blocking");
+
+#if defined(WITH_JIT_TUNING)
+        ALOGD("JIT: Code cache patches: %d", gDvmJit.codeCachePatches);
+
+        ALOGD("JIT: Lookups: %d hits, %d misses; %d normal, %d punt",
+             gDvmJit.addrLookupsFound, gDvmJit.addrLookupsNotFound,
+             gDvmJit.normalExit, gDvmJit.puntExit);
+
+        ALOGD("JIT: ICHits: %d", gDvmICHitCount);
+
+        ALOGD("JIT: noChainExit: %d IC miss, %d interp callsite, "
+             "%d switch overflow",
+             gDvmJit.noChainExit[kInlineCacheMiss],
+             gDvmJit.noChainExit[kCallsiteInterpreted],
+             gDvmJit.noChainExit[kSwitchOverflow]);
+
+        ALOGD("JIT: ICPatch: %d init, %d rejected, %d lock-free, %d queued, "
+             "%d dropped",
+             gDvmJit.icPatchInit, gDvmJit.icPatchRejected,
+             gDvmJit.icPatchLockFree, gDvmJit.icPatchQueued,
+             gDvmJit.icPatchDropped);
+
+        ALOGD("JIT: Invoke: %d mono, %d poly, %d native, %d return",
+             gDvmJit.invokeMonomorphic, gDvmJit.invokePolymorphic,
+             gDvmJit.invokeNative, gDvmJit.returnOp);
+        ALOGD("JIT: Inline: %d mgetter, %d msetter, %d pgetter, %d psetter",
+             gDvmJit.invokeMonoGetterInlined, gDvmJit.invokeMonoSetterInlined,
+             gDvmJit.invokePolyGetterInlined, gDvmJit.invokePolySetterInlined);
+        ALOGD("JIT: Total compilation time: %llu ms", gDvmJit.jitTime / 1000);
+        ALOGD("JIT: Avg unit compilation time: %llu us",
+             gDvmJit.numCompilations == 0 ? 0 :
+             gDvmJit.jitTime / gDvmJit.numCompilations);
+        ALOGD("JIT: Potential GC blocked by compiler: max %llu us / "
+             "avg %llu us (%d)",
+             gDvmJit.maxCompilerThreadBlockGCTime,
+             gDvmJit.numCompilerThreadBlockGC == 0 ?
+                 0 : gDvmJit.compilerThreadBlockGCTime /
+                     gDvmJit.numCompilerThreadBlockGC,
+             gDvmJit.numCompilerThreadBlockGC);
+#endif
+
+        ALOGD("JIT: %d Translation chains, %d interp stubs",
+             gDvmJit.translationChains, stubs);
+        if (gDvmJit.profileMode == kTraceProfilingContinuous) {
+            dvmCompilerSortAndPrintTraceProfiles();
+        }
+    }
+}
+
+
+/* End current trace now & don't include current instruction */
+void dvmJitEndTraceSelect(Thread* self, const u2* dPC)
+{
+    if (self->jitState == kJitTSelect) {
+        self->jitState = kJitTSelectEnd;
+    }
+    if (self->jitState == kJitTSelectEnd) {
+        // Clean up and finish now.
+        dvmCheckJit(dPC, self);
+    }
+}
+
+/*
+ * Find an entry in the JitTable, creating if necessary.
+ * Returns null if table is full.
+ */
+static JitEntry *lookupAndAdd(const u2* dPC, bool callerLocked,
+                              bool isMethodEntry)
+{
+    u4 chainEndMarker = gDvmJit.jitTableSize;
+    u4 idx = dvmJitHash(dPC);
+
+    /*
+     * Walk the bucket chain to find an exact match for our PC and trace/method
+     * type
+     */
+    while ((gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) &&
+           ((gDvmJit.pJitEntryTable[idx].dPC != dPC) ||
+            (gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry !=
+             isMethodEntry))) {
+        idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+    }
+
+    if (gDvmJit.pJitEntryTable[idx].dPC != dPC ||
+        gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry != isMethodEntry) {
+        /*
+         * No match.  Aquire jitTableLock and find the last
+         * slot in the chain. Possibly continue the chain walk in case
+         * some other thread allocated the slot we were looking
+         * at previuosly (perhaps even the dPC we're trying to enter).
+         */
+        if (!callerLocked)
+            dvmLockMutex(&gDvmJit.tableLock);
+        /*
+         * At this point, if .dPC is NULL, then the slot we're
+         * looking at is the target slot from the primary hash
+         * (the simple, and common case).  Otherwise we're going
+         * to have to find a free slot and chain it.
+         */
+        ANDROID_MEMBAR_FULL(); /* Make sure we reload [].dPC after lock */
+        if (gDvmJit.pJitEntryTable[idx].dPC != NULL) {
+            u4 prev;
+            while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+                if (gDvmJit.pJitEntryTable[idx].dPC == dPC &&
+                    gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry ==
+                        isMethodEntry) {
+                    /* Another thread got there first for this dPC */
+                    if (!callerLocked)
+                        dvmUnlockMutex(&gDvmJit.tableLock);
+                    return &gDvmJit.pJitEntryTable[idx];
+                }
+                idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+            }
+            /* Here, idx should be pointing to the last cell of an
+             * active chain whose last member contains a valid dPC */
+            assert(gDvmJit.pJitEntryTable[idx].dPC != NULL);
+            /* Linear walk to find a free cell and add it to the end */
+            prev = idx;
+            while (true) {
+                idx++;
+                if (idx == chainEndMarker)
+                    idx = 0;  /* Wraparound */
+                if ((gDvmJit.pJitEntryTable[idx].dPC == NULL) ||
+                    (idx == prev))
+                    break;
+            }
+            if (idx != prev) {
+                JitEntryInfoUnion oldValue;
+                JitEntryInfoUnion newValue;
+                /*
+                 * Although we hold the lock so that noone else will
+                 * be trying to update a chain field, the other fields
+                 * packed into the word may be in use by other threads.
+                 */
+                do {
+                    oldValue = gDvmJit.pJitEntryTable[prev].u;
+                    newValue = oldValue;
+                    newValue.info.chain = idx;
+                } while (android_atomic_release_cas(oldValue.infoWord,
+                        newValue.infoWord,
+                        &gDvmJit.pJitEntryTable[prev].u.infoWord) != 0);
+            }
+        }
+        if (gDvmJit.pJitEntryTable[idx].dPC == NULL) {
+            gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry = isMethodEntry;
+            /*
+             * Initialize codeAddress and allocate the slot.  Must
+             * happen in this order (since dPC is set, the entry is live.
+             */
+            android_atomic_release_store((int32_t)dPC,
+                 (volatile int32_t *)(void *)&gDvmJit.pJitEntryTable[idx].dPC);
+            /* for simulator mode, we need to initialized codeAddress to null */
+            gDvmJit.pJitEntryTable[idx].codeAddress = NULL;
+            gDvmJit.pJitEntryTable[idx].dPC = dPC;
+            gDvmJit.jitTableEntriesUsed++;
+        } else {
+            /* Table is full */
+            idx = chainEndMarker;
+        }
+        if (!callerLocked)
+            dvmUnlockMutex(&gDvmJit.tableLock);
+    }
+    return (idx == chainEndMarker) ? NULL : &gDvmJit.pJitEntryTable[idx];
+}
+
+/* Dump a trace description */
+void dvmJitDumpTraceDesc(JitTraceDescription *trace)
+{
+    int i;
+    bool done = false;
+    const u2* dpc;
+    const u2* dpcBase;
+    int curFrag = 0;
+    ALOGD("===========================================");
+    ALOGD("Trace dump %#x, Method %s off %#x",(int)trace,
+         trace->method->name,trace->trace[curFrag].info.frag.startOffset);
+    dpcBase = trace->method->insns;
+    while (!done) {
+        DecodedInstruction decInsn;
+        if (trace->trace[curFrag].isCode) {
+            ALOGD("Frag[%d]- Insts: %d, start: %#x, hint: %#x, end: %d",
+                 curFrag, trace->trace[curFrag].info.frag.numInsts,
+                 trace->trace[curFrag].info.frag.startOffset,
+                 trace->trace[curFrag].info.frag.hint,
+                 trace->trace[curFrag].info.frag.runEnd);
+            dpc = dpcBase + trace->trace[curFrag].info.frag.startOffset;
+            for (i=0; i<trace->trace[curFrag].info.frag.numInsts; i++) {
+                dexDecodeInstruction(dpc, &decInsn);
+                ALOGD("    0x%04x - %s %#x",(dpc-dpcBase),
+                     dexGetOpcodeName(decInsn.opcode),(int)dpc);
+                dpc += dexGetWidthFromOpcode(decInsn.opcode);
+            }
+            if (trace->trace[curFrag].info.frag.runEnd) {
+                done = true;
+            }
+        } else {
+            ALOGD("Frag[%d]- META info: 0x%08x", curFrag,
+                 (int)trace->trace[curFrag].info.meta);
+        }
+        curFrag++;
+    }
+    ALOGD("-------------------------------------------");
+}
+
+/*
+ * Append the class ptr of "this" and the current method ptr to the current
+ * trace. That is, the trace runs will contain the following components:
+ *  + trace run that ends with an invoke (existing entry)
+ *  + thisClass (new)
+ *  + calleeMethod (new)
+ */
+static void insertClassMethodInfo(Thread* self,
+                                  const ClassObject* thisClass,
+                                  const Method* calleeMethod,
+                                  const DecodedInstruction* insn)
+{
+    int currTraceRun = ++self->currTraceRun;
+    self->trace[currTraceRun].info.meta = thisClass ?
+                                    (void *) thisClass->descriptor : NULL;
+    self->trace[currTraceRun].isCode = false;
+
+    currTraceRun = ++self->currTraceRun;
+    self->trace[currTraceRun].info.meta = thisClass ?
+                                    (void *) thisClass->classLoader : NULL;
+    self->trace[currTraceRun].isCode = false;
+
+    currTraceRun = ++self->currTraceRun;
+    self->trace[currTraceRun].info.meta = (void *) calleeMethod;
+    self->trace[currTraceRun].isCode = false;
+}
+
+/*
+ * Check if the next instruction following the invoke is a move-result and if
+ * so add it to the trace. That is, this will add the trace run that includes
+ * the move-result to the trace list.
+ *
+ *  + trace run that ends with an invoke (existing entry)
+ *  + thisClass (existing entry)
+ *  + calleeMethod (existing entry)
+ *  + move result (new)
+ *
+ * lastPC, len, offset are all from the preceding invoke instruction
+ */
+static void insertMoveResult(const u2 *lastPC, int len, int offset,
+                             Thread *self)
+{
+    DecodedInstruction nextDecInsn;
+    const u2 *moveResultPC = lastPC + len;
+
+    dexDecodeInstruction(moveResultPC, &nextDecInsn);
+    if ((nextDecInsn.opcode != OP_MOVE_RESULT) &&
+        (nextDecInsn.opcode != OP_MOVE_RESULT_WIDE) &&
+        (nextDecInsn.opcode != OP_MOVE_RESULT_OBJECT))
+        return;
+
+    /* We need to start a new trace run */
+    int currTraceRun = ++self->currTraceRun;
+    self->currRunHead = moveResultPC;
+    self->trace[currTraceRun].info.frag.startOffset = offset + len;
+    self->trace[currTraceRun].info.frag.numInsts = 1;
+    self->trace[currTraceRun].info.frag.runEnd = false;
+    self->trace[currTraceRun].info.frag.hint = kJitHintNone;
+    self->trace[currTraceRun].isCode = true;
+    self->totalTraceLen++;
+
+    self->currRunLen = dexGetWidthFromInstruction(moveResultPC);
+}
+
+/*
+ * Adds to the current trace request one instruction at a time, just
+ * before that instruction is interpreted.  This is the primary trace
+ * selection function.  NOTE: return instruction are handled a little
+ * differently.  In general, instructions are "proposed" to be added
+ * to the current trace prior to interpretation.  If the interpreter
+ * then successfully completes the instruction, is will be considered
+ * part of the request.  This allows us to examine machine state prior
+ * to interpretation, and also abort the trace request if the instruction
+ * throws or does something unexpected.  However, return instructions
+ * will cause an immediate end to the translation request - which will
+ * be passed to the compiler before the return completes.  This is done
+ * in response to special handling of returns by the interpreter (and
+ * because returns cannot throw in a way that causes problems for the
+ * translated code.
+ */
+void dvmCheckJit(const u2* pc, Thread* self)
+{
+    const ClassObject *thisClass = self->callsiteClass;
+    const Method* curMethod = self->methodToCall;
+    int flags, len;
+    int allDone = false;
+    /* Stay in break/single-stop mode for the next instruction */
+    bool stayOneMoreInst = false;
+
+    /* Prepare to handle last PC and stage the current PC & method*/
+    const u2 *lastPC = self->lastPC;
+
+    self->lastPC = pc;
+
+    switch (self->jitState) {
+        int offset;
+        DecodedInstruction decInsn;
+        case kJitTSelect:
+            /* First instruction - just remember the PC and exit */
+            if (lastPC == NULL) break;
+            /* Grow the trace around the last PC if jitState is kJitTSelect */
+            dexDecodeInstruction(lastPC, &decInsn);
+#if TRACE_OPCODE_FILTER
+            /* Only add JIT support opcode to trace. End the trace if
+             * this opcode is not supported.
+             */
+            if (!dvmIsOpcodeSupportedByJit(decInsn.opcode)) {
+                self->jitState = kJitTSelectEnd;
+                break;
+            }
+#endif
+            /*
+             * Treat {PACKED,SPARSE}_SWITCH as trace-ending instructions due
+             * to the amount of space it takes to generate the chaining
+             * cells.
+             */
+            if (self->totalTraceLen != 0 &&
+                (decInsn.opcode == OP_PACKED_SWITCH ||
+                 decInsn.opcode == OP_SPARSE_SWITCH)) {
+                self->jitState = kJitTSelectEnd;
+                break;
+            }
+
+#if defined(SHOW_TRACE)
+            ALOGD("TraceGen: adding %s. lpc:%#x, pc:%#x",
+                 dexGetOpcodeName(decInsn.opcode), (int)lastPC, (int)pc);
+#endif
+            flags = dexGetFlagsFromOpcode(decInsn.opcode);
+            len = dexGetWidthFromInstruction(lastPC);
+            offset = lastPC - self->traceMethod->insns;
+            assert((unsigned) offset <
+                   dvmGetMethodInsnsSize(self->traceMethod));
+            if (lastPC != self->currRunHead + self->currRunLen) {
+                int currTraceRun;
+                /* We need to start a new trace run */
+                currTraceRun = ++self->currTraceRun;
+                self->currRunLen = 0;
+                self->currRunHead = (u2*)lastPC;
+                self->trace[currTraceRun].info.frag.startOffset = offset;
+                self->trace[currTraceRun].info.frag.numInsts = 0;
+                self->trace[currTraceRun].info.frag.runEnd = false;
+                self->trace[currTraceRun].info.frag.hint = kJitHintNone;
+                self->trace[currTraceRun].isCode = true;
+            }
+            self->trace[self->currTraceRun].info.frag.numInsts++;
+            self->totalTraceLen++;
+            self->currRunLen += len;
+
+            /*
+             * If the last instruction is an invoke, we will try to sneak in
+             * the move-result* (if existent) into a separate trace run.
+             */
+            {
+              int needReservedRun = (flags & kInstrInvoke) ? 1 : 0;
+
+              /* Will probably never hit this with the current trace builder */
+              if (self->currTraceRun ==
+                   (MAX_JIT_RUN_LEN - 1 - needReservedRun)) {
+                self->jitState = kJitTSelectEnd;
+              }
+            }
+
+            if (!dexIsGoto(flags) &&
+                  ((flags & (kInstrCanBranch |
+                             kInstrCanSwitch |
+                             kInstrCanReturn |
+                             kInstrInvoke)) != 0)) {
+                    self->jitState = kJitTSelectEnd;
+#if defined(SHOW_TRACE)
+                ALOGD("TraceGen: ending on %s, basic block end",
+                     dexGetOpcodeName(decInsn.opcode));
+#endif
+
+                /*
+                 * If the current invoke is a {virtual,interface}, get the
+                 * current class/method pair into the trace as well.
+                 * If the next instruction is a variant of move-result, insert
+                 * it to the trace too.
+                 */
+                if (flags & kInstrInvoke) {
+                    insertClassMethodInfo(self, thisClass, curMethod,
+                                          &decInsn);
+                    insertMoveResult(lastPC, len, offset, self);
+                }
+            }
+            /* Break on throw or self-loop */
+            if ((decInsn.opcode == OP_THROW) || (lastPC == pc)){
+                self->jitState = kJitTSelectEnd;
+            }
+            if (self->totalTraceLen >= JIT_MAX_TRACE_LEN) {
+                self->jitState = kJitTSelectEnd;
+            }
+            if ((flags & kInstrCanReturn) != kInstrCanReturn) {
+                break;
+            }
+            else {
+                /*
+                 * Last instruction is a return - stay in the dbg interpreter
+                 * for one more instruction if it is a non-void return, since
+                 * we don't want to start a trace with move-result as the first
+                 * instruction (which is already included in the trace
+                 * containing the invoke.
+                 */
+                if (decInsn.opcode != OP_RETURN_VOID) {
+                    stayOneMoreInst = true;
+                }
+            }
+            /* NOTE: intentional fallthrough for returns */
+        case kJitTSelectEnd:
+            {
+                /* Empty trace - set to bail to interpreter */
+                if (self->totalTraceLen == 0) {
+                    dvmJitSetCodeAddr(self->currTraceHead,
+                                      dvmCompilerGetInterpretTemplate(),
+                                      dvmCompilerGetInterpretTemplateSet(),
+                                      false /* Not method entry */, 0);
+                    self->jitState = kJitDone;
+                    allDone = true;
+                    break;
+                }
+
+                int lastTraceDesc = self->currTraceRun;
+
+                /* Extend a new empty desc if the last slot is meta info */
+                if (!self->trace[lastTraceDesc].isCode) {
+                    lastTraceDesc = ++self->currTraceRun;
+                    self->trace[lastTraceDesc].info.frag.startOffset = 0;
+                    self->trace[lastTraceDesc].info.frag.numInsts = 0;
+                    self->trace[lastTraceDesc].info.frag.hint = kJitHintNone;
+                    self->trace[lastTraceDesc].isCode = true;
+                }
+
+                /* Mark the end of the trace runs */
+                self->trace[lastTraceDesc].info.frag.runEnd = true;
+
+                JitTraceDescription* desc =
+                   (JitTraceDescription*)malloc(sizeof(JitTraceDescription) +
+                     sizeof(JitTraceRun) * (self->currTraceRun+1));
+
+                if (desc == NULL) {
+                    ALOGE("Out of memory in trace selection");
+                    dvmJitStopTranslationRequests();
+                    self->jitState = kJitDone;
+                    allDone = true;
+                    break;
+                }
+
+                desc->method = self->traceMethod;
+                memcpy((char*)&(desc->trace[0]),
+                    (char*)&(self->trace[0]),
+                    sizeof(JitTraceRun) * (self->currTraceRun+1));
+#if defined(SHOW_TRACE)
+                ALOGD("TraceGen:  trace done, adding to queue");
+                dvmJitDumpTraceDesc(desc);
+#endif
+                if (dvmCompilerWorkEnqueue(
+                       self->currTraceHead,kWorkOrderTrace,desc)) {
+                    /* Work order successfully enqueued */
+                    if (gDvmJit.blockingMode) {
+                        dvmCompilerDrainQueue();
+                    }
+                } else {
+                    /*
+                     * Make sure the descriptor for the abandoned work order is
+                     * freed.
+                     */
+                    free(desc);
+                }
+                self->jitState = kJitDone;
+                allDone = true;
+            }
+            break;
+        case kJitDone:
+            allDone = true;
+            break;
+        case kJitNot:
+            allDone = true;
+            break;
+        default:
+            ALOGE("Unexpected JIT state: %d", self->jitState);
+            dvmAbort();
+            break;
+    }
+
+    /*
+     * If we're done with trace selection, switch off the control flags.
+     */
+     if (allDone) {
+         dvmDisableSubMode(self, kSubModeJitTraceBuild);
+         if (stayOneMoreInst) {
+             // Clear jitResumeNPC explicitly since we know we don't need it
+             // here.
+             self->jitResumeNPC = NULL;
+             // Keep going in single-step mode for at least one more inst
+             if (self->singleStepCount == 0)
+                 self->singleStepCount = 1;
+             dvmEnableSubMode(self, kSubModeCountedStep);
+         }
+     }
+     return;
+}
+
+JitEntry *dvmJitFindEntry(const u2* pc, bool isMethodEntry)
+{
+    int idx = dvmJitHash(pc);
+
+    /* Expect a high hit rate on 1st shot */
+    if ((gDvmJit.pJitEntryTable[idx].dPC == pc) &&
+        (gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry == isMethodEntry))
+        return &gDvmJit.pJitEntryTable[idx];
+    else {
+        int chainEndMarker = gDvmJit.jitTableSize;
+        while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+            idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+            if ((gDvmJit.pJitEntryTable[idx].dPC == pc) &&
+                (gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry ==
+                isMethodEntry))
+                return &gDvmJit.pJitEntryTable[idx];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Walk through the JIT profile table and find the corresponding JIT code, in
+ * the specified format (ie trace vs method). This routine needs to be fast.
+ */
+void* getCodeAddrCommon(const u2* dPC, bool methodEntry)
+{
+    int idx = dvmJitHash(dPC);
+    const u2* pc = gDvmJit.pJitEntryTable[idx].dPC;
+    if (pc != NULL) {
+        bool hideTranslation = dvmJitHideTranslation();
+        if (pc == dPC &&
+            gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry == methodEntry) {
+            int offset = (gDvmJit.profileMode >= kTraceProfilingContinuous) ?
+                 0 : gDvmJit.pJitEntryTable[idx].u.info.profileOffset;
+            intptr_t codeAddress =
+                (intptr_t)gDvmJit.pJitEntryTable[idx].codeAddress;
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.addrLookupsFound++;
+#endif
+            return hideTranslation || !codeAddress ?  NULL :
+                  (void *)(codeAddress + offset);
+        } else {
+            int chainEndMarker = gDvmJit.jitTableSize;
+            while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+                idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+                if (gDvmJit.pJitEntryTable[idx].dPC == dPC &&
+                    gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry ==
+                        methodEntry) {
+                    int offset = (gDvmJit.profileMode >=
+                        kTraceProfilingContinuous) ? 0 :
+                        gDvmJit.pJitEntryTable[idx].u.info.profileOffset;
+                    intptr_t codeAddress =
+                        (intptr_t)gDvmJit.pJitEntryTable[idx].codeAddress;
+#if defined(WITH_JIT_TUNING)
+                    gDvmJit.addrLookupsFound++;
+#endif
+                    return hideTranslation || !codeAddress ? NULL :
+                        (void *)(codeAddress + offset);
+                }
+            }
+        }
+    }
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.addrLookupsNotFound++;
+#endif
+    return NULL;
+}
+
+/*
+ * If a translated code address, in trace format, exists for the davik byte code
+ * pointer return it.
+ */
+void* dvmJitGetTraceAddr(const u2* dPC)
+{
+    return getCodeAddrCommon(dPC, false /* method entry */);
+}
+
+/*
+ * If a translated code address, in whole-method format, exists for the davik
+ * byte code pointer return it.
+ */
+void* dvmJitGetMethodAddr(const u2* dPC)
+{
+    return getCodeAddrCommon(dPC, true /* method entry */);
+}
+
+/*
+ * Similar to dvmJitGetTraceAddr, but returns null if the calling
+ * thread is in a single-step mode.
+ */
+void* dvmJitGetTraceAddrThread(const u2* dPC, Thread* self)
+{
+    return (self->interpBreak.ctl.breakFlags != 0) ? NULL :
+            getCodeAddrCommon(dPC, false /* method entry */);
+}
+
+/*
+ * Similar to dvmJitGetMethodAddr, but returns null if the calling
+ * thread is in a single-step mode.
+ */
+void* dvmJitGetMethodAddrThread(const u2* dPC, Thread* self)
+{
+    return (self->interpBreak.ctl.breakFlags != 0) ? NULL :
+            getCodeAddrCommon(dPC, true /* method entry */);
+}
+
+/*
+ * Register the translated code pointer into the JitTable.
+ * NOTE: Once a codeAddress field transitions from initial state to
+ * JIT'd code, it must not be altered without first halting all
+ * threads.  We defer the setting of the profile prefix size until
+ * after the new code address is set to ensure that the prefix offset
+ * is never applied to the initial interpret-only translation.  All
+ * translations with non-zero profile prefixes will still be correct
+ * if entered as if the profile offset is 0, but the interpret-only
+ * template cannot handle a non-zero prefix.
+ * NOTE: JitTable must not be in danger of reset while this
+ * code is executing. see Issue 4271784 for details.
+ */
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set,
+                       bool isMethodEntry, int profilePrefixSize)
+{
+    JitEntryInfoUnion oldValue;
+    JitEntryInfoUnion newValue;
+    /*
+     * Get the JitTable slot for this dPC (or create one if JitTable
+     * has been reset between the time the trace was requested and
+     * now.
+     */
+    JitEntry *jitEntry = isMethodEntry ?
+        lookupAndAdd(dPC, false /* caller holds tableLock */, isMethodEntry) :
+                     dvmJitFindEntry(dPC, isMethodEntry);
+    assert(jitEntry);
+    /* Note: order of update is important */
+    do {
+        oldValue = jitEntry->u;
+        newValue = oldValue;
+        newValue.info.isMethodEntry = isMethodEntry;
+        newValue.info.instructionSet = set;
+        newValue.info.profileOffset = profilePrefixSize;
+    } while (android_atomic_release_cas(
+             oldValue.infoWord, newValue.infoWord,
+             &jitEntry->u.infoWord) != 0);
+    jitEntry->codeAddress = nPC;
+}
+
+/*
+ * Determine if valid trace-bulding request is active.  If so, set
+ * the proper flags in interpBreak and return.  Trace selection will
+ * then begin normally via dvmCheckBefore.
+ */
+void dvmJitCheckTraceRequest(Thread* self)
+{
+    int i;
+    /*
+     * A note on trace "hotness" filtering:
+     *
+     * Our first level trigger is intentionally loose - we need it to
+     * fire easily not just to identify potential traces to compile, but
+     * also to allow re-entry into the code cache.
+     *
+     * The 2nd level filter (done here) exists to be selective about
+     * what we actually compile.  It works by requiring the same
+     * trace head "key" (defined as filterKey below) to appear twice in
+     * a relatively short period of time.   The difficulty is defining the
+     * shape of the filterKey.  Unfortunately, there is no "one size fits
+     * all" approach.
+     *
+     * For spiky execution profiles dominated by a smallish
+     * number of very hot loops, we would want the second-level filter
+     * to be very selective.  A good selective filter is requiring an
+     * exact match of the Dalvik PC.  In other words, defining filterKey as:
+     *     intptr_t filterKey = (intptr_t)self->interpSave.pc
+     *
+     * However, for flat execution profiles we do best when aggressively
+     * translating.  A heuristically decent proxy for this is to use
+     * the value of the method pointer containing the trace as the filterKey.
+     * Intuitively, this is saying that once any trace in a method appears hot,
+     * immediately translate any other trace from that same method that
+     * survives the first-level filter.  Here, filterKey would be defined as:
+     *     intptr_t filterKey = (intptr_t)self->interpSave.method
+     *
+     * The problem is that we can't easily detect whether we're dealing
+     * with a spiky or flat profile.  If we go with the "pc" match approach,
+     * flat profiles perform poorly.  If we go with the loose "method" match,
+     * we end up generating a lot of useless translations.  Probably the
+     * best approach in the future will be to retain profile information
+     * across runs of each application in order to determine it's profile,
+     * and then choose once we have enough history.
+     *
+     * However, for now we've decided to chose a compromise filter scheme that
+     * includes elements of both.  The high order bits of the filter key
+     * are drawn from the enclosing method, and are combined with a slice
+     * of the low-order bits of the Dalvik pc of the trace head.  The
+     * looseness of the filter can be adjusted by changing with width of
+     * the Dalvik pc slice (JIT_TRACE_THRESH_FILTER_PC_BITS).  The wider
+     * the slice, the tighter the filter.
+     *
+     * Note: the fixed shifts in the function below reflect assumed word
+     * alignment for method pointers, and half-word alignment of the Dalvik pc.
+     * for method pointers and half-word alignment for dalvik pc.
+     */
+    u4 methodKey = (u4)self->interpSave.method <<
+                   (JIT_TRACE_THRESH_FILTER_PC_BITS - 2);
+    u4 pcKey = ((u4)self->interpSave.pc >> 1) &
+               ((1 << JIT_TRACE_THRESH_FILTER_PC_BITS) - 1);
+    intptr_t filterKey = (intptr_t)(methodKey | pcKey);
+
+    // Shouldn't be here if already building a trace.
+    assert((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild)==0);
+
+    /* Check if the JIT request can be handled now */
+    if ((gDvmJit.pJitEntryTable != NULL) &&
+        ((self->interpBreak.ctl.breakFlags & kInterpSingleStep) == 0)){
+        /* Bypass the filter for hot trace requests or during stress mode */
+        if (self->jitState == kJitTSelectRequest &&
+            gDvmJit.threshold > 6) {
+            /* Two-level filtering scheme */
+            for (i=0; i< JIT_TRACE_THRESH_FILTER_SIZE; i++) {
+                if (filterKey == self->threshFilter[i]) {
+                    self->threshFilter[i] = 0; // Reset filter entry
+                    break;
+                }
+            }
+            if (i == JIT_TRACE_THRESH_FILTER_SIZE) {
+                /*
+                 * Use random replacement policy - otherwise we could miss a
+                 * large loop that contains more traces than the size of our
+                 * filter array.
+                 */
+                i = rand() % JIT_TRACE_THRESH_FILTER_SIZE;
+                self->threshFilter[i] = filterKey;
+                self->jitState = kJitDone;
+            }
+        }
+
+        /* If the compiler is backlogged, cancel any JIT actions */
+        if (gDvmJit.compilerQueueLength >= gDvmJit.compilerHighWater) {
+            self->jitState = kJitDone;
+        }
+
+        /*
+         * Check for additional reasons that might force the trace select
+         * request to be dropped
+         */
+        if (self->jitState == kJitTSelectRequest ||
+            self->jitState == kJitTSelectRequestHot) {
+            if (dvmJitFindEntry(self->interpSave.pc, false)) {
+                /* In progress - nothing do do */
+               self->jitState = kJitDone;
+            } else {
+                JitEntry *slot = lookupAndAdd(self->interpSave.pc,
+                                              false /* lock */,
+                                              false /* method entry */);
+                if (slot == NULL) {
+                    /*
+                     * Table is full.  This should have been
+                     * detected by the compiler thread and the table
+                     * resized before we run into it here.  Assume bad things
+                     * are afoot and disable profiling.
+                     */
+                    self->jitState = kJitDone;
+                    ALOGD("JIT: JitTable full, disabling profiling");
+                    dvmJitStopTranslationRequests();
+                }
+            }
+        }
+
+        switch (self->jitState) {
+            case kJitTSelectRequest:
+            case kJitTSelectRequestHot:
+                self->jitState = kJitTSelect;
+                self->traceMethod = self->interpSave.method;
+                self->currTraceHead = self->interpSave.pc;
+                self->currTraceRun = 0;
+                self->totalTraceLen = 0;
+                self->currRunHead = self->interpSave.pc;
+                self->currRunLen = 0;
+                self->trace[0].info.frag.startOffset =
+                     self->interpSave.pc - self->interpSave.method->insns;
+                self->trace[0].info.frag.numInsts = 0;
+                self->trace[0].info.frag.runEnd = false;
+                self->trace[0].info.frag.hint = kJitHintNone;
+                self->trace[0].isCode = true;
+                self->lastPC = 0;
+                /* Turn on trace selection mode */
+                dvmEnableSubMode(self, kSubModeJitTraceBuild);
+#if defined(SHOW_TRACE)
+                ALOGD("Starting trace for %s at %#x",
+                     self->interpSave.method->name, (int)self->interpSave.pc);
+#endif
+                break;
+            case kJitDone:
+                break;
+            default:
+                ALOGE("Unexpected JIT state: %d", self->jitState);
+                dvmAbort();
+        }
+    } else {
+        /* Cannot build trace this time */
+        self->jitState = kJitDone;
+    }
+}
+
+/*
+ * Resizes the JitTable.  Must be a power of 2, and returns true on failure.
+ * Stops all threads, and thus is a heavyweight operation. May only be called
+ * by the compiler thread.
+ */
+bool dvmJitResizeJitTable( unsigned int size )
+{
+    JitEntry *pNewTable;
+    JitEntry *pOldTable;
+    JitEntry tempEntry;
+    unsigned int oldSize;
+    unsigned int i;
+
+    assert(gDvmJit.pJitEntryTable != NULL);
+    assert(size && !(size & (size - 1)));   /* Is power of 2? */
+
+    ALOGI("Jit: resizing JitTable from %d to %d", gDvmJit.jitTableSize, size);
+
+    if (size <= gDvmJit.jitTableSize) {
+        return true;
+    }
+
+    /* Make sure requested size is compatible with chain field width */
+    tempEntry.u.info.chain = size;
+    if (tempEntry.u.info.chain != size) {
+        ALOGD("Jit: JitTable request of %d too big", size);
+        return true;
+    }
+
+    pNewTable = (JitEntry*)calloc(size, sizeof(*pNewTable));
+    if (pNewTable == NULL) {
+        return true;
+    }
+    for (i=0; i< size; i++) {
+        pNewTable[i].u.info.chain = size;  /* Initialize chain termination */
+    }
+
+    /* Stop all other interpreting/jit'ng threads */
+    dvmSuspendAllThreads(SUSPEND_FOR_TBL_RESIZE);
+
+    pOldTable = gDvmJit.pJitEntryTable;
+    oldSize = gDvmJit.jitTableSize;
+
+    dvmLockMutex(&gDvmJit.tableLock);
+    gDvmJit.pJitEntryTable = pNewTable;
+    gDvmJit.jitTableSize = size;
+    gDvmJit.jitTableMask = size - 1;
+    gDvmJit.jitTableEntriesUsed = 0;
+
+    for (i=0; i < oldSize; i++) {
+        if (pOldTable[i].dPC) {
+            JitEntry *p;
+            u2 chain;
+            p = lookupAndAdd(pOldTable[i].dPC, true /* holds tableLock*/,
+                             pOldTable[i].u.info.isMethodEntry);
+            p->codeAddress = pOldTable[i].codeAddress;
+            /* We need to preserve the new chain field, but copy the rest */
+            chain = p->u.info.chain;
+            p->u = pOldTable[i].u;
+            p->u.info.chain = chain;
+        }
+    }
+
+    dvmUnlockMutex(&gDvmJit.tableLock);
+
+    free(pOldTable);
+
+    /* Restart the world */
+    dvmResumeAllThreads(SUSPEND_FOR_TBL_RESIZE);
+
+    return false;
+}
+
+/*
+ * Reset the JitTable to the initial clean state.
+ */
+void dvmJitResetTable()
+{
+    JitEntry *jitEntry = gDvmJit.pJitEntryTable;
+    unsigned int size = gDvmJit.jitTableSize;
+    unsigned int i;
+
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Note: If need to preserve any existing counts. Do so here. */
+    if (gDvmJit.pJitTraceProfCounters) {
+        for (i=0; i < JIT_PROF_BLOCK_BUCKETS; i++) {
+            if (gDvmJit.pJitTraceProfCounters->buckets[i])
+                memset((void *) gDvmJit.pJitTraceProfCounters->buckets[i],
+                       0, sizeof(JitTraceCounter_t) * JIT_PROF_BLOCK_ENTRIES);
+        }
+        gDvmJit.pJitTraceProfCounters->next = 0;
+    }
+
+    memset((void *) jitEntry, 0, sizeof(JitEntry) * size);
+    for (i=0; i< size; i++) {
+        jitEntry[i].u.info.chain = size;  /* Initialize chain termination */
+    }
+    gDvmJit.jitTableEntriesUsed = 0;
+    dvmUnlockMutex(&gDvmJit.tableLock);
+}
+
+/*
+ * Return the address of the next trace profile counter.  This address
+ * will be embedded in the generated code for the trace, and thus cannot
+ * change while the trace exists.
+ */
+JitTraceCounter_t *dvmJitNextTraceCounter()
+{
+    int idx = gDvmJit.pJitTraceProfCounters->next / JIT_PROF_BLOCK_ENTRIES;
+    int elem = gDvmJit.pJitTraceProfCounters->next % JIT_PROF_BLOCK_ENTRIES;
+    JitTraceCounter_t *res;
+    /* Lazily allocate blocks of counters */
+    if (!gDvmJit.pJitTraceProfCounters->buckets[idx]) {
+        JitTraceCounter_t *p =
+              (JitTraceCounter_t*) calloc(JIT_PROF_BLOCK_ENTRIES, sizeof(*p));
+        if (!p) {
+            ALOGE("Failed to allocate block of trace profile counters");
+            dvmAbort();
+        }
+        gDvmJit.pJitTraceProfCounters->buckets[idx] = p;
+    }
+    res = &gDvmJit.pJitTraceProfCounters->buckets[idx][elem];
+    gDvmJit.pJitTraceProfCounters->next++;
+    return res;
+}
+
+/*
+ * Float/double conversion requires clamping to min and max of integer form.  If
+ * target doesn't support this normally, use these.
+ */
+s8 dvmJitd2l(double d)
+{
+    static const double kMaxLong = (double)(s8)0x7fffffffffffffffULL;
+    static const double kMinLong = (double)(s8)0x8000000000000000ULL;
+    if (d >= kMaxLong)
+        return (s8)0x7fffffffffffffffULL;
+    else if (d <= kMinLong)
+        return (s8)0x8000000000000000ULL;
+    else if (d != d) // NaN case
+        return 0;
+    else
+        return (s8)d;
+}
+
+s8 dvmJitf2l(float f)
+{
+    static const float kMaxLong = (float)(s8)0x7fffffffffffffffULL;
+    static const float kMinLong = (float)(s8)0x8000000000000000ULL;
+    if (f >= kMaxLong)
+        return (s8)0x7fffffffffffffffULL;
+    else if (f <= kMinLong)
+        return (s8)0x8000000000000000ULL;
+    else if (f != f) // NaN case
+        return 0;
+    else
+        return (s8)f;
+}
+
+/* Should only be called by the compiler thread */
+void dvmJitChangeProfileMode(TraceProfilingModes newState)
+{
+    if (gDvmJit.profileMode != newState) {
+        gDvmJit.profileMode = newState;
+        dvmJitUnchainAll();
+    }
+}
+
+void dvmJitTraceProfilingOn()
+{
+    if (gDvmJit.profileMode == kTraceProfilingPeriodicOff)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingPeriodicOn);
+    else if (gDvmJit.profileMode == kTraceProfilingDisabled)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingContinuous);
+}
+
+void dvmJitTraceProfilingOff()
+{
+    if (gDvmJit.profileMode == kTraceProfilingPeriodicOn)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingPeriodicOff);
+    else if (gDvmJit.profileMode == kTraceProfilingContinuous)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingDisabled);
+}
+
+/*
+ * Update JIT-specific info in Thread structure for a single thread
+ */
+void dvmJitUpdateThreadStateSingle(Thread* thread)
+{
+    thread->pJitProfTable = gDvmJit.pProfTable;
+    thread->jitThreshold = gDvmJit.threshold;
+}
+
+/*
+ * Walk through the thread list and refresh all local copies of
+ * JIT global state (which was placed there for fast access).
+ */
+void dvmJitUpdateThreadStateAll()
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    dvmLockThreadList(self);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        dvmJitUpdateThreadStateSingle(thread);
+    }
+    dvmUnlockThreadList();
+
+}
+#endif /* WITH_JIT */
diff --git a/vm/interp/Jit.h b/vm/interp/Jit.h
new file mode 100644
index 0000000..962040b
--- /dev/null
+++ b/vm/interp/Jit.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Jit control
+ */
+#ifndef DALVIK_INTERP_JIT_H_
+#define DALVIK_INTERP_JIT_H_
+
+#include "InterpDefs.h"
+#include "mterp/common/jit-config.h"
+
+#define JIT_MAX_TRACE_LEN 100
+
+#if defined (WITH_SELF_VERIFICATION)
+
+#define REG_SPACE 256                /* default size of shadow space */
+#define HEAP_SPACE JIT_MAX_TRACE_LEN /* default size of heap space */
+
+struct ShadowHeap {
+    int addr;
+    int data;
+};
+
+struct InstructionTrace {
+    int addr;
+    DecodedInstruction decInsn;
+};
+
+struct ShadowSpace {
+    const u2* startPC;          /* starting pc of jitted region */
+    u4* fp;                     /* starting fp of jitted region */
+    const Method *method;
+    DvmDex* methodClassDex;
+    JValue retval;
+    const u1* interpStackEnd;
+    SelfVerificationState jitExitState;  /* exit point for JIT'ed code */
+    SelfVerificationState selfVerificationState;  /* current SV running state */
+    const u2* endPC;            /* ending pc of jitted region */
+    void* shadowFP;       /* pointer to fp in shadow space */
+    int* registerSpace;         /* copy of register state */
+    int registerSpaceSize;      /* current size of register space */
+    ShadowHeap heapSpace[HEAP_SPACE]; /* copy of heap space */
+    ShadowHeap* heapSpaceTail;        /* tail pointer to heapSpace */
+    const void* endShadowFP;    /* ending fp in shadow space */
+    InstructionTrace trace[JIT_MAX_TRACE_LEN]; /* opcode trace for debugging */
+    int traceLength;            /* counter for current trace length */
+};
+
+/*
+ * Self verification functions.
+ */
+extern "C" {
+void* dvmSelfVerificationShadowSpaceAlloc(Thread* self);
+void dvmSelfVerificationShadowSpaceFree(Thread* self);
+void* dvmSelfVerificationSaveState(const u2* pc, u4* fp,
+                                   Thread* self,
+                                   int targetTrace);
+void* dvmSelfVerificationRestoreState(const u2* pc, u4* fp,
+                                      SelfVerificationState exitPoint,
+                                      Thread *self);
+void dvmCheckSelfVerification(const u2* pc, Thread* self);
+}
+#endif
+
+/*
+ * Offsets for metadata in the trace run array from the trace that ends with
+ * invoke instructions.
+ */
+#define JIT_TRACE_CLASS_DESC    1
+#define JIT_TRACE_CLASS_LOADER  2
+#define JIT_TRACE_CUR_METHOD    3
+
+/*
+ * JitTable hash function.
+ */
+
+static inline u4 dvmJitHashMask( const u2* p, u4 mask ) {
+    return ((((u4)p>>12)^(u4)p)>>1) & (mask);
+}
+
+static inline u4 dvmJitHash( const u2* p ) {
+    return dvmJitHashMask( p, gDvmJit.jitTableMask );
+}
+
+/*
+ * The width of the chain field in JitEntryInfo sets the upper
+ * bound on the number of translations.  Be careful if changing
+ * the size of JitEntry struct - the Dalvik PC to JitEntry
+ * hash functions have built-in knowledge of the size.
+ */
+#define JIT_ENTRY_CHAIN_WIDTH 2
+#define JIT_MAX_ENTRIES (1 << (JIT_ENTRY_CHAIN_WIDTH * 8))
+
+/*
+ * The trace profiling counters are allocated in blocks and individual
+ * counters must not move so long as any referencing trace exists.
+ */
+#define JIT_PROF_BLOCK_ENTRIES 1024
+#define JIT_PROF_BLOCK_BUCKETS (JIT_MAX_ENTRIES / JIT_PROF_BLOCK_ENTRIES)
+
+typedef s4 JitTraceCounter_t;
+
+struct JitTraceProfCounters {
+    unsigned int           next;
+    JitTraceCounter_t      *buckets[JIT_PROF_BLOCK_BUCKETS];
+};
+
+/*
+ * Entries in the JIT's address lookup hash table.
+ * Fields which may be updated by multiple threads packed into a
+ * single 32-bit word to allow use of atomic update.
+ */
+
+struct JitEntryInfo {
+    unsigned int           isMethodEntry:1;
+    unsigned int           inlineCandidate:1;
+    unsigned int           profileEnabled:1;
+    JitInstructionSetType  instructionSet:3;
+    unsigned int           profileOffset:5;
+    unsigned int           unused:5;
+    u2                     chain;                 /* Index of next in chain */
+};
+
+union JitEntryInfoUnion {
+    JitEntryInfo info;
+    volatile int infoWord;
+};
+
+struct JitEntry {
+    JitEntryInfoUnion   u;
+    const u2*           dPC;            /* Dalvik code address */
+    void*               codeAddress;    /* Code address of native translation */
+};
+
+extern "C" {
+void dvmCheckJit(const u2* pc, Thread* self);
+void* dvmJitGetTraceAddr(const u2* dPC);
+void* dvmJitGetMethodAddr(const u2* dPC);
+void* dvmJitGetTraceAddrThread(const u2* dPC, Thread* self);
+void* dvmJitGetMethodAddrThread(const u2* dPC, Thread* self);
+void dvmJitCheckTraceRequest(Thread* self);
+void dvmJitStopTranslationRequests(void);
+#if defined(WITH_JIT_TUNING)
+void dvmBumpNoChain(int from);
+void dvmBumpNormal(void);
+void dvmBumpPunt(int from);
+#endif
+void dvmJitStats(void);
+bool dvmJitResizeJitTable(unsigned int size);
+void dvmJitResetTable(void);
+JitEntry *dvmJitFindEntry(const u2* pc, bool isMethodEntry);
+s8 dvmJitd2l(double d);
+s8 dvmJitf2l(float f);
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set,
+                       bool isMethodEntry, int profilePrefixSize);
+void dvmJitEndTraceSelect(Thread* self, const u2* dPC);
+JitTraceCounter_t *dvmJitNextTraceCounter(void);
+void dvmJitTraceProfilingOff(void);
+void dvmJitTraceProfilingOn(void);
+void dvmJitChangeProfileMode(TraceProfilingModes newState);
+void dvmJitDumpTraceDesc(JitTraceDescription *trace);
+void dvmJitUpdateThreadStateSingle(Thread* threead);
+void dvmJitUpdateThreadStateAll(void);
+void dvmJitResumeTranslation(Thread* self, const u2* pc, const u4* fp);
+}
+
+#endif  // DALVIK_INTERP_JIT_H_
diff --git a/vm/interp/README.txt b/vm/interp/README.txt
new file mode 100644
index 0000000..170d2b1
--- /dev/null
+++ b/vm/interp/README.txt
@@ -0,0 +1,3 @@
+Dalvik interpreter entry point.
+
+The "mterp" directory now holds the interpreter implementation.
diff --git a/vm/interp/Stack.cpp b/vm/interp/Stack.cpp
new file mode 100644
index 0000000..9ef92c7
--- /dev/null
+++ b/vm/interp/Stack.cpp
@@ -0,0 +1,1408 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Stacks and their uses (e.g. native --> interpreted method calls).
+ *
+ * See the majestic ASCII art in Stack.h.
+ */
+#include "Dalvik.h"
+#include "jni.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <corkscrew/backtrace.h>
+#endif
+
+/*
+ * Initialize the interpreter stack in a new thread.
+ *
+ * Currently this doesn't do much, since we don't need to zero out the
+ * stack (and we really don't want to if it was created with mmap).
+ */
+bool dvmInitInterpStack(Thread* thread, int stackSize)
+{
+    assert(thread->interpStackStart != NULL);
+
+    assert(thread->interpSave.curFrame == NULL);
+
+    return true;
+}
+
+/*
+ * We're calling an interpreted method from an internal VM function or
+ * via reflection.
+ *
+ * Push a frame for an interpreted method onto the stack.  This is only
+ * used when calling into interpreted code from native code.  (The
+ * interpreter does its own stack frame manipulation for interp-->interp
+ * calls.)
+ *
+ * The size we need to reserve is the sum of parameters, local variables,
+ * saved goodies, and outbound parameters.
+ *
+ * We start by inserting a "break" frame, which ensures that the interpreter
+ * hands control back to us after the function we call returns or an
+ * uncaught exception is thrown.
+ */
+static bool dvmPushInterpFrame(Thread* self, const Method* method)
+{
+    StackSaveArea* saveBlock;
+    StackSaveArea* breakSaveBlock;
+    int stackReq;
+    u1* stackPtr;
+
+    assert(!dvmIsNativeMethod(method));
+    assert(!dvmIsAbstractMethod(method));
+
+    stackReq = method->registersSize * 4        // params + locals
+                + sizeof(StackSaveArea) * 2     // break frame + regular frame
+                + method->outsSize * 4;         // args to other methods
+
+    if (self->interpSave.curFrame != NULL)
+        stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    else
+        stackPtr = self->interpStackStart;
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space */
+        ALOGW("Stack overflow on call to interp "
+             "(req=%d top=%p cur=%p size=%d %s.%s)",
+            stackReq, self->interpStackStart, self->interpSave.curFrame,
+            self->interpStackSize, method->clazz->descriptor, method->name);
+        dvmHandleStackOverflow(self, method);
+        assert(dvmCheckException(self));
+        return false;
+    }
+
+    /*
+     * Shift the stack pointer down, leaving space for the function's
+     * args/registers and save area.
+     */
+    stackPtr -= sizeof(StackSaveArea);
+    breakSaveBlock = (StackSaveArea*)stackPtr;
+    stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea);
+    saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+    /* debug -- memset the new stack, unless we want valgrind's help */
+    memset(stackPtr - (method->outsSize*4), 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+    breakSaveBlock->prevSave =
+       (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame);
+    saveBlock->prevSave = breakSaveBlock;
+#endif
+
+    breakSaveBlock->prevFrame = self->interpSave.curFrame;
+    breakSaveBlock->savedPc = NULL;             // not required
+    breakSaveBlock->xtra.localRefCookie = 0;    // not required
+    breakSaveBlock->method = NULL;
+    saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
+    saveBlock->savedPc = NULL;                  // not required
+    saveBlock->xtra.currentPc = NULL;           // not required?
+    saveBlock->method = method;
+
+    LOGVV("PUSH frame: old=%p new=%p (size=%d)",
+        self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+    return true;
+}
+
+/*
+ * We're calling a JNI native method from an internal VM fuction or
+ * via reflection.  This is also used to create the "fake" native-method
+ * frames at the top of the interpreted stack.
+ *
+ * This actually pushes two frames; the first is a "break" frame.
+ *
+ * The top frame has additional space for JNI local reference tracking.
+ */
+bool dvmPushJNIFrame(Thread* self, const Method* method)
+{
+    StackSaveArea* saveBlock;
+    StackSaveArea* breakSaveBlock;
+    int stackReq;
+    u1* stackPtr;
+
+    assert(dvmIsNativeMethod(method));
+
+    stackReq = method->registersSize * 4        // params only
+                + sizeof(StackSaveArea) * 2;    // break frame + regular frame
+
+    if (self->interpSave.curFrame != NULL)
+        stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    else
+        stackPtr = self->interpStackStart;
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space */
+        ALOGW("Stack overflow on call to native "
+             "(req=%d top=%p cur=%p size=%d '%s')",
+            stackReq, self->interpStackStart, self->interpSave.curFrame,
+            self->interpStackSize, method->name);
+        dvmHandleStackOverflow(self, method);
+        assert(dvmCheckException(self));
+        return false;
+    }
+
+    /*
+     * Shift the stack pointer down, leaving space for just the stack save
+     * area for the break frame, then shift down farther for the full frame.
+     * We leave space for the method args, which are copied in later.
+     */
+    stackPtr -= sizeof(StackSaveArea);
+    breakSaveBlock = (StackSaveArea*)stackPtr;
+    stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea);
+    saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+    /* debug -- memset the new stack */
+    memset(stackPtr, 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+    if (self->interpSave.curFrame == NULL)
+        breakSaveBlock->prevSave = NULL;
+    else {
+        void* fp = FP_FROM_SAVEAREA(self->interpSave.curFrame);
+        breakSaveBlock->prevSave = (StackSaveArea*)fp;
+    }
+    saveBlock->prevSave = breakSaveBlock;
+#endif
+
+    breakSaveBlock->prevFrame = self->interpSave.curFrame;
+    breakSaveBlock->savedPc = NULL;             // not required
+    breakSaveBlock->xtra.localRefCookie = 0;    // not required
+    breakSaveBlock->method = NULL;
+    saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
+    saveBlock->savedPc = NULL;                  // not required
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+    saveBlock->method = method;
+
+    LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)",
+        self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+    return true;
+}
+
+/*
+ * This is used by the JNI PushLocalFrame call.  We push a new frame onto
+ * the stack that has no ins, outs, or locals, and no break frame above it.
+ * It's strictly used for tracking JNI local refs, and will be popped off
+ * by dvmPopFrame if it's not removed explicitly.
+ */
+bool dvmPushLocalFrame(Thread* self, const Method* method)
+{
+    StackSaveArea* saveBlock;
+    int stackReq;
+    u1* stackPtr;
+
+    assert(dvmIsNativeMethod(method));
+
+    stackReq = sizeof(StackSaveArea);       // regular frame
+
+    assert(self->interpSave.curFrame != NULL);
+    stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame);
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space; let JNI throw the exception */
+        ALOGW("Stack overflow on PushLocal "
+             "(req=%d top=%p cur=%p size=%d '%s')",
+            stackReq, self->interpStackStart, self->interpSave.curFrame,
+            self->interpStackSize, method->name);
+        dvmHandleStackOverflow(self, method);
+        assert(dvmCheckException(self));
+        return false;
+    }
+
+    /*
+     * Shift the stack pointer down, leaving space for just the stack save
+     * area for the break frame, then shift down farther for the full frame.
+     */
+    stackPtr -= sizeof(StackSaveArea);
+    saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+    /* debug -- memset the new stack */
+    memset(stackPtr, 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+    saveBlock->prevSave =
+        (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame);
+#endif
+
+    saveBlock->prevFrame = self->interpSave.curFrame;
+    saveBlock->savedPc = NULL;                  // not required
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+    saveBlock->method = method;
+
+    LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)",
+        self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+    return true;
+}
+
+/*
+ * Pop one frame pushed on by JNI PushLocalFrame.
+ *
+ * If we've gone too far, the previous frame is either a break frame or
+ * an interpreted frame.  Either way, the method pointer won't match.
+ */
+bool dvmPopLocalFrame(Thread* self)
+{
+    StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+
+    assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));
+    if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) {
+        /*
+         * The previous frame doesn't have the same method pointer -- we've
+         * been asked to pop too much.
+         */
+        assert(dvmIsBreakFrame((u4*)saveBlock->prevFrame) ||
+               !dvmIsNativeMethod(
+                       SAVEAREA_FROM_FP(saveBlock->prevFrame)->method));
+        return false;
+    }
+
+    LOGVV("POP JNI local frame: removing %s, now %s",
+        saveBlock->method->name,
+        SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name);
+    dvmPopJniLocals(self, saveBlock);
+    self->interpSave.curFrame = saveBlock->prevFrame;
+
+    return true;
+}
+
+/*
+ * Pop a frame we added.  There should be one method frame and one break
+ * frame.
+ *
+ * If JNI Push/PopLocalFrame calls were mismatched, we might end up
+ * popping multiple method frames before we find the break.
+ *
+ * Returns "false" if there was no frame to pop.
+ */
+static bool dvmPopFrame(Thread* self)
+{
+    StackSaveArea* saveBlock;
+
+    if (self->interpSave.curFrame == NULL)
+        return false;
+
+    saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));
+
+    /*
+     * Remove everything up to the break frame.  If this was a call into
+     * native code, pop the JNI local references table.
+     */
+    while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) {
+        /* probably a native->native JNI call */
+
+        if (dvmIsNativeMethod(saveBlock->method)) {
+            LOGVV("Popping JNI stack frame for %s.%s%s",
+                saveBlock->method->clazz->descriptor,
+                saveBlock->method->name,
+                (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ?
+                "" : " (JNI local)");
+            dvmPopJniLocals(self, saveBlock);
+        }
+
+        saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame);
+    }
+    if (saveBlock->method != NULL) {
+        ALOGE("PopFrame missed the break");
+        assert(false);
+        dvmAbort();     // stack trashed -- nowhere to go in this thread
+    }
+
+    LOGVV("POP frame: cur=%p new=%p",
+        self->interpSave.curFrame, saveBlock->prevFrame);
+
+    self->interpSave.curFrame = saveBlock->prevFrame;
+    return true;
+}
+
+/*
+ * Common code for dvmCallMethodV/A and dvmInvokeMethod.
+ *
+ * Pushes a call frame on, advancing self->interpSave.curFrame.
+ */
+static ClassObject* callPrep(Thread* self, const Method* method, Object* obj,
+    bool checkAccess)
+{
+    ClassObject* clazz;
+
+#ifndef NDEBUG
+    if (self->status != THREAD_RUNNING) {
+        ALOGW("threadid=%d: status=%d on call to %s.%s -",
+            self->threadId, self->status,
+            method->clazz->descriptor, method->name);
+    }
+#endif
+
+    assert(self != NULL);
+    assert(method != NULL);
+
+    if (obj != NULL)
+        clazz = obj->clazz;
+    else
+        clazz = method->clazz;
+
+    IF_LOGVV() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        LOGVV("thread=%d native code calling %s.%s %s", self->threadId,
+            clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    if (checkAccess) {
+        /* needed for java.lang.reflect.Method.invoke */
+        if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->interpSave.curFrame),
+                method))
+        {
+            /* note this throws IAException, not IAError */
+            dvmThrowIllegalAccessException("access to method denied");
+            return NULL;
+        }
+    }
+
+    /*
+     * Push a call frame on.  If there isn't enough room for ins, locals,
+     * outs, and the saved state, it will throw an exception.
+     *
+     * This updates self->interpSave.curFrame.
+     */
+    if (dvmIsNativeMethod(method)) {
+        /* native code calling native code the hard way */
+        if (!dvmPushJNIFrame(self, method)) {
+            assert(dvmCheckException(self));
+            return NULL;
+        }
+    } else {
+        /* native code calling interpreted code */
+        if (!dvmPushInterpFrame(self, method)) {
+            assert(dvmCheckException(self));
+            return NULL;
+        }
+    }
+
+    return clazz;
+}
+
+/*
+ * Issue a method call.
+ *
+ * Pass in NULL for "obj" on calls to static methods.
+ *
+ * (Note this can't be inlined because it takes a variable number of args.)
+ */
+void dvmCallMethod(Thread* self, const Method* method, Object* obj,
+    JValue* pResult, ...)
+{
+    va_list args;
+    va_start(args, pResult);
+    dvmCallMethodV(self, method, obj, false, pResult, args);
+    va_end(args);
+}
+
+/*
+ * Issue a method call with a variable number of arguments.  We process
+ * the contents of "args" by scanning the method signature.
+ *
+ * Pass in NULL for "obj" on calls to static methods.
+ *
+ * We don't need to take the class as an argument because, in Dalvik,
+ * we don't need to worry about static synchronized methods.
+ */
+void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, va_list args)
+{
+    const char* desc = &(method->shorty[1]); // [0] is the return type.
+    int verifyCount = 0;
+    ClassObject* clazz;
+    u4* ins;
+
+    clazz = callPrep(self, method, obj, false);
+    if (clazz == NULL)
+        return;
+
+    /* "ins" for new frame start at frame pointer plus locals */
+    ins = ((u4*)self->interpSave.curFrame) +
+           (method->registersSize - method->insSize);
+
+    //ALOGD("  FP is %p, INs live at >= %p", self->interpSave.curFrame, ins);
+
+    /* put "this" pointer into in0 if appropriate */
+    if (!dvmIsStaticMethod(method)) {
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+        assert(obj != NULL && dvmIsHeapAddress(obj));
+#endif
+        *ins++ = (u4) obj;
+        verifyCount++;
+    }
+
+    while (*desc != '\0') {
+        switch (*(desc++)) {
+            case 'D': case 'J': {
+                u8 val = va_arg(args, u8);
+                memcpy(ins, &val, 8);       // EABI prevents direct store
+                ins += 2;
+                verifyCount += 2;
+                break;
+            }
+            case 'F': {
+                /* floats were normalized to doubles; convert back */
+                float f = (float) va_arg(args, double);
+                *ins++ = dvmFloatToU4(f);
+                verifyCount++;
+                break;
+            }
+            case 'L': {     /* 'shorty' descr uses L for all refs, incl array */
+                void* arg = va_arg(args, void*);
+                assert(obj == NULL || dvmIsHeapAddress(obj));
+                jobject argObj = reinterpret_cast<jobject>(arg);
+                if (fromJni)
+                    *ins++ = (u4) dvmDecodeIndirectRef(self, argObj);
+                else
+                    *ins++ = (u4) argObj;
+                verifyCount++;
+                break;
+            }
+            default: {
+                /* Z B C S I -- all passed as 32-bit integers */
+                *ins++ = va_arg(args, u4);
+                verifyCount++;
+                break;
+            }
+        }
+    }
+
+#ifndef NDEBUG
+    if (verifyCount != method->insSize) {
+        ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,
+            method->insSize, clazz->descriptor, method->name);
+        assert(false);
+        goto bail;
+    }
+#endif
+
+    //dvmDumpThreadStack(dvmThreadSelf());
+
+    if (dvmIsNativeMethod(method)) {
+        TRACE_METHOD_ENTER(self, method);
+        /*
+         * Because we leave no space for local variables, "curFrame" points
+         * directly at the method arguments.
+         */
+        (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,
+                              method, self);
+        TRACE_METHOD_EXIT(self, method);
+    } else {
+        dvmInterpret(self, method, pResult);
+    }
+
+#ifndef NDEBUG
+bail:
+#endif
+    dvmPopFrame(self);
+}
+
+/*
+ * Issue a method call with arguments provided in an array.  We process
+ * the contents of "args" by scanning the method signature.
+ *
+ * The values were likely placed into an uninitialized jvalue array using
+ * the field specifiers, which means that sub-32-bit fields (e.g. short,
+ * boolean) may not have 32 or 64 bits of valid data.  This is different
+ * from the varargs invocation where the C compiler does a widening
+ * conversion when calling a function.  As a result, we have to be a
+ * little more precise when pulling stuff out.
+ *
+ * "args" may be NULL if the method has no arguments.
+ */
+void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, const jvalue* args)
+{
+    const char* desc = &(method->shorty[1]); // [0] is the return type.
+    int verifyCount = 0;
+    ClassObject* clazz;
+    u4* ins;
+
+    clazz = callPrep(self, method, obj, false);
+    if (clazz == NULL)
+        return;
+
+    /* "ins" for new frame start at frame pointer plus locals */
+    ins = ((u4*)self->interpSave.curFrame) +
+        (method->registersSize - method->insSize);
+
+    /* put "this" pointer into in0 if appropriate */
+    if (!dvmIsStaticMethod(method)) {
+        assert(obj != NULL);
+        *ins++ = (u4) obj;              /* obj is a "real" ref */
+        verifyCount++;
+    }
+
+    while (*desc != '\0') {
+        switch (*desc++) {
+        case 'D':                       /* 64-bit quantity; have to use */
+        case 'J':                       /*  memcpy() in case of mis-alignment */
+            memcpy(ins, &args->j, 8);
+            ins += 2;
+            verifyCount++;              /* this needs an extra push */
+            break;
+        case 'L':                       /* includes array refs */
+            if (fromJni)
+                *ins++ = (u4) dvmDecodeIndirectRef(self, args->l);
+            else
+                *ins++ = (u4) args->l;
+            break;
+        case 'F':
+        case 'I':
+            *ins++ = args->i;           /* full 32 bits */
+            break;
+        case 'S':
+            *ins++ = args->s;           /* 16 bits, sign-extended */
+            break;
+        case 'C':
+            *ins++ = args->c;           /* 16 bits, unsigned */
+            break;
+        case 'B':
+            *ins++ = args->b;           /* 8 bits, sign-extended */
+            break;
+        case 'Z':
+            *ins++ = args->z;           /* 8 bits, zero or non-zero */
+            break;
+        default:
+            ALOGE("Invalid char %c in short signature of %s.%s",
+                *(desc-1), clazz->descriptor, method->name);
+            assert(false);
+            goto bail;
+        }
+
+        verifyCount++;
+        args++;
+    }
+
+#ifndef NDEBUG
+    if (verifyCount != method->insSize) {
+        ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,
+            method->insSize, clazz->descriptor, method->name);
+        assert(false);
+        goto bail;
+    }
+#endif
+
+    if (dvmIsNativeMethod(method)) {
+        TRACE_METHOD_ENTER(self, method);
+        /*
+         * Because we leave no space for local variables, "curFrame" points
+         * directly at the method arguments.
+         */
+        (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,
+                              method, self);
+        TRACE_METHOD_EXIT(self, method);
+    } else {
+        dvmInterpret(self, method, pResult);
+    }
+
+bail:
+    dvmPopFrame(self);
+}
+
+static void throwArgumentTypeMismatch(int argIndex, ClassObject* expected, DataObject* arg) {
+    std::string expectedClassName(dvmHumanReadableDescriptor(expected->descriptor));
+    std::string actualClassName = dvmHumanReadableType(arg);
+    dvmThrowExceptionFmt(gDvm.exIllegalArgumentException, "argument %d should have type %s, got %s",
+            argIndex + 1, expectedClassName.c_str(), actualClassName.c_str());
+}
+
+/*
+ * Invoke a method, using the specified arguments and return type, through
+ * one of the reflection interfaces.  Could be a virtual or direct method
+ * (including constructors).  Used for reflection.
+ *
+ * Deals with boxing/unboxing primitives and performs widening conversions.
+ *
+ * "invokeObj" will be null for a static method.
+ *
+ * If the invocation returns with an exception raised, we have to wrap it.
+ */
+Object* dvmInvokeMethod(Object* obj, const Method* method,
+    ArrayObject* argList, ArrayObject* params, ClassObject* returnType,
+    bool noAccessCheck)
+{
+    ClassObject* clazz;
+    Object* retObj = NULL;
+    Thread* self = dvmThreadSelf();
+    s4* ins;
+    int verifyCount, argListLength;
+    JValue retval;
+    bool needPop = false;
+
+    /* verify arg count */
+    if (argList != NULL)
+        argListLength = argList->length;
+    else
+        argListLength = 0;
+    if (argListLength != (int) params->length) {
+        dvmThrowExceptionFmt(gDvm.exIllegalArgumentException,
+            "wrong number of arguments; expected %d, got %d",
+            params->length, argListLength);
+        return NULL;
+    }
+
+    clazz = callPrep(self, method, obj, !noAccessCheck);
+    if (clazz == NULL)
+        return NULL;
+    needPop = true;
+
+    /* "ins" for new frame start at frame pointer plus locals */
+    ins = ((s4*)self->interpSave.curFrame) +
+        (method->registersSize - method->insSize);
+    verifyCount = 0;
+
+    //ALOGD("  FP is %p, INs live at >= %p", self->interpSave.curFrame, ins);
+
+    /* put "this" pointer into in0 if appropriate */
+    if (!dvmIsStaticMethod(method)) {
+        assert(obj != NULL);
+        *ins++ = (s4) obj;
+        verifyCount++;
+    }
+
+    /*
+     * Copy the args onto the stack.  Primitive types are converted when
+     * necessary, and object types are verified.
+     */
+    DataObject** args = (DataObject**)(void*)argList->contents;
+    ClassObject** types = (ClassObject**)(void*)params->contents;
+    for (int i = 0; i < argListLength; i++) {
+        int width = dvmConvertArgument(*args++, *types++, ins);
+        if (width < 0) {
+            dvmPopFrame(self);      // throw wants to pull PC out of stack
+            needPop = false;
+            throwArgumentTypeMismatch(i, *(types-1), *(args-1));
+            goto bail;
+        }
+
+        ins += width;
+        verifyCount += width;
+    }
+
+#ifndef NDEBUG
+    if (verifyCount != method->insSize) {
+        ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,
+            method->insSize, clazz->descriptor, method->name);
+        assert(false);
+        goto bail;
+    }
+#endif
+
+    if (dvmIsNativeMethod(method)) {
+        TRACE_METHOD_ENTER(self, method);
+        /*
+         * Because we leave no space for local variables, "curFrame" points
+         * directly at the method arguments.
+         */
+        (*method->nativeFunc)((u4*)self->interpSave.curFrame, &retval,
+                              method, self);
+        TRACE_METHOD_EXIT(self, method);
+    } else {
+        dvmInterpret(self, method, &retval);
+    }
+
+    /*
+     * Pop the frame immediately.  The "wrap" calls below can cause
+     * allocations, and we don't want the GC to walk the now-dead frame.
+     */
+    dvmPopFrame(self);
+    needPop = false;
+
+    /*
+     * If an exception is raised, wrap and replace.  This is necessary
+     * because the invoked method could have thrown a checked exception
+     * that the caller wasn't prepared for.
+     *
+     * We might be able to do this up in the interpreted code, but that will
+     * leave us with a shortened stack trace in the top-level exception.
+     */
+    if (dvmCheckException(self)) {
+        dvmWrapException("Ljava/lang/reflect/InvocationTargetException;");
+    } else {
+        /*
+         * If this isn't a void method or constructor, convert the return type
+         * to an appropriate object.
+         *
+         * We don't do this when an exception is raised because the value
+         * in "retval" is undefined.
+         */
+        if (returnType != NULL) {
+            retObj = (Object*)dvmBoxPrimitive(retval, returnType);
+            dvmReleaseTrackedAlloc(retObj, NULL);
+        }
+    }
+
+bail:
+    if (needPop) {
+        dvmPopFrame(self);
+    }
+    return retObj;
+}
+
+struct LineNumFromPcContext {
+    u4 address;
+    u4 lineNum;
+};
+
+static int lineNumForPcCb(void *cnxt, u4 address, u4 lineNum)
+{
+    LineNumFromPcContext *pContext = (LineNumFromPcContext *)cnxt;
+
+    // We know that this callback will be called in
+    // ascending address order, so keep going until we find
+    // a match or we've just gone past it.
+
+    if (address > pContext->address) {
+        // The line number from the previous positions callback
+        // wil be the final result.
+        return 1;
+    }
+
+    pContext->lineNum = lineNum;
+
+    return (address == pContext->address) ? 1 : 0;
+}
+
+/*
+ * Determine the source file line number based on the program counter.
+ * "pc" is an offset, in 16-bit units, from the start of the method's code.
+ *
+ * Returns -1 if no match was found (possibly because the source files were
+ * compiled without "-g", so no line number information is present).
+ * Returns -2 for native methods (as expected in exception traces).
+ */
+int dvmLineNumFromPC(const Method* method, u4 relPc)
+{
+    const DexCode* pDexCode = dvmGetMethodCode(method);
+
+    if (pDexCode == NULL) {
+        if (dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method))
+            return -2;
+        return -1;      /* can happen for abstract method stub */
+    }
+
+    LineNumFromPcContext context;
+    memset(&context, 0, sizeof(context));
+    context.address = relPc;
+    // A method with no line number info should return -1
+    context.lineNum = -1;
+
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, pDexCode,
+            method->clazz->descriptor,
+            method->prototype.protoIdx,
+            method->accessFlags,
+            lineNumForPcCb, NULL, &context);
+
+    return context.lineNum;
+}
+
+/*
+ * Compute the frame depth.
+ *
+ * Excludes "break" frames.
+ */
+int dvmComputeExactFrameDepth(const void* fp)
+{
+    int count = 0;
+
+    for ( ; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
+        if (!dvmIsBreakFrame((u4*)fp))
+            count++;
+    }
+
+    return count;
+}
+
+/*
+ * Compute the "vague" frame depth, which is just a pointer subtraction.
+ * The result is NOT an overly generous assessment of the number of
+ * frames; the only meaningful use is to compare against the result of
+ * an earlier invocation.
+ *
+ * Useful for implementing single-step debugger modes, which may need to
+ * call this for every instruction.
+ */
+int dvmComputeVagueFrameDepth(Thread* thread, const void* fp)
+{
+    const u1* interpStackStart = thread->interpStackStart;
+
+    assert((u1*) fp >= interpStackStart - thread->interpStackSize);
+    assert((u1*) fp < interpStackStart);
+    return interpStackStart - (u1*) fp;
+}
+
+/*
+ * Get the calling frame.  Pass in the current fp.
+ *
+ * Skip "break" frames and reflection invoke frames.
+ */
+void* dvmGetCallerFP(const void* curFrame)
+{
+    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+    StackSaveArea* saveArea;
+
+retry:
+    if (dvmIsBreakFrame((u4*)caller)) {
+        /* pop up one more */
+        caller = SAVEAREA_FROM_FP(caller)->prevFrame;
+        if (caller == NULL)
+            return NULL;        /* hit the top */
+
+        /*
+         * If we got here by java.lang.reflect.Method.invoke(), we don't
+         * want to return Method's class loader.  Shift up one and try
+         * again.
+         */
+        saveArea = SAVEAREA_FROM_FP(caller);
+        if (dvmIsReflectionMethod(saveArea->method)) {
+            caller = saveArea->prevFrame;
+            assert(caller != NULL);
+            goto retry;
+        }
+    }
+
+    return caller;
+}
+
+/*
+ * Get the caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class.
+ */
+ClassObject* dvmGetCallerClass(const void* curFrame)
+{
+    void* caller;
+
+    caller = dvmGetCallerFP(curFrame);
+    if (caller == NULL)
+        return NULL;
+
+    return SAVEAREA_FROM_FP(caller)->method->clazz;
+}
+
+/*
+ * Get the caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller2Class(const void* curFrame)
+{
+    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+    void* callerCaller;
+
+    /* at the top? */
+    if (dvmIsBreakFrame((u4*)caller) &&
+        SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+        return NULL;
+
+    /* go one more */
+    callerCaller = dvmGetCallerFP(caller);
+    if (callerCaller == NULL)
+        return NULL;
+
+    return SAVEAREA_FROM_FP(callerCaller)->method->clazz;
+}
+
+/*
+ * Get the caller's caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller3Class(const void* curFrame)
+{
+    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+    int i;
+
+    /* at the top? */
+    if (dvmIsBreakFrame((u4*)caller) &&
+        SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+        return NULL;
+
+    /* Walk up two frames if possible. */
+    for (i = 0; i < 2; i++) {
+        caller = dvmGetCallerFP(caller);
+        if (caller == NULL)
+            return NULL;
+    }
+
+    return SAVEAREA_FROM_FP(caller)->method->clazz;
+}
+
+/*
+ * Fill a flat array of methods that comprise the current interpreter
+ * stack trace.  Pass in the current frame ptr.  Break frames are
+ * skipped, but reflection invocations are not.
+ *
+ * The current frame will be in element 0.
+ */
+void dvmFillStackTraceArray(const void* fp, const Method** array, size_t length)
+{
+    assert(fp != NULL);
+    assert(array != NULL);
+    size_t i = 0;
+    while (fp != NULL) {
+        if (!dvmIsBreakFrame((u4*)fp)) {
+            assert(i < length);
+            array[i++] = SAVEAREA_FROM_FP(fp)->method;
+        }
+        fp = SAVEAREA_FROM_FP(fp)->prevFrame;
+    }
+}
+
+/*
+ * Open up the reserved area and throw an exception.  The reserved area
+ * should only be needed to create and initialize the exception itself.
+ *
+ * If we already opened it and we're continuing to overflow, abort the VM.
+ *
+ * We have to leave the "reserved" area open until the "catch" handler has
+ * finished doing its processing.  This is because the catch handler may
+ * need to resolve classes, which requires calling into the class loader if
+ * the classes aren't already in the "initiating loader" list.
+ */
+void dvmHandleStackOverflow(Thread* self, const Method* method)
+{
+    /*
+     * Can we make the reserved area available?
+     */
+    if (self->stackOverflowed) {
+        /*
+         * Already did, nothing to do but bail.
+         */
+        ALOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting",
+            self->threadId);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    /* open it up to the full range */
+    ALOGI("threadid=%d: stack overflow on call to %s.%s:%s",
+        self->threadId,
+        method->clazz->descriptor, method->name, method->shorty);
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    ALOGI("  method requires %d+%d+%d=%d bytes, fp is %p (%d left)",
+        method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4,
+        (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea),
+        saveArea, (u1*) saveArea - self->interpStackEnd);
+    ALOGI("  expanding stack end (%p to %p)", self->interpStackEnd,
+        self->interpStackStart - self->interpStackSize);
+    //dvmDumpThread(self, false);
+    self->interpStackEnd = self->interpStackStart - self->interpStackSize;
+    self->stackOverflowed = true;
+
+    /*
+     * If we were trying to throw an exception when the stack overflowed,
+     * we will blow up when doing the class lookup on StackOverflowError
+     * because of the pending exception.  So, we clear it and make it
+     * the cause of the SOE.
+     */
+    Object* excep = dvmGetException(self);
+    if (excep != NULL) {
+        ALOGW("Stack overflow while throwing exception");
+        dvmClearException(self);
+    }
+    dvmThrowChainedException(gDvm.exStackOverflowError, NULL, excep);
+}
+
+/*
+ * Reduce the available stack size.  By this point we should have finished
+ * our overflow processing.
+ */
+void dvmCleanupStackOverflow(Thread* self, const Object* exception)
+{
+    const u1* newStackEnd;
+
+    assert(self->stackOverflowed);
+
+    if (exception->clazz != gDvm.exStackOverflowError) {
+        /* exception caused during SOE, not the SOE itself */
+        return;
+    }
+
+    newStackEnd = (self->interpStackStart - self->interpStackSize)
+        + STACK_OVERFLOW_RESERVE;
+    if ((u1*)self->interpSave.curFrame <= newStackEnd) {
+        ALOGE("Can't shrink stack: curFrame is in reserved area (%p %p)",
+            self->interpStackEnd, self->interpSave.curFrame);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    self->interpStackEnd = newStackEnd;
+    self->stackOverflowed = false;
+
+    ALOGI("Shrank stack (to %p, curFrame is %p)", self->interpStackEnd,
+        self->interpSave.curFrame);
+}
+
+
+/*
+ * Extract the object that is the target of a monitor-enter instruction
+ * in the top stack frame of "thread".
+ *
+ * The other thread might be alive, so this has to work carefully.
+ *
+ * The thread list lock must be held.
+ *
+ * Returns "true" if we successfully recover the object.  "*pOwner" will
+ * be NULL if we can't determine the owner for some reason (e.g. race
+ * condition on ownership transfer).
+ */
+static bool extractMonitorEnterObject(Thread* thread, Object** pLockObj,
+    Thread** pOwner)
+{
+    void* framePtr = thread->interpSave.curFrame;
+
+    if (framePtr == NULL || dvmIsBreakFrame((u4*)framePtr))
+        return false;
+
+    const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+    const Method* method = saveArea->method;
+    const u2* currentPc = saveArea->xtra.currentPc;
+
+    /* check Method* */
+    if (!dvmLinearAllocContains(method, sizeof(Method))) {
+        ALOGD("ExtrMon: method %p not valid", method);
+        return false;
+    }
+
+    /* check currentPc */
+    u4 insnsSize = dvmGetMethodInsnsSize(method);
+    if (currentPc < method->insns ||
+        currentPc >= method->insns + insnsSize)
+    {
+        ALOGD("ExtrMon: insns %p not valid (%p - %p)",
+            currentPc, method->insns, method->insns + insnsSize);
+        return false;
+    }
+
+    /* check the instruction */
+    if ((*currentPc & 0xff) != OP_MONITOR_ENTER) {
+        ALOGD("ExtrMon: insn at %p is not monitor-enter (0x%02x)",
+            currentPc, *currentPc & 0xff);
+        return false;
+    }
+
+    /* get and check the register index */
+    unsigned int reg = *currentPc >> 8;
+    if (reg >= method->registersSize) {
+        ALOGD("ExtrMon: invalid register %d (max %d)",
+            reg, method->registersSize);
+        return false;
+    }
+
+    /* get and check the object in that register */
+    u4* fp = (u4*) framePtr;
+    Object* obj = (Object*) fp[reg];
+    if (obj != NULL && !dvmIsHeapAddress(obj)) {
+        ALOGD("ExtrMon: invalid object %p at %p[%d]", obj, fp, reg);
+        return false;
+    }
+    *pLockObj = obj;
+
+    /*
+     * Try to determine the object's lock holder; it's okay if this fails.
+     *
+     * We're assuming the thread list lock is already held by this thread.
+     * If it's not, we may be living dangerously if we have to scan through
+     * the thread list to find a match.  (The VM will generally be in a
+     * suspended state when executing here, so this is a minor concern
+     * unless we're dumping while threads are running, in which case there's
+     * a good chance of stuff blowing up anyway.)
+     */
+    *pOwner = dvmGetObjectLockHolder(obj);
+
+    return true;
+}
+
+static void printWaitMessage(const DebugOutputTarget* target, const char* detail, Object* obj,
+        Thread* thread)
+{
+    std::string msg(StringPrintf("  - waiting %s <%p> ", detail, obj));
+
+    if (obj->clazz != gDvm.classJavaLangClass) {
+        // I(16573)   - waiting on <0xf5feda38> (a java.util.LinkedList)
+        // I(16573)   - waiting on <0xf5ed54f8> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
+        msg += "(a " + dvmHumanReadableType(obj) + ")";
+    }
+
+    if (thread != NULL) {
+        std::string threadName(dvmGetThreadName(thread));
+        StringAppendF(&msg, " held by tid=%d (%s)", thread->threadId, threadName.c_str());
+    }
+
+    dvmPrintDebugMessage(target, "%s\n", msg.c_str());
+}
+
+/*
+ * Dump stack frames, starting from the specified frame and moving down.
+ *
+ * Each frame holds a pointer to the currently executing method, and the
+ * saved program counter from the caller ("previous" frame).  This means
+ * we don't have the PC for the current method on the stack, which is
+ * pretty reasonable since it's in the "PC register" for the VM.  Because
+ * exceptions need to show the correct line number we actually *do* have
+ * an updated version in the fame's "xtra.currentPc", but it's unreliable.
+ *
+ * Note "framePtr" could be NULL in rare circumstances.
+ */
+static void dumpFrames(const DebugOutputTarget* target, void* framePtr,
+    Thread* thread)
+{
+    const StackSaveArea* saveArea;
+    const Method* method;
+    int checkCount = 0;
+    const u2* currentPc = NULL;
+    bool first = true;
+
+    /*
+     * We call functions that require us to be holding the thread list lock.
+     * It's probable that the caller has already done so, but it's not
+     * guaranteed.  If it's not locked, lock it now.
+     */
+    bool needThreadUnlock = dvmTryLockThreadList();
+
+    /*
+     * The "currentPc" is updated whenever we execute an instruction that
+     * might throw an exception.  Show it here.
+     */
+    if (framePtr != NULL && !dvmIsBreakFrame((u4*)framePtr)) {
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+
+        if (saveArea->xtra.currentPc != NULL)
+            currentPc = saveArea->xtra.currentPc;
+    }
+
+    while (framePtr != NULL) {
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = saveArea->method;
+
+        if (dvmIsBreakFrame((u4*)framePtr)) {
+            //dvmPrintDebugMessage(target, "  (break frame)\n");
+        } else {
+            int relPc;
+
+            if (currentPc != NULL)
+                relPc = currentPc - saveArea->method->insns;
+            else
+                relPc = -1;
+
+            std::string methodName(dvmHumanReadableMethod(method, false));
+            if (dvmIsNativeMethod(method)) {
+                dvmPrintDebugMessage(target, "  at %s(Native Method)\n",
+                        methodName.c_str());
+            } else {
+                dvmPrintDebugMessage(target, "  at %s(%s:%s%d)\n",
+                        methodName.c_str(), dvmGetMethodSourceFile(method),
+                        (relPc >= 0 && first) ? "~" : "",
+                        relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));
+            }
+
+            if (first) {
+                /*
+                 * Decorate WAIT and MONITOR threads with some detail on
+                 * the first frame.
+                 *
+                 * warning: wait status not stable, even in suspend
+                 */
+                if (thread->status == THREAD_WAIT ||
+                    thread->status == THREAD_TIMED_WAIT)
+                {
+                    Monitor* mon = thread->waitMonitor;
+                    Object* obj = dvmGetMonitorObject(mon);
+                    if (obj != NULL) {
+                        Thread* joinThread = NULL;
+                        if (obj->clazz == gDvm.classJavaLangVMThread) {
+                            joinThread = dvmGetThreadFromThreadObject(obj);
+                        }
+                        printWaitMessage(target, "on", obj, joinThread);
+                    }
+                } else if (thread->status == THREAD_MONITOR) {
+                    Object* obj;
+                    Thread* owner;
+                    if (extractMonitorEnterObject(thread, &obj, &owner)) {
+                        printWaitMessage(target, "to lock", obj, owner);
+                    }
+                }
+            }
+        }
+
+        /*
+         * Get saved PC for previous frame.  There's no savedPc in a "break"
+         * frame, because that represents native or interpreted code
+         * invoked by the VM.  The saved PC is sitting in the "PC register",
+         * a local variable on the native stack.
+         */
+        currentPc = saveArea->savedPc;
+
+        first = false;
+
+        if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) {
+            ALOGW("Warning: loop in stack trace at frame %d (%p -> %p)",
+                checkCount, framePtr, saveArea->prevFrame);
+            break;
+        }
+        framePtr = saveArea->prevFrame;
+
+        checkCount++;
+        if (checkCount > 300) {
+            dvmPrintDebugMessage(target,
+                "  ***** printed %d frames, not showing any more\n",
+                checkCount);
+            break;
+        }
+    }
+
+    if (needThreadUnlock) {
+        dvmUnlockThreadList();
+    }
+}
+
+
+/*
+ * Dump the stack for the specified thread.
+ */
+void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread)
+{
+    dumpFrames(target, thread->interpSave.curFrame, thread);
+}
+
+/*
+ * Dump the stack for the specified thread, which is still running.
+ *
+ * This is very dangerous, because stack frames are being pushed on and
+ * popped off, and if the thread exits we'll be looking at freed memory.
+ * The plan here is to take a snapshot of the stack and then dump that
+ * to try to minimize the chances of catching it mid-update.  This should
+ * work reasonably well on a single-CPU system.
+ *
+ * There is a small chance that calling here will crash the VM.
+ */
+void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread)
+{
+    StackSaveArea* saveArea;
+    const u1* origStack;
+    u1* stackCopy = NULL;
+    int origSize, fpOffset;
+    void* fp;
+    int depthLimit = 200;
+
+    if (thread == NULL || thread->interpSave.curFrame == NULL) {
+        dvmPrintDebugMessage(target,
+            "DumpRunning: Thread at %p has no curFrame (threadid=%d)\n",
+            thread, (thread != NULL) ? thread->threadId : 0);
+        return;
+    }
+
+    /* wait for a full quantum */
+    sched_yield();
+
+    /* copy the info we need, then the stack itself */
+    origSize = thread->interpStackSize;
+    origStack = (const u1*) thread->interpStackStart - origSize;
+    stackCopy = (u1*) malloc(origSize);
+    fpOffset = (u1*) thread->interpSave.curFrame - origStack;
+    memcpy(stackCopy, origStack, origSize);
+
+    /*
+     * Run through the stack and rewrite the "prev" pointers.
+     */
+    //ALOGI("DR: fpOff=%d (from %p %p)",fpOffset, origStack,
+    //     thread->interpSave.curFrame);
+    fp = stackCopy + fpOffset;
+    while (true) {
+        int prevOffset;
+
+        if (depthLimit-- < 0) {
+            /* we're probably screwed */
+            dvmPrintDebugMessage(target, "DumpRunning: depth limit hit\n");
+            dvmAbort();
+        }
+        saveArea = SAVEAREA_FROM_FP(fp);
+        if (saveArea->prevFrame == NULL)
+            break;
+
+        prevOffset = (u1*) saveArea->prevFrame - origStack;
+        if (prevOffset < 0 || prevOffset > origSize) {
+            dvmPrintDebugMessage(target,
+                "DumpRunning: bad offset found: %d (from %p %p)\n",
+                prevOffset, origStack, saveArea->prevFrame);
+            saveArea->prevFrame = NULL;
+            break;
+        }
+
+        saveArea->prevFrame = (u4*)(stackCopy + prevOffset);
+        fp = saveArea->prevFrame;
+    }
+
+    /*
+     * We still need to pass the Thread for some monitor wait stuff.
+     */
+    dumpFrames(target, stackCopy + fpOffset, thread);
+    free(stackCopy);
+}
+
+/*
+ * Dump the native stack for the specified thread.
+ */
+void dvmDumpNativeStack(const DebugOutputTarget* target, pid_t tid)
+{
+#ifdef HAVE_ANDROID_OS
+    const size_t MAX_DEPTH = 32;
+    backtrace_frame_t backtrace[MAX_DEPTH];
+    ssize_t frames = unwind_backtrace_thread(tid, backtrace, 0, MAX_DEPTH);
+    if (frames > 0) {
+        backtrace_symbol_t backtrace_symbols[MAX_DEPTH];
+        get_backtrace_symbols(backtrace, frames, backtrace_symbols);
+
+        for (size_t i = 0; i < size_t(frames); i++) {
+            char line[MAX_BACKTRACE_LINE_LENGTH];
+            format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
+                    line, MAX_BACKTRACE_LINE_LENGTH);
+            dvmPrintDebugMessage(target, "  %s\n", line);
+        }
+
+        free_backtrace_symbols(backtrace_symbols, frames);
+    } else {
+        dvmPrintDebugMessage(target, "  (native backtrace unavailable)\n");
+    }
+#endif
+}
diff --git a/vm/interp/Stack.h b/vm/interp/Stack.h
new file mode 100644
index 0000000..06e7ec5
--- /dev/null
+++ b/vm/interp/Stack.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Stack frames, and uses thereof.
+ */
+#ifndef DALVIK_INTERP_STACK_H_
+#define DALVIK_INTERP_STACK_H_
+
+#include "jni.h"
+#include <stdarg.h>
+
+/*
+Stack layout
+
+In what follows, the "top" of the stack is at a low position in memory,
+and the "bottom" of the stack is in a high position (put more simply,
+they grow downward).  They may be merged with the native stack at a
+later date.  The interpreter assumes that they have a fixed size,
+determined when the thread is created.
+
+Dalvik's registers (of which there can be up to 64K) map to the "ins"
+(method arguments) and "locals" (local variables).  The "outs" (arguments
+to called methods) are specified by the "invoke" operand.  The return
+value, which is passed through the interpreter rather than on the stack,
+is retrieved with a "move-result" instruction.
+
+    Low addresses (0x00000000)
+
+                     +- - - - - - - - -+
+                     -  out0           -
+                     +-----------------+  <-- stack ptr (top of stack)
+                     +  VM-specific    +
+                     +  internal goop  +
+                     +-----------------+  <-- curFrame: FP for cur function
+                     +  v0 == local0   +
++-----------------+  +-----------------+
++  out0           +  +  v1 == in0      +
++-----------------+  +-----------------+
++  out1           +  +  v2 == in1      +
++-----------------+  +-----------------+
++  VM-specific    +
++  internal goop  +
++-----------------+  <-- frame ptr (FP) for previous function
++  v0 == local0   +
++-----------------+
++  v1 == local1   +
++-----------------+
++  v2 == in0      +
++-----------------+
++  v3 == in1      +
++-----------------+
++  v4 == in2      +
++-----------------+
+-                 -
+-                 -
+-                 -
++-----------------+  <-- interpStackStart
+
+    High addresses (0xffffffff)
+
+Note the "ins" and "outs" overlap -- values pushed into the "outs" area
+become the parameters to the called method.  The VM guarantees that there
+will be enough room for all possible "outs" on the stack before calling
+into a method.
+
+All "V registers" are 32 bits, and all stack entries are 32-bit aligned.
+Registers are accessed as a positive offset from the frame pointer,
+e.g. register v2 is fp[2].  64-bit quantities are stored in two adjacent
+registers, addressed by the lower-numbered register, and are in host order.
+64-bit quantities do not need to start in an even-numbered register.
+
+We push two stack frames on when calling an interpreted or native method
+directly from the VM (e.g. invoking <clinit> or via reflection "invoke()").
+The first is a "break" frame, which allows us to tell when a call return or
+exception unroll has reached the VM call site.  Without the break frame the
+stack might look like an uninterrupted series of interpreted method calls.
+The second frame is for the method itself.
+
+The "break" frame is used as an alternative to adding additional fields
+to the StackSaveArea struct itself.  They are recognized by having a
+NULL method pointer.
+
+When calling a native method from interpreted code, the stack setup is
+essentially identical to calling an interpreted method.  Because it's a
+native method, though, there are never any "locals" or "outs".
+
+For native calls into JNI, we want to store a table of local references
+on the stack.  The GC needs to scan them while the native code is running,
+and we want to trivially discard them when the method returns.  See JNI.c
+for a discussion of how this is managed.  In particular note that it is
+possible to push additional call frames on without calling a method.
+*/
+
+
+struct StackSaveArea;
+
+//#define PAD_SAVE_AREA       /* help debug stack trampling */
+
+/*
+ * The VM-specific internal goop.
+ *
+ * The idea is to mimic a typical native stack frame, with copies of the
+ * saved PC and FP.  At some point we'd like to have interpreted and
+ * native code share the same stack, though this makes portability harder.
+ */
+struct StackSaveArea {
+#ifdef PAD_SAVE_AREA
+    u4          pad0, pad1, pad2;
+#endif
+
+#ifdef EASY_GDB
+    /* make it easier to trek through stack frames in GDB */
+    StackSaveArea* prevSave;
+#endif
+
+    /* saved frame pointer for previous frame, or NULL if this is at bottom */
+    u4*         prevFrame;
+
+    /* saved program counter (from method in caller's frame) */
+    const u2*   savedPc;
+
+    /* pointer to method we're *currently* executing; handy for exceptions */
+    const Method* method;
+
+    union {
+        /* for JNI native methods: bottom of local reference segment */
+        u4          localRefCookie;
+
+        /* for interpreted methods: saved current PC, for exception stack
+         * traces and debugger traces */
+        const u2*   currentPc;
+    } xtra;
+
+    /* Native return pointer for JIT, or 0 if interpreted */
+    const u2* returnAddr;
+#ifdef PAD_SAVE_AREA
+    u4          pad3, pad4, pad5;
+#endif
+};
+
+/* move between the stack save area and the frame pointer */
+#define SAVEAREA_FROM_FP(_fp)   ((StackSaveArea*)(_fp) -1)
+#define FP_FROM_SAVEAREA(_save) ((u4*) ((StackSaveArea*)(_save) +1))
+
+/* when calling a function, get a pointer to outs[0] */
+#define OUTS_FROM_FP(_fp, _argCount) \
+    ((u4*) ((u1*)SAVEAREA_FROM_FP(_fp) - sizeof(u4) * (_argCount)))
+
+/* reserve this many bytes for handling StackOverflowError */
+#define STACK_OVERFLOW_RESERVE  768
+
+/*
+ * Determine if the frame pointer points to a "break frame".
+ */
+INLINE bool dvmIsBreakFrame(const u4* fp)
+{
+    return SAVEAREA_FROM_FP(fp)->method == NULL;
+}
+
+/*
+ * Initialize the interp stack (call this after allocating storage and
+ * setting thread->interpStackStart).
+ */
+bool dvmInitInterpStack(Thread* thread, int stackSize);
+
+/*
+ * Push a native method frame directly onto the stack.  Used to push the
+ * "fake" native frames at the top of each thread stack.
+ */
+bool dvmPushJNIFrame(Thread* thread, const Method* method);
+
+/*
+ * JNI local frame management.
+ */
+bool dvmPushLocalFrame(Thread* thread, const Method* method);
+bool dvmPopLocalFrame(Thread* thread);
+
+/*
+ * Call an interpreted method from native code.  If this is being called
+ * from a JNI function, references in the argument list will be converted
+ * back to pointers.
+ *
+ * "obj" should be NULL for "direct" methods.
+ */
+void dvmCallMethod(Thread* self, const Method* method, Object* obj,
+    JValue* pResult, ...);
+void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, va_list args);
+void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, const jvalue* args);
+
+/*
+ * Invoke a method, using the specified arguments and return type, through
+ * a reflection interface.
+ *
+ * Deals with boxing/unboxing primitives and performs widening conversions.
+ *
+ * "obj" should be null for a static method.
+ *
+ * "params" and "returnType" come from the Method object, so we don't have
+ * to re-generate them from the method signature.  "returnType" should be
+ * NULL if we're invoking a constructor.
+ */
+Object* dvmInvokeMethod(Object* invokeObj, const Method* meth,
+    ArrayObject* argList, ArrayObject* params, ClassObject* returnType,
+    bool noAccessCheck);
+
+/*
+ * Determine the source file line number, given the program counter offset
+ * into the specified method.  Returns -2 for native methods, -1 if no
+ * match was found.
+ */
+extern "C" int dvmLineNumFromPC(const Method* method, u4 relPc);
+
+/*
+ * Given a frame pointer, compute the current call depth.  The value can be
+ * "exact" (a count of non-break frames) or "vague" (just subtracting
+ * pointers to give relative values).
+ */
+int dvmComputeExactFrameDepth(const void* fp);
+int dvmComputeVagueFrameDepth(Thread* thread, const void* fp);
+
+/*
+ * Get the frame pointer for the caller's stack frame.
+ */
+void* dvmGetCallerFP(const void* curFrame);
+
+/*
+ * Get the class of the method that called us.
+ */
+ClassObject* dvmGetCallerClass(const void* curFrame);
+
+/*
+ * Get the caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller2Class(const void* curFrame);
+
+/*
+ * Get the caller's caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller3Class(const void* curFrame);
+
+/*
+ * Fill an array of method pointers representing the current stack
+ * trace (element 0 is current frame).
+ */
+void dvmFillStackTraceArray(const void* fp, const Method** array, size_t length);
+
+/*
+ * Common handling for stack overflow.
+ */
+extern "C" void dvmHandleStackOverflow(Thread* self, const Method* method);
+extern "C" void dvmCleanupStackOverflow(Thread* self, const Object* exception);
+
+/* debugging; dvmDumpThread() is probably a better starting point */
+void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread);
+void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread);
+void dvmDumpNativeStack(const DebugOutputTarget* target, pid_t tid);
+
+#endif  // DALVIK_INTERP_STACK_H_
diff --git a/vm/jdwp/ExpandBuf.cpp b/vm/jdwp/ExpandBuf.cpp
new file mode 100644
index 0000000..cc3557e
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Implementation of an expandable byte buffer.  Designed for serializing
+ * primitive values, e.g. JDWP replies.
+ */
+#include "jdwp/ExpandBuf.h"
+#include "Bits.h"
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Data structure used to track buffer use.
+ */
+struct ExpandBuf {
+    u1*     storage;
+    int     curLen;
+    int     maxLen;
+};
+
+#define kInitialStorage 64
+
+/*
+ * Allocate a JdwpBuf and some initial storage.
+ */
+ExpandBuf* expandBufAlloc()
+{
+    ExpandBuf* newBuf;
+
+    newBuf = (ExpandBuf*) malloc(sizeof(*newBuf));
+    newBuf->storage = (u1*) malloc(kInitialStorage);
+    newBuf->curLen = 0;
+    newBuf->maxLen = kInitialStorage;
+
+    return newBuf;
+}
+
+/*
+ * Free a JdwpBuf and associated storage.
+ */
+void expandBufFree(ExpandBuf* pBuf)
+{
+    if (pBuf == NULL)
+        return;
+
+    free(pBuf->storage);
+    free(pBuf);
+}
+
+/*
+ * Get a pointer to the start of the buffer.
+ */
+u1* expandBufGetBuffer(ExpandBuf* pBuf)
+{
+    return pBuf->storage;
+}
+
+/*
+ * Get the amount of data currently in the buffer.
+ */
+size_t expandBufGetLength(ExpandBuf* pBuf)
+{
+    return pBuf->curLen;
+}
+
+
+/*
+ * Ensure that the buffer has enough space to hold incoming data.  If it
+ * doesn't, resize the buffer.
+ */
+static void ensureSpace(ExpandBuf* pBuf, int newCount)
+{
+    u1* newPtr;
+
+    if (pBuf->curLen + newCount <= pBuf->maxLen)
+        return;
+
+    while (pBuf->curLen + newCount > pBuf->maxLen)
+        pBuf->maxLen *= 2;
+
+    newPtr = (u1*) realloc(pBuf->storage, pBuf->maxLen);
+    if (newPtr == NULL) {
+        ALOGE("realloc(%d) failed", pBuf->maxLen);
+        abort();
+    }
+
+    pBuf->storage = newPtr;
+}
+
+/*
+ * Allocate some space in the buffer.
+ */
+u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize)
+{
+    u1* gapStart;
+
+    ensureSpace(pBuf, gapSize);
+    gapStart = pBuf->storage + pBuf->curLen;
+    /* do we want to garbage-fill the gap for debugging? */
+    pBuf->curLen += gapSize;
+
+    return gapStart;
+}
+
+/*
+ * Append a byte.
+ */
+void expandBufAdd1(ExpandBuf* pBuf, u1 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    *(pBuf->storage + pBuf->curLen) = val;
+    pBuf->curLen++;
+}
+
+/*
+ * Append two big-endian bytes.
+ */
+void expandBufAdd2BE(ExpandBuf* pBuf, u2 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    set2BE(pBuf->storage + pBuf->curLen, val);
+    pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Append four big-endian bytes.
+ */
+void expandBufAdd4BE(ExpandBuf* pBuf, u4 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    set4BE(pBuf->storage + pBuf->curLen, val);
+    pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Append eight big-endian bytes.
+ */
+void expandBufAdd8BE(ExpandBuf* pBuf, u8 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    set8BE(pBuf->storage + pBuf->curLen, val);
+    pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Add a UTF8 string as a 4-byte length followed by a non-NULL-terminated
+ * string.
+ *
+ * Because these strings are coming out of the VM, it's safe to assume that
+ * they can be null-terminated (either they don't have null bytes or they
+ * have stored null bytes in a multi-byte encoding).
+ */
+void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str)
+{
+    int strLen = strlen((const char*)str);
+
+    ensureSpace(pBuf, sizeof(u4) + strLen);
+    setUtf8String(pBuf->storage + pBuf->curLen, str);
+    pBuf->curLen += sizeof(u4) + strLen;
+}
diff --git a/vm/jdwp/ExpandBuf.h b/vm/jdwp/ExpandBuf.h
new file mode 100644
index 0000000..0bbc0c6
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Expanding byte buffer, with primitives for appending basic data types.
+ */
+#ifndef DALVIK_JDWP_EXPANDBUF_H_
+#define DALVIK_JDWP_EXPANDBUF_H_
+
+#include "Common.h"     // need u1/u2/u4/u8 types
+
+struct ExpandBuf;   /* private */
+
+/* create a new struct */
+ExpandBuf* expandBufAlloc(void);
+/* free storage */
+void expandBufFree(ExpandBuf* pBuf);
+
+/*
+ * Accessors.  The buffer pointer and length will only be valid until more
+ * data is added.
+ */
+u1* expandBufGetBuffer(ExpandBuf* pBuf);
+size_t expandBufGetLength(ExpandBuf* pBuf);
+
+/*
+ * The "add" operations allocate additional storage and append the data.
+ *
+ * There are no "get" operations included with this "class", other than
+ * GetBuffer().  If you want to get or set data from a position other
+ * than the end, get a pointer to the buffer and use the inline functions
+ * defined elsewhere.
+ *
+ * expandBufAddSpace() returns a pointer to the *start* of the region
+ * added.
+ */
+u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize);
+void expandBufAdd1(ExpandBuf* pBuf, u1 val);
+void expandBufAdd2BE(ExpandBuf* pBuf, u2 val);
+void expandBufAdd4BE(ExpandBuf* pBuf, u4 val);
+void expandBufAdd8BE(ExpandBuf* pBuf, u8 val);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str);
+
+#endif  // DALVIK_JDWP_EXPANDBUF_H_
diff --git a/vm/jdwp/Jdwp.h b/vm/jdwp/Jdwp.h
new file mode 100644
index 0000000..467cecd
--- /dev/null
+++ b/vm/jdwp/Jdwp.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JDWP "public" interface.  The main body of the VM should only use JDWP
+ * structures and functions declared here.
+ *
+ * The JDWP code follows the DalvikVM rules for naming conventions, but
+ * attempts to remain independent of VM innards (e.g. it doesn't access VM
+ * data structures directly).  All calls go through Debugger.c.
+ */
+#ifndef DALVIK_JDWP_JDWP_H_
+#define DALVIK_JDWP_JDWP_H_
+
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/ExpandBuf.h"
+#include "Common.h"
+#include "Bits.h"
+#include <pthread.h>
+
+struct JdwpState;       /* opaque */
+
+/*
+ * Fundamental types.
+ *
+ * ObjectId and RefTypeId must be the same size.
+ */
+typedef u4 FieldId;     /* static or instance field */
+typedef u4 MethodId;    /* any kind of method, including constructors */
+typedef u8 ObjectId;    /* any object (threadID, stringID, arrayID, etc) */
+typedef u8 RefTypeId;   /* like ObjectID, but unique for Class objects */
+typedef u8 FrameId;     /* short-lived stack frame ID */
+
+/*
+ * Match these with the type sizes.  This way we don't have to pass
+ * a value and a length.
+ */
+INLINE FieldId dvmReadFieldId(const u1** pBuf)      { return read4BE(pBuf); }
+INLINE MethodId dvmReadMethodId(const u1** pBuf)    { return read4BE(pBuf); }
+INLINE ObjectId dvmReadObjectId(const u1** pBuf)    { return read8BE(pBuf); }
+INLINE RefTypeId dvmReadRefTypeId(const u1** pBuf)  { return read8BE(pBuf); }
+INLINE FrameId dvmReadFrameId(const u1** pBuf)      { return read8BE(pBuf); }
+INLINE void dvmSetFieldId(u1* buf, FieldId val)     { return set4BE(buf, val); }
+INLINE void dvmSetMethodId(u1* buf, MethodId val)   { return set4BE(buf, val); }
+INLINE void dvmSetObjectId(u1* buf, ObjectId val)   { return set8BE(buf, val); }
+INLINE void dvmSetRefTypeId(u1* buf, RefTypeId val) { return set8BE(buf, val); }
+INLINE void dvmSetFrameId(u1* buf, FrameId val)     { return set8BE(buf, val); }
+INLINE void expandBufAddFieldId(ExpandBuf* pReply, FieldId id) {
+    expandBufAdd4BE(pReply, id);
+}
+INLINE void expandBufAddMethodId(ExpandBuf* pReply, MethodId id) {
+    expandBufAdd4BE(pReply, id);
+}
+INLINE void expandBufAddObjectId(ExpandBuf* pReply, ObjectId id) {
+    expandBufAdd8BE(pReply, id);
+}
+INLINE void expandBufAddRefTypeId(ExpandBuf* pReply, RefTypeId id) {
+    expandBufAdd8BE(pReply, id);
+}
+INLINE void expandBufAddFrameId(ExpandBuf* pReply, FrameId id) {
+    expandBufAdd8BE(pReply, id);
+}
+
+
+/*
+ * Holds a JDWP "location".
+ */
+struct JdwpLocation {
+    u1          typeTag;        /* class or interface? */
+    RefTypeId   classId;        /* method->clazz */
+    MethodId    methodId;       /* method in which "idx" resides */
+    u8          idx;            /* relative index into code block */
+};
+
+/*
+ * How we talk to the debugger.
+ */
+enum JdwpTransportType {
+    kJdwpTransportUnknown = 0,
+    kJdwpTransportSocket,       /* transport=dt_socket */
+    kJdwpTransportAndroidAdb,   /* transport=dt_android_adb */
+};
+
+/*
+ * Holds collection of JDWP initialization parameters.
+ */
+struct JdwpStartupParams {
+    JdwpTransportType transport;
+    bool        server;
+    bool        suspend;
+    char        host[64];
+    short       port;
+    /* more will be here someday */
+};
+
+/*
+ * Perform one-time initialization.
+ *
+ * Among other things, this binds to a port to listen for a connection from
+ * the debugger.
+ *
+ * Returns a newly-allocated JdwpState struct on success, or NULL on failure.
+ */
+JdwpState* dvmJdwpStartup(const JdwpStartupParams* params);
+
+/*
+ * Shut everything down.
+ */
+void dvmJdwpShutdown(JdwpState* state);
+
+/*
+ * Returns "true" if a debugger or DDM is connected.
+ */
+bool dvmJdwpIsActive(JdwpState* state);
+
+/*
+ * Return the debugger thread's handle, or 0 if the debugger thread isn't
+ * running.
+ */
+pthread_t dvmJdwpGetDebugThread(JdwpState* state);
+
+/*
+ * Get time, in milliseconds, since the last debugger activity.
+ */
+s8 dvmJdwpLastDebuggerActivity(JdwpState* state);
+
+/*
+ * When we hit a debugger event that requires suspension, it's important
+ * that we wait for the thread to suspend itself before processing any
+ * additional requests.  (Otherwise, if the debugger immediately sends a
+ * "resume thread" command, the resume might arrive before the thread has
+ * suspended itself.)
+ *
+ * The thread should call the "set" function before sending the event to
+ * the debugger.  The main JDWP handler loop calls "get" before processing
+ * an event, and will wait for thread suspension if it's set.  Once the
+ * thread has suspended itself, the JDWP handler calls "clear" and
+ * continues processing the current event.  This works in the suspend-all
+ * case because the event thread doesn't suspend itself until everything
+ * else has suspended.
+ *
+ * It's possible that multiple threads could encounter thread-suspending
+ * events at the same time, so we grab a mutex in the "set" call, and
+ * release it in the "clear" call.
+ */
+//ObjectId dvmJdwpGetWaitForEventThread(JdwpState* state);
+void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId);
+void dvmJdwpClearWaitForEventThread(JdwpState* state);
+
+/*
+ * Network functions.
+ */
+bool dvmJdwpCheckConnection(JdwpState* state);
+bool dvmJdwpAcceptConnection(JdwpState* state);
+bool dvmJdwpEstablishConnection(JdwpState* state);
+void dvmJdwpCloseConnection(JdwpState* state);
+bool dvmJdwpProcessIncoming(JdwpState* state);
+
+
+/*
+ * These notify the debug code that something interesting has happened.  This
+ * could be a thread starting or ending, an exception, or an opportunity
+ * for a breakpoint.  These calls do not mean that an event the debugger
+ * is interested has happened, just that something has happened that the
+ * debugger *might* be interested in.
+ *
+ * The item of interest may trigger multiple events, some or all of which
+ * are grouped together in a single response.
+ *
+ * The event may cause the current thread or all threads (except the
+ * JDWP support thread) to be suspended.
+ */
+
+/*
+ * The VM has finished initializing.  Only called when the debugger is
+ * connected at the time initialization completes.
+ */
+bool dvmJdwpPostVMStart(JdwpState* state, bool suspend);
+
+/*
+ * A location of interest has been reached.  This is used for breakpoints,
+ * single-stepping, and method entry/exit.  (JDWP requires that these four
+ * events are grouped together in a single response.)
+ *
+ * In some cases "*pLoc" will just have a method and class name, e.g. when
+ * issuing a MethodEntry on a native method.
+ *
+ * "eventFlags" indicates the types of events that have occurred.
+ */
+bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
+    ObjectId thisPtr, int eventFlags);
+
+/*
+ * An exception has been thrown.
+ *
+ * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught.
+ */
+bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
+    ObjectId excepId, RefTypeId excepClassId, const JdwpLocation* pCatchLoc,
+    ObjectId thisPtr);
+
+/*
+ * A thread has started or stopped.
+ */
+bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start);
+
+/*
+ * Class has been prepared.
+ */
+bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
+    const char* signature, int status);
+
+/*
+ * The VM is about to stop.
+ */
+bool dvmJdwpPostVMDeath(JdwpState* state);
+
+/*
+ * Send up a chunk of DDM data.
+ */
+void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
+    int iovcnt);
+
+#endif  // DALVIK_JDWP_JDWP_H_
diff --git a/vm/jdwp/JdwpAdb.cpp b/vm/jdwp/JdwpAdb.cpp
new file mode 100644
index 0000000..87db1d2
--- /dev/null
+++ b/vm/jdwp/JdwpAdb.cpp
@@ -0,0 +1,746 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+
+/*
+ * The JDWP <-> ADB transport protocol is explained in detail
+ * in system/core/adb/jdwp_service.c. Here's a summary.
+ *
+ * 1/ when the JDWP thread starts, it tries to connect to a Unix
+ *    domain stream socket (@jdwp-control) that is opened by the
+ *    ADB daemon.
+ *
+ * 2/ it then sends the current process PID as a string of 4 hexadecimal
+ *    chars (no terminating zero)
+ *
+ * 3/ then, it uses recvmsg to receive file descriptors from the
+ *    daemon. each incoming file descriptor is a pass-through to
+ *    a given JDWP debugger, that can be used to read the usual
+ *    JDWP-handshake, etc...
+ */
+
+#define kInputBufferSize    8192
+
+#define kMagicHandshake     "JDWP-Handshake"
+#define kMagicHandshakeLen  (sizeof(kMagicHandshake)-1)
+
+#define kJdwpControlName    "\0jdwp-control"
+#define kJdwpControlNameLen (sizeof(kJdwpControlName)-1)
+
+struct JdwpNetState : public JdwpNetStateBase {
+    int                 controlSock;
+    bool                awaitingHandshake;
+    bool                shuttingDown;
+    int                 wakeFds[2];
+
+    int                 inputCount;
+    unsigned char       inputBuffer[kInputBufferSize];
+
+    socklen_t           controlAddrLen;
+    union {
+        struct sockaddr_un  controlAddrUn;
+        struct sockaddr     controlAddrPlain;
+    } controlAddr;
+
+    JdwpNetState()
+    {
+        controlSock = -1;
+        awaitingHandshake = false;
+        shuttingDown = false;
+        wakeFds[0] = -1;
+        wakeFds[1] = -1;
+
+        inputCount = 0;
+
+        controlAddr.controlAddrUn.sun_family = AF_UNIX;
+        controlAddrLen = sizeof(controlAddr.controlAddrUn.sun_family) +
+                kJdwpControlNameLen;
+        memcpy(controlAddr.controlAddrUn.sun_path, kJdwpControlName,
+                kJdwpControlNameLen);
+    }
+};
+
+static void
+adbStateFree( JdwpNetState*  netState )
+{
+    if (netState == NULL)
+        return;
+
+    if (netState->clientSock >= 0) {
+        shutdown(netState->clientSock, SHUT_RDWR);
+        close(netState->clientSock);
+    }
+    if (netState->controlSock >= 0) {
+        shutdown(netState->controlSock, SHUT_RDWR);
+        close(netState->controlSock);
+    }
+    if (netState->wakeFds[0] >= 0) {
+        close(netState->wakeFds[0]);
+        netState->wakeFds[0] = -1;
+    }
+    if (netState->wakeFds[1] >= 0) {
+        close(netState->wakeFds[1]);
+        netState->wakeFds[1] = -1;
+    }
+
+    delete netState;
+}
+
+/*
+ * Do initial prep work, e.g. binding to ports and opening files.  This
+ * runs in the main thread, before the JDWP thread starts, so it shouldn't
+ * do anything that might block forever.
+ */
+static bool startup(struct JdwpState* state, const JdwpStartupParams* pParams)
+{
+    JdwpNetState*  netState;
+
+    ALOGV("ADB transport startup");
+
+    state->netState = netState = new JdwpNetState;
+    if (netState == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Receive a file descriptor from ADB.  The fd can be used to communicate
+ * directly with a debugger or DDMS.
+ *
+ * Returns the file descriptor on success.  On failure, returns -1 and
+ * closes netState->controlSock.
+ */
+static int  receiveClientFd(JdwpNetState*  netState)
+{
+    struct msghdr    msg;
+    struct cmsghdr*  cmsg;
+    struct iovec     iov;
+    char             dummy = '!';
+    union {
+        struct cmsghdr cm;
+        char buffer[CMSG_SPACE(sizeof(int))];
+    } cm_un;
+    int              ret;
+
+    iov.iov_base       = &dummy;
+    iov.iov_len        = 1;
+    msg.msg_name       = NULL;
+    msg.msg_namelen    = 0;
+    msg.msg_iov        = &iov;
+    msg.msg_iovlen     = 1;
+    msg.msg_flags      = 0;
+    msg.msg_control    = cm_un.buffer;
+    msg.msg_controllen = sizeof(cm_un.buffer);
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len   = msg.msg_controllen;
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type  = SCM_RIGHTS;
+    ((int*)(void*)CMSG_DATA(cmsg))[0] = -1;
+
+    do {
+        ret = recvmsg(netState->controlSock, &msg, 0);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret <= 0) {
+        if (ret < 0) {
+            ALOGW("receiving file descriptor from ADB failed (socket %d): %s",
+                 netState->controlSock, strerror(errno));
+        }
+        close(netState->controlSock);
+        netState->controlSock = -1;
+        return -1;
+    }
+
+    return ((int*)(void*)CMSG_DATA(cmsg))[0];
+}
+
+/*
+ * Block forever, waiting for a debugger to connect to us.  Called from the
+ * JDWP thread.
+ *
+ * This needs to un-block and return "false" if the VM is shutting down.  It
+ * should return "true" when it successfully accepts a connection.
+ */
+static bool acceptConnection(struct JdwpState* state)
+{
+    JdwpNetState*  netState = state->netState;
+    int retryCount = 0;
+
+    /* first, ensure that we get a connection to the ADB daemon */
+
+retry:
+    if (netState->shuttingDown)
+        return false;
+
+    if (netState->controlSock < 0) {
+        int        sleep_ms     = 500;
+        const int  sleep_max_ms = 2*1000;
+        char       buff[5];
+
+        netState->controlSock = socket(PF_UNIX, SOCK_STREAM, 0);
+        if (netState->controlSock < 0) {
+            ALOGE("Could not create ADB control socket:%s",
+                 strerror(errno));
+            return false;
+        }
+
+        if (pipe(netState->wakeFds) < 0) {
+            ALOGE("pipe failed");
+            return false;
+        }
+
+        snprintf(buff, sizeof(buff), "%04x", getpid());
+        buff[4] = 0;
+
+        for (;;) {
+            /*
+             * If adbd isn't running, because USB debugging was disabled or
+             * perhaps the system is restarting it for "adb root", the
+             * connect() will fail.  We loop here forever waiting for it
+             * to come back.
+             *
+             * Waking up and polling every couple of seconds is generally a
+             * bad thing to do, but we only do this if the application is
+             * debuggable *and* adbd isn't running.  Still, for the sake
+             * of battery life, we should consider timing out and giving
+             * up after a few minutes in case somebody ships an app with
+             * the debuggable flag set.
+             */
+            int  ret = connect(netState->controlSock,
+                               &netState->controlAddr.controlAddrPlain,
+                               netState->controlAddrLen);
+            if (!ret) {
+                if (!socket_peer_is_trusted(netState->controlSock)) {
+                    if (shutdown(netState->controlSock, SHUT_RDWR)) {
+                        ALOGE("trouble shutting down socket: %s", strerror(errno));
+                    }
+                    return false;
+                }
+
+                /* now try to send our pid to the ADB daemon */
+                do {
+                    ret = send( netState->controlSock, buff, 4, 0 );
+                } while (ret < 0 && errno == EINTR);
+
+                if (ret >= 0) {
+                    ALOGV("PID sent as '%.*s' to ADB", 4, buff);
+                    break;
+                }
+
+                ALOGE("Weird, can't send JDWP process pid to ADB: %s",
+                     strerror(errno));
+                return false;
+            }
+            ALOGV("Can't connect to ADB control socket:%s",
+                 strerror(errno));
+
+            usleep( sleep_ms*1000 );
+
+            sleep_ms += (sleep_ms >> 1);
+            if (sleep_ms > sleep_max_ms)
+                sleep_ms = sleep_max_ms;
+
+            if (netState->shuttingDown)
+                return false;
+        }
+    }
+
+    ALOGV("trying to receive file descriptor from ADB");
+    /* now we can receive a client file descriptor */
+    netState->clientSock = receiveClientFd(netState);
+    if (netState->shuttingDown)
+        return false;       // suppress logs and additional activity
+
+    if (netState->clientSock < 0) {
+        if (++retryCount > 5) {
+            ALOGE("adb connection max retries exceeded");
+            return false;
+        }
+        goto retry;
+    } else {
+        ALOGV("received file descriptor %d from ADB", netState->clientSock);
+        netState->awaitingHandshake = 1;
+        netState->inputCount = 0;
+        return true;
+    }
+}
+
+/*
+ * Connect out to a debugger (for server=n).  Not required.
+ */
+static bool establishConnection(struct JdwpState* state)
+{
+    return false;
+}
+
+/*
+ * Close a connection from a debugger (which may have already dropped us).
+ * Only called from the JDWP thread.
+ */
+static void closeConnection(struct JdwpState* state)
+{
+    JdwpNetState* netState;
+
+    assert(state != NULL && state->netState != NULL);
+
+    netState = state->netState;
+    if (netState->clientSock < 0)
+        return;
+
+    ALOGV("+++ closed JDWP <-> ADB connection");
+
+    close(netState->clientSock);
+    netState->clientSock = -1;
+}
+
+/*
+ * Close all network stuff, including the socket we use to listen for
+ * new connections.
+ *
+ * May be called from a non-JDWP thread, e.g. when the VM is shutting down.
+ */
+static void adbStateShutdown(struct JdwpNetState* netState)
+{
+    int  controlSock;
+    int  clientSock;
+
+    if (netState == NULL)
+        return;
+
+    netState->shuttingDown = true;
+
+    clientSock = netState->clientSock;
+    if (clientSock >= 0) {
+        shutdown(clientSock, SHUT_RDWR);
+        netState->clientSock = -1;
+    }
+
+    controlSock = netState->controlSock;
+    if (controlSock >= 0) {
+        shutdown(controlSock, SHUT_RDWR);
+        netState->controlSock = -1;
+    }
+
+    if (netState->wakeFds[1] >= 0) {
+        ALOGV("+++ writing to wakePipe");
+        write(netState->wakeFds[1], "", 1);
+    }
+}
+
+static void netShutdown(JdwpState* state)
+{
+    adbStateShutdown(state->netState);
+}
+
+/*
+ * Free up anything we put in state->netState.  This is called after
+ * "netShutdown", after the JDWP thread has stopped.
+ */
+static void netFree(struct JdwpState* state)
+{
+    JdwpNetState*  netState = state->netState;
+
+    adbStateFree(netState);
+}
+
+/*
+ * Is a debugger connected to us?
+ */
+static bool isConnected(struct JdwpState* state)
+{
+    return (state->netState != NULL   &&
+            state->netState->clientSock >= 0);
+}
+
+/*
+ * Are we still waiting for the JDWP handshake?
+ */
+static bool awaitingHandshake(struct JdwpState* state)
+{
+    return state->netState->awaitingHandshake;
+}
+
+/*
+ * Figure out if we have a full packet in the buffer.
+ */
+static bool haveFullPacket(JdwpNetState* netState)
+{
+    long length;
+
+    if (netState->awaitingHandshake)
+        return (netState->inputCount >= (int) kMagicHandshakeLen);
+
+    if (netState->inputCount < 4)
+        return false;
+
+    length = get4BE(netState->inputBuffer);
+    return (netState->inputCount >= length);
+}
+
+/*
+ * Consume bytes from the buffer.
+ *
+ * This would be more efficient with a circular buffer.  However, we're
+ * usually only going to find one packet, which is trivial to handle.
+ */
+static void consumeBytes(JdwpNetState* netState, int count)
+{
+    assert(count > 0);
+    assert(count <= netState->inputCount);
+
+    if (count == netState->inputCount) {
+        netState->inputCount = 0;
+        return;
+    }
+
+    memmove(netState->inputBuffer, netState->inputBuffer + count,
+        netState->inputCount - count);
+    netState->inputCount -= count;
+}
+
+/*
+ * Handle a packet.  Returns "false" if we encounter a connection-fatal error.
+ */
+static bool handlePacket(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    const unsigned char* buf = netState->inputBuffer;
+    JdwpReqHeader hdr;
+    u4 length, id;
+    u1 flags, cmdSet, cmd;
+    u2 error;
+    bool reply;
+    int dataLen;
+
+    cmd = cmdSet = 0;       // shut up gcc
+
+    length = read4BE(&buf);
+    id = read4BE(&buf);
+    flags = read1(&buf);
+    if ((flags & kJDWPFlagReply) != 0) {
+        reply = true;
+        error = read2BE(&buf);
+    } else {
+        reply = false;
+        cmdSet = read1(&buf);
+        cmd = read1(&buf);
+    }
+
+    assert((int) length <= netState->inputCount);
+    dataLen = length - (buf - netState->inputBuffer);
+
+    if (!reply) {
+        ExpandBuf* pReply = expandBufAlloc();
+
+        hdr.length = length;
+        hdr.id = id;
+        hdr.cmdSet = cmdSet;
+        hdr.cmd = cmd;
+        dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
+        if (expandBufGetLength(pReply) > 0) {
+            ssize_t cc = netState->writePacket(pReply);
+
+            if (cc != (ssize_t) expandBufGetLength(pReply)) {
+                ALOGE("Failed sending reply to debugger: %s", strerror(errno));
+                expandBufFree(pReply);
+                return false;
+            }
+        } else {
+            ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd);
+        }
+        expandBufFree(pReply);
+    } else {
+        ALOGV("reply?!");
+        assert(false);
+    }
+
+    ALOGV("----------");
+
+    consumeBytes(netState, length);
+    return true;
+}
+
+/*
+ * Process incoming data.  If no data is available, this will block until
+ * some arrives.
+ *
+ * If we get a full packet, handle it.
+ *
+ * To take some of the mystery out of life, we want to reject incoming
+ * connections if we already have a debugger attached.  If we don't, the
+ * debugger will just mysteriously hang until it times out.  We could just
+ * close the listen socket, but there's a good chance we won't be able to
+ * bind to the same port again, which would confuse utilities.
+ *
+ * Returns "false" on error (indicating that the connection has been severed),
+ * "true" if things are still okay.
+ */
+static bool processIncoming(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    int readCount;
+
+    assert(netState->clientSock >= 0);
+
+    if (!haveFullPacket(netState)) {
+        /* read some more, looping until we have data */
+        errno = 0;
+        while (1) {
+            int selCount;
+            fd_set readfds;
+            int maxfd = -1;
+            int fd;
+
+            FD_ZERO(&readfds);
+
+            /* configure fds; note these may get zapped by another thread */
+            fd = netState->controlSock;
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+                if (maxfd < fd)
+                    maxfd = fd;
+            }
+            fd = netState->clientSock;
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+                if (maxfd < fd)
+                    maxfd = fd;
+            }
+            fd = netState->wakeFds[0];
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+                if (maxfd < fd)
+                    maxfd = fd;
+            } else {
+                ALOGI("NOTE: entering select w/o wakepipe");
+            }
+
+            if (maxfd < 0) {
+                ALOGV("+++ all fds are closed");
+                return false;
+            }
+
+            /*
+             * Select blocks until it sees activity on the file descriptors.
+             * Closing the local file descriptor does not count as activity,
+             * so we can't rely on that to wake us up (it works for read()
+             * and accept(), but not select()).
+             *
+             * We can do one of three things: (1) send a signal and catch
+             * EINTR, (2) open an additional fd ("wakePipe") and write to
+             * it when it's time to exit, or (3) time out periodically and
+             * re-issue the select.  We're currently using #2, as it's more
+             * reliable than #1 and generally better than #3.  Wastes two fds.
+             */
+            selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+            if (selCount < 0) {
+                if (errno == EINTR)
+                    continue;
+                ALOGE("select failed: %s", strerror(errno));
+                goto fail;
+            }
+
+            if (netState->wakeFds[0] >= 0 &&
+                FD_ISSET(netState->wakeFds[0], &readfds))
+            {
+                ALOGD("Got wake-up signal, bailing out of select");
+                goto fail;
+            }
+            if (netState->controlSock >= 0 &&
+                FD_ISSET(netState->controlSock, &readfds))
+            {
+                int  sock = receiveClientFd(netState);
+                if (sock >= 0) {
+                    ALOGI("Ignoring second debugger -- accepting and dropping");
+                    close(sock);
+                } else {
+                    assert(netState->controlSock < 0);
+                    /*
+                     * Remote side most likely went away, so our next read
+                     * on netState->clientSock will fail and throw us out
+                     * of the loop.
+                     */
+                }
+            }
+            if (netState->clientSock >= 0 &&
+                FD_ISSET(netState->clientSock, &readfds))
+            {
+                readCount = read(netState->clientSock,
+                                netState->inputBuffer + netState->inputCount,
+                    sizeof(netState->inputBuffer) - netState->inputCount);
+                if (readCount < 0) {
+                    /* read failed */
+                    if (errno != EINTR)
+                        goto fail;
+                    ALOGD("+++ EINTR hit");
+                    return true;
+                } else if (readCount == 0) {
+                    /* EOF hit -- far end went away */
+                    ALOGV("+++ peer disconnected");
+                    goto fail;
+                } else
+                    break;
+            }
+        }
+
+        netState->inputCount += readCount;
+        if (!haveFullPacket(netState))
+            return true;        /* still not there yet */
+    }
+
+    /*
+     * Special-case the initial handshake.  For some bizarre reason we're
+     * expected to emulate bad tty settings by echoing the request back
+     * exactly as it was sent.  Note the handshake is always initiated by
+     * the debugger, no matter who connects to whom.
+     *
+     * Other than this one case, the protocol [claims to be] stateless.
+     */
+    if (netState->awaitingHandshake) {
+        int cc;
+
+        if (memcmp(netState->inputBuffer,
+                kMagicHandshake, kMagicHandshakeLen) != 0)
+        {
+            ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer);
+            goto fail;
+        }
+
+        errno = 0;
+        cc = write(netState->clientSock, netState->inputBuffer,
+                kMagicHandshakeLen);
+        if (cc != kMagicHandshakeLen) {
+            ALOGE("Failed writing handshake bytes: %s (%d of %d)",
+                strerror(errno), cc, (int) kMagicHandshakeLen);
+            goto fail;
+        }
+
+        consumeBytes(netState, kMagicHandshakeLen);
+        netState->awaitingHandshake = false;
+        ALOGV("+++ handshake complete");
+        return true;
+    }
+
+    /*
+     * Handle this packet.
+     */
+    return handlePacket(state);
+
+fail:
+    closeConnection(state);
+    return false;
+}
+
+/*
+ * Send a request.
+ *
+ * The entire packet must be sent with a single write() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
+{
+    JdwpNetState* netState = state->netState;
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    errno = 0;
+
+    ssize_t cc = netState->writePacket(pReq);
+
+    if (cc != (ssize_t) expandBufGetLength(pReq)) {
+        ALOGE("Failed sending req to debugger: %s (%d of %d)",
+            strerror(errno), (int) cc, (int) expandBufGetLength(pReq));
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Send a request that was split into multiple buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
+    int iovcnt)
+{
+    JdwpNetState* netState = state->netState;
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    size_t expected = 0;
+    int i;
+    for (i = 0; i < iovcnt; i++)
+        expected += iov[i].iov_len;
+
+    ssize_t actual = netState->writeBufferedPacket(iov, iovcnt);
+
+    if ((size_t)actual != expected) {
+        ALOGE("Failed sending b-req to debugger: %s (%d of %zu)",
+            strerror(errno), (int) actual, expected);
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Our functions.
+ */
+static const JdwpTransport socketTransport = {
+    startup,
+    acceptConnection,
+    establishConnection,
+    closeConnection,
+    netShutdown,
+    netFree,
+    isConnected,
+    awaitingHandshake,
+    processIncoming,
+    sendRequest,
+    sendBufferedRequest
+};
+
+/*
+ * Return our set.
+ */
+const JdwpTransport* dvmJdwpAndroidAdbTransport()
+{
+    return &socketTransport;
+}
diff --git a/vm/jdwp/JdwpConstants.cpp b/vm/jdwp/JdwpConstants.cpp
new file mode 100644
index 0000000..e98ff43
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * String constants to go along with enumerated values.  (Pity we don't
+ * have enumerated constant reflection in C.)  These are only needed for
+ * making the output human-readable.
+ */
+#include "jdwp/JdwpConstants.h"
+
+/*
+ * Return a string for the error code.
+ */
+const char* dvmJdwpErrorStr(JdwpError error)
+{
+    switch (error) {
+    case ERR_NONE:
+        return "NONE";
+    case ERR_INVALID_THREAD:
+        return "INVALID_THREAD";
+    case ERR_INVALID_THREAD_GROUP:
+        return "INVALID_THREAD_GROUP";
+    case ERR_INVALID_PRIORITY:
+        return "INVALID_PRIORITY";
+    case ERR_THREAD_NOT_SUSPENDED:
+        return "THREAD_NOT_SUSPENDED";
+    case ERR_THREAD_SUSPENDED:
+        return "THREAD_SUSPENDED";
+    case ERR_INVALID_OBJECT:
+        return "INVALID_OBJEC";
+    case ERR_INVALID_CLASS:
+        return "INVALID_CLASS";
+    case ERR_CLASS_NOT_PREPARED:
+        return "CLASS_NOT_PREPARED";
+    case ERR_INVALID_METHODID:
+        return "INVALID_METHODID";
+    case ERR_INVALID_LOCATION:
+        return "INVALID_LOCATION";
+    case ERR_INVALID_FIELDID:
+        return "INVALID_FIELDID";
+    case ERR_INVALID_FRAMEID:
+        return "INVALID_FRAMEID";
+    case ERR_NO_MORE_FRAMES:
+        return "NO_MORE_FRAMES";
+    case ERR_OPAQUE_FRAME:
+        return "OPAQUE_FRAME";
+    case ERR_NOT_CURRENT_FRAME:
+        return "NOT_CURRENT_FRAME";
+    case ERR_TYPE_MISMATCH:
+        return "TYPE_MISMATCH";
+    case ERR_INVALID_SLOT:
+        return "INVALID_SLOT";
+    case ERR_DUPLICATE:
+        return "DUPLICATE";
+    case ERR_NOT_FOUND:
+        return "NOT_FOUND";
+    case ERR_INVALID_MONITOR:
+        return "INVALID_MONITOR";
+    case ERR_NOT_MONITOR_OWNER:
+        return "NOT_MONITOR_OWNER";
+    case ERR_INTERRUPT:
+        return "INTERRUPT";
+    case ERR_INVALID_CLASS_FORMAT:
+        return "INVALID_CLASS_FORMAT";
+    case ERR_CIRCULAR_CLASS_DEFINITION:
+        return "CIRCULAR_CLASS_DEFINITION";
+    case ERR_FAILS_VERIFICATION:
+        return "FAILS_VERIFICATION";
+    case ERR_ADD_METHOD_NOT_IMPLEMENTED:
+        return "ADD_METHOD_NOT_IMPLEMENTED";
+    case ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED:
+        return "SCHEMA_CHANGE_NOT_IMPLEMENTED";
+    case ERR_INVALID_TYPESTATE:
+        return "INVALID_TYPESTATE";
+    case ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED:
+        return "HIERARCHY_CHANGE_NOT_IMPLEMENTED";
+    case ERR_DELETE_METHOD_NOT_IMPLEMENTED:
+        return "DELETE_METHOD_NOT_IMPLEMENTED";
+    case ERR_UNSUPPORTED_VERSION:
+        return "UNSUPPORTED_VERSION";
+    case ERR_NAMES_DONT_MATCH:
+        return "NAMES_DONT_MATCH";
+    case ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
+        return "CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
+    case ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
+        return "METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
+    case ERR_NOT_IMPLEMENTED:
+        return "NOT_IMPLEMENTED";
+    case ERR_NULL_POINTER:
+        return "NULL_POINTER";
+    case ERR_ABSENT_INFORMATION:
+        return "ABSENT_INFORMATION";
+    case ERR_INVALID_EVENT_TYPE:
+        return "INVALID_EVENT_TYPE";
+    case ERR_ILLEGAL_ARGUMENT:
+        return "ILLEGAL_ARGUMENT";
+    case ERR_OUT_OF_MEMORY:
+        return "OUT_OF_MEMORY";
+    case ERR_ACCESS_DENIED:
+        return "ACCESS_DENIED";
+    case ERR_VM_DEAD:
+        return "VM_DEAD";
+    case ERR_INTERNAL:
+        return "INTERNAL";
+    case ERR_UNATTACHED_THREAD:
+        return "UNATTACHED_THREAD";
+    case ERR_INVALID_TAG:
+        return "INVALID_TAG";
+    case ERR_ALREADY_INVOKING:
+        return "ALREADY_INVOKING";
+    case ERR_INVALID_INDEX:
+        return "INVALID_INDEX";
+    case ERR_INVALID_LENGTH:
+        return "INVALID_LENGTH";
+    case ERR_INVALID_STRING:
+        return "INVALID_STRING";
+    case ERR_INVALID_CLASS_LOADER:
+        return "INVALID_CLASS_LOADER";
+    case ERR_INVALID_ARRAY:
+        return "INVALID_ARRAY";
+    case ERR_TRANSPORT_LOAD:
+        return "TRANSPORT_LOAD";
+    case ERR_TRANSPORT_INIT:
+        return "TRANSPORT_INIT";
+    case ERR_NATIVE_METHOD:
+        return "NATIVE_METHOD";
+    case ERR_INVALID_COUNT:
+        return "INVALID_COUNT";
+    default:
+        return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the EventKind.
+ */
+const char* dvmJdwpEventKindStr(JdwpEventKind kind)
+{
+    switch (kind) {
+    case EK_SINGLE_STEP:        return "SINGLE_STEP";
+    case EK_BREAKPOINT:         return "BREAKPOINT";
+    case EK_FRAME_POP:          return "FRAME_POP";
+    case EK_EXCEPTION:          return "EXCEPTION";
+    case EK_USER_DEFINED:       return "USER_DEFINED";
+    case EK_THREAD_START:       return "THREAD_START";
+    /*case EK_THREAD_END:         return "THREAD_END";*/
+    case EK_CLASS_PREPARE:      return "CLASS_PREPARE";
+    case EK_CLASS_UNLOAD:       return "CLASS_UNLOAD";
+    case EK_CLASS_LOAD:         return "CLASS_LOAD";
+    case EK_FIELD_ACCESS:       return "FIELD_ACCESS";
+    case EK_FIELD_MODIFICATION: return "FIELD_MODIFICATION";
+    case EK_EXCEPTION_CATCH:    return "EXCEPTION_CATCH";
+    case EK_METHOD_ENTRY:       return "METHOD_ENTRY";
+    case EK_METHOD_EXIT:        return "METHOD_EXIT";
+    case EK_VM_INIT:            return "VM_INIT";
+    case EK_VM_DEATH:           return "VM_DEATH";
+    case EK_VM_DISCONNECTED:    return "VM_DISCONNECTED";
+    /*case EK_VM_START:           return "VM_START";*/
+    case EK_THREAD_DEATH:       return "THREAD_DEATH";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the ModKind.
+ */
+const char* dvmJdwpModKindStr(JdwpModKind kind)
+{
+    switch (kind) {
+    case MK_COUNT:              return "COUNT";
+    case MK_CONDITIONAL:        return "CONDITIONAL";
+    case MK_THREAD_ONLY:        return "THREAD_ONLY";
+    case MK_CLASS_ONLY:         return "CLASS_ONLY";
+    case MK_CLASS_MATCH:        return "CLASS_MATCH";
+    case MK_CLASS_EXCLUDE:      return "CLASS_EXCLUDE";
+    case MK_LOCATION_ONLY:      return "LOCATION_ONLY";
+    case MK_EXCEPTION_ONLY:     return "EXCEPTION_ONLY";
+    case MK_FIELD_ONLY:         return "FIELD_ONLY";
+    case MK_STEP:               return "STEP";
+    case MK_INSTANCE_ONLY:      return "INSTANCE_ONLY";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the StepDepth.
+ */
+const char* dvmJdwpStepDepthStr(JdwpStepDepth depth)
+{
+    switch (depth) {
+    case SD_INTO:               return "INTO";
+    case SD_OVER:               return "OVER";
+    case SD_OUT:                return "OUT";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the StepSize.
+ */
+const char* dvmJdwpStepSizeStr(JdwpStepSize size)
+{
+    switch (size) {
+    case SS_MIN:                return "MIN";
+    case SS_LINE:               return "LINE";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the SuspendPolicy.
+ */
+const char* dvmJdwpSuspendPolicyStr(JdwpSuspendPolicy policy)
+{
+    switch (policy) {
+    case SP_NONE:               return "NONE";
+    case SP_EVENT_THREAD:       return "EVENT_THREAD";
+    case SP_ALL:                return "ALL";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the SuspendStatus.
+ */
+const char* dvmJdwpSuspendStatusStr(JdwpSuspendStatus status)
+{
+    switch (status) {
+    case SUSPEND_STATUS_NOT_SUSPENDED: return "Not SUSPENDED";
+    case SUSPEND_STATUS_SUSPENDED:     return "SUSPENDED";
+    default:                           return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the ThreadStatus.
+ */
+const char* dvmJdwpThreadStatusStr(JdwpThreadStatus status)
+{
+    switch (status) {
+    case TS_ZOMBIE:             return "ZOMBIE";
+    case TS_RUNNING:            return "RUNNING";
+    case TS_SLEEPING:           return "SLEEPING";
+    case TS_MONITOR:            return "MONITOR";
+    case TS_WAIT:               return "WAIT";
+    default:                    return "?UNKNOWN?";
+    }
+};
diff --git a/vm/jdwp/JdwpConstants.h b/vm/jdwp/JdwpConstants.h
new file mode 100644
index 0000000..9913759
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * These come out of the JDWP documentation.
+ */
+#ifndef DALVIK_JDWP_JDWPCONSTANTS_H_
+#define DALVIK_JDWP_JDWPCONSTANTS_H_
+
+/*
+ * Error constants.
+ */
+enum JdwpError {
+    ERR_NONE                                        = 0,
+    ERR_INVALID_THREAD                              = 10,
+    ERR_INVALID_THREAD_GROUP                        = 11,
+    ERR_INVALID_PRIORITY                            = 12,
+    ERR_THREAD_NOT_SUSPENDED                        = 13,
+    ERR_THREAD_SUSPENDED                            = 14,
+    ERR_INVALID_OBJECT                              = 20,
+    ERR_INVALID_CLASS                               = 21,
+    ERR_CLASS_NOT_PREPARED                          = 22,
+    ERR_INVALID_METHODID                            = 23,
+    ERR_INVALID_LOCATION                            = 24,
+    ERR_INVALID_FIELDID                             = 25,
+    ERR_INVALID_FRAMEID                             = 30,
+    ERR_NO_MORE_FRAMES                              = 31,
+    ERR_OPAQUE_FRAME                                = 32,
+    ERR_NOT_CURRENT_FRAME                           = 33,
+    ERR_TYPE_MISMATCH                               = 34,
+    ERR_INVALID_SLOT                                = 35,
+    ERR_DUPLICATE                                   = 40,
+    ERR_NOT_FOUND                                   = 41,
+    ERR_INVALID_MONITOR                             = 50,
+    ERR_NOT_MONITOR_OWNER                           = 51,
+    ERR_INTERRUPT                                   = 52,
+    ERR_INVALID_CLASS_FORMAT                        = 60,
+    ERR_CIRCULAR_CLASS_DEFINITION                   = 61,
+    ERR_FAILS_VERIFICATION                          = 62,
+    ERR_ADD_METHOD_NOT_IMPLEMENTED                  = 63,
+    ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED               = 64,
+    ERR_INVALID_TYPESTATE                           = 65,
+    ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED            = 66,
+    ERR_DELETE_METHOD_NOT_IMPLEMENTED               = 67,
+    ERR_UNSUPPORTED_VERSION                         = 68,
+    ERR_NAMES_DONT_MATCH                            = 69,
+    ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED      = 70,
+    ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED     = 71,
+    ERR_NOT_IMPLEMENTED                             = 99,
+    ERR_NULL_POINTER                                = 100,
+    ERR_ABSENT_INFORMATION                          = 101,
+    ERR_INVALID_EVENT_TYPE                          = 102,
+    ERR_ILLEGAL_ARGUMENT                            = 103,
+    ERR_OUT_OF_MEMORY                               = 110,
+    ERR_ACCESS_DENIED                               = 111,
+    ERR_VM_DEAD                                     = 112,
+    ERR_INTERNAL                                    = 113,
+    ERR_UNATTACHED_THREAD                           = 115,
+    ERR_INVALID_TAG                                 = 500,
+    ERR_ALREADY_INVOKING                            = 502,
+    ERR_INVALID_INDEX                               = 503,
+    ERR_INVALID_LENGTH                              = 504,
+    ERR_INVALID_STRING                              = 506,
+    ERR_INVALID_CLASS_LOADER                        = 507,
+    ERR_INVALID_ARRAY                               = 508,
+    ERR_TRANSPORT_LOAD                              = 509,
+    ERR_TRANSPORT_INIT                              = 510,
+    ERR_NATIVE_METHOD                               = 511,
+    ERR_INVALID_COUNT                               = 512,
+};
+const char* dvmJdwpErrorStr(JdwpError error);
+
+
+/*
+ * ClassStatus constants.  These are bit flags that can be ORed together.
+ */
+enum JdwpClassStatus {
+    CS_VERIFIED             = 0x01,
+    CS_PREPARED             = 0x02,
+    CS_INITIALIZED          = 0x04,
+    CS_ERROR                = 0x08,
+};
+
+/*
+ * EventKind constants.
+ */
+enum JdwpEventKind {
+    EK_SINGLE_STEP          = 1,
+    EK_BREAKPOINT           = 2,
+    EK_FRAME_POP            = 3,
+    EK_EXCEPTION            = 4,
+    EK_USER_DEFINED         = 5,
+    EK_THREAD_START         = 6,
+    EK_THREAD_END           = 7,
+    EK_CLASS_PREPARE        = 8,
+    EK_CLASS_UNLOAD         = 9,
+    EK_CLASS_LOAD           = 10,
+    EK_FIELD_ACCESS         = 20,
+    EK_FIELD_MODIFICATION   = 21,
+    EK_EXCEPTION_CATCH      = 30,
+    EK_METHOD_ENTRY         = 40,
+    EK_METHOD_EXIT          = 41,
+    EK_VM_INIT              = 90,
+    EK_VM_DEATH             = 99,
+    EK_VM_DISCONNECTED      = 100,  /* "Never sent across JDWP */
+    EK_VM_START             = EK_VM_INIT,
+    EK_THREAD_DEATH         = EK_THREAD_END,
+};
+const char* dvmJdwpEventKindStr(JdwpEventKind kind);
+
+/*
+ * Values for "modKind" in EventRequest.Set.
+ */
+enum JdwpModKind {
+    MK_COUNT                = 1,
+    MK_CONDITIONAL          = 2,
+    MK_THREAD_ONLY          = 3,
+    MK_CLASS_ONLY           = 4,
+    MK_CLASS_MATCH          = 5,
+    MK_CLASS_EXCLUDE        = 6,
+    MK_LOCATION_ONLY        = 7,
+    MK_EXCEPTION_ONLY       = 8,
+    MK_FIELD_ONLY           = 9,
+    MK_STEP                 = 10,
+    MK_INSTANCE_ONLY        = 11,
+};
+const char* dvmJdwpModKindStr(JdwpModKind kind);
+
+/*
+ * InvokeOptions constants (bit flags).
+ */
+enum JdwpInvokeOptions {
+    INVOKE_SINGLE_THREADED  = 0x01,
+    INVOKE_NONVIRTUAL       = 0x02,
+};
+
+/*
+ * StepDepth constants.
+ */
+enum JdwpStepDepth {
+    SD_INTO                 = 0,    /* step into method calls */
+    SD_OVER                 = 1,    /* step over method calls */
+    SD_OUT                  = 2,    /* step out of current method */
+};
+const char* dvmJdwpStepDepthStr(JdwpStepDepth depth);
+
+/*
+ * StepSize constants.
+ */
+enum JdwpStepSize {
+    SS_MIN                  = 0,    /* step by minimum (e.g. 1 bytecode inst) */
+    SS_LINE                 = 1,    /* if possible, step to next line */
+};
+const char* dvmJdwpStepSizeStr(JdwpStepSize size);
+
+/*
+ * SuspendPolicy constants.
+ */
+enum JdwpSuspendPolicy {
+    SP_NONE                 = 0,    /* suspend no threads */
+    SP_EVENT_THREAD         = 1,    /* suspend event thread */
+    SP_ALL                  = 2,    /* suspend all threads */
+};
+const char* dvmJdwpSuspendPolicyStr(JdwpSuspendPolicy policy);
+
+/*
+ * SuspendStatus constants.
+ */
+enum JdwpSuspendStatus {
+    SUSPEND_STATUS_NOT_SUSPENDED = 0,
+    SUSPEND_STATUS_SUSPENDED     = 1,
+};
+const char* dvmJdwpSuspendStatusStr(JdwpSuspendStatus status);
+
+/*
+ * ThreadStatus constants.
+ */
+enum JdwpThreadStatus {
+    TS_ZOMBIE               = 0,
+    TS_RUNNING              = 1,        // RUNNING
+    TS_SLEEPING             = 2,        // (in Thread.sleep())
+    TS_MONITOR              = 3,        // WAITING (monitor wait)
+    TS_WAIT                 = 4,        // (in Object.wait())
+};
+const char* dvmJdwpThreadStatusStr(JdwpThreadStatus status);
+
+/*
+ * TypeTag constants.
+ */
+enum JdwpTypeTag {
+    TT_CLASS                = 1,
+    TT_INTERFACE            = 2,
+    TT_ARRAY                = 3,
+};
+
+/*
+ * Tag constants.
+ */
+enum JdwpType {
+    JT_ARRAY                 = '[',
+    JT_BYTE                  = 'B',
+    JT_CHAR                  = 'C',
+    JT_OBJECT                = 'L',
+    JT_FLOAT                 = 'F',
+    JT_DOUBLE                = 'D',
+    JT_INT                   = 'I',
+    JT_LONG                  = 'J',
+    JT_SHORT                 = 'S',
+    JT_VOID                  = 'V',
+    JT_BOOLEAN               = 'Z',
+    JT_STRING                = 's',
+    JT_THREAD                = 't',
+    JT_THREAD_GROUP          = 'g',
+    JT_CLASS_LOADER          = 'l',
+    JT_CLASS_OBJECT          = 'c',
+};
+
+#endif  // DALVIK_JDWP_JDWPCONSTANTS_H_
diff --git a/vm/jdwp/JdwpEvent.cpp b/vm/jdwp/JdwpEvent.cpp
new file mode 100644
index 0000000..2793487
--- /dev/null
+++ b/vm/jdwp/JdwpEvent.cpp
@@ -0,0 +1,1278 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Send events to the debugger.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/JdwpHandler.h"
+#include "jdwp/JdwpEvent.h"
+#include "jdwp/ExpandBuf.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>     /* for offsetof() */
+#include <unistd.h>
+
+/*
+General notes:
+
+The event add/remove stuff usually happens from the debugger thread,
+in response to requests from the debugger, but can also happen as the
+result of an event in an arbitrary thread (e.g. an event with a "count"
+mod expires).  It's important to keep the event list locked when processing
+events.
+
+Event posting can happen from any thread.  The JDWP thread will not usually
+post anything but VM start/death, but if a JDWP request causes a class
+to be loaded, the ClassPrepare event will come from the JDWP thread.
+
+
+We can have serialization issues when we post an event to the debugger.
+For example, a thread could send an "I hit a breakpoint and am suspending
+myself" message to the debugger.  Before it manages to suspend itself, the
+debugger's response ("not interested, resume thread") arrives and is
+processed.  We try to resume a thread that hasn't yet suspended.
+
+This means that, after posting an event to the debugger, we need to wait
+for the event thread to suspend itself (and, potentially, all other threads)
+before processing any additional requests from the debugger.  While doing
+so we need to be aware that multiple threads may be hitting breakpoints
+or other events simultaneously, so we either need to wait for all of them
+or serialize the events with each other.
+
+The current mechanism works like this:
+  Event thread:
+   - If I'm going to suspend, grab the "I am posting an event" token.  Wait
+     for it if it's not currently available.
+   - Post the event to the debugger.
+   - If appropriate, suspend others and then myself.  As part of suspending
+     myself, release the "I am posting" token.
+  JDWP thread:
+   - When an event arrives, see if somebody is posting an event.  If so,
+     sleep until we can acquire the "I am posting an event" token.  Release
+     it immediately and continue processing -- the event we have already
+     received should not interfere with other events that haven't yet
+     been posted.
+
+Some care must be taken to avoid deadlock:
+
+ - thread A and thread B exit near-simultaneously, and post thread-death
+   events with a "suspend all" clause
+ - thread A gets the event token, thread B sits and waits for it
+ - thread A wants to suspend all other threads, but thread B is waiting
+   for the token and can't be suspended
+
+So we need to mark thread B in such a way that thread A doesn't wait for it.
+
+If we just bracket the "grab event token" call with a change to VMWAIT
+before sleeping, the switch back to RUNNING state when we get the token
+will cause thread B to suspend (remember, thread A's global suspend is
+still in force, even after it releases the token).  Suspending while
+holding the event token is very bad, because it prevents the JDWP thread
+from processing incoming messages.
+
+We need to change to VMWAIT state at the *start* of posting an event,
+and stay there until we either finish posting the event or decide to
+put ourselves to sleep.  That way we don't interfere with anyone else and
+don't allow anyone else to interfere with us.
+*/
+
+
+#define kJdwpEventCommandSet    64
+#define kJdwpCompositeCommand   100
+
+/*
+ * Stuff to compare against when deciding if a mod matches.  Only the
+ * values for mods valid for the event being evaluated will be filled in.
+ * The rest will be zeroed.
+ */
+struct ModBasket {
+    const JdwpLocation* pLoc;           /* LocationOnly */
+    const char*         className;      /* ClassMatch/ClassExclude */
+    ObjectId            threadId;       /* ThreadOnly */
+    RefTypeId           classId;        /* ClassOnly */
+    RefTypeId           excepClassId;   /* ExceptionOnly */
+    bool                caught;         /* ExceptionOnly */
+    FieldId             field;          /* FieldOnly */
+    ObjectId            thisPtr;        /* InstanceOnly */
+    /* nothing for StepOnly -- handled differently */
+};
+
+/*
+ * Get the next "request" serial number.  We use this when sending
+ * packets to the debugger.
+ */
+u4 dvmJdwpNextRequestSerial(JdwpState* state)
+{
+    dvmDbgLockMutex(&state->serialLock);
+    u4 result = state->requestSerial++;
+    dvmDbgUnlockMutex(&state->serialLock);
+
+    return result;
+}
+
+/*
+ * Get the next "event" serial number.  We use this in the response to
+ * message type EventRequest.Set.
+ */
+u4 dvmJdwpNextEventSerial(JdwpState* state)
+{
+    dvmDbgLockMutex(&state->serialLock);
+    u4 result = state->eventSerial++;
+    dvmDbgUnlockMutex(&state->serialLock);
+
+    return result;
+}
+
+/*
+ * Lock the "event" mutex, which guards the list of registered events.
+ */
+static void lockEventMutex(JdwpState* state)
+{
+    //dvmDbgThreadWaiting();
+    dvmDbgLockMutex(&state->eventLock);
+    //dvmDbgThreadRunning();
+}
+
+/*
+ * Unlock the "event" mutex.
+ */
+static void unlockEventMutex(JdwpState* state)
+{
+    dvmDbgUnlockMutex(&state->eventLock);
+}
+
+/*
+ * Dump an event to the log file.
+ */
+static void dumpEvent(const JdwpEvent* pEvent)
+{
+    ALOGI("Event id=0x%4x %p (prev=%p next=%p):",
+        pEvent->requestId, pEvent, pEvent->prev, pEvent->next);
+    ALOGI("  kind=%s susp=%s modCount=%d",
+        dvmJdwpEventKindStr(pEvent->eventKind),
+        dvmJdwpSuspendPolicyStr(pEvent->suspendPolicy),
+        pEvent->modCount);
+
+    for (int i = 0; i < pEvent->modCount; i++) {
+        const JdwpEventMod* pMod = &pEvent->mods[i];
+        JdwpModKind kind = static_cast<JdwpModKind>(pMod->modKind);
+        ALOGI("  %s", dvmJdwpModKindStr(kind));
+        /* TODO - show details */
+    }
+}
+
+/*
+ * Add an event to the list.  Ordering is not important.
+ *
+ * If something prevents the event from being registered, e.g. it's a
+ * single-step request on a thread that doesn't exist, the event will
+ * not be added to the list, and an appropriate error will be returned.
+ */
+JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent)
+{
+    lockEventMutex(state);
+
+    assert(state != NULL);
+    assert(pEvent != NULL);
+    assert(pEvent->prev == NULL);
+    assert(pEvent->next == NULL);
+
+    /*
+     * If one or more "break"-type mods are used, register them with
+     * the interpreter.
+     */
+    for (int i = 0; i < pEvent->modCount; i++) {
+        const JdwpEventMod* pMod = &pEvent->mods[i];
+        if (pMod->modKind == MK_LOCATION_ONLY) {
+            /* should only be for Breakpoint, Step, and Exception */
+            dvmDbgWatchLocation(&pMod->locationOnly.loc);
+        } else if (pMod->modKind == MK_STEP) {
+            /* should only be for EK_SINGLE_STEP; should only be one */
+            JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size);
+            JdwpStepDepth depth = static_cast<JdwpStepDepth>(pMod->step.depth);
+            dvmDbgConfigureStep(pMod->step.threadId, size, depth);
+        } else if (pMod->modKind == MK_FIELD_ONLY) {
+            /* should be for EK_FIELD_ACCESS or EK_FIELD_MODIFICATION */
+            dumpEvent(pEvent);  /* TODO - need for field watches */
+        }
+    }
+
+    /*
+     * Add to list.
+     */
+    if (state->eventList != NULL) {
+        pEvent->next = state->eventList;
+        state->eventList->prev = pEvent;
+    }
+    state->eventList = pEvent;
+    state->numEvents++;
+
+    unlockEventMutex(state);
+
+    return ERR_NONE;
+}
+
+/*
+ * Remove an event from the list.  This will also remove the event from
+ * any optimization tables, e.g. breakpoints.
+ *
+ * Does not free the JdwpEvent.
+ *
+ * Grab the eventLock before calling here.
+ */
+static void unregisterEvent(JdwpState* state, JdwpEvent* pEvent)
+{
+    if (pEvent->prev == NULL) {
+        /* head of the list */
+        assert(state->eventList == pEvent);
+
+        state->eventList = pEvent->next;
+    } else {
+        pEvent->prev->next = pEvent->next;
+    }
+
+    if (pEvent->next != NULL) {
+        pEvent->next->prev = pEvent->prev;
+        pEvent->next = NULL;
+    }
+    pEvent->prev = NULL;
+
+    /*
+     * Unhook us from the interpreter, if necessary.
+     */
+    for (int i = 0; i < pEvent->modCount; i++) {
+        JdwpEventMod* pMod = &pEvent->mods[i];
+        if (pMod->modKind == MK_LOCATION_ONLY) {
+            /* should only be for Breakpoint, Step, and Exception */
+            dvmDbgUnwatchLocation(&pMod->locationOnly.loc);
+        }
+        if (pMod->modKind == MK_STEP) {
+            /* should only be for EK_SINGLE_STEP; should only be one */
+            dvmDbgUnconfigureStep(pMod->step.threadId);
+        }
+    }
+
+    state->numEvents--;
+    assert(state->numEvents != 0 || state->eventList == NULL);
+}
+
+/*
+ * Remove the event with the given ID from the list.
+ *
+ * Failure to find the event isn't really an error, but it is a little
+ * weird.  (It looks like Eclipse will try to be extra careful and will
+ * explicitly remove one-off single-step events.)
+ */
+void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId)
+{
+    lockEventMutex(state);
+
+    JdwpEvent* pEvent = state->eventList;
+    while (pEvent != NULL) {
+        if (pEvent->requestId == requestId) {
+            unregisterEvent(state, pEvent);
+            dvmJdwpEventFree(pEvent);
+            goto done;      /* there can be only one with a given ID */
+        }
+
+        pEvent = pEvent->next;
+    }
+
+    //ALOGD("Odd: no match when removing event reqId=0x%04x", requestId);
+
+done:
+    unlockEventMutex(state);
+}
+
+/*
+ * Remove all entries from the event list.
+ */
+void dvmJdwpUnregisterAll(JdwpState* state)
+{
+    lockEventMutex(state);
+
+    JdwpEvent* pEvent = state->eventList;
+    while (pEvent != NULL) {
+        JdwpEvent* pNextEvent = pEvent->next;
+
+        unregisterEvent(state, pEvent);
+        dvmJdwpEventFree(pEvent);
+        pEvent = pNextEvent;
+    }
+
+    state->eventList = NULL;
+
+    unlockEventMutex(state);
+}
+
+
+
+/*
+ * Allocate a JdwpEvent struct with enough space to hold the specified
+ * number of mod records.
+ */
+JdwpEvent* dvmJdwpEventAlloc(int numMods)
+{
+    JdwpEvent* newEvent;
+    int allocSize = offsetof(JdwpEvent, mods) +
+                    numMods * sizeof(newEvent->mods[0]);
+
+    newEvent = (JdwpEvent*)calloc(1, allocSize);
+    return newEvent;
+}
+
+/*
+ * Free a JdwpEvent.
+ *
+ * Do not call this until the event has been removed from the list.
+ */
+void dvmJdwpEventFree(JdwpEvent* pEvent)
+{
+    if (pEvent == NULL)
+        return;
+
+    /* make sure it was removed from the list */
+    assert(pEvent->prev == NULL);
+    assert(pEvent->next == NULL);
+    /* want to assert state->eventList != pEvent */
+
+    /*
+     * Free any hairy bits in the mods.
+     */
+    for (int i = 0; i < pEvent->modCount; i++) {
+        if (pEvent->mods[i].modKind == MK_CLASS_MATCH) {
+            free(pEvent->mods[i].classMatch.classPattern);
+            pEvent->mods[i].classMatch.classPattern = NULL;
+        }
+        if (pEvent->mods[i].modKind == MK_CLASS_EXCLUDE) {
+            free(pEvent->mods[i].classExclude.classPattern);
+            pEvent->mods[i].classExclude.classPattern = NULL;
+        }
+    }
+
+    free(pEvent);
+}
+
+/*
+ * Allocate storage for matching events.  To keep things simple we
+ * use an array with enough storage for the entire list.
+ *
+ * The state->eventLock should be held before calling.
+ */
+static JdwpEvent** allocMatchList(JdwpState* state)
+{
+    return (JdwpEvent**) malloc(sizeof(JdwpEvent*) * state->numEvents);
+}
+
+/*
+ * Run through the list and remove any entries with an expired "count" mod
+ * from the event list, then free the match list.
+ */
+static void cleanupMatchList(JdwpState* state, JdwpEvent** matchList,
+    int matchCount)
+{
+    JdwpEvent** ppEvent = matchList;
+
+    while (matchCount--) {
+        JdwpEvent* pEvent = *ppEvent;
+
+        for (int i = 0; i < pEvent->modCount; i++) {
+            if (pEvent->mods[i].modKind == MK_COUNT &&
+                pEvent->mods[i].count.count == 0)
+            {
+                ALOGV("##### Removing expired event");
+                unregisterEvent(state, pEvent);
+                dvmJdwpEventFree(pEvent);
+                break;
+            }
+        }
+
+        ppEvent++;
+    }
+
+    free(matchList);
+}
+
+/*
+ * Match a string against a "restricted regular expression", which is just
+ * a string that may start or end with '*' (e.g. "*.Foo" or "java.*").
+ *
+ * ("Restricted name globbing" might have been a better term.)
+ */
+static bool patternMatch(const char* pattern, const char* target)
+{
+    int patLen = strlen(pattern);
+
+    if (pattern[0] == '*') {
+        int targetLen = strlen(target);
+        patLen--;
+        // TODO: remove printf when we find a test case to verify this
+        ALOGE(">>> comparing '%s' to '%s'",
+            pattern+1, target + (targetLen-patLen));
+
+        if (targetLen < patLen)
+            return false;
+        return strcmp(pattern+1, target + (targetLen-patLen)) == 0;
+    } else if (pattern[patLen-1] == '*') {
+        return strncmp(pattern, target, patLen-1) == 0;
+    } else {
+        return strcmp(pattern, target) == 0;
+    }
+}
+
+/*
+ * See if two locations are equal.
+ *
+ * It's tempting to do a bitwise compare ("struct ==" or memcmp), but if
+ * the storage wasn't zeroed out there could be undefined values in the
+ * padding.  Besides, the odds of "idx" being equal while the others aren't
+ * is very small, so this is usually just a simple integer comparison.
+ */
+static inline bool locationMatch(const JdwpLocation* pLoc1,
+    const JdwpLocation* pLoc2)
+{
+    return pLoc1->idx == pLoc2->idx &&
+           pLoc1->methodId == pLoc2->methodId &&
+           pLoc1->classId == pLoc2->classId &&
+           pLoc1->typeTag == pLoc2->typeTag;
+}
+
+/*
+ * See if the event's mods match up with the contents of "basket".
+ *
+ * If we find a Count mod before rejecting an event, we decrement it.  We
+ * need to do this even if later mods cause us to ignore the event.
+ */
+static bool modsMatch(JdwpState* state, JdwpEvent* pEvent, ModBasket* basket)
+{
+    JdwpEventMod* pMod = pEvent->mods;
+
+    for (int i = pEvent->modCount; i > 0; i--, pMod++) {
+        switch (pMod->modKind) {
+        case MK_COUNT:
+            assert(pMod->count.count > 0);
+            pMod->count.count--;
+            break;
+        case MK_CONDITIONAL:
+            assert(false);  // should not be getting these
+            break;
+        case MK_THREAD_ONLY:
+            if (pMod->threadOnly.threadId != basket->threadId)
+                return false;
+            break;
+        case MK_CLASS_ONLY:
+            if (!dvmDbgMatchType(basket->classId, pMod->classOnly.refTypeId))
+                return false;
+            break;
+        case MK_CLASS_MATCH:
+            if (!patternMatch(pMod->classMatch.classPattern,
+                    basket->className))
+                return false;
+            break;
+        case MK_CLASS_EXCLUDE:
+            if (patternMatch(pMod->classMatch.classPattern,
+                    basket->className))
+                return false;
+            break;
+        case MK_LOCATION_ONLY:
+            if (!locationMatch(&pMod->locationOnly.loc, basket->pLoc))
+                return false;
+            break;
+        case MK_EXCEPTION_ONLY:
+            if (pMod->exceptionOnly.refTypeId != 0 &&
+                !dvmDbgMatchType(basket->excepClassId,
+                                 pMod->exceptionOnly.refTypeId))
+                return false;
+            if ((basket->caught && !pMod->exceptionOnly.caught) ||
+                (!basket->caught && !pMod->exceptionOnly.uncaught))
+                return false;
+            break;
+        case MK_FIELD_ONLY:
+            if (!dvmDbgMatchType(basket->classId, pMod->fieldOnly.refTypeId) ||
+                    pMod->fieldOnly.fieldId != basket->field)
+                return false;
+            break;
+        case MK_STEP:
+            if (pMod->step.threadId != basket->threadId)
+                return false;
+            break;
+        case MK_INSTANCE_ONLY:
+            if (pMod->instanceOnly.objectId != basket->thisPtr)
+                return false;
+            break;
+        default:
+            ALOGE("unhandled mod kind %d", pMod->modKind);
+            assert(false);
+            break;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Find all events of type "eventKind" with mods that match up with the
+ * rest of the arguments.
+ *
+ * Found events are appended to "matchList", and "*pMatchCount" is advanced,
+ * so this may be called multiple times for grouped events.
+ *
+ * DO NOT call this multiple times for the same eventKind, as Count mods are
+ * decremented during the scan.
+ */
+static void findMatchingEvents(JdwpState* state, JdwpEventKind eventKind,
+    ModBasket* basket, JdwpEvent** matchList, int* pMatchCount)
+{
+    /* start after the existing entries */
+    matchList += *pMatchCount;
+
+    JdwpEvent* pEvent = state->eventList;
+    while (pEvent != NULL) {
+        if (pEvent->eventKind == eventKind && modsMatch(state, pEvent, basket))
+        {
+            *matchList++ = pEvent;
+            (*pMatchCount)++;
+        }
+
+        pEvent = pEvent->next;
+    }
+}
+
+/*
+ * Scan through the list of matches and determine the most severe
+ * suspension policy.
+ */
+static JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** matchList,
+    int matchCount)
+{
+    JdwpSuspendPolicy policy = SP_NONE;
+
+    while (matchCount--) {
+        if ((*matchList)->suspendPolicy > policy)
+            policy = (*matchList)->suspendPolicy;
+        matchList++;
+    }
+
+    return policy;
+}
+
+/*
+ * Three possibilities:
+ *  SP_NONE - do nothing
+ *  SP_EVENT_THREAD - suspend ourselves
+ *  SP_ALL - suspend everybody except JDWP support thread
+ */
+static void suspendByPolicy(JdwpState* state, JdwpSuspendPolicy suspendPolicy)
+{
+    if (suspendPolicy == SP_NONE)
+        return;
+
+    if (suspendPolicy == SP_ALL) {
+        dvmDbgSuspendVM(true);
+    } else {
+        assert(suspendPolicy == SP_EVENT_THREAD);
+    }
+
+    /* this is rare but possible -- see CLASS_PREPARE handling */
+    if (dvmDbgGetThreadSelfId() == state->debugThreadId) {
+        ALOGI("NOTE: suspendByPolicy not suspending JDWP thread");
+        return;
+    }
+
+    DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
+    while (true) {
+        pReq->ready = true;
+        dvmDbgSuspendSelf();
+        pReq->ready = false;
+
+        /*
+         * The JDWP thread has told us (and possibly all other threads) to
+         * resume.  See if it has left anything in our DebugInvokeReq mailbox.
+         */
+        if (!pReq->invokeNeeded) {
+            /*LOGD("suspendByPolicy: no invoke needed");*/
+            break;
+        }
+
+        /* grab this before posting/suspending again */
+        dvmJdwpSetWaitForEventThread(state, dvmDbgGetThreadSelfId());
+
+        /* leave pReq->invokeNeeded raised so we can check reentrancy */
+        ALOGV("invoking method...");
+        dvmDbgExecuteMethod(pReq);
+
+        pReq->err = ERR_NONE;
+
+        /* clear this before signaling */
+        pReq->invokeNeeded = false;
+
+        ALOGV("invoke complete, signaling and self-suspending");
+        dvmDbgLockMutex(&pReq->lock);
+        dvmDbgCondSignal(&pReq->cv);
+        dvmDbgUnlockMutex(&pReq->lock);
+    }
+}
+
+/*
+ * Determine if there is a method invocation in progress in the current
+ * thread.
+ *
+ * We look at the "invokeNeeded" flag in the per-thread DebugInvokeReq
+ * state.  If set, we're in the process of invoking a method.
+ */
+static bool invokeInProgress(JdwpState* state)
+{
+    DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
+    return pReq->invokeNeeded;
+}
+
+/*
+ * We need the JDWP thread to hold off on doing stuff while we post an
+ * event and then suspend ourselves.
+ *
+ * Call this with a threadId of zero if you just want to wait for the
+ * current thread operation to complete.
+ *
+ * This could go to sleep waiting for another thread, so it's important
+ * that the thread be marked as VMWAIT before calling here.
+ */
+void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId)
+{
+    bool waited = false;
+
+    /* this is held for very brief periods; contention is unlikely */
+    dvmDbgLockMutex(&state->eventThreadLock);
+
+    /*
+     * If another thread is already doing stuff, wait for it.  This can
+     * go to sleep indefinitely.
+     */
+    while (state->eventThreadId != 0) {
+        ALOGV("event in progress (0x%llx), 0x%llx sleeping",
+            state->eventThreadId, threadId);
+        waited = true;
+        dvmDbgCondWait(&state->eventThreadCond, &state->eventThreadLock);
+    }
+
+    if (waited || threadId != 0)
+        ALOGV("event token grabbed (0x%llx)", threadId);
+    if (threadId != 0)
+        state->eventThreadId = threadId;
+
+    dvmDbgUnlockMutex(&state->eventThreadLock);
+}
+
+/*
+ * Clear the threadId and signal anybody waiting.
+ */
+void dvmJdwpClearWaitForEventThread(JdwpState* state)
+{
+    /*
+     * Grab the mutex.  Don't try to go in/out of VMWAIT mode, as this
+     * function is called by dvmSuspendSelf(), and the transition back
+     * to RUNNING would confuse it.
+     */
+    dvmDbgLockMutex(&state->eventThreadLock);
+
+    assert(state->eventThreadId != 0);
+    ALOGV("cleared event token (0x%llx)", state->eventThreadId);
+
+    state->eventThreadId = 0;
+
+    dvmDbgCondSignal(&state->eventThreadCond);
+
+    dvmDbgUnlockMutex(&state->eventThreadLock);
+}
+
+
+/*
+ * Prep an event.  Allocates storage for the message and leaves space for
+ * the header.
+ */
+static ExpandBuf* eventPrep()
+{
+    ExpandBuf* pReq = expandBufAlloc();
+    expandBufAddSpace(pReq, kJDWPHeaderLen);
+
+    return pReq;
+}
+
+/*
+ * Write the header into the buffer and send the packet off to the debugger.
+ *
+ * Takes ownership of "pReq" (currently discards it).
+ */
+static void eventFinish(JdwpState* state, ExpandBuf* pReq)
+{
+    u1* buf = expandBufGetBuffer(pReq);
+
+    set4BE(buf, expandBufGetLength(pReq));
+    set4BE(buf+4, dvmJdwpNextRequestSerial(state));
+    set1(buf+8, 0);     /* flags */
+    set1(buf+9, kJdwpEventCommandSet);
+    set1(buf+10, kJdwpCompositeCommand);
+
+    dvmJdwpSendRequest(state, pReq);
+
+    expandBufFree(pReq);
+}
+
+
+/*
+ * Tell the debugger that we have finished initializing.  This is always
+ * sent, even if the debugger hasn't requested it.
+ *
+ * This should be sent "before the main thread is started and before
+ * any application code has been executed".  The thread ID in the message
+ * must be for the main thread.
+ */
+bool dvmJdwpPostVMStart(JdwpState* state, bool suspend)
+{
+    JdwpSuspendPolicy suspendPolicy;
+    ObjectId threadId = dvmDbgGetThreadSelfId();
+
+    if (suspend)
+        suspendPolicy = SP_ALL;
+    else
+        suspendPolicy = SP_NONE;
+
+    /* probably don't need this here */
+    lockEventMutex(state);
+
+    ExpandBuf* pReq = NULL;
+    if (true) {
+        ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_START));
+        ALOGV("  suspendPolicy=%s", dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, 1);
+
+        expandBufAdd1(pReq, EK_VM_START);
+        expandBufAdd4BE(pReq, 0);       /* requestId */
+        expandBufAdd8BE(pReq, threadId);
+    }
+
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    return true;
+}
+
+/*
+ * A location of interest has been reached.  This handles:
+ *   Breakpoint
+ *   SingleStep
+ *   MethodEntry
+ *   MethodExit
+ * These four types must be grouped together in a single response.  The
+ * "eventFlags" indicates the type of event(s) that have happened.
+ *
+ * Valid mods:
+ *   Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, InstanceOnly
+ *   LocationOnly (for breakpoint/step only)
+ *   Step (for step only)
+ *
+ * Interesting test cases:
+ *  - Put a breakpoint on a native method.  Eclipse creates METHOD_ENTRY
+ *    and METHOD_EXIT events with a ClassOnly mod on the method's class.
+ *  - Use "run to line".  Eclipse creates a BREAKPOINT with Count=1.
+ *  - Single-step to a line with a breakpoint.  Should get a single
+ *    event message with both events in it.
+ */
+bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
+    ObjectId thisPtr, int eventFlags)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    char* nameAlloc = NULL;
+
+    memset(&basket, 0, sizeof(basket));
+    basket.pLoc = pLoc;
+    basket.classId = pLoc->classId;
+    basket.thisPtr = thisPtr;
+    basket.threadId = dvmDbgGetThreadSelfId();
+    basket.className = nameAlloc =
+        dvmDescriptorToName(dvmDbgGetClassDescriptor(pLoc->classId));
+
+    /*
+     * On rare occasions we may need to execute interpreted code in the VM
+     * while handling a request from the debugger.  Don't fire breakpoints
+     * while doing so.  (I don't think we currently do this at all, so
+     * this is mostly paranoia.)
+     */
+    if (basket.threadId == state->debugThreadId) {
+        ALOGV("Ignoring location event in JDWP thread");
+        free(nameAlloc);
+        return false;
+    }
+
+    /*
+     * The debugger variable display tab may invoke the interpreter to format
+     * complex objects.  We want to ignore breakpoints and method entry/exit
+     * traps while working on behalf of the debugger.
+     *
+     * If we don't ignore them, the VM will get hung up, because we'll
+     * suspend on a breakpoint while the debugger is still waiting for its
+     * method invocation to complete.
+     */
+    if (invokeInProgress(state)) {
+        ALOGV("Not checking breakpoints during invoke (%s)", basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    if ((eventFlags & DBG_BREAKPOINT) != 0)
+        findMatchingEvents(state, EK_BREAKPOINT, &basket, matchList,
+            &matchCount);
+    if ((eventFlags & DBG_SINGLE_STEP) != 0)
+        findMatchingEvents(state, EK_SINGLE_STEP, &basket, matchList,
+            &matchCount);
+    if ((eventFlags & DBG_METHOD_ENTRY) != 0)
+        findMatchingEvents(state, EK_METHOD_ENTRY, &basket, matchList,
+            &matchCount);
+    if ((eventFlags & DBG_METHOD_EXIT) != 0)
+        findMatchingEvents(state, EK_METHOD_EXIT, &basket, matchList,
+            &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) %s.%s thread=%llx code=%llx)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.className,
+            dvmDbgGetMethodName(pLoc->classId, pLoc->methodId),
+            basket.threadId, pLoc->idx);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+            dvmJdwpAddLocation(pReq, pLoc);
+        }
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    free(nameAlloc);
+    return matchCount != 0;
+}
+
+/*
+ * A thread is starting or stopping.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly
+ */
+bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+
+    assert(threadId == dvmDbgGetThreadSelfId());
+
+    /*
+     * I don't think this can happen.
+     */
+    if (invokeInProgress(state)) {
+        ALOGW("Not posting thread change during invoke");
+        return false;
+    }
+
+    ModBasket basket;
+    memset(&basket, 0, sizeof(basket));
+    basket.threadId = threadId;
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    if (start)
+        findMatchingEvents(state, EK_THREAD_START, &basket, matchList,
+            &matchCount);
+    else
+        findMatchingEvents(state, EK_THREAD_DEATH, &basket, matchList,
+            &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) thread=%llx)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+        }
+
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    return matchCount != 0;
+}
+
+/*
+ * Send a polite "VM is dying" message to the debugger.
+ *
+ * Skips the usual "event token" stuff.
+ */
+bool dvmJdwpPostVMDeath(JdwpState* state)
+{
+    ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_DEATH));
+
+    ExpandBuf* pReq = eventPrep();
+    expandBufAdd1(pReq, SP_NONE);
+    expandBufAdd4BE(pReq, 1);
+
+    expandBufAdd1(pReq, EK_VM_DEATH);
+    expandBufAdd4BE(pReq, 0);
+    eventFinish(state, pReq);
+    return true;
+}
+
+
+/*
+ * An exception has been thrown.  It may or may not have been caught.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, LocationOnly,
+ *    ExceptionOnly, InstanceOnly
+ *
+ * The "exceptionId" has not been added to the GC-visible object registry,
+ * because there's a pretty good chance that we're not going to send it
+ * up the debugger.
+ */
+bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
+    ObjectId exceptionId, RefTypeId exceptionClassId,
+    const JdwpLocation* pCatchLoc, ObjectId thisPtr)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    char* nameAlloc = NULL;
+
+    memset(&basket, 0, sizeof(basket));
+    basket.pLoc = pThrowLoc;
+    basket.classId = pThrowLoc->classId;
+    basket.threadId = dvmDbgGetThreadSelfId();
+    basket.className = nameAlloc =
+        dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
+    basket.excepClassId = exceptionClassId;
+    basket.caught = (pCatchLoc->classId != 0);
+    basket.thisPtr = thisPtr;
+
+    /* don't try to post an exception caused by the debugger */
+    if (invokeInProgress(state)) {
+        ALOGV("Not posting exception hit during invoke (%s)",basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    findMatchingEvents(state, EK_EXCEPTION, &basket, matchList, &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) thread=%llx exceptId=%llx caught=%d)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId, exceptionId, basket.caught);
+        ALOGV("  throw: %d %llx %x %lld (%s.%s)", pThrowLoc->typeTag,
+            pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
+            dvmDbgGetClassDescriptor(pThrowLoc->classId),
+            dvmDbgGetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
+        if (pCatchLoc->classId == 0) {
+            ALOGV("  catch: (not caught)");
+        } else {
+            ALOGV("  catch: %d %llx %x %lld (%s.%s)", pCatchLoc->typeTag,
+                pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
+                dvmDbgGetClassDescriptor(pCatchLoc->classId),
+                dvmDbgGetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
+        }
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+
+            dvmJdwpAddLocation(pReq, pThrowLoc);
+            expandBufAdd1(pReq, JT_OBJECT);
+            expandBufAdd8BE(pReq, exceptionId);
+            dvmJdwpAddLocation(pReq, pCatchLoc);
+        }
+
+        /* don't let the GC discard it */
+        dvmDbgRegisterObjectId(exceptionId);
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    free(nameAlloc);
+    return matchCount != 0;
+}
+
+/*
+ * Announce that a class has been loaded.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
+ */
+bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
+    const char* signature, int status)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    char* nameAlloc = NULL;
+
+    memset(&basket, 0, sizeof(basket));
+    basket.classId = refTypeId;
+    basket.threadId = dvmDbgGetThreadSelfId();
+    basket.className = nameAlloc =
+        dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
+
+    /* suppress class prep caused by debugger */
+    if (invokeInProgress(state)) {
+        ALOGV("Not posting class prep caused by invoke (%s)",basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    findMatchingEvents(state, EK_CLASS_PREPARE, &basket, matchList,
+        &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) thread=%llx)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        if (basket.threadId == state->debugThreadId) {
+            /*
+             * JDWP says that, for a class prep in the debugger thread, we
+             * should set threadId to null and if any threads were supposed
+             * to be suspended then we suspend all other threads.
+             */
+            ALOGV("  NOTE: class prepare in debugger thread!");
+            basket.threadId = 0;
+            if (suspendPolicy == SP_EVENT_THREAD)
+                suspendPolicy = SP_ALL;
+        }
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+
+            expandBufAdd1(pReq, tag);
+            expandBufAdd8BE(pReq, refTypeId);
+            expandBufAddUtf8String(pReq, (const u1*) signature);
+            expandBufAdd4BE(pReq, status);
+        }
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    free(nameAlloc);
+    return matchCount != 0;
+}
+
+/*
+ * Unload a class.
+ *
+ * Valid mods:
+ *  Count, ClassMatch, ClassExclude
+ */
+bool dvmJdwpPostClassUnload(JdwpState* state, RefTypeId refTypeId)
+{
+    assert(false);      // TODO
+    return false;
+}
+
+/*
+ * Get or set a field.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, FieldOnly,
+ *    InstanceOnly
+ */
+bool dvmJdwpPostFieldAccess(JdwpState* state, int STUFF, ObjectId thisPtr,
+    bool modified, JValue newValue)
+{
+    assert(false);      // TODO
+    return false;
+}
+
+/*
+ * Send up a chunk of DDM data.
+ *
+ * While this takes the form of a JDWP "event", it doesn't interact with
+ * other debugger traffic, and can't suspend the VM, so we skip all of
+ * the fun event token gymnastics.
+ */
+void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
+    int iovcnt)
+{
+    u1 header[kJDWPHeaderLen + 8];
+    size_t dataLen = 0;
+
+    assert(iov != NULL);
+    assert(iovcnt > 0 && iovcnt < 10);
+
+    /*
+     * "Wrap" the contents of the iovec with a JDWP/DDMS header.  We do
+     * this by creating a new copy of the vector with space for the header.
+     */
+    struct iovec wrapiov[iovcnt+1];
+    for (int i = 0; i < iovcnt; i++) {
+        wrapiov[i+1].iov_base = iov[i].iov_base;
+        wrapiov[i+1].iov_len = iov[i].iov_len;
+        dataLen += iov[i].iov_len;
+    }
+
+    /* form the header (JDWP plus DDMS) */
+    set4BE(header, sizeof(header) + dataLen);
+    set4BE(header+4, dvmJdwpNextRequestSerial(state));
+    set1(header+8, 0);     /* flags */
+    set1(header+9, kJDWPDdmCmdSet);
+    set1(header+10, kJDWPDdmCmd);
+    set4BE(header+11, type);
+    set4BE(header+15, dataLen);
+
+    wrapiov[0].iov_base = header;
+    wrapiov[0].iov_len = sizeof(header);
+
+    /*
+     * Make sure we're in VMWAIT in case the write blocks.
+     */
+    int oldStatus = dvmDbgThreadWaiting();
+    dvmJdwpSendBufferedRequest(state, wrapiov, iovcnt+1);
+    dvmDbgThreadContinuing(oldStatus);
+}
diff --git a/vm/jdwp/JdwpEvent.h b/vm/jdwp/JdwpEvent.h
new file mode 100644
index 0000000..c7d3e3c
--- /dev/null
+++ b/vm/jdwp/JdwpEvent.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Handle registration of events, and debugger event notification.
+ */
+#ifndef DALVIK_JDWP_JDWPEVENT_H_
+#define DALVIK_JDWP_JDWPEVENT_H_
+
+#include "JdwpConstants.h"
+#include "ExpandBuf.h"
+
+/*
+ * Event modifiers.  A JdwpEvent may have zero or more of these.
+ */
+union JdwpEventMod {
+    u1      modKind;                /* JdwpModKind */
+    struct {
+        u1          modKind;
+        int         count;
+    } count;
+    struct {
+        u1          modKind;
+        u4          exprId;
+    } conditional;
+    struct {
+        u1          modKind;
+        ObjectId    threadId;
+    } threadOnly;
+    struct {
+        u1          modKind;
+        RefTypeId   refTypeId;
+    } classOnly;
+    struct {
+        u1          modKind;
+        char*       classPattern;
+    } classMatch;
+    struct {
+        u1          modKind;
+        char*       classPattern;
+    } classExclude;
+    struct {
+        u1          modKind;
+        JdwpLocation loc;
+    } locationOnly;
+    struct {
+        u1          modKind;
+        u1          caught;
+        u1          uncaught;
+        RefTypeId   refTypeId;
+    } exceptionOnly;
+    struct {
+        u1          modKind;
+        RefTypeId   refTypeId;
+        FieldId     fieldId;
+    } fieldOnly;
+    struct {
+        u1          modKind;
+        ObjectId    threadId;
+        int         size;           /* JdwpStepSize */
+        int         depth;          /* JdwpStepDepth */
+    } step;
+    struct {
+        u1          modKind;
+        ObjectId    objectId;
+    } instanceOnly;
+};
+
+/*
+ * One of these for every registered event.
+ *
+ * We over-allocate the struct to hold the modifiers.
+ */
+struct JdwpEvent {
+    JdwpEvent* prev;           /* linked list */
+    JdwpEvent* next;
+
+    JdwpEventKind eventKind;      /* what kind of event is this? */
+    JdwpSuspendPolicy suspendPolicy;  /* suspend all, none, or self? */
+    int modCount;       /* #of entries in mods[] */
+    u4 requestId;      /* serial#, reported to debugger */
+
+    JdwpEventMod mods[1];        /* MUST be last field in struct */
+};
+
+/*
+ * Allocate an event structure with enough space.
+ */
+JdwpEvent* dvmJdwpEventAlloc(int numMods);
+void dvmJdwpEventFree(JdwpEvent* pEvent);
+
+/*
+ * Register an event by adding it to the event list.
+ *
+ * "*pEvent" must be storage allocated with jdwpEventAlloc().  The caller
+ * may discard its pointer after calling this.
+ */
+JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent);
+
+/*
+ * Unregister an event, given the requestId.
+ */
+void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId);
+
+/*
+ * Unregister all events.
+ */
+void dvmJdwpUnregisterAll(JdwpState* state);
+
+/*
+ * Send an event, formatted into "pReq", to the debugger.
+ *
+ * (Messages are sent asynchronously, and do not receive a reply.)
+ */
+bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq);
+
+#endif  // DALVIK_JDWP_JDWPEVENT_H_
diff --git a/vm/jdwp/JdwpHandler.cpp b/vm/jdwp/JdwpHandler.cpp
new file mode 100644
index 0000000..5ce432c
--- /dev/null
+++ b/vm/jdwp/JdwpHandler.cpp
@@ -0,0 +1,1973 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handle messages from debugger.
+ *
+ * GENERAL NOTE: we're not currently testing the message length for
+ * correctness.  This is usually a bad idea, but here we can probably
+ * get away with it so long as the debugger isn't broken.  We can
+ * change the "read" macros to use "dataLen" to avoid wandering into
+ * bad territory, and have a single "is dataLen correct" check at the
+ * end of each function.  Not needed at this time.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include "jdwp/JdwpEvent.h"
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/ExpandBuf.h"
+
+#include "Bits.h"
+#include "Atomic.h"
+#include "DalvikVersion.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Helper function: read a "location" from an input buffer.
+ */
+static void jdwpReadLocation(const u1** pBuf, JdwpLocation* pLoc)
+{
+    memset(pLoc, 0, sizeof(*pLoc));     /* allows memcmp() later */
+    pLoc->typeTag = read1(pBuf);
+    pLoc->classId = dvmReadObjectId(pBuf);
+    pLoc->methodId = dvmReadMethodId(pBuf);
+    pLoc->idx = read8BE(pBuf);
+}
+
+/*
+ * Helper function: write a "location" into the reply buffer.
+ */
+void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc)
+{
+    expandBufAdd1(pReply, pLoc->typeTag);
+    expandBufAddObjectId(pReply, pLoc->classId);
+    expandBufAddMethodId(pReply, pLoc->methodId);
+    expandBufAdd8BE(pReply, pLoc->idx);
+}
+
+/*
+ * Helper function: read a variable-width value from the input buffer.
+ */
+static u8 jdwpReadValue(const u1** pBuf, int width)
+{
+    u8 value;
+
+    switch (width) {
+    case 1:     value = read1(pBuf);                break;
+    case 2:     value = read2BE(pBuf);              break;
+    case 4:     value = read4BE(pBuf);              break;
+    case 8:     value = read8BE(pBuf);              break;
+    default:    value = (u8) -1; assert(false);     break;
+    }
+
+    return value;
+}
+
+/*
+ * Helper function: write a variable-width value into the output input buffer.
+ */
+static void jdwpWriteValue(ExpandBuf* pReply, int width, u8 value)
+{
+    switch (width) {
+    case 1:     expandBufAdd1(pReply, value);       break;
+    case 2:     expandBufAdd2BE(pReply, value);     break;
+    case 4:     expandBufAdd4BE(pReply, value);     break;
+    case 8:     expandBufAdd8BE(pReply, value);     break;
+    default:    assert(false);                      break;
+    }
+}
+
+/*
+ * Common code for *_InvokeMethod requests.
+ *
+ * If "isConstructor" is set, this returns "objectId" rather than the
+ * expected-to-be-void return value of the called function.
+ */
+static JdwpError finishInvoke(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply,
+    ObjectId threadId, ObjectId objectId, RefTypeId classId, MethodId methodId,
+    bool isConstructor)
+{
+    assert(!isConstructor || objectId != 0);
+
+    u4 numArgs = read4BE(&buf);
+
+    ALOGV("    --> threadId=%llx objectId=%llx", threadId, objectId);
+    ALOGV("        classId=%llx methodId=%x %s.%s",
+        classId, methodId,
+        dvmDbgGetClassDescriptor(classId),
+        dvmDbgGetMethodName(classId, methodId));
+    ALOGV("        %d args:", numArgs);
+
+    u8* argArray = NULL;
+    if (numArgs > 0)
+        argArray = (ObjectId*) malloc(sizeof(ObjectId) * numArgs);
+
+    for (u4 i = 0; i < numArgs; i++) {
+        u1 typeTag = read1(&buf);
+        int width = dvmDbgGetTagWidth(typeTag);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("          '%c'(%d): 0x%llx", typeTag, width, value);
+        argArray[i] = value;
+    }
+
+    u4 options = read4BE(&buf);  /* enum InvokeOptions bit flags */
+    ALOGV("        options=0x%04x%s%s", options,
+        (options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
+        (options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
+
+
+    u1 resultTag;
+    u8 resultValue;
+    ObjectId exceptObjId;
+    JdwpError err = dvmDbgInvokeMethod(threadId, objectId, classId, methodId,
+            numArgs, argArray, options,
+            &resultTag, &resultValue, &exceptObjId);
+    if (err != ERR_NONE)
+        goto bail;
+
+    if (err == ERR_NONE) {
+        if (isConstructor) {
+            expandBufAdd1(pReply, JT_OBJECT);
+            expandBufAddObjectId(pReply, objectId);
+        } else {
+            int width = dvmDbgGetTagWidth(resultTag);
+
+            expandBufAdd1(pReply, resultTag);
+            if (width != 0)
+                jdwpWriteValue(pReply, width, resultValue);
+        }
+        expandBufAdd1(pReply, JT_OBJECT);
+        expandBufAddObjectId(pReply, exceptObjId);
+
+        ALOGV("  --> returned '%c' 0x%llx (except=%08llx)",
+            resultTag, resultValue, exceptObjId);
+
+        /* show detailed debug output */
+        if (resultTag == JT_STRING && exceptObjId == 0) {
+            if (resultValue != 0) {
+                char* str = dvmDbgStringToUtf8(resultValue);
+                ALOGV("      string '%s'", str);
+                free(str);
+            } else {
+                ALOGV("      string (null)");
+            }
+        }
+    }
+
+bail:
+    free(argArray);
+    return err;
+}
+
+
+/*
+ * Request for version info.
+ */
+static JdwpError handleVM_Version(JdwpState* state, const u1* buf,
+    int dataLen, ExpandBuf* pReply)
+{
+    char tmpBuf[128];
+
+    /* text information on VM version */
+    sprintf(tmpBuf, "Android DalvikVM %d.%d.%d",
+        DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+    expandBufAddUtf8String(pReply, (const u1*) tmpBuf);
+    /* JDWP version numbers */
+    expandBufAdd4BE(pReply, 1);        // major
+    expandBufAdd4BE(pReply, 5);        // minor
+    /* VM JRE version */
+    expandBufAddUtf8String(pReply, (const u1*) "1.5.0");  /* e.g. 1.5.0_04 */
+    /* target VM name */
+    expandBufAddUtf8String(pReply, (const u1*) "DalvikVM");
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a class JNI signature (e.g. "Ljava/lang/Error;"), return the
+ * referenceTypeID.  We need to send back more than one if the class has
+ * been loaded by multiple class loaders.
+ */
+static JdwpError handleVM_ClassesBySignature(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    size_t strLen;
+    char* classDescriptor = readNewUtf8String(&buf, &strLen);
+    ALOGV("  Req for class by signature '%s'", classDescriptor);
+
+    /*
+     * TODO: if a class with the same name has been loaded multiple times
+     * (by different class loaders), we're supposed to return each of them.
+     *
+     * NOTE: this may mangle "className".
+     */
+    u4 numClasses;
+    RefTypeId refTypeId;
+    if (!dvmDbgFindLoadedClassBySignature(classDescriptor, &refTypeId)) {
+        /* not currently loaded */
+        ALOGV("    --> no match!");
+        numClasses = 0;
+    } else {
+        /* just the one */
+        numClasses = 1;
+    }
+
+    expandBufAdd4BE(pReply, numClasses);
+
+    if (numClasses > 0) {
+        u1 typeTag;
+        u4 status;
+
+        /* get class vs. interface and status flags */
+        dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
+
+        expandBufAdd1(pReply, typeTag);
+        expandBufAddRefTypeId(pReply, refTypeId);
+        expandBufAdd4BE(pReply, status);
+    }
+
+    free(classDescriptor);
+
+    return ERR_NONE;
+}
+
+/*
+ * Handle request for the thread IDs of all running threads.
+ *
+ * We exclude ourselves from the list, because we don't allow ourselves
+ * to be suspended, and that violates some JDWP expectations.
+ */
+static JdwpError handleVM_AllThreads(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId* pThreadIds;
+    u4 threadCount;
+    dvmDbgGetAllThreads(&pThreadIds, &threadCount);
+
+    expandBufAdd4BE(pReply, threadCount);
+
+    ObjectId* walker = pThreadIds;
+    for (u4 i = 0; i < threadCount; i++) {
+        expandBufAddObjectId(pReply, *walker++);
+    }
+
+    free(pThreadIds);
+
+    return ERR_NONE;
+}
+
+/*
+ * List all thread groups that do not have a parent.
+ */
+static JdwpError handleVM_TopLevelThreadGroups(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    /*
+     * TODO: maintain a list of parentless thread groups in the VM.
+     *
+     * For now, just return "system".  Application threads are created
+     * in "main", which is a child of "system".
+     */
+    u4 groups = 1;
+    expandBufAdd4BE(pReply, groups);
+    //threadGroupId = debugGetMainThreadGroup();
+    //expandBufAdd8BE(pReply, threadGroupId);
+    ObjectId threadGroupId = dvmDbgGetSystemThreadGroupId();
+    expandBufAddObjectId(pReply, threadGroupId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Respond with the sizes of the basic debugger types.
+ *
+ * All IDs are 8 bytes.
+ */
+static JdwpError handleVM_IDSizes(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    expandBufAdd4BE(pReply, sizeof(FieldId));
+    expandBufAdd4BE(pReply, sizeof(MethodId));
+    expandBufAdd4BE(pReply, sizeof(ObjectId));
+    expandBufAdd4BE(pReply, sizeof(RefTypeId));
+    expandBufAdd4BE(pReply, sizeof(FrameId));
+    return ERR_NONE;
+}
+
+/*
+ * The debugger is politely asking to disconnect.  We're good with that.
+ *
+ * We could resume threads and clean up pinned references, but we can do
+ * that when the TCP connection drops.
+ */
+static JdwpError handleVM_Dispose(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    return ERR_NONE;
+}
+
+/*
+ * Suspend the execution of the application running in the VM (i.e. suspend
+ * all threads).
+ *
+ * This needs to increment the "suspend count" on all threads.
+ */
+static JdwpError handleVM_Suspend(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    dvmDbgSuspendVM(false);
+    return ERR_NONE;
+}
+
+/*
+ * Resume execution.  Decrements the "suspend count" of all threads.
+ */
+static JdwpError handleVM_Resume(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    dvmDbgResumeVM();
+    return ERR_NONE;
+}
+
+/*
+ * The debugger wants the entire VM to exit.
+ */
+static JdwpError handleVM_Exit(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u4 exitCode = get4BE(buf);
+
+    ALOGW("Debugger is telling the VM to exit with code=%d", exitCode);
+
+    dvmDbgExit(exitCode);
+    return ERR_NOT_IMPLEMENTED;     // shouldn't get here
+}
+
+/*
+ * Create a new string in the VM and return its ID.
+ *
+ * (Ctrl-Shift-I in Eclipse on an array of objects causes it to create the
+ * string "java.util.Arrays".)
+ */
+static JdwpError handleVM_CreateString(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    size_t strLen;
+    char* str = readNewUtf8String(&buf, &strLen);
+
+    ALOGV("  Req to create string '%s'", str);
+
+    ObjectId stringId = dvmDbgCreateString(str);
+    if (stringId == 0)
+        return ERR_OUT_OF_MEMORY;
+
+    expandBufAddObjectId(pReply, stringId);
+    return ERR_NONE;
+}
+
+/*
+ * Tell the debugger what we are capable of.
+ */
+static JdwpError handleVM_Capabilities(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    expandBufAdd1(pReply, false);   /* canWatchFieldModification */
+    expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
+    expandBufAdd1(pReply, false);   /* canGetBytecodes */
+    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
+    expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
+    expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
+    expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
+    return ERR_NONE;
+}
+
+/*
+ * Return classpath and bootclasspath.
+ */
+static JdwpError handleVM_ClassPaths(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    char baseDir[2] = "/";
+
+    /*
+     * TODO: make this real.  Not important for remote debugging, but
+     * might be useful for local debugging.
+     */
+    u4 classPaths = 1;
+    u4 bootClassPaths = 0;
+
+    expandBufAddUtf8String(pReply, (const u1*) baseDir);
+    expandBufAdd4BE(pReply, classPaths);
+    for (u4 i = 0; i < classPaths; i++) {
+        expandBufAddUtf8String(pReply, (const u1*) ".");
+    }
+
+    expandBufAdd4BE(pReply, bootClassPaths);
+    for (u4 i = 0; i < classPaths; i++) {
+        /* add bootclasspath components as strings */
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Release a list of object IDs.  (Seen in jdb.)
+ *
+ * Currently does nothing.
+ */
+static JdwpError HandleVM_DisposeObjects(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    return ERR_NONE;
+}
+
+/*
+ * Tell the debugger what we are capable of.
+ */
+static JdwpError handleVM_CapabilitiesNew(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    expandBufAdd1(pReply, false);   /* canWatchFieldModification */
+    expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
+    expandBufAdd1(pReply, false);   /* canGetBytecodes */
+    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
+    expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
+    expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
+    expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
+    expandBufAdd1(pReply, false);   /* canRedefineClasses */
+    expandBufAdd1(pReply, false);   /* canAddMethod */
+    expandBufAdd1(pReply, false);   /* canUnrestrictedlyRedefineClasses */
+    expandBufAdd1(pReply, false);   /* canPopFrames */
+    expandBufAdd1(pReply, false);   /* canUseInstanceFilters */
+    expandBufAdd1(pReply, false);   /* canGetSourceDebugExtension */
+    expandBufAdd1(pReply, false);   /* canRequestVMDeathEvent */
+    expandBufAdd1(pReply, false);   /* canSetDefaultStratum */
+    expandBufAdd1(pReply, false);   /* 1.6: canGetInstanceInfo */
+    expandBufAdd1(pReply, false);   /* 1.6: canRequestMonitorEvents */
+    expandBufAdd1(pReply, false);   /* 1.6: canGetMonitorFrameInfo */
+    expandBufAdd1(pReply, false);   /* 1.6: canUseSourceNameFilters */
+    expandBufAdd1(pReply, false);   /* 1.6: canGetConstantPool */
+    expandBufAdd1(pReply, false);   /* 1.6: canForceEarlyReturn */
+
+    /* fill in reserved22 through reserved32; note count started at 1 */
+    for (int i = 22; i <= 32; i++)
+        expandBufAdd1(pReply, false);   /* reservedN */
+    return ERR_NONE;
+}
+
+/*
+ * Cough up the complete list of classes.
+ */
+static JdwpError handleVM_AllClassesWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u4 numClasses = 0;
+    RefTypeId* classRefBuf = NULL;
+
+    dvmDbgGetClassList(&numClasses, &classRefBuf);
+
+    expandBufAdd4BE(pReply, numClasses);
+
+    for (u4 i = 0; i < numClasses; i++) {
+        static const u1 genericSignature[1] = "";
+        u1 refTypeTag;
+        const char* signature;
+        u4 status;
+
+        dvmDbgGetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
+
+        expandBufAdd1(pReply, refTypeTag);
+        expandBufAddRefTypeId(pReply, classRefBuf[i]);
+        expandBufAddUtf8String(pReply, (const u1*) signature);
+        expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, status);
+    }
+
+    free(classRefBuf);
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeID, return a string with the JNI reference type
+ * signature (e.g. "Ljava/lang/Error;").
+ */
+static JdwpError handleRT_Signature(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for signature of refTypeId=0x%llx", refTypeId);
+    const char* signature = dvmDbgGetSignature(refTypeId);
+    expandBufAddUtf8String(pReply, (const u1*) signature);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the modifiers (a/k/a access flags) for a reference type.
+ */
+static JdwpError handleRT_Modifiers(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    u4 modBits = dvmDbgGetAccessFlags(refTypeId);
+
+    expandBufAdd4BE(pReply, modBits);
+
+    return ERR_NONE;
+}
+
+/*
+ * Get values from static fields in a reference type.
+ */
+static JdwpError handleRT_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    u4 numFields = read4BE(&buf);
+
+    ALOGV("  RT_GetValues %u:", numFields);
+
+    expandBufAdd4BE(pReply, numFields);
+    for (u4 i = 0; i < numFields; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+        dvmDbgGetStaticFieldValue(refTypeId, fieldId, pReply);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Get the name of the source file in which a reference type was declared.
+ */
+static JdwpError handleRT_SourceFile(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    const char* fileName = dvmDbgGetSourceFile(refTypeId);
+    if (fileName != NULL) {
+        expandBufAddUtf8String(pReply, (const u1*) fileName);
+        return ERR_NONE;
+    } else {
+        return ERR_ABSENT_INFORMATION;
+    }
+}
+
+/*
+ * Return the current status of the reference type.
+ */
+static JdwpError handleRT_Status(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    /* get status flags */
+    u1 typeTag;
+    u4 status;
+    dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
+    expandBufAdd4BE(pReply, status);
+    return ERR_NONE;
+}
+
+/*
+ * Return interfaces implemented directly by this class.
+ */
+static JdwpError handleRT_Interfaces(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for interfaces in %llx (%s)", refTypeId,
+        dvmDbgGetClassDescriptor(refTypeId));
+
+    dvmDbgOutputAllInterfaces(refTypeId, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the class object corresponding to this type.
+ */
+static JdwpError handleRT_ClassObject(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    ObjectId classObjId = dvmDbgGetClassObject(refTypeId);
+
+    ALOGV("  RefTypeId %llx -> ObjectId %llx", refTypeId, classObjId);
+
+    expandBufAddObjectId(pReply, classObjId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the value of the SourceDebugExtension attribute.
+ *
+ * JDB seems interested, but DEX files don't currently support this.
+ */
+static JdwpError handleRT_SourceDebugExtension(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    /* referenceTypeId in, string out */
+    return ERR_ABSENT_INFORMATION;
+}
+
+/*
+ * Like RT_Signature but with the possibility of a "generic signature".
+ */
+static JdwpError handleRT_SignatureWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    static const u1 genericSignature[1] = "";
+
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for signature of refTypeId=0x%llx", refTypeId);
+    const char* signature = dvmDbgGetSignature(refTypeId);
+    if (signature != NULL) {
+        expandBufAddUtf8String(pReply, (const u1*) signature);
+    } else {
+        ALOGW("No signature for refTypeId=0x%llx", refTypeId);
+        expandBufAddUtf8String(pReply, (const u1*) "Lunknown;");
+    }
+    expandBufAddUtf8String(pReply, genericSignature);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the instance of java.lang.ClassLoader that loaded the specified
+ * reference type, or null if it was loaded by the system loader.
+ */
+static JdwpError handleRT_ClassLoader(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    expandBufAddObjectId(pReply, dvmDbgGetClassLoader(refTypeId));
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeId, return a block of stuff that describes the
+ * fields declared by a class.
+ */
+static JdwpError handleRT_FieldsWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    ALOGV("  Req for fields in refTypeId=0x%llx", refTypeId);
+    ALOGV("  --> '%s'", dvmDbgGetSignature(refTypeId));
+
+    dvmDbgOutputAllFields(refTypeId, true, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeID, return a block of goodies describing the
+ * methods declared by a class.
+ */
+static JdwpError handleRT_MethodsWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for methods in refTypeId=0x%llx", refTypeId);
+    ALOGV("  --> '%s'", dvmDbgGetSignature(refTypeId));
+
+    dvmDbgOutputAllMethods(refTypeId, true, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the immediate superclass of a class.
+ */
+static JdwpError handleCT_Superclass(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+
+    RefTypeId superClassId = dvmDbgGetSuperclass(classId);
+
+    expandBufAddRefTypeId(pReply, superClassId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Set static class values.
+ */
+static JdwpError handleCT_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    u4 values = read4BE(&buf);
+
+    ALOGV("  Req to set %d values in classId=%llx", values, classId);
+
+    for (u4 i = 0; i < values; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+        u1 fieldTag = dvmDbgGetStaticFieldBasicTag(classId, fieldId);
+        int width = dvmDbgGetTagWidth(fieldTag);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("    --> field=%x tag=%c -> %lld", fieldId, fieldTag, value);
+        dvmDbgSetStaticFieldValue(classId, fieldId, value, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Invoke a static method.
+ *
+ * Example: Eclipse sometimes uses java/lang/Class.forName(String s) on
+ * values in the "variables" display.
+ */
+static JdwpError handleCT_InvokeMethod(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    ObjectId threadId = dvmReadObjectId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    return finishInvoke(state, buf, dataLen, pReply,
+            threadId, 0, classId, methodId, false);
+}
+
+/*
+ * Create a new object of the requested type, and invoke the specified
+ * constructor.
+ *
+ * Example: in IntelliJ, create a watch on "new String(myByteArray)" to
+ * see the contents of a byte[] as a string.
+ */
+static JdwpError handleCT_NewInstance(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    ObjectId threadId = dvmReadObjectId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    ALOGV("Creating instance of %s", dvmDbgGetClassDescriptor(classId));
+    ObjectId objectId = dvmDbgCreateObject(classId);
+    if (objectId == 0)
+        return ERR_OUT_OF_MEMORY;
+
+    return finishInvoke(state, buf, dataLen, pReply,
+            threadId, objectId, classId, methodId, true);
+}
+
+/*
+ * Create a new array object of the requested type and length.
+ */
+static JdwpError handleAT_newInstance(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId arrayTypeId = dvmReadRefTypeId(&buf);
+    u4 length = read4BE(&buf);
+
+    ALOGV("Creating array %s[%u]",
+        dvmDbgGetClassDescriptor(arrayTypeId), length);
+    ObjectId objectId = dvmDbgCreateArrayObject(arrayTypeId, length);
+    if (objectId == 0)
+        return ERR_OUT_OF_MEMORY;
+
+    expandBufAdd1(pReply, JT_ARRAY);
+    expandBufAddObjectId(pReply, objectId);
+    return ERR_NONE;
+}
+
+/*
+ * Return line number information for the method, if present.
+ */
+static JdwpError handleM_LineTable(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    ALOGV("  Req for line table in %s.%s",
+        dvmDbgGetClassDescriptor(refTypeId),
+        dvmDbgGetMethodName(refTypeId,methodId));
+
+    dvmDbgOutputLineTable(refTypeId, methodId, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Pull out the LocalVariableTable goodies.
+ */
+static JdwpError handleM_VariableTableWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    ALOGV("  Req for LocalVarTab in class=%s method=%s",
+        dvmDbgGetClassDescriptor(classId),
+        dvmDbgGetMethodName(classId, methodId));
+
+    /*
+     * We could return ERR_ABSENT_INFORMATION here if the DEX file was
+     * built without local variable information.  That will cause Eclipse
+     * to make a best-effort attempt at displaying local variables
+     * anonymously.  However, the attempt isn't very good, so we're probably
+     * better off just not showing anything.
+     */
+    dvmDbgOutputVariableTable(classId, methodId, true, pReply);
+    return ERR_NONE;
+}
+
+/*
+ * Given an object reference, return the runtime type of the object
+ * (class or array).
+ *
+ * This can get called on different things, e.g. threadId gets
+ * passed in here.
+ */
+static JdwpError handleOR_ReferenceType(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    ALOGV("  Req for type of objectId=0x%llx", objectId);
+
+    u1 refTypeTag;
+    RefTypeId typeId;
+    dvmDbgGetObjectType(objectId, &refTypeTag, &typeId);
+
+    expandBufAdd1(pReply, refTypeTag);
+    expandBufAddRefTypeId(pReply, typeId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Get values from the fields of an object.
+ */
+static JdwpError handleOR_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    u4 numFields = read4BE(&buf);
+
+    ALOGV("  Req for %d fields from objectId=0x%llx", numFields, objectId);
+
+    expandBufAdd4BE(pReply, numFields);
+
+    for (u4 i = 0; i < numFields; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+        dvmDbgGetFieldValue(objectId, fieldId, pReply);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Set values in the fields of an object.
+ */
+static JdwpError handleOR_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    u4 numFields = read4BE(&buf);
+
+    ALOGV("  Req to set %d fields in objectId=0x%llx", numFields, objectId);
+
+    for (u4 i = 0; i < numFields; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+
+        u1 fieldTag = dvmDbgGetFieldBasicTag(objectId, fieldId);
+        int width = dvmDbgGetTagWidth(fieldTag);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("    --> fieldId=%x tag='%c'(%d) value=%lld",
+            fieldId, fieldTag, width, value);
+
+        dvmDbgSetFieldValue(objectId, fieldId, value, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Invoke an instance method.  The invocation must occur in the specified
+ * thread, which must have been suspended by an event.
+ *
+ * The call is synchronous.  All threads in the VM are resumed, unless the
+ * SINGLE_THREADED flag is set.
+ *
+ * If you ask Eclipse to "inspect" an object (or ask JDB to "print" an
+ * object), it will try to invoke the object's toString() function.  This
+ * feature becomes crucial when examining ArrayLists with Eclipse.
+ */
+static JdwpError handleOR_InvokeMethod(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    ObjectId threadId = dvmReadObjectId(&buf);
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    return finishInvoke(state, buf, dataLen, pReply,
+            threadId, objectId, classId, methodId, false);
+}
+
+/*
+ * Disable garbage collection of the specified object.
+ */
+static JdwpError handleOR_DisableCollection(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    // this is currently a no-op
+    return ERR_NONE;
+}
+
+/*
+ * Enable garbage collection of the specified object.
+ */
+static JdwpError handleOR_EnableCollection(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    // this is currently a no-op
+    return ERR_NONE;
+}
+
+/*
+ * Determine whether an object has been garbage collected.
+ */
+static JdwpError handleOR_IsCollected(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId;
+
+    objectId = dvmReadObjectId(&buf);
+    ALOGV("  Req IsCollected(0x%llx)", objectId);
+
+    // TODO: currently returning false; must integrate with GC
+    expandBufAdd1(pReply, 0);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the string value in a string object.
+ */
+static JdwpError handleSR_Value(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId stringObject = dvmReadObjectId(&buf);
+    char* str = dvmDbgStringToUtf8(stringObject);
+
+    ALOGV("  Req for str %llx --> '%s'", stringObject, str);
+
+    expandBufAddUtf8String(pReply, (u1*) str);
+    free(str);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return a thread's name.
+ */
+static JdwpError handleTR_Name(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    ALOGV("  Req for name of thread 0x%llx", threadId);
+    char* name = dvmDbgGetThreadName(threadId);
+    if (name == NULL)
+        return ERR_INVALID_THREAD;
+
+    expandBufAddUtf8String(pReply, (u1*) name);
+    free(name);
+
+    return ERR_NONE;
+}
+
+/*
+ * Suspend the specified thread.
+ *
+ * It's supposed to remain suspended even if interpreted code wants to
+ * resume it; only the JDI is allowed to resume it.
+ */
+static JdwpError handleTR_Suspend(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    if (threadId == dvmDbgGetThreadSelfId()) {
+        ALOGI("  Warning: ignoring request to suspend self");
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+    ALOGV("  Req to suspend thread 0x%llx", threadId);
+
+    dvmDbgSuspendThread(threadId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Resume the specified thread.
+ */
+static JdwpError handleTR_Resume(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    if (threadId == dvmDbgGetThreadSelfId()) {
+        ALOGI("  Warning: ignoring request to resume self");
+        return ERR_NONE;
+    }
+    ALOGV("  Req to resume thread 0x%llx", threadId);
+
+    dvmDbgResumeThread(threadId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return status of specified thread.
+ */
+static JdwpError handleTR_Status(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    ALOGV("  Req for status of thread 0x%llx", threadId);
+
+    u4 threadStatus;
+    u4 suspendStatus;
+    if (!dvmDbgGetThreadStatus(threadId, &threadStatus, &suspendStatus))
+        return ERR_INVALID_THREAD;
+
+    ALOGV("    --> %s, %s",
+        dvmJdwpThreadStatusStr((JdwpThreadStatus) threadStatus),
+        dvmJdwpSuspendStatusStr((JdwpSuspendStatus) suspendStatus));
+
+    expandBufAdd4BE(pReply, threadStatus);
+    expandBufAdd4BE(pReply, suspendStatus);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the thread group that the specified thread is a member of.
+ */
+static JdwpError handleTR_ThreadGroup(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    /* currently not handling these */
+    ObjectId threadGroupId = dvmDbgGetThreadGroup(threadId);
+    expandBufAddObjectId(pReply, threadGroupId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the current call stack of a suspended thread.
+ *
+ * If the thread isn't suspended, the error code isn't defined, but should
+ * be THREAD_NOT_SUSPENDED.
+ */
+static JdwpError handleTR_Frames(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    u4 startFrame = read4BE(&buf);
+    u4 length = read4BE(&buf);
+
+    if (!dvmDbgThreadExists(threadId))
+        return ERR_INVALID_THREAD;
+    if (!dvmDbgIsSuspended(threadId)) {
+        ALOGV("  Rejecting req for frames in running thread '%s' (%llx)",
+            dvmDbgGetThreadName(threadId), threadId);
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+
+    int frameCount = dvmDbgGetThreadFrameCount(threadId);
+
+    ALOGV("  Request for frames: threadId=%llx start=%d length=%d [count=%d]",
+        threadId, startFrame, length, frameCount);
+    if (frameCount <= 0)
+        return ERR_THREAD_NOT_SUSPENDED;    /* == 0 means 100% native */
+
+    if (length == (u4) -1)
+        length = frameCount;
+    assert((int) startFrame >= 0 && (int) startFrame < frameCount);
+    assert((int) (startFrame + length) <= frameCount);
+
+    u4 frames = length;
+    expandBufAdd4BE(pReply, frames);
+    for (u4 i = startFrame; i < (startFrame+length); i++) {
+        FrameId frameId;
+        JdwpLocation loc;
+
+        dvmDbgGetThreadFrame(threadId, i, &frameId, &loc);
+
+        expandBufAdd8BE(pReply, frameId);
+        dvmJdwpAddLocation(pReply, &loc);
+
+        LOGVV("    Frame %d: id=%llx loc={type=%d cls=%llx mth=%x loc=%llx}",
+            i, frameId, loc.typeTag, loc.classId, loc.methodId, loc.idx);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the #of frames on the specified thread, which must be suspended.
+ */
+static JdwpError handleTR_FrameCount(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    if (!dvmDbgThreadExists(threadId))
+        return ERR_INVALID_THREAD;
+    if (!dvmDbgIsSuspended(threadId)) {
+        ALOGV("  Rejecting req for frames in running thread '%s' (%llx)",
+            dvmDbgGetThreadName(threadId), threadId);
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+
+    int frameCount = dvmDbgGetThreadFrameCount(threadId);
+    if (frameCount < 0)
+        return ERR_INVALID_THREAD;
+    expandBufAdd4BE(pReply, (u4)frameCount);
+
+    return ERR_NONE;
+}
+
+/*
+ * Get the monitor that the thread is waiting on.
+ */
+static JdwpError handleTR_CurrentContendedMonitor(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId;
+
+    threadId = dvmReadObjectId(&buf);
+
+    // TODO: create an Object to represent the monitor (we're currently
+    // just using a raw Monitor struct in the VM)
+
+    return ERR_NOT_IMPLEMENTED;
+}
+
+/*
+ * Return the suspend count for the specified thread.
+ *
+ * (The thread *might* still be running -- it might not have examined
+ * its suspend count recently.)
+ */
+static JdwpError handleTR_SuspendCount(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    u4 suspendCount = dvmDbgGetThreadSuspendCount(threadId);
+    expandBufAdd4BE(pReply, suspendCount);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the name of a thread group.
+ *
+ * The Eclipse debugger recognizes "main" and "system" as special.
+ */
+static JdwpError handleTGR_Name(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadGroupId = dvmReadObjectId(&buf);
+    ALOGV("  Req for name of threadGroupId=0x%llx", threadGroupId);
+
+    char* name = dvmDbgGetThreadGroupName(threadGroupId);
+    if (name != NULL)
+        expandBufAddUtf8String(pReply, (u1*) name);
+    else {
+        expandBufAddUtf8String(pReply, (u1*) "BAD-GROUP-ID");
+        ALOGW("bad thread group ID");
+    }
+
+    free(name);
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the thread group -- if any -- that contains the specified
+ * thread group.
+ */
+static JdwpError handleTGR_Parent(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId groupId = dvmReadObjectId(&buf);
+
+    ObjectId parentGroup = dvmDbgGetThreadGroupParent(groupId);
+    expandBufAddObjectId(pReply, parentGroup);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the active threads and thread groups that are part of the
+ * specified thread group.
+ */
+static JdwpError handleTGR_Children(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadGroupId = dvmReadObjectId(&buf);
+    ALOGV("  Req for threads in threadGroupId=0x%llx", threadGroupId);
+
+    ObjectId* pThreadIds;
+    u4 threadCount;
+    dvmDbgGetThreadGroupThreads(threadGroupId, &pThreadIds, &threadCount);
+
+    expandBufAdd4BE(pReply, threadCount);
+
+    for (u4 i = 0; i < threadCount; i++)
+        expandBufAddObjectId(pReply, pThreadIds[i]);
+    free(pThreadIds);
+
+    /*
+     * TODO: finish support for child groups
+     *
+     * For now, just show that "main" is a child of "system".
+     */
+    if (threadGroupId == dvmDbgGetSystemThreadGroupId()) {
+        expandBufAdd4BE(pReply, 1);
+        expandBufAddObjectId(pReply, dvmDbgGetMainThreadGroupId());
+    } else {
+        expandBufAdd4BE(pReply, 0);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the #of components in the array.
+ */
+static JdwpError handleAR_Length(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId arrayId = dvmReadObjectId(&buf);
+    ALOGV("  Req for length of array 0x%llx", arrayId);
+
+    u4 arrayLength = dvmDbgGetArrayLength(arrayId);
+
+    ALOGV("    --> %d", arrayLength);
+
+    expandBufAdd4BE(pReply, arrayLength);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the values from an array.
+ */
+static JdwpError handleAR_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId arrayId = dvmReadObjectId(&buf);
+    u4 firstIndex = read4BE(&buf);
+    u4 length = read4BE(&buf);
+
+    u1 tag = dvmDbgGetArrayElementTag(arrayId);
+    ALOGV("  Req for array values 0x%llx first=%d len=%d (elem tag=%c)",
+        arrayId, firstIndex, length, tag);
+
+    expandBufAdd1(pReply, tag);
+    expandBufAdd4BE(pReply, length);
+
+    if (!dvmDbgOutputArray(arrayId, firstIndex, length, pReply))
+        return ERR_INVALID_LENGTH;
+
+    return ERR_NONE;
+}
+
+/*
+ * Set values in an array.
+ */
+static JdwpError handleAR_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId arrayId = dvmReadObjectId(&buf);
+    u4 firstIndex = read4BE(&buf);
+    u4 values = read4BE(&buf);
+
+    ALOGV("  Req to set array values 0x%llx first=%d count=%d",
+        arrayId, firstIndex, values);
+
+    if (!dvmDbgSetArrayElements(arrayId, firstIndex, values, buf))
+        return ERR_INVALID_LENGTH;
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the set of classes visible to a class loader.  All classes which
+ * have the class loader as a defining or initiating loader are returned.
+ */
+static JdwpError handleCLR_VisibleClasses(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId classLoaderObject;
+    u4 numClasses = 0;
+    RefTypeId* classRefBuf = NULL;
+    int i;
+
+    classLoaderObject = dvmReadObjectId(&buf);
+
+    dvmDbgGetVisibleClassList(classLoaderObject, &numClasses, &classRefBuf);
+
+    expandBufAdd4BE(pReply, numClasses);
+    for (i = 0; i < (int) numClasses; i++) {
+        u1 refTypeTag;
+
+        refTypeTag = dvmDbgGetClassObjectType(classRefBuf[i]);
+
+        expandBufAdd1(pReply, refTypeTag);
+        expandBufAddRefTypeId(pReply, classRefBuf[i]);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Set an event trigger.
+ *
+ * Reply with a requestID.
+ */
+static JdwpError handleER_Set(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    const u1* origBuf = buf;
+
+    u1 eventKind = read1(&buf);
+    u1 suspendPolicy = read1(&buf);
+    u4 modifierCount = read4BE(&buf);
+
+    LOGVV("  Set(kind=%s(%u) suspend=%s(%u) mods=%u)",
+        dvmJdwpEventKindStr(eventKind), eventKind,
+        dvmJdwpSuspendPolicyStr(suspendPolicy), suspendPolicy,
+        modifierCount);
+
+    assert(modifierCount < 256);    /* reasonableness check */
+
+    JdwpEvent* pEvent = dvmJdwpEventAlloc(modifierCount);
+    pEvent->eventKind = static_cast<JdwpEventKind>(eventKind);
+    pEvent->suspendPolicy = static_cast<JdwpSuspendPolicy>(suspendPolicy);
+    pEvent->modCount = modifierCount;
+
+    /*
+     * Read modifiers.  Ordering may be significant (see explanation of Count
+     * mods in JDWP doc).
+     */
+    for (u4 idx = 0; idx < modifierCount; idx++) {
+        u1 modKind = read1(&buf);
+
+        pEvent->mods[idx].modKind = modKind;
+
+        switch (modKind) {
+        case MK_COUNT:          /* report once, when "--count" reaches 0 */
+            {
+                u4 count = read4BE(&buf);
+                LOGVV("    Count: %u", count);
+                if (count == 0)
+                    return ERR_INVALID_COUNT;
+                pEvent->mods[idx].count.count = count;
+            }
+            break;
+        case MK_CONDITIONAL:    /* conditional on expression) */
+            {
+                u4 exprId = read4BE(&buf);
+                LOGVV("    Conditional: %d", exprId);
+                pEvent->mods[idx].conditional.exprId = exprId;
+            }
+            break;
+        case MK_THREAD_ONLY:    /* only report events in specified thread */
+            {
+                ObjectId threadId = dvmReadObjectId(&buf);
+                LOGVV("    ThreadOnly: %llx", threadId);
+                pEvent->mods[idx].threadOnly.threadId = threadId;
+            }
+            break;
+        case MK_CLASS_ONLY:     /* for ClassPrepare, MethodEntry */
+            {
+                RefTypeId clazzId = dvmReadRefTypeId(&buf);
+                LOGVV("    ClassOnly: %llx (%s)",
+                    clazzId, dvmDbgGetClassDescriptor(clazzId));
+                pEvent->mods[idx].classOnly.refTypeId = clazzId;
+            }
+            break;
+        case MK_CLASS_MATCH:    /* restrict events to matching classes */
+            {
+                char* pattern;
+                size_t strLen;
+
+                pattern = readNewUtf8String(&buf, &strLen);
+                LOGVV("    ClassMatch: '%s'", pattern);
+                /* pattern is "java.foo.*", we want "java/foo/ *" */
+                pEvent->mods[idx].classMatch.classPattern =
+                    dvmDotToSlash(pattern);
+                free(pattern);
+            }
+            break;
+        case MK_CLASS_EXCLUDE:  /* restrict events to non-matching classes */
+            {
+                char* pattern;
+                size_t strLen;
+
+                pattern = readNewUtf8String(&buf, &strLen);
+                LOGVV("    ClassExclude: '%s'", pattern);
+                pEvent->mods[idx].classExclude.classPattern =
+                    dvmDotToSlash(pattern);
+                free(pattern);
+            }
+            break;
+        case MK_LOCATION_ONLY:  /* restrict certain events based on loc */
+            {
+                JdwpLocation loc;
+
+                jdwpReadLocation(&buf, &loc);
+                LOGVV("    LocationOnly: typeTag=%d classId=%llx methodId=%x idx=%llx",
+                    loc.typeTag, loc.classId, loc.methodId, loc.idx);
+                pEvent->mods[idx].locationOnly.loc = loc;
+            }
+            break;
+        case MK_EXCEPTION_ONLY: /* modifies EK_EXCEPTION events */
+            {
+                RefTypeId exceptionOrNull;      /* null == all exceptions */
+                u1 caught, uncaught;
+
+                exceptionOrNull = dvmReadRefTypeId(&buf);
+                caught = read1(&buf);
+                uncaught = read1(&buf);
+                LOGVV("    ExceptionOnly: type=%llx(%s) caught=%d uncaught=%d",
+                    exceptionOrNull, (exceptionOrNull == 0) ? "null"
+                        : dvmDbgGetClassDescriptor(exceptionOrNull),
+                    caught, uncaught);
+
+                pEvent->mods[idx].exceptionOnly.refTypeId = exceptionOrNull;
+                pEvent->mods[idx].exceptionOnly.caught = caught;
+                pEvent->mods[idx].exceptionOnly.uncaught = uncaught;
+            }
+            break;
+        case MK_FIELD_ONLY:     /* for field access/mod events */
+            {
+                RefTypeId declaring = dvmReadRefTypeId(&buf);
+                FieldId fieldId = dvmReadFieldId(&buf);
+                LOGVV("    FieldOnly: %llx %x", declaring, fieldId);
+                pEvent->mods[idx].fieldOnly.refTypeId = declaring;
+                pEvent->mods[idx].fieldOnly.fieldId = fieldId;
+            }
+            break;
+        case MK_STEP:           /* for use with EK_SINGLE_STEP */
+            {
+                ObjectId threadId;
+                u4 size, depth;
+
+                threadId = dvmReadObjectId(&buf);
+                size = read4BE(&buf);
+                depth = read4BE(&buf);
+                LOGVV("    Step: thread=%llx size=%s depth=%s",
+                    threadId, dvmJdwpStepSizeStr(size),
+                    dvmJdwpStepDepthStr(depth));
+
+                pEvent->mods[idx].step.threadId = threadId;
+                pEvent->mods[idx].step.size = size;
+                pEvent->mods[idx].step.depth = depth;
+            }
+            break;
+        case MK_INSTANCE_ONLY:  /* report events related to a specific obj */
+            {
+                ObjectId instance = dvmReadObjectId(&buf);
+                LOGVV("    InstanceOnly: %llx", instance);
+                pEvent->mods[idx].instanceOnly.objectId = instance;
+            }
+            break;
+        default:
+            ALOGW("GLITCH: unsupported modKind=%d", modKind);
+            break;
+        }
+    }
+
+    /*
+     * Make sure we consumed all data.  It is possible that the remote side
+     * has sent us bad stuff, but for now we blame ourselves.
+     */
+    if (buf != origBuf + dataLen) {
+        ALOGW("GLITCH: dataLen is %d, we have consumed %d", dataLen,
+            (int) (buf - origBuf));
+    }
+
+    /*
+     * We reply with an integer "requestID".
+     */
+    u4 requestId = dvmJdwpNextEventSerial(state);
+    expandBufAdd4BE(pReply, requestId);
+
+    pEvent->requestId = requestId;
+
+    ALOGV("    --> event requestId=%#x", requestId);
+
+    /* add it to the list */
+    JdwpError err = dvmJdwpRegisterEvent(state, pEvent);
+    if (err != ERR_NONE) {
+        /* registration failed, probably because event is bogus */
+        dvmJdwpEventFree(pEvent);
+        ALOGW("WARNING: event request rejected");
+    }
+    return err;
+}
+
+/*
+ * Clear an event.  Failure to find an event with a matching ID is a no-op
+ * and does not return an error.
+ */
+static JdwpError handleER_Clear(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u1 eventKind;
+    eventKind = read1(&buf);
+    u4 requestId = read4BE(&buf);
+
+    ALOGV("  Req to clear eventKind=%d requestId=%#x", eventKind, requestId);
+
+    dvmJdwpUnregisterEventById(state, requestId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the values of arguments and local variables.
+ */
+static JdwpError handleSF_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    FrameId frameId = dvmReadFrameId(&buf);
+    u4 slots = read4BE(&buf);
+
+    ALOGV("  Req for %d slots in threadId=%llx frameId=%llx",
+        slots, threadId, frameId);
+
+    expandBufAdd4BE(pReply, slots);     /* "int values" */
+    for (u4 i = 0; i < slots; i++) {
+        u4 slot = read4BE(&buf);
+        u1 reqSigByte = read1(&buf);
+
+        ALOGV("    --> slot %d '%c'", slot, reqSigByte);
+
+        int width = dvmDbgGetTagWidth(reqSigByte);
+        u1* ptr = expandBufAddSpace(pReply, width+1);
+        dvmDbgGetLocalValue(threadId, frameId, slot, reqSigByte, ptr, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Set the values of arguments and local variables.
+ */
+static JdwpError handleSF_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    FrameId frameId = dvmReadFrameId(&buf);
+    u4 slots = read4BE(&buf);
+
+    ALOGV("  Req to set %d slots in threadId=%llx frameId=%llx",
+        slots, threadId, frameId);
+
+    for (u4 i = 0; i < slots; i++) {
+        u4 slot = read4BE(&buf);
+        u1 sigByte = read1(&buf);
+        int width = dvmDbgGetTagWidth(sigByte);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("    --> slot %d '%c' %llx", slot, sigByte, value);
+        dvmDbgSetLocalValue(threadId, frameId, slot, sigByte, value, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the value of "this" for the specified frame.
+ */
+static JdwpError handleSF_ThisObject(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    FrameId frameId = dvmReadFrameId(&buf);
+
+    ObjectId objectId;
+    if (!dvmDbgGetThisObject(threadId, frameId, &objectId))
+        return ERR_INVALID_FRAMEID;
+
+    u1 objectTag = dvmDbgGetObjectTag(objectId);
+    ALOGV("  Req for 'this' in thread=%llx frame=%llx --> %llx %s '%c'",
+        threadId, frameId, objectId, dvmDbgGetObjectTypeName(objectId),
+        (char)objectTag);
+
+    expandBufAdd1(pReply, objectTag);
+    expandBufAddObjectId(pReply, objectId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the reference type reflected by this class object.
+ *
+ * This appears to be required because ReferenceTypeId values are NEVER
+ * reused, whereas ClassIds can be recycled like any other object.  (Either
+ * that, or I have no idea what this is for.)
+ */
+static JdwpError handleCOR_ReflectedType(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classObjectId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for refTypeId for class=%llx (%s)",
+        classObjectId, dvmDbgGetClassDescriptor(classObjectId));
+
+    /* just hand the type back to them */
+    if (dvmDbgIsInterface(classObjectId))
+        expandBufAdd1(pReply, TT_INTERFACE);
+    else
+        expandBufAdd1(pReply, TT_CLASS);
+    expandBufAddRefTypeId(pReply, classObjectId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Handle a DDM packet with a single chunk in it.
+ */
+static JdwpError handleDDM_Chunk(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u1* replyBuf = NULL;
+    int replyLen = -1;
+
+    ALOGV("  Handling DDM packet (%.4s)", buf);
+
+    /*
+     * On first DDM packet, notify all handlers that DDM is running.
+     */
+    if (!state->ddmActive) {
+        state->ddmActive = true;
+        dvmDbgDdmConnected();
+    }
+
+    /*
+     * If they want to send something back, we copy it into the buffer.
+     * A no-copy approach would be nicer.
+     *
+     * TODO: consider altering the JDWP stuff to hold the packet header
+     * in a separate buffer.  That would allow us to writev() DDM traffic
+     * instead of copying it into the expanding buffer.  The reduction in
+     * heap requirements is probably more valuable than the efficiency.
+     */
+    if (dvmDbgDdmHandlePacket(buf, dataLen, &replyBuf, &replyLen)) {
+        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);
+
+struct JdwpHandlerMap {
+    u1  cmdSet;
+    u1  cmd;
+    JdwpRequestHandler  func;
+    const char* descr;
+};
+
+/*
+ * Map commands to functions.
+ *
+ * Command sets 0-63 are incoming requests, 64-127 are outbound requests,
+ * and 128-256 are vendor-defined.
+ */
+static const JdwpHandlerMap gHandlerMap[] = {
+    /* VirtualMachine command set (1) */
+    { 1,    1,  handleVM_Version,       "VirtualMachine.Version" },
+    { 1,    2,  handleVM_ClassesBySignature,
+                                        "VirtualMachine.ClassesBySignature" },
+    //1,    3,  VirtualMachine.AllClasses
+    { 1,    4,  handleVM_AllThreads,    "VirtualMachine.AllThreads" },
+    { 1,    5,  handleVM_TopLevelThreadGroups,
+                                        "VirtualMachine.TopLevelThreadGroups" },
+    { 1,    6,  handleVM_Dispose,       "VirtualMachine.Dispose" },
+    { 1,    7,  handleVM_IDSizes,       "VirtualMachine.IDSizes" },
+    { 1,    8,  handleVM_Suspend,       "VirtualMachine.Suspend" },
+    { 1,    9,  handleVM_Resume,        "VirtualMachine.Resume" },
+    { 1,    10, handleVM_Exit,          "VirtualMachine.Exit" },
+    { 1,    11, handleVM_CreateString,  "VirtualMachine.CreateString" },
+    { 1,    12, handleVM_Capabilities,  "VirtualMachine.Capabilities" },
+    { 1,    13, handleVM_ClassPaths,    "VirtualMachine.ClassPaths" },
+    { 1,    14, HandleVM_DisposeObjects, "VirtualMachine.DisposeObjects" },
+    //1,    15, HoldEvents
+    //1,    16, ReleaseEvents
+    { 1,    17, handleVM_CapabilitiesNew,
+                                        "VirtualMachine.CapabilitiesNew" },
+    //1,    18, RedefineClasses
+    //1,    19, SetDefaultStratum
+    { 1,    20, handleVM_AllClassesWithGeneric,
+                                        "VirtualMachine.AllClassesWithGeneric"},
+    //1,    21, InstanceCounts
+
+    /* ReferenceType command set (2) */
+    { 2,    1,  handleRT_Signature,     "ReferenceType.Signature" },
+    { 2,    2,  handleRT_ClassLoader,   "ReferenceType.ClassLoader" },
+    { 2,    3,  handleRT_Modifiers,     "ReferenceType.Modifiers" },
+    //2,    4,  Fields
+    //2,    5,  Methods
+    { 2,    6,  handleRT_GetValues,     "ReferenceType.GetValues" },
+    { 2,    7,  handleRT_SourceFile,    "ReferenceType.SourceFile" },
+    //2,    8,  NestedTypes
+    { 2,    9,  handleRT_Status,        "ReferenceType.Status" },
+    { 2,    10, handleRT_Interfaces,    "ReferenceType.Interfaces" },
+    { 2,    11, handleRT_ClassObject,   "ReferenceType.ClassObject" },
+    { 2,    12, handleRT_SourceDebugExtension,
+                                        "ReferenceType.SourceDebugExtension" },
+    { 2,    13, handleRT_SignatureWithGeneric,
+                                        "ReferenceType.SignatureWithGeneric" },
+    { 2,    14, handleRT_FieldsWithGeneric,
+                                        "ReferenceType.FieldsWithGeneric" },
+    { 2,    15, handleRT_MethodsWithGeneric,
+                                        "ReferenceType.MethodsWithGeneric" },
+    //2,    16, Instances
+    //2,    17, ClassFileVersion
+    //2,    18, ConstantPool
+
+    /* ClassType command set (3) */
+    { 3,    1,  handleCT_Superclass,    "ClassType.Superclass" },
+    { 3,    2,  handleCT_SetValues,     "ClassType.SetValues" },
+    { 3,    3,  handleCT_InvokeMethod,  "ClassType.InvokeMethod" },
+    { 3,    4,  handleCT_NewInstance,   "ClassType.NewInstance" },
+
+    /* ArrayType command set (4) */
+    { 4,    1,  handleAT_newInstance,   "ArrayType.NewInstance" },
+
+    /* InterfaceType command set (5) */
+
+    /* Method command set (6) */
+    { 6,    1,  handleM_LineTable,      "Method.LineTable" },
+    //6,    2,  VariableTable
+    //6,    3,  Bytecodes
+    //6,    4,  IsObsolete
+    { 6,    5,  handleM_VariableTableWithGeneric,
+                                        "Method.VariableTableWithGeneric" },
+
+    /* Field command set (8) */
+
+    /* ObjectReference command set (9) */
+    { 9,    1,  handleOR_ReferenceType, "ObjectReference.ReferenceType" },
+    { 9,    2,  handleOR_GetValues,     "ObjectReference.GetValues" },
+    { 9,    3,  handleOR_SetValues,     "ObjectReference.SetValues" },
+    //9,    4,  (not defined)
+    //9,    5,  MonitorInfo
+    { 9,    6,  handleOR_InvokeMethod,  "ObjectReference.InvokeMethod" },
+    { 9,    7,  handleOR_DisableCollection,
+                                        "ObjectReference.DisableCollection" },
+    { 9,    8,  handleOR_EnableCollection,
+                                        "ObjectReference.EnableCollection" },
+    { 9,    9,  handleOR_IsCollected,   "ObjectReference.IsCollected" },
+    //9,    10, ReferringObjects
+
+    /* StringReference command set (10) */
+    { 10,   1,  handleSR_Value,         "StringReference.Value" },
+
+    /* ThreadReference command set (11) */
+    { 11,   1,  handleTR_Name,          "ThreadReference.Name" },
+    { 11,   2,  handleTR_Suspend,       "ThreadReference.Suspend" },
+    { 11,   3,  handleTR_Resume,        "ThreadReference.Resume" },
+    { 11,   4,  handleTR_Status,        "ThreadReference.Status" },
+    { 11,   5,  handleTR_ThreadGroup,   "ThreadReference.ThreadGroup" },
+    { 11,   6,  handleTR_Frames,        "ThreadReference.Frames" },
+    { 11,   7,  handleTR_FrameCount,    "ThreadReference.FrameCount" },
+    //11,   8,  OwnedMonitors
+    { 11,   9,  handleTR_CurrentContendedMonitor,
+                                    "ThreadReference.CurrentContendedMonitor" },
+    //11,   10, Stop
+    //11,   11, Interrupt
+    { 11,   12, handleTR_SuspendCount,  "ThreadReference.SuspendCount" },
+    //11,   13, OwnedMonitorsStackDepthInfo
+    //11,   14, ForceEarlyReturn
+
+    /* ThreadGroupReference command set (12) */
+    { 12,   1,  handleTGR_Name,         "ThreadGroupReference.Name" },
+    { 12,   2,  handleTGR_Parent,       "ThreadGroupReference.Parent" },
+    { 12,   3,  handleTGR_Children,     "ThreadGroupReference.Children" },
+
+    /* ArrayReference command set (13) */
+    { 13,   1,  handleAR_Length,        "ArrayReference.Length" },
+    { 13,   2,  handleAR_GetValues,     "ArrayReference.GetValues" },
+    { 13,   3,  handleAR_SetValues,     "ArrayReference.SetValues" },
+
+    /* ClassLoaderReference command set (14) */
+    { 14,   1,  handleCLR_VisibleClasses,
+                                        "ClassLoaderReference.VisibleClasses" },
+
+    /* EventRequest command set (15) */
+    { 15,   1,  handleER_Set,           "EventRequest.Set" },
+    { 15,   2,  handleER_Clear,         "EventRequest.Clear" },
+    //15,   3,  ClearAllBreakpoints
+
+    /* StackFrame command set (16) */
+    { 16,   1,  handleSF_GetValues,     "StackFrame.GetValues" },
+    { 16,   2,  handleSF_SetValues,     "StackFrame.SetValues" },
+    { 16,   3,  handleSF_ThisObject,    "StackFrame.ThisObject" },
+    //16,   4,  PopFrames
+
+    /* ClassObjectReference command set (17) */
+    { 17,   1,  handleCOR_ReflectedType,"ClassObjectReference.ReflectedType" },
+
+    /* Event command set (64) */
+    //64,  100, Composite   <-- sent from VM to debugger, never received by VM
+
+    { 199,  1,  handleDDM_Chunk,        "DDM.Chunk" },
+};
+
+
+/*
+ * Process a request from the debugger.
+ *
+ * On entry, the JDWP thread is in VMWAIT.
+ */
+void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    JdwpError result = ERR_NONE;
+    int i, respLen;
+
+    if (pHeader->cmdSet != kJDWPDdmCmdSet) {
+        /*
+         * Activity from a debugger, not merely ddms.  Mark us as having an
+         * active debugger session, and zero out the last-activity timestamp
+         * so waitForDebugger() doesn't return if we stall for a bit here.
+         */
+        dvmDbgActive();
+        dvmQuasiAtomicSwap64(0, &state->lastActivityWhen);
+    }
+
+    /*
+     * If a debugger event has fired in another thread, wait until the
+     * initiating thread has suspended itself before processing messages
+     * from the debugger.  Otherwise we (the JDWP thread) could be told to
+     * resume the thread before it has suspended.
+     *
+     * We call with an argument of zero to wait for the current event
+     * thread to finish, and then clear the block.  Depending on the thread
+     * suspend policy, this may allow events in other threads to fire,
+     * but those events have no bearing on what the debugger has sent us
+     * in the current request.
+     *
+     * Note that we MUST clear the event token before waking the event
+     * thread up, or risk waiting for the thread to suspend after we've
+     * told it to resume.
+     */
+    dvmJdwpSetWaitForEventThread(state, 0);
+
+    /*
+     * Tell the VM that we're running and shouldn't be interrupted by GC.
+     * Do this after anything that can stall indefinitely.
+     */
+    dvmDbgThreadRunning();
+
+    expandBufAddSpace(pReply, kJDWPHeaderLen);
+
+    for (i = 0; i < (int) NELEM(gHandlerMap); i++) {
+        if (gHandlerMap[i].cmdSet == pHeader->cmdSet &&
+            gHandlerMap[i].cmd == pHeader->cmd)
+        {
+            ALOGV("REQ: %s (cmd=%d/%d dataLen=%d id=0x%06x)",
+                gHandlerMap[i].descr, pHeader->cmdSet, pHeader->cmd,
+                dataLen, pHeader->id);
+            result = (*gHandlerMap[i].func)(state, buf, dataLen, pReply);
+            break;
+        }
+    }
+    if (i == NELEM(gHandlerMap)) {
+        ALOGE("REQ: UNSUPPORTED (cmd=%d/%d dataLen=%d id=0x%06x)",
+            pHeader->cmdSet, pHeader->cmd, dataLen, pHeader->id);
+        if (dataLen > 0)
+            dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
+        assert(!"command not implemented");      // make it *really* obvious
+        result = ERR_NOT_IMPLEMENTED;
+    }
+
+    /*
+     * Set up the reply header.
+     *
+     * If we encountered an error, only send the header back.
+     */
+    u1* replyBuf = expandBufGetBuffer(pReply);
+    set4BE(replyBuf + 4, pHeader->id);
+    set1(replyBuf + 8, kJDWPFlagReply);
+    set2BE(replyBuf + 9, result);
+    if (result == ERR_NONE)
+        set4BE(replyBuf + 0, expandBufGetLength(pReply));
+    else
+        set4BE(replyBuf + 0, kJDWPHeaderLen);
+
+    respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
+    IF_ALOG(LOG_VERBOSE, LOG_TAG) {
+        ALOGV("reply: dataLen=%d err=%s(%d)%s", respLen,
+            dvmJdwpErrorStr(result), result,
+            result != ERR_NONE ? " **FAILED**" : "");
+        if (respLen > 0)
+            dvmPrintHexDumpDbg(expandBufGetBuffer(pReply) + kJDWPHeaderLen,
+                respLen, LOG_TAG);
+    }
+
+    /*
+     * Update last-activity timestamp.  We really only need this during
+     * the initial setup.  Only update if this is a non-DDMS packet.
+     */
+    if (pHeader->cmdSet != kJDWPDdmCmdSet) {
+        dvmQuasiAtomicSwap64(dvmJdwpGetNowMsec(), &state->lastActivityWhen);
+    }
+
+    /* tell the VM that GC is okay again */
+    dvmDbgThreadWaiting();
+}
diff --git a/vm/jdwp/JdwpHandler.h b/vm/jdwp/JdwpHandler.h
new file mode 100644
index 0000000..6381270
--- /dev/null
+++ b/vm/jdwp/JdwpHandler.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Handle requests.
+ */
+#ifndef DALVIK_JDWP_JDWPHANDLER_H_
+#define DALVIK_JDWP_JDWPHANDLER_H_
+
+#include "Common.h"
+#include "ExpandBuf.h"
+
+/*
+ * JDWP message header for a request.
+ */
+struct JdwpReqHeader {
+    u4  length;
+    u4  id;
+    u1  cmdSet;
+    u1  cmd;
+};
+
+/*
+ * Process a request from the debugger.
+ *
+ * "buf" points past the header, to the content of the message.  "dataLen"
+ * can therefore be zero.
+ */
+void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
+    const u1* buf, int dataLen, ExpandBuf* pReply);
+
+/* helper function */
+void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc);
+
+#endif  // DALVIK_JDWP_JDWPHANDLER_H_
diff --git a/vm/jdwp/JdwpMain.cpp b/vm/jdwp/JdwpMain.cpp
new file mode 100644
index 0000000..90e4c45
--- /dev/null
+++ b/vm/jdwp/JdwpMain.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JDWP initialization.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "Dalvik.h"
+#include "Atomic.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+
+static void* jdwpThreadStart(void* arg);
+
+/*
+ * JdwpNetStateBase class implementation
+ */
+JdwpNetStateBase::JdwpNetStateBase()
+{
+    clientSock = -1;
+    dvmDbgInitMutex(&socketLock);
+}
+
+/*
+ * Write a packet. Grabs a mutex to assure atomicity.
+ */
+ssize_t JdwpNetStateBase::writePacket(ExpandBuf* pReply)
+{
+    dvmDbgLockMutex(&socketLock);
+    ssize_t cc = write(clientSock, expandBufGetBuffer(pReply),
+            expandBufGetLength(pReply));
+    dvmDbgUnlockMutex(&socketLock);
+
+    return cc;
+}
+
+/*
+ * Write a buffered packet. Grabs a mutex to assure atomicity.
+ */
+ssize_t JdwpNetStateBase::writeBufferedPacket(const struct iovec* iov,
+    int iovcnt)
+{
+    dvmDbgLockMutex(&socketLock);
+    ssize_t actual = writev(clientSock, iov, iovcnt);
+    dvmDbgUnlockMutex(&socketLock);
+
+    return actual;
+}
+
+/*
+ * Initialize JDWP.
+ *
+ * Does not return until JDWP thread is running, but may return before
+ * the thread is accepting network connections.
+ */
+JdwpState* dvmJdwpStartup(const JdwpStartupParams* pParams)
+{
+    JdwpState* state = NULL;
+
+    /* comment this out when debugging JDWP itself */
+    android_setMinPriority(LOG_TAG, ANDROID_LOG_DEBUG);
+
+    state = (JdwpState*) calloc(1, sizeof(JdwpState));
+
+    state->params = *pParams;
+
+    state->requestSerial = 0x10000000;
+    state->eventSerial = 0x20000000;
+    dvmDbgInitMutex(&state->threadStartLock);
+    dvmDbgInitMutex(&state->attachLock);
+    dvmDbgInitMutex(&state->serialLock);
+    dvmDbgInitMutex(&state->eventLock);
+    state->eventThreadId = 0;
+    dvmDbgInitMutex(&state->eventThreadLock);
+    dvmDbgInitCond(&state->threadStartCond);
+    dvmDbgInitCond(&state->attachCond);
+    dvmDbgInitCond(&state->eventThreadCond);
+
+    switch (pParams->transport) {
+    case kJdwpTransportSocket:
+        // ALOGD("prepping for JDWP over TCP");
+        state->transport = dvmJdwpSocketTransport();
+        break;
+    case kJdwpTransportAndroidAdb:
+        // ALOGD("prepping for JDWP over ADB");
+        state->transport = dvmJdwpAndroidAdbTransport();
+        /* TODO */
+        break;
+    default:
+        ALOGE("Unknown transport %d", pParams->transport);
+        assert(false);
+        goto fail;
+    }
+
+    if (!dvmJdwpNetStartup(state, pParams))
+        goto fail;
+
+    /*
+     * Grab a mutex or two before starting the thread.  This ensures they
+     * won't signal the cond var before we're waiting.
+     */
+    dvmDbgLockMutex(&state->threadStartLock);
+    if (pParams->suspend)
+        dvmDbgLockMutex(&state->attachLock);
+
+    /*
+     * We have bound to a port, or are trying to connect outbound to a
+     * debugger.  Create the JDWP thread and let it continue the mission.
+     */
+    if (!dvmCreateInternalThread(&state->debugThreadHandle, "JDWP",
+            jdwpThreadStart, state))
+    {
+        /* state is getting tossed, but unlock these anyway for cleanliness */
+        dvmDbgUnlockMutex(&state->threadStartLock);
+        if (pParams->suspend)
+            dvmDbgUnlockMutex(&state->attachLock);
+        goto fail;
+    }
+
+    /*
+     * Wait until the thread finishes basic initialization.
+     * TODO: cond vars should be waited upon in a loop
+     */
+    dvmDbgCondWait(&state->threadStartCond, &state->threadStartLock);
+    dvmDbgUnlockMutex(&state->threadStartLock);
+
+
+    /*
+     * For suspend=y, wait for the debugger to connect to us or for us to
+     * connect to the debugger.
+     *
+     * The JDWP thread will signal us when it connects successfully or
+     * times out (for timeout=xxx), so we have to check to see what happened
+     * when we wake up.
+     */
+    if (pParams->suspend) {
+        dvmChangeStatus(NULL, THREAD_VMWAIT);
+        dvmDbgCondWait(&state->attachCond, &state->attachLock);
+        dvmDbgUnlockMutex(&state->attachLock);
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+
+        if (!dvmJdwpIsActive(state)) {
+            ALOGE("JDWP connection failed");
+            goto fail;
+        }
+
+        ALOGI("JDWP connected");
+
+        /*
+         * Ordinarily we would pause briefly to allow the debugger to set
+         * breakpoints and so on, but for "suspend=y" the VM init code will
+         * pause the VM when it sends the VM_START message.
+         */
+    }
+
+    return state;
+
+fail:
+    dvmJdwpShutdown(state);     // frees state
+    return NULL;
+}
+
+/*
+ * Reset all session-related state.  There should not be an active connection
+ * to the client at this point.  The rest of the VM still thinks there is
+ * a debugger attached.
+ *
+ * This includes freeing up the debugger event list.
+ */
+void dvmJdwpResetState(JdwpState* state)
+{
+    /* could reset the serial numbers, but no need to */
+
+    dvmJdwpUnregisterAll(state);
+    assert(state->eventList == NULL);
+
+    /*
+     * Should not have one of these in progress.  If the debugger went away
+     * mid-request, though, we could see this.
+     */
+    if (state->eventThreadId != 0) {
+        ALOGW("WARNING: resetting state while event in progress");
+        assert(false);
+    }
+}
+
+/*
+ * Tell the JDWP thread to shut down.  Frees "state".
+ */
+void dvmJdwpShutdown(JdwpState* state)
+{
+    void* threadReturn;
+
+    if (state == NULL)
+        return;
+
+    if (dvmJdwpIsTransportDefined(state)) {
+        if (dvmJdwpIsConnected(state))
+            dvmJdwpPostVMDeath(state);
+
+        /*
+         * Close down the network to inspire the thread to halt.
+         */
+        if (gDvm.verboseShutdown)
+            ALOGD("JDWP shutting down net...");
+        dvmJdwpNetShutdown(state);
+
+        if (state->debugThreadStarted) {
+            state->run = false;
+            if (pthread_join(state->debugThreadHandle, &threadReturn) != 0) {
+                ALOGW("JDWP thread join failed");
+            }
+        }
+
+        if (gDvm.verboseShutdown)
+            ALOGD("JDWP freeing netstate...");
+        dvmJdwpNetFree(state);
+        state->netState = NULL;
+    }
+    assert(state->netState == NULL);
+
+    dvmJdwpResetState(state);
+    free(state);
+}
+
+/*
+ * Are we talking to a debugger?
+ */
+bool dvmJdwpIsActive(JdwpState* state)
+{
+    return dvmJdwpIsConnected(state);
+}
+
+/*
+ * Entry point for JDWP thread.  The thread was created through the VM
+ * mechanisms, so there is a java/lang/Thread associated with us.
+ */
+static void* jdwpThreadStart(void* arg)
+{
+    JdwpState* state = (JdwpState*) arg;
+
+    ALOGV("JDWP: thread running");
+
+    /*
+     * Finish initializing "state", then notify the creating thread that
+     * we're running.
+     */
+    state->debugThreadHandle = dvmThreadSelf()->handle;
+    state->run = true;
+    android_atomic_release_store(true, &state->debugThreadStarted);
+
+    dvmDbgLockMutex(&state->threadStartLock);
+    dvmDbgCondBroadcast(&state->threadStartCond);
+    dvmDbgUnlockMutex(&state->threadStartLock);
+
+    /* set the thread state to VMWAIT so GCs don't wait for us */
+    dvmDbgThreadWaiting();
+
+    /*
+     * Loop forever if we're in server mode, processing connections.  In
+     * non-server mode, we bail out of the thread when the debugger drops
+     * us.
+     *
+     * We broadcast a notification when a debugger attaches, after we
+     * successfully process the handshake.
+     */
+    while (state->run) {
+        bool first;
+
+        if (state->params.server) {
+            /*
+             * Block forever, waiting for a connection.  To support the
+             * "timeout=xxx" option we'll need to tweak this.
+             */
+            if (!dvmJdwpAcceptConnection(state))
+                break;
+        } else {
+            /*
+             * If we're not acting as a server, we need to connect out to the
+             * debugger.  To support the "timeout=xxx" option we need to
+             * have a timeout if the handshake reply isn't received in a
+             * reasonable amount of time.
+             */
+            if (!dvmJdwpEstablishConnection(state)) {
+                /* wake anybody who was waiting for us to succeed */
+                dvmDbgLockMutex(&state->attachLock);
+                dvmDbgCondBroadcast(&state->attachCond);
+                dvmDbgUnlockMutex(&state->attachLock);
+                break;
+            }
+        }
+
+        /* prep debug code to handle the new connection */
+        dvmDbgConnected();
+
+        /* process requests until the debugger drops */
+        first = true;
+        while (true) {
+            // sanity check -- shouldn't happen?
+            if (dvmThreadSelf()->status != THREAD_VMWAIT) {
+                ALOGE("JDWP thread no longer in VMWAIT (now %d); resetting",
+                    dvmThreadSelf()->status);
+                dvmDbgThreadWaiting();
+            }
+
+            if (!dvmJdwpProcessIncoming(state))     /* blocking read */
+                break;
+
+            if (first && !dvmJdwpAwaitingHandshake(state)) {
+                /* handshake worked, tell the interpreter that we're active */
+                first = false;
+
+                /* set thread ID; requires object registry to be active */
+                state->debugThreadId = dvmDbgGetThreadSelfId();
+
+                /* wake anybody who's waiting for us */
+                dvmDbgLockMutex(&state->attachLock);
+                dvmDbgCondBroadcast(&state->attachCond);
+                dvmDbgUnlockMutex(&state->attachLock);
+            }
+        }
+
+        dvmJdwpCloseConnection(state);
+
+        if (state->ddmActive) {
+            state->ddmActive = false;
+
+            /* broadcast the disconnect; must be in RUNNING state */
+            dvmDbgThreadRunning();
+            dvmDbgDdmDisconnected();
+            dvmDbgThreadWaiting();
+        }
+
+        /* release session state, e.g. remove breakpoint instructions */
+        dvmJdwpResetState(state);
+
+        /* tell the interpreter that the debugger is no longer around */
+        dvmDbgDisconnected();
+
+        /* if we had threads suspended, resume them now */
+        dvmUndoDebuggerSuspensions();
+
+        /* if we connected out, this was a one-shot deal */
+        if (!state->params.server)
+            state->run = false;
+    }
+
+    /* back to running, for thread shutdown */
+    dvmDbgThreadRunning();
+
+    ALOGV("JDWP: thread exiting");
+    return NULL;
+}
+
+
+/*
+ * Return the thread handle, or (pthread_t)0 if the debugger isn't running.
+ */
+pthread_t dvmJdwpGetDebugThread(JdwpState* state)
+{
+    if (state == NULL)
+        return 0;
+
+    return state->debugThreadHandle;
+}
+
+
+/*
+ * Support routines for waitForDebugger().
+ *
+ * We can't have a trivial "waitForDebugger" function that returns the
+ * instant the debugger connects, because we run the risk of executing code
+ * before the debugger has had a chance to configure breakpoints or issue
+ * suspend calls.  It would be nice to just sit in the suspended state, but
+ * most debuggers don't expect any threads to be suspended when they attach.
+ *
+ * There's no JDWP event we can post to tell the debugger, "we've stopped,
+ * and we like it that way".  We could send a fake breakpoint, which should
+ * cause the debugger to immediately send a resume, but the debugger might
+ * send the resume immediately or might throw an exception of its own upon
+ * receiving a breakpoint event that it didn't ask for.
+ *
+ * What we really want is a "wait until the debugger is done configuring
+ * stuff" event.  We can approximate this with a "wait until the debugger
+ * has been idle for a brief period".
+ */
+
+/*
+ * Get a notion of the current time, in milliseconds.
+ */
+s8 dvmJdwpGetNowMsec()
+{
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000LL + now.tv_nsec / 1000000LL;
+#else
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    return now.tv_sec * 1000LL + now.tv_usec / 1000LL;
+#endif
+}
+
+/*
+ * Return the time, in milliseconds, since the last debugger activity.
+ *
+ * Returns -1 if no debugger is attached, or 0 if we're in the middle of
+ * processing a debugger request.
+ */
+s8 dvmJdwpLastDebuggerActivity(JdwpState* state)
+{
+    if (!gDvm.debuggerActive) {
+        ALOGD("dvmJdwpLastDebuggerActivity: no active debugger");
+        return -1;
+    }
+
+    s8 last = dvmQuasiAtomicRead64(&state->lastActivityWhen);
+
+    /* initializing or in the middle of something? */
+    if (last == 0) {
+        ALOGV("+++ last=busy");
+        return 0;
+    }
+
+    /* now get the current time */
+    s8 now = dvmJdwpGetNowMsec();
+    assert(now >= last);
+
+    ALOGV("+++ debugger interval=%lld", now - last);
+    return now - last;
+}
diff --git a/vm/jdwp/JdwpPriv.h b/vm/jdwp/JdwpPriv.h
new file mode 100644
index 0000000..67a953f
--- /dev/null
+++ b/vm/jdwp/JdwpPriv.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JDWP internal interfaces.
+ */
+#ifndef DALVIK_JDWP_JDWPPRIV_H_
+#define DALVIK_JDWP_JDWPPRIV_H_
+
+#define LOG_TAG "jdwp"
+
+#include "jdwp/Jdwp.h"
+#include "jdwp/JdwpEvent.h"
+#include "Debugger.h"
+
+#include <pthread.h>
+#include <sys/uio.h>
+
+/*
+ * JDWP constants.
+ */
+#define kJDWPHeaderLen  11
+#define kJDWPFlagReply  0x80
+
+/* DDM support */
+#define kJDWPDdmCmdSet  199     /* 0xc7, or 'G'+128 */
+#define kJDWPDdmCmd     1
+
+
+/*
+ * Transport-specific network status.
+ */
+struct JdwpNetState;
+struct JdwpState;
+
+/*
+ * Transport functions.
+ */
+struct JdwpTransport {
+    bool (*startup)(struct JdwpState* state, const JdwpStartupParams* pParams);
+    bool (*accept)(struct JdwpState* state);
+    bool (*establish)(struct JdwpState* state);
+    void (*close)(struct JdwpState* state);
+    void (*shutdown)(struct JdwpState* state);
+    void (*free)(struct JdwpState* state);
+    bool (*isConnected)(struct JdwpState* state);
+    bool (*awaitingHandshake)(struct JdwpState* state);
+    bool (*processIncoming)(struct JdwpState* state);
+    bool (*sendRequest)(struct JdwpState* state, ExpandBuf* pReq);
+    bool (*sendBufferedRequest)(struct JdwpState* state,
+        const struct iovec* iov, int iovcnt);
+};
+
+const JdwpTransport* dvmJdwpSocketTransport();
+const JdwpTransport* dvmJdwpAndroidAdbTransport();
+
+
+/*
+ * State for JDWP functions.
+ */
+struct JdwpState {
+    JdwpStartupParams   params;
+
+    /* wait for creation of the JDWP thread */
+    pthread_mutex_t threadStartLock;
+    pthread_cond_t  threadStartCond;
+
+    int             debugThreadStarted;
+    pthread_t       debugThreadHandle;
+    ObjectId        debugThreadId;
+    bool            run;
+
+    const JdwpTransport*    transport;
+    JdwpNetState*   netState;
+
+    /* for wait-for-debugger */
+    pthread_mutex_t attachLock;
+    pthread_cond_t  attachCond;
+
+    /* time of last debugger activity, in milliseconds */
+    s8              lastActivityWhen;
+
+    /* global counters and a mutex to protect them */
+    u4              requestSerial;
+    u4              eventSerial;
+    pthread_mutex_t serialLock;
+
+    /*
+     * Events requested by the debugger (breakpoints, class prep, etc).
+     */
+    int             numEvents;      /* #of elements in eventList */
+    JdwpEvent*      eventList;      /* linked list of events */
+    pthread_mutex_t eventLock;      /* guards numEvents/eventList */
+
+    /*
+     * Synchronize suspension of event thread (to avoid receiving "resume"
+     * events before the thread has finished suspending itself).
+     */
+    pthread_mutex_t eventThreadLock;
+    pthread_cond_t  eventThreadCond;
+    ObjectId        eventThreadId;
+
+    /*
+     * DDM support.
+     */
+    bool            ddmActive;
+};
+
+/*
+ * Base class for JdwpNetState
+ */
+class JdwpNetStateBase {
+public:
+    int             clientSock;     /* active connection to debugger */
+
+    JdwpNetStateBase();
+    ssize_t writePacket(ExpandBuf* pReply);
+    ssize_t writeBufferedPacket(const struct iovec* iov, int iovcnt);
+
+private:
+    pthread_mutex_t socketLock;     /* socket synchronization */
+};
+
+
+/* reset all session-specific data */
+void dvmJdwpResetState(JdwpState* state);
+
+/* atomic ops to get next serial number */
+u4 dvmJdwpNextRequestSerial(JdwpState* state);
+u4 dvmJdwpNextEventSerial(JdwpState* state);
+
+/* get current time, in msec */
+s8 dvmJdwpGetNowMsec(void);
+
+
+/*
+ * Transport functions.
+ */
+INLINE bool dvmJdwpNetStartup(JdwpState* state,
+    const JdwpStartupParams* pParams)
+{
+    return (*state->transport->startup)(state, pParams);
+}
+INLINE bool dvmJdwpAcceptConnection(JdwpState* state) {
+    return (*state->transport->accept)(state);
+}
+INLINE bool dvmJdwpEstablishConnection(JdwpState* state) {
+    return (*state->transport->establish)(state);
+}
+INLINE void dvmJdwpCloseConnection(JdwpState* state) {
+    (*state->transport->close)(state);
+}
+INLINE void dvmJdwpNetShutdown(JdwpState* state) {
+    (*state->transport->shutdown)(state);
+}
+INLINE void dvmJdwpNetFree(JdwpState* state) {
+    (*state->transport->free)(state);
+}
+INLINE bool dvmJdwpIsTransportDefined(JdwpState* state) {
+    return state != NULL && state->transport != NULL;
+}
+INLINE bool dvmJdwpIsConnected(JdwpState* state) {
+    return state != NULL && (*state->transport->isConnected)(state);
+}
+INLINE bool dvmJdwpAwaitingHandshake(JdwpState* state) {
+    return (*state->transport->awaitingHandshake)(state);
+}
+INLINE bool dvmJdwpProcessIncoming(JdwpState* state) {
+    return (*state->transport->processIncoming)(state);
+}
+INLINE bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq) {
+    return (*state->transport->sendRequest)(state, pReq);
+}
+INLINE bool dvmJdwpSendBufferedRequest(JdwpState* state,
+    const struct iovec* iov, int iovcnt)
+{
+    return (*state->transport->sendBufferedRequest)(state, iov, iovcnt);
+}
+
+#endif  // DALVIK_JDWP_JDWPPRIV_H_
diff --git a/vm/jdwp/JdwpSocket.cpp b/vm/jdwp/JdwpSocket.cpp
new file mode 100644
index 0000000..ad0a287
--- /dev/null
+++ b/vm/jdwp/JdwpSocket.cpp
@@ -0,0 +1,910 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JDWP TCP socket network code.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include "Bits.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define kBasePort           8000
+#define kMaxPort            8040
+
+#define kInputBufferSize    8192
+
+#define kMagicHandshake     "JDWP-Handshake"
+#define kMagicHandshakeLen  (sizeof(kMagicHandshake)-1)
+
+// fwd
+static void netShutdown(JdwpNetState* state);
+static void netFree(JdwpNetState* state);
+
+
+/*
+ * JDWP network state.
+ *
+ * We only talk to one debugger at a time.
+ */
+struct JdwpNetState : public JdwpNetStateBase {
+    short   listenPort;
+    int     listenSock;         /* listen for connection from debugger */
+    int     wakePipe[2];        /* break out of select */
+
+    struct in_addr remoteAddr;
+    unsigned short remotePort;
+
+    bool    awaitingHandshake;  /* waiting for "JDWP-Handshake" */
+
+    /* pending data from the network; would be more efficient as circular buf */
+    unsigned char  inputBuffer[kInputBufferSize];
+    int     inputCount;
+
+    JdwpNetState()
+    {
+        listenPort  = 0;
+        listenSock  = -1;
+        wakePipe[0] = -1;
+        wakePipe[1] = -1;
+
+        awaitingHandshake = false;
+
+        inputCount = 0;
+    }
+};
+
+static JdwpNetState* netStartup(short port);
+
+/*
+ * Set up some stuff for transport=dt_socket.
+ */
+static bool prepareSocket(JdwpState* state, const JdwpStartupParams* pParams)
+{
+    unsigned short port;
+
+    if (pParams->server) {
+        if (pParams->port != 0) {
+            /* try only the specified port */
+            port = pParams->port;
+            state->netState = netStartup(port);
+        } else {
+            /* scan through a range of ports, binding to the first available */
+            for (port = kBasePort; port <= kMaxPort; port++) {
+                state->netState = netStartup(port);
+                if (state->netState != NULL)
+                    break;
+            }
+        }
+        if (state->netState == NULL) {
+            ALOGE("JDWP net startup failed (req port=%d)", pParams->port);
+            return false;
+        }
+    } else {
+        port = pParams->port;   // used in a debug msg later
+        state->netState = netStartup(-1);
+    }
+
+    if (pParams->suspend)
+        ALOGI("JDWP will wait for debugger on port %d", port);
+    else
+        ALOGD("JDWP will %s on port %d",
+            pParams->server ? "listen" : "connect", port);
+
+    return true;
+}
+
+
+/*
+ * Are we still waiting for the handshake string?
+ */
+static bool awaitingHandshake(JdwpState* state)
+{
+    return state->netState->awaitingHandshake;
+}
+
+/*
+ * Initialize JDWP stuff.
+ *
+ * Allocates a new state structure.  If "port" is non-negative, this also
+ * tries to bind to a listen port.  If "port" is less than zero, we assume
+ * we're preparing for an outbound connection, and return without binding
+ * to anything.
+ *
+ * This may be called several times if we're probing for a port.
+ *
+ * Returns 0 on success.
+ */
+static JdwpNetState* netStartup(short port)
+{
+    int one = 1;
+    JdwpNetState* netState = new JdwpNetState;
+
+    if (port < 0)
+        return netState;
+
+    assert(port != 0);
+
+    netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (netState->listenSock < 0) {
+        ALOGE("Socket create failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    /* allow immediate re-use */
+    if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
+            sizeof(one)) < 0)
+    {
+        ALOGE("setsockopt(SO_REUSEADDR) failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    union {
+        struct sockaddr_in  addrInet;
+        struct sockaddr     addrPlain;
+    } addr;
+    addr.addrInet.sin_family = AF_INET;
+    addr.addrInet.sin_port = htons(port);
+    inet_aton("127.0.0.1", &addr.addrInet.sin_addr);
+
+    if (bind(netState->listenSock, &addr.addrPlain, sizeof(addr)) != 0) {
+        ALOGV("attempt to bind to port %u failed: %s", port, strerror(errno));
+        goto fail;
+    }
+
+    netState->listenPort = port;
+    LOGVV("+++ bound to port %d", netState->listenPort);
+
+    if (listen(netState->listenSock, 5) != 0) {
+        ALOGE("Listen failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    return netState;
+
+fail:
+    netShutdown(netState);
+    netFree(netState);
+    return NULL;
+}
+
+/*
+ * Shut down JDWP listener.  Don't free state.
+ *
+ * Note that "netState" may be partially initialized if "startup" failed.
+ *
+ * This may be called from a non-JDWP thread as part of shutting the
+ * JDWP thread down.
+ *
+ * (This is currently called several times during startup as we probe
+ * for an open port.)
+ */
+static void netShutdown(JdwpNetState* netState)
+{
+    if (netState == NULL)
+        return;
+
+    int listenSock = netState->listenSock;
+    int clientSock = netState->clientSock;
+
+    /* clear these out so it doesn't wake up and try to reuse them */
+    netState->listenSock = netState->clientSock = -1;
+
+    /* "shutdown" dislodges blocking read() and accept() calls */
+    if (listenSock >= 0) {
+        shutdown(listenSock, SHUT_RDWR);
+        close(listenSock);
+    }
+    if (clientSock >= 0) {
+        shutdown(clientSock, SHUT_RDWR);
+        close(clientSock);
+    }
+
+    /* if we might be sitting in select, kick us loose */
+    if (netState->wakePipe[1] >= 0) {
+        ALOGV("+++ writing to wakePipe");
+        (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;
+    }
+
+    delete netState;
+}
+static void netFreeExtern(JdwpState* state)
+{
+    netFree(state->netState);
+}
+
+/*
+ * Returns "true" if we're connected to a debugger.
+ */
+static bool isConnected(JdwpState* state)
+{
+    return (state->netState != NULL &&
+            state->netState->clientSock >= 0);
+}
+
+/*
+ * Returns "true" if the fd is ready, "false" if not.
+ */
+#if 0
+static bool isFdReadable(int sock)
+{
+    fd_set readfds;
+    struct timeval tv;
+    int count;
+
+    FD_ZERO(&readfds);
+    FD_SET(sock, &readfds);
+
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    count = select(sock+1, &readfds, NULL, NULL, &tv);
+    if (count <= 0)
+        return false;
+
+    if (FD_ISSET(sock, &readfds))   /* make sure it's our fd */
+        return true;
+
+    ALOGE("WEIRD: odd behavior in select (count=%d)", count);
+    return false;
+}
+#endif
+
+#if 0
+/*
+ * Check to see if we have a pending connection from the debugger.
+ *
+ * Returns true on success (meaning a connection is available).
+ */
+static bool checkConnection(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+
+    assert(netState->listenSock >= 0);
+    /* not expecting to be called when debugger is actively connected */
+    assert(netState->clientSock < 0);
+
+    if (!isFdReadable(netState->listenSock))
+        return false;
+    return true;
+}
+#endif
+
+/*
+ * Disable the TCP Nagle algorithm, which delays transmission of outbound
+ * packets until the previous transmissions have been acked.  JDWP does a
+ * lot of back-and-forth with small packets, so this may help.
+ */
+static int setNoDelay(int fd)
+{
+    int cc, on = 1;
+
+    cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+    assert(cc == 0);
+    return cc;
+}
+
+/*
+ * Accept a connection.  This will block waiting for somebody to show up.
+ * If that's not desirable, use checkConnection() to make sure something
+ * is pending.
+ */
+static bool acceptConnection(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    union {
+        struct sockaddr_in  addrInet;
+        struct sockaddr     addrPlain;
+    } addr;
+    socklen_t addrlen;
+    int sock;
+
+    if (netState->listenSock < 0)
+        return false;       /* you're not listening! */
+
+    assert(netState->clientSock < 0);      /* must not already be talking */
+
+    addrlen = sizeof(addr);
+    do {
+        sock = accept(netState->listenSock, &addr.addrPlain, &addrlen);
+        if (sock < 0 && errno != EINTR) {
+            // When we call shutdown() on the socket, accept() returns with
+            // EINVAL.  Don't gripe about it.
+            if (errno == EINVAL)
+                LOGVV("accept failed: %s", strerror(errno));
+            else
+                ALOGE("accept failed: %s", strerror(errno));
+            return false;
+        }
+    } while (sock < 0);
+
+    netState->remoteAddr = addr.addrInet.sin_addr;
+    netState->remotePort = ntohs(addr.addrInet.sin_port);
+    ALOGV("+++ accepted connection from %s:%u",
+        inet_ntoa(netState->remoteAddr), netState->remotePort);
+
+    netState->clientSock = sock;
+    netState->awaitingHandshake = true;
+    netState->inputCount = 0;
+
+    ALOGV("Setting TCP_NODELAY on accepted socket");
+    setNoDelay(netState->clientSock);
+
+    if (pipe(netState->wakePipe) < 0) {
+        ALOGE("pipe failed");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Create a connection to a waiting debugger.
+ */
+static bool establishConnection(JdwpState* state)
+{
+    union {
+        struct sockaddr_in  addrInet;
+        struct sockaddr     addrPlain;
+    } addr;
+    struct hostent* pEntry;
+    int h_errno;
+
+    assert(state != NULL && state->netState != NULL);
+    assert(!state->params.server);
+    assert(state->params.host[0] != '\0');
+    assert(state->params.port != 0);
+
+    /*
+     * Start by resolving the host name.
+     */
+//#undef HAVE_GETHOSTBYNAME_R
+//#warning "forcing non-R"
+#ifdef HAVE_GETHOSTBYNAME_R
+    struct hostent he;
+    char auxBuf[128];
+    int cc = gethostbyname_r(state->params.host, &he, auxBuf, sizeof(auxBuf),
+            &pEntry, &h_errno);
+    if (cc != 0) {
+        ALOGW("gethostbyname_r('%s') failed: %s",
+            state->params.host, strerror(errno));
+        return false;
+    }
+
+#else
+    h_errno = 0;
+    pEntry = gethostbyname(state->params.host);
+    if (pEntry == NULL) {
+        ALOGW("gethostbyname('%s') failed: %s",
+            state->params.host, strerror(h_errno));
+        return false;
+    }
+#endif
+
+    /* copy it out ASAP to minimize risk of multithreaded annoyances */
+    memcpy(&addr.addrInet.sin_addr, pEntry->h_addr, pEntry->h_length);
+    addr.addrInet.sin_family = pEntry->h_addrtype;
+
+    addr.addrInet.sin_port = htons(state->params.port);
+
+    ALOGI("Connecting out to '%s' %d",
+        inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port));
+
+    /*
+     * Create a socket.
+     */
+    JdwpNetState* netState;
+    netState = state->netState;
+    netState->clientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (netState->clientSock < 0) {
+        ALOGE("Unable to create socket: %s", strerror(errno));
+        return false;
+    }
+
+    /*
+     * Try to connect.
+     */
+    if (connect(netState->clientSock, &addr.addrPlain, sizeof(addr)) != 0) {
+        ALOGE("Unable to connect to %s:%d: %s",
+            inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port),
+            strerror(errno));
+        close(netState->clientSock);
+        netState->clientSock = -1;
+        return false;
+    }
+
+    ALOGI("Connection established to %s (%s:%d)",
+        state->params.host, inet_ntoa(addr.addrInet.sin_addr),
+        ntohs(addr.addrInet.sin_port));
+    netState->awaitingHandshake = true;
+    netState->inputCount = 0;
+
+    setNoDelay(netState->clientSock);
+
+    if (pipe(netState->wakePipe) < 0) {
+        ALOGE("pipe failed");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Close the connection to the debugger.
+ *
+ * Reset the state so we're ready to receive a new connection.
+ */
+static void closeConnection(JdwpState* state)
+{
+    JdwpNetState* netState;
+
+    assert(state != NULL && state->netState != NULL);
+
+    netState = state->netState;
+    if (netState->clientSock < 0)
+        return;
+
+    ALOGV("+++ closed connection to %s:%u",
+        inet_ntoa(netState->remoteAddr), netState->remotePort);
+
+    close(netState->clientSock);
+    netState->clientSock = -1;
+
+    return;
+}
+
+/*
+ * Figure out if we have a full packet in the buffer.
+ */
+static bool haveFullPacket(JdwpNetState* netState)
+{
+    long length;
+
+    if (netState->awaitingHandshake)
+        return (netState->inputCount >= (int) kMagicHandshakeLen);
+
+    if (netState->inputCount < 4)
+        return false;
+
+    length = get4BE(netState->inputBuffer);
+    return (netState->inputCount >= length);
+}
+
+/*
+ * Consume bytes from the buffer.
+ *
+ * This would be more efficient with a circular buffer.  However, we're
+ * usually only going to find one packet, which is trivial to handle.
+ */
+static void consumeBytes(JdwpNetState* netState, int count)
+{
+    assert(count > 0);
+    assert(count <= netState->inputCount);
+
+    if (count == netState->inputCount) {
+        netState->inputCount = 0;
+        return;
+    }
+
+    memmove(netState->inputBuffer, netState->inputBuffer + count,
+        netState->inputCount - count);
+    netState->inputCount -= count;
+}
+
+/*
+ * Dump the contents of a packet to stdout.
+ */
+#if 0
+static void dumpPacket(const unsigned char* packetBuf)
+{
+    const unsigned char* buf = packetBuf;
+    u4 length, id;
+    u1 flags, cmdSet, cmd;
+    u2 error;
+    bool reply;
+    int dataLen;
+
+    cmd = cmdSet = 0xcc;
+
+    length = read4BE(&buf);
+    id = read4BE(&buf);
+    flags = read1(&buf);
+    if ((flags & kJDWPFlagReply) != 0) {
+        reply = true;
+        error = read2BE(&buf);
+    } else {
+        reply = false;
+        cmdSet = read1(&buf);
+        cmd = read1(&buf);
+    }
+
+    dataLen = length - (buf - packetBuf);
+
+    ALOGV("--- %s: dataLen=%u id=0x%08x flags=0x%02x cmd=%d/%d",
+        reply ? "reply" : "req",
+        dataLen, id, flags, cmdSet, cmd);
+    if (dataLen > 0)
+        dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
+}
+#endif
+
+/*
+ * Handle a packet.  Returns "false" if we encounter a connection-fatal error.
+ */
+static bool handlePacket(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    const unsigned char* buf = netState->inputBuffer;
+    JdwpReqHeader hdr;
+    u4 length, id;
+    u1 flags, cmdSet, cmd;
+    u2 error;
+    bool reply;
+    int dataLen;
+
+    cmd = cmdSet = 0;       // shut up gcc
+
+    /*dumpPacket(netState->inputBuffer);*/
+
+    length = read4BE(&buf);
+    id = read4BE(&buf);
+    flags = read1(&buf);
+    if ((flags & kJDWPFlagReply) != 0) {
+        reply = true;
+        error = read2BE(&buf);
+    } else {
+        reply = false;
+        cmdSet = read1(&buf);
+        cmd = read1(&buf);
+    }
+
+    assert((int) length <= netState->inputCount);
+    dataLen = length - (buf - netState->inputBuffer);
+
+    if (!reply) {
+        ExpandBuf* pReply = expandBufAlloc();
+
+        hdr.length = length;
+        hdr.id = id;
+        hdr.cmdSet = cmdSet;
+        hdr.cmd = cmd;
+        dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
+        if (expandBufGetLength(pReply) > 0) {
+            ssize_t cc = netState->writePacket(pReply);
+
+            if (cc != (ssize_t) expandBufGetLength(pReply)) {
+                ALOGE("Failed sending reply to debugger: %s", strerror(errno));
+                expandBufFree(pReply);
+                return false;
+            }
+        } else {
+            ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd);
+        }
+        expandBufFree(pReply);
+    } else {
+        ALOGV("reply?!");
+        assert(false);
+    }
+
+    ALOGV("----------");
+
+    consumeBytes(netState, length);
+    return true;
+}
+
+/*
+ * Process incoming data.  If no data is available, this will block until
+ * some arrives.
+ *
+ * If we get a full packet, handle it.
+ *
+ * To take some of the mystery out of life, we want to reject incoming
+ * connections if we already have a debugger attached.  If we don't, the
+ * debugger will just mysteriously hang until it times out.  We could just
+ * close the listen socket, but there's a good chance we won't be able to
+ * bind to the same port again, which would confuse utilities.
+ *
+ * Returns "false" on error (indicating that the connection has been severed),
+ * "true" if things are still okay.
+ */
+static bool processIncoming(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    int readCount;
+
+    assert(netState->clientSock >= 0);
+
+    if (!haveFullPacket(netState)) {
+        /* read some more, looping until we have data */
+        errno = 0;
+        while (1) {
+            int selCount;
+            fd_set readfds;
+            int maxfd;
+            int fd;
+
+            maxfd = netState->listenSock;
+            if (netState->clientSock > maxfd)
+                maxfd = netState->clientSock;
+            if (netState->wakePipe[0] > maxfd)
+                maxfd = netState->wakePipe[0];
+
+            if (maxfd < 0) {
+                ALOGV("+++ all fds are closed");
+                return false;
+            }
+
+            FD_ZERO(&readfds);
+
+            /* configure fds; note these may get zapped by another thread */
+            fd = netState->listenSock;
+            if (fd >= 0)
+                FD_SET(fd, &readfds);
+            fd = netState->clientSock;
+            if (fd >= 0)
+                FD_SET(fd, &readfds);
+            fd = netState->wakePipe[0];
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+            } else {
+                ALOGI("NOTE: entering select w/o wakepipe");
+            }
+
+            /*
+             * Select blocks until it sees activity on the file descriptors.
+             * Closing the local file descriptor does not count as activity,
+             * so we can't rely on that to wake us up (it works for read()
+             * and accept(), but not select()).
+             *
+             * We can do one of three things: (1) send a signal and catch
+             * EINTR, (2) open an additional fd ("wakePipe") and write to
+             * it when it's time to exit, or (3) time out periodically and
+             * re-issue the select.  We're currently using #2, as it's more
+             * reliable than #1 and generally better than #3.  Wastes two fds.
+             */
+            selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+            if (selCount < 0) {
+                if (errno == EINTR)
+                    continue;
+                ALOGE("select failed: %s", strerror(errno));
+                goto fail;
+            }
+
+            if (netState->wakePipe[0] >= 0 &&
+                FD_ISSET(netState->wakePipe[0], &readfds))
+            {
+                if (netState->listenSock >= 0)
+                    ALOGE("Exit wake set, but not exiting?");
+                else
+                    ALOGD("Got wake-up signal, bailing out of select");
+                goto fail;
+            }
+            if (netState->listenSock >= 0 &&
+                FD_ISSET(netState->listenSock, &readfds))
+            {
+                ALOGI("Ignoring second debugger -- accepting and dropping");
+                union {
+                    struct sockaddr_in   addrInet;
+                    struct sockaddr      addrPlain;
+                } addr;
+                socklen_t addrlen;
+                int tmpSock;
+                tmpSock = accept(netState->listenSock, &addr.addrPlain,
+                                &addrlen);
+                if (tmpSock < 0)
+                    ALOGI("Weird -- accept failed");
+                else
+                    close(tmpSock);
+            }
+            if (netState->clientSock >= 0 &&
+                FD_ISSET(netState->clientSock, &readfds))
+            {
+                readCount = read(netState->clientSock,
+                                netState->inputBuffer + netState->inputCount,
+                    sizeof(netState->inputBuffer) - netState->inputCount);
+                if (readCount < 0) {
+                    /* read failed */
+                    if (errno != EINTR)
+                        goto fail;
+                    ALOGD("+++ EINTR hit");
+                    return true;
+                } else if (readCount == 0) {
+                    /* EOF hit -- far end went away */
+                    ALOGD("+++ peer disconnected");
+                    goto fail;
+                } else
+                    break;
+            }
+        }
+
+        netState->inputCount += readCount;
+        if (!haveFullPacket(netState))
+            return true;        /* still not there yet */
+    }
+
+    /*
+     * Special-case the initial handshake.  For some bizarre reason we're
+     * expected to emulate bad tty settings by echoing the request back
+     * exactly as it was sent.  Note the handshake is always initiated by
+     * the debugger, no matter who connects to whom.
+     *
+     * Other than this one case, the protocol [claims to be] stateless.
+     */
+    if (netState->awaitingHandshake) {
+        int cc;
+
+        if (memcmp(netState->inputBuffer,
+                kMagicHandshake, kMagicHandshakeLen) != 0)
+        {
+            ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer);
+            goto fail;
+        }
+
+        errno = 0;
+        cc = write(netState->clientSock, netState->inputBuffer,
+                kMagicHandshakeLen);
+        if (cc != kMagicHandshakeLen) {
+            ALOGE("Failed writing handshake bytes: %s (%d of %d)",
+                strerror(errno), cc, (int) kMagicHandshakeLen);
+            goto fail;
+        }
+
+        consumeBytes(netState, kMagicHandshakeLen);
+        netState->awaitingHandshake = false;
+        ALOGV("+++ handshake complete");
+        return true;
+    }
+
+    /*
+     * Handle this packet.
+     */
+    return handlePacket(state);
+
+fail:
+    closeConnection(state);
+    return false;
+}
+
+/*
+ * Send a request.
+ *
+ * The entire packet must be sent with a single write() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
+{
+    JdwpNetState* netState = state->netState;
+
+    /*dumpPacket(expandBufGetBuffer(pReq));*/
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    errno = 0;
+    ssize_t cc = netState->writePacket(pReq);
+
+    if (cc != (ssize_t) expandBufGetLength(pReq)) {
+        ALOGE("Failed sending req to debugger: %s (%d of %d)",
+            strerror(errno), (int) cc, (int) expandBufGetLength(pReq));
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Send a request that was split into multiple buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
+    int iovcnt)
+{
+    JdwpNetState* netState = state->netState;
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    size_t expected = 0;
+    int i;
+    for (i = 0; i < iovcnt; i++)
+        expected += iov[i].iov_len;
+
+    ssize_t actual = netState->writeBufferedPacket(iov, iovcnt);
+
+    if ((size_t)actual != expected) {
+        ALOGE("Failed sending b-req to debugger: %s (%d of %zu)",
+            strerror(errno), (int) actual, expected);
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Our functions.
+ *
+ * We can't generally share the implementations with other transports,
+ * even if they're also socket-based, because our JdwpNetState will be
+ * different from theirs.
+ */
+static const JdwpTransport socketTransport = {
+    prepareSocket,
+    acceptConnection,
+    establishConnection,
+    closeConnection,
+    netShutdownExtern,
+    netFreeExtern,
+    isConnected,
+    awaitingHandshake,
+    processIncoming,
+    sendRequest,
+    sendBufferedRequest,
+};
+
+/*
+ * Return our set.
+ */
+const JdwpTransport* dvmJdwpSocketTransport()
+{
+    return &socketTransport;
+}
diff --git a/vm/jdwp/README.txt b/vm/jdwp/README.txt
new file mode 100644
index 0000000..c97a069
--- /dev/null
+++ b/vm/jdwp/README.txt
@@ -0,0 +1,12 @@
+Java Debug Wire Protocol support
+
+This is a reasonably complete implementation, but only messages that are
+actually generated by debuggers have been implemented.  The reasoning
+behind this is that it's better to leave a call unimplemented than have
+something that appears implemented but has never been tested.
+
+An attempt has been made to keep the implementation distinct from the VM,
+with Debugger.c acting as a sort of portability layer, so that the code
+might be useful in other projects.  Once you get multiple simultaneous
+events and debugger requests with thread suspension bouncing around,
+though, it's difficult to keep things "generic".
diff --git a/vm/mterp/Makefile-mterp b/vm/mterp/Makefile-mterp
new file mode 100644
index 0000000..2f96d59
--- /dev/null
+++ b/vm/mterp/Makefile-mterp
@@ -0,0 +1,51 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Makefile for the Dalvik modular interpreter.  This is not currently
+# integrated into the build system.
+#
+
+SHELL := /bin/sh
+
+# Build system has TARGET_ARCH=arm, but we need the exact architecture.
+# The base assumption for an ARM platform is ARMv5TE, but we may want to
+# support older ARMv4 devices, or use special features from ARMv6 or VFP.
+# The simulator build is "desktop".
+#
+# To generate sources for all targets:
+# for arch in desktop armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+#
+#TARGET_ARCH_EXT := armv5te
+
+OUTPUT_DIR := out
+
+# Accumulate all possible dependencies for the generated files in a very
+# conservative fashion.  If it's not one of the generated files in "out",
+# assume it's a dependency.
+SOURCE_DEPS := \
+	$(shell find . -path ./$(OUTPUT_DIR) -prune -o -type f -print) \
+	../Android.mk ../../Android.mk
+
+# Source files generated by the script.  There's always one C and one
+# assembly file, though in practice one or the other could be empty.
+GEN_SOURCES := \
+	$(OUTPUT_DIR)/InterpC-$(TARGET_ARCH_EXT).c \
+	$(OUTPUT_DIR)/InterpAsm-$(TARGET_ARCH_EXT).S
+
+target: $(GEN_SOURCES)
+
+$(GEN_SOURCES): $(SOURCE_DEPS)
+	@mkdir -p out
+	./gen-mterp.py $(TARGET_ARCH_EXT) $(OUTPUT_DIR)
diff --git a/vm/mterp/Mterp.cpp b/vm/mterp/Mterp.cpp
new file mode 100644
index 0000000..bfeada2
--- /dev/null
+++ b/vm/mterp/Mterp.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Mterp entry point and support functions.
+ */
+#include "Dalvik.h"
+#include "mterp/Mterp.h"
+
+#include <stddef.h>
+
+
+/*
+ * Verify some constants used by the mterp interpreter.
+ */
+bool dvmCheckAsmConstants()
+{
+    bool failed = false;
+
+#ifndef DVM_NO_ASM_INTERP
+
+#ifndef DVM_JMP_TABLE_MTERP
+    extern void* dvmAsmInstructionStart[];
+    extern void* dvmAsmInstructionEnd[];
+#endif
+
+#define ASM_DEF_VERIFY
+#include "mterp/common/asm-constants.h"
+
+    if (failed) {
+        ALOGE("Please correct the values in mterp/common/asm-constants.h");
+        dvmAbort();
+    }
+
+#ifndef DVM_JMP_TABLE_MTERP
+    /*
+     * If we're using computed goto instruction transitions, make sure
+     * none of the handlers overflows the 64-byte limit.  This won't tell
+     * which one did, but if any one is too big the total size will
+     * overflow.
+     */
+#if defined(__mips__)
+    const int width = 128;
+#else
+    const int width = 64;
+#endif
+    int interpSize = (uintptr_t) dvmAsmInstructionEnd -
+                     (uintptr_t) dvmAsmInstructionStart;
+    if (interpSize != 0 && interpSize != kNumPackedOpcodes*width) {
+        ALOGE("ERROR: unexpected asm interp size %d", interpSize);
+        ALOGE("(did an instruction handler exceed %d bytes?)", width);
+        dvmAbort();
+    }
+#endif
+
+#endif // ndef DVM_NO_ASM_INTERP
+
+    return !failed;
+}
+
+
+/*
+ * "Mterp entry point.
+ */
+void dvmMterpStd(Thread* self)
+{
+    /* configure mterp items */
+    self->interpSave.methodClassDex = self->interpSave.method->clazz->pDvmDex;
+
+    IF_LOGVV() {
+        char* desc = dexProtoCopyMethodDescriptor(
+                         &self->interpSave.method->prototype);
+        LOGVV("mterp threadid=%d : %s.%s %s",
+            dvmThreadSelf()->threadId,
+            self->interpSave.method->clazz->descriptor,
+            self->interpSave.method->name,
+            desc);
+        free(desc);
+    }
+    //ALOGI("self is %p, pc=%p, fp=%p", self, self->interpSave.pc,
+    //      self->interpSave.curFrame);
+    //ALOGI("first instruction is 0x%04x", self->interpSave.pc[0]);
+
+    /*
+     * Handle any ongoing profiling and prep for debugging
+     */
+    if (self->interpBreak.ctl.subMode != 0) {
+        TRACE_METHOD_ENTER(self, self->interpSave.method);
+        self->debugIsMethodEntry = true;   // Always true on startup
+    }
+
+    dvmMterpStdRun(self);
+
+#ifdef LOG_INSTR
+    ALOGD("|-- Leaving interpreter loop");
+#endif
+}
diff --git a/vm/mterp/Mterp.h b/vm/mterp/Mterp.h
new file mode 100644
index 0000000..6762f67
--- /dev/null
+++ b/vm/mterp/Mterp.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Some declarations used throughout mterp.
+ */
+#ifndef DALVIK_MTERP_MTERP_H_
+#define DALVIK_MTERP_MTERP_H_
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#if defined(WITH_JIT)
+#include "interp/Jit.h"
+#endif
+
+/*
+ * Call this during initialization to verify that the values in asm-constants.h
+ * are still correct.
+ */
+extern "C" bool dvmCheckAsmConstants(void);
+
+/*
+ * Local entry and exit points.  The platform-specific implementation must
+ * provide these two.
+ */
+extern "C" void dvmMterpStdRun(Thread* self);
+extern "C" void dvmMterpStdBail(Thread* self);
+
+/*
+ * Helper for common_printMethod(), invoked from the assembly
+ * interpreter.
+ */
+extern "C" void dvmMterpPrintMethod(Method* method);
+
+#endif  // DALVIK_MTERP_MTERP_H_
diff --git a/vm/mterp/NOTES.txt b/vm/mterp/NOTES.txt
new file mode 100644
index 0000000..5dc7caa
--- /dev/null
+++ b/vm/mterp/NOTES.txt
@@ -0,0 +1,71 @@
+Interpreter Notes
+
+
+==== Thread suspension and GC points ====
+
+The interpreter is expected to use a safe-point mechanism to allow thread
+suspension for garbage collection and the debugger.  This typically
+means an explicit check of the thread-suspend flag on backward branches
+(including self-branches on certain instructions), exception throws,
+and method returns.  The interpreter must also be prepared to switch in
+and out of the "debug" interpreter at these points.
+
+There are other ways for a thread to be stopped when a GC happens, notably:
+
+ - object allocation (either while executing an instruction that performs
+   allocation, or indirectly by allocating an exception when something
+   goes wrong)
+ - transitions to native code or indefinite wait (e.g. monitor-enter)
+
+(A debugger can in theory cause the interpreter to advance one instruction
+at a time, but since that only happens in the "debug" interpreter it's not
+necessary for authors of platform-specific code to worry about it.)
+
+For the most part the implementation does not need to worry about these
+things, but they matter when considering the contents of Dalvik's virtual
+registers for "precise" garbage collection.  So, all opcode handlers must
+follow this rule:
+
+ * Do not modify the contents of a virtual register before executing
+   code that can pause the thread.
+
+This should be fairly hard to violate given the nature of essentially all
+instructions, which will compute a result and then finish by storing that
+result into the specified destination register.  Using a virtual register
+to hold a partial or temporary result is not allowed.  Virtual registers
+must not be modified if we abort the instruction with an exception.
+
+
+==== Method results and GC ====
+
+The return value from a method is held in local storage (on the native
+stack for the portable interpreter, and in glue->retval for asm).  It's not
+accessible to a GC.  In practice this isn't a problem, because if the
+following instruction is not a "move-result" then the result is ignored,
+and none of the move-result* instructions are GC points.
+
+(This is potentially an issue when debugging, since we can theoretically
+single-step by individual bytecodes, but in practice we always step by
+source lines and move-result is grouped with the method invoke.)
+
+This suggests a rule:
+
+ * Don't do anything that can cause a GC in the invoke-* handler after
+   a method returns successfully.
+
+Unsuccessful returns, such as a native method call that returns with an
+exception pending, are not interesting because the return value is ignored.
+
+If it's not possible to obey this rule, then we need to track the value
+used in a return-object instruction for a brief period.  The easiest way
+to accomplish this is to store it in the interpreted stack where the GC
+can find it, and use a live-precise GC to ignore the value.
+
+The "trackref" functions can also be used, but they add overhead to method
+calls returning objects, and ensuring that we stop tracking the reference
+when it's no longer needed can be awkward.
+
+Any solution must work correctly when returning into or returning from native
+code.  JNI handles returns from interp->native by adding the value to the
+local references table, but returns from native->interp are simply stored
+in the usual "retval".
diff --git a/vm/mterp/README.txt b/vm/mterp/README.txt
new file mode 100644
index 0000000..6106740
--- /dev/null
+++ b/vm/mterp/README.txt
@@ -0,0 +1,305 @@
+Dalvik "mterp" README
+
+NOTE: Find rebuilding instructions at the bottom of this file.
+
+
+==== Overview ====
+
+This is the source code for the Dalvik interpreter.  The core of the
+original version was implemented as a single C function, but to improve
+performance we rewrote it in assembly.  To make this and future assembly
+ports easier and less error-prone, we used a modular approach that allows
+development of platform-specific code one opcode at a time.
+
+The original all-in-one-function C version still exists as the "portable"
+interpreter, and is generated using the same sources and tools that
+generate the platform-specific versions.
+
+Every configuration has a "config-*" file that controls how the sources
+are generated.  The sources are written into the "out" directory, where
+they are picked up by the Android build system.
+
+The best way to become familiar with the interpreter is to look at the
+generated files in the "out" directory, such as out/InterpC-portstd.c,
+rather than trying to look at the various component pieces in (say)
+armv5te.
+
+
+==== Platform-specific source generation ====
+
+The architecture-specific config files determine what goes into two
+generated output files (InterpC-<arch>.c, InterpAsm-<arch>.S).  The goal is
+to make it easy to swap C and assembly sources during initial development
+and testing, and to provide a way to use architecture-specific versions of
+some operations (e.g. making use of PLD instructions on ARMv6 or avoiding
+CLZ on ARMv4T).
+
+Depending on architecture, instruction-to-instruction transitions may
+be done as either computed goto or jump table.  In the computed goto
+variant, each instruction handler is allocated a fixed-size area (e.g. 64
+byte).  "Overflow" code is tacked on to the end.  In the jump table variant,
+all of the instructions handlers are contiguous and may be of any size.
+The interpreter style is selected via the "handler-size" command (see below).
+
+When a C implementation for an instruction is desired, the assembly
+version packs all local state into the Thread structure and passes
+that to the C function.  Updates to the state are pulled out of
+"Thread" on return.
+
+The "arch" value should indicate an architecture family with common
+programming characteristics, so "armv5te" would work for all ARMv5TE CPUs,
+but might not be backward- or forward-compatible.  (We *might* want to
+specify the ABI model as well, e.g. "armv5te-eabi", but currently that adds
+verbosity without value.)
+
+
+==== Config file format ====
+
+The config files are parsed from top to bottom.  Each line in the file
+may be blank, hold a comment (line starts with '#'), or be a command.
+
+The commands are:
+
+  handler-style <computed-goto|jump-table|all-c>
+
+    Specify which style of interpreter to generate.  In computed-goto,
+    each handler is allocated a fixed region, allowing transitions to
+    be done via table-start-address + (opcode * handler-size). With
+    jump-table style, handlers may be of any length, and the generated
+    table is an array of pointers to the handlers. The "all-c" style is
+    for the portable interpreter (which is implemented completely in C).
+    [Note: all-c is distinct from an "allstubs" configuration.  In both
+    configurations, all handlers are the C versions, but the allstubs
+    configuration uses the assembly outer loop and assembly stubs to
+    transition to the handlers].  This command is required, and must be
+    the first command in the config file.
+
+  handler-size <bytes>
+
+    Specify the size of the fixed region, in bytes.  On most platforms
+    this will need to be a power of 2.  For jump-table and all-c
+    implementations, this command is ignored.
+
+  import <filename>
+
+    The specified file is included immediately, in its entirety.  No
+    substitutions are performed.  ".cpp" and ".h" files are copied to the
+    C output, ".S" files are copied to the asm output.
+
+  asm-stub <filename>
+
+    The named file will be included whenever an assembly "stub" is needed
+    to transfer control to a handler written in C.  Text substitution is
+    performed on the opcode name.  This command is not applicable to
+    to "all-c" configurations.
+
+  asm-alt-stub <filename>
+
+    When present, this command will cause the generation of an alternate
+    set of entry points (for computed-goto interpreters) or an alternate
+    jump table (for jump-table interpreters).
+
+  op-start <directory>
+
+    Indicates the start of the opcode list.  Must precede any "op"
+    commands.  The specified directory is the default location to pull
+    instruction files from.
+
+  op <opcode> <directory>
+
+    Can only appear after "op-start" and before "op-end".  Overrides the
+    default source file location of the specified opcode.  The opcode
+    definition will come from the specified file, e.g. "op OP_NOP armv5te"
+    will load from "armv5te/OP_NOP.S".  A substitution dictionary will be
+    applied (see below).
+
+  alt <opcode> <directory>
+
+    Can only appear after "op-start" and before "op-end".  Similar to the
+    "op" command above, but denotes a source file to override the entry
+    in the alternate handler table.  The opcode definition will come from
+    the specified file, e.g. "alt OP_NOP armv5te" will load from
+    "armv5te/ALT_OP_NOP.S".  A substitution dictionary will be applied
+    (see below).
+
+  op-end
+
+    Indicates the end of the opcode list.  All kNumPackedOpcodes
+    opcodes are emitted when this is seen, followed by any code that
+    didn't fit inside the fixed-size instruction handler space.
+
+The order of "op" and "alt" directives are not significant; the generation
+tool will extract ordering info from the VM sources.
+
+Typically the form in which most opcodes currently exist is used in
+the "op-start" directive.  For a new port you would start with "c",
+and add architecture-specific "op" entries as you write instructions.
+When complete it will default to the target architecture, and you insert
+"c" ops to stub out platform-specific code.
+
+For the <directory> specified in the "op" command, the "c" directory
+is special in two ways: (1) the sources are assumed to be C code, and
+will be inserted into the generated C file; (2) when a C implementation
+is emitted, a "glue stub" is emitted in the assembly source file.
+(The generator script always emits kNumPackedOpcodes assembly
+instructions, unless "asm-stub" was left blank, in which case it only
+emits some labels.)
+
+
+==== Instruction file format ====
+
+The assembly instruction files are simply fragments of assembly sources.
+The starting label will be provided by the generation tool, as will
+declarations for the segment type and alignment.  The expected target
+assembler is GNU "as", but others will work (may require fiddling with
+some of the pseudo-ops emitted by the generation tool).
+
+The C files do a bunch of fancy things with macros in an attempt to share
+code with the portable interpreter.  (This is expected to be reduced in
+the future.)
+
+A substitution dictionary is applied to all opcode fragments as they are
+appended to the output.  Substitutions can look like "$value" or "${value}".
+
+The dictionary always includes:
+
+  $opcode - opcode name, e.g. "OP_NOP"
+  $opnum - opcode number, e.g. 0 for OP_NOP
+  $handler_size_bytes - max size of an instruction handler, in bytes
+  $handler_size_bits - max size of an instruction handler, log 2
+
+Both C and assembly sources will be passed through the C pre-processor,
+so you can take advantage of C-style comments and preprocessor directives
+like "#define".
+
+Some generator operations are available.
+
+  %include "filename" [subst-dict]
+
+    Includes the file, which should look like "armv5te/OP_NOP.S".  You can
+    specify values for the substitution dictionary, using standard Python
+    syntax.  For example, this:
+      %include "armv5te/unop.S" {"result":"r1"}
+    would insert "armv5te/unop.S" at the current file position, replacing
+    occurrences of "$result" with "r1".
+
+  %default <subst-dict>
+
+    Specify default substitution dictionary values, using standard Python
+    syntax.  Useful if you want to have a "base" version and variants.
+
+  %break
+
+    Identifies the split between the main portion of the instruction
+    handler (which must fit in "handler-size" bytes) and the "sister"
+    code, which is appended to the end of the instruction handler block.
+    In jump table implementations, %break is ignored.
+
+  %verify "message"
+
+    Leave a note to yourself about what needs to be tested.  (This may
+    turn into something more interesting someday; for now, it just gets
+    stripped out before the output is generated.)
+
+The generation tool does *not* print a warning if your instructions
+exceed "handler-size", but the VM will abort on startup if it detects an
+oversized handler.  On architectures with fixed-width instructions this
+is easy to work with, on others this you will need to count bytes.
+
+
+==== Using C constants from assembly sources ====
+
+The file "common/asm-constants.h" has some definitions for constant
+values, structure sizes, and struct member offsets.  The format is fairly
+restricted, as simple macros are used to massage it for use with both C
+(where it is verified) and assembly (where the definitions are used).
+
+If a constant in the file becomes out of sync, the VM will log an error
+message and abort during startup.
+
+
+==== Development tips ====
+
+If you need to debug the initial piece of an opcode handler, and your
+debug code expands it beyond the handler size limit, you can insert a
+generic header at the top:
+
+    b       ${opcode}_start
+%break
+${opcode}_start:
+
+If you already have a %break, it's okay to leave it in place -- the second
+%break is ignored.
+
+
+==== Rebuilding ====
+
+If you change any of the source file fragments, you need to rebuild the
+combined source files in the "out" directory.  Make sure the files in
+"out" are editable, then:
+
+    $ cd mterp
+    $ ./rebuild.sh
+
+As of this writing, this requires Python 2.5. You may see inscrutible
+error messages or just general failure if you have a different version
+of Python installed.
+
+The ultimate goal is to have the build system generate the necessary
+output files without requiring this separate step, but we're not yet
+ready to require Python in the build.
+
+==== Interpreter Control ====
+
+The central mechanism for interpreter control is the InterpBreak struture
+that is found in each thread's Thread struct (see vm/Thread.h).  There
+is one mandatory field, and two optional fields:
+
+    subMode - required, describes debug/profile/special operation
+    breakFlags & curHandlerTable - optional, used lower subMode polling costs
+
+The subMode field is a bitmask which records all currently active
+special modes of operation.  For example, when Traceview profiling
+is active, kSubModeMethodTrace is set.  This bit informs the interpreter
+that it must notify the profiling subsystem on each method entry and
+return.  There are similar bits for an active debugging session,
+instruction count profiling, pending thread suspension request, etc.
+
+To support special subMode operation the simplest mechanism for the
+interpreter is to poll the subMode field before interpreting each Dalvik
+bytecode and take any required action.  In fact, this is precisely
+what the portable interpreter does.  The "FINISH" macro expands to
+include a test of subMode and subsequent call to the "dvmCheckBefore()".
+
+Per-instruction polling, however, is expensive and subMode operation is
+relative rare.  For normal operation we'd like to avoid having to perform
+any checks unless a special subMode is actually in effect.  This is
+where curHandlerTable and breakFlags come in to play.
+
+The mterp fast interpreter achieves much of its performance advantage
+over the portable interpreter through its efficient mechanism of
+transitioning from one Dalvik bytecode to the next.  Mterp for ARM targets
+uses a computed-goto mechanism, in which the handler entrypoints are
+located at the base of the handler table + (opcode * 64).  Mterp for x86
+targets instead uses a jump table of handler entry points indexed
+by the Dalvik opcode.  To support efficient handling of special subModes,
+mterp supports two sets of handler entries (for ARM) or two jump
+tables (for x86).  One handler set is optimized for speed and performs no
+inter-instruction checks (mainHandlerTable in the Thread structure), while
+the other includes a test of the subMode field (altHandlerTable).
+
+In normal operation (i.e. subMode == 0), the dedicated register rIBASE
+(r8 for ARM, edx for x86) holds a mainHandlerTable.  If we need to switch
+to a subMode that requires inter-instruction checking, rIBASE is changed
+to altHandlerTable.  Note that this change is not immediate.  What is actually
+changed is the value of curHandlerTable - which is part of the interpBreak
+structure.  Rather than explicitly check for changes, each thread will
+blindly refresh rIBASE at backward branches, exception throws and returns.
+
+The breakFlags field tells the interpreter control mechanism whether
+curHandlerTable should hold the real or alternate handler base.  If
+non-zero, we use the altHandlerBase.  The bits within breakFlags
+tells dvmCheckBefore which set of subModes need to be checked.
+
+See dvmCheckBefore() for subMode handling, and dvmEnableSubMode(),
+dvmDisableSubMode() for switching on and off.
diff --git a/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S b/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..5a5ad1d
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"faddd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..9823765
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"faddd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_FLOAT.S b/vm/mterp/arm-vfp/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..22023ec
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fadds   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e787589
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fadds   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S b/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..b75216e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S b/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..eade97d
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S b/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..6e85fe7
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S b/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..bdeb0be
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S b/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..11770ad
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fdivd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..a52f434
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fdivd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_FLOAT.S b/vm/mterp/arm-vfp/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..2e82ada
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fdivs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..2147583
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fdivs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..33d5b61
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopNarrower.S" {"instr":"fcvtsd  s0, d0"}
diff --git a/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S b/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..2ef4838
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopNarrower.S" {"instr":"ftosizd  s0, d0"}
diff --git a/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..0acb3d8
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopWider.S" {"instr":"fcvtds  d0, s0"}
diff --git a/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S b/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..d0a9a2e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funop.S" {"instr":"ftosizs s1, s0"}
diff --git a/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S b/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..6eb430e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopWider.S" {"instr":"fsitod  d0, s0"}
diff --git a/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S b/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..698bdc7
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funop.S" {"instr":"fsitos  s1, s0"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S b/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..7563191
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fmuld   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..eadf101
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fmuld   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_FLOAT.S b/vm/mterp/arm-vfp/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..bb3ab42
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fmuls   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..3918537
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fmuls   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S b/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..d40e083
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fsubd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..705124f
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fsubd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_FLOAT.S b/vm/mterp/arm-vfp/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..0bf2bc0
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fsubs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e214068
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fsubs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/README.txt b/vm/mterp/arm-vfp/README.txt
new file mode 100644
index 0000000..c94e1e8
--- /dev/null
+++ b/vm/mterp/arm-vfp/README.txt
@@ -0,0 +1,7 @@
+Instruction handlers that take advantage of ARM VFP.  These work with VFP
+v2 and v3 (VFPLite).
+
+The ARM code driving the floating-point calculations will run on ARMv5TE
+and later.  It assumes that word alignment is sufficient for double-word
+accesses (which is true for some ARMv5 and all ARMv6/v7), to avoid having
+to transfer double-precision values in two steps.
diff --git a/vm/mterp/arm-vfp/fbinop.S b/vm/mterp/arm-vfp/fbinop.S
new file mode 100644
index 0000000..ff9683e
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinop.S
@@ -0,0 +1,23 @@
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $instr                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinop2addr.S b/vm/mterp/arm-vfp/fbinop2addr.S
new file mode 100644
index 0000000..85b9fab
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinop2addr.S
@@ -0,0 +1,21 @@
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    $instr                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinopWide.S b/vm/mterp/arm-vfp/fbinopWide.S
new file mode 100644
index 0000000..2b9ad69
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinopWide.S
@@ -0,0 +1,23 @@
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $instr                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinopWide2addr.S b/vm/mterp/arm-vfp/fbinopWide2addr.S
new file mode 100644
index 0000000..15d9424
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinopWide2addr.S
@@ -0,0 +1,22 @@
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    $instr                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funop.S b/vm/mterp/arm-vfp/funop.S
new file mode 100644
index 0000000..a5846ce
--- /dev/null
+++ b/vm/mterp/arm-vfp/funop.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    $instr                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funopNarrower.S b/vm/mterp/arm-vfp/funopNarrower.S
new file mode 100644
index 0000000..7ae1676
--- /dev/null
+++ b/vm/mterp/arm-vfp/funopNarrower.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    $instr                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funopWider.S b/vm/mterp/arm-vfp/funopWider.S
new file mode 100644
index 0000000..055b851
--- /dev/null
+++ b/vm/mterp/arm-vfp/funopWider.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    $instr                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_ADD_DOUBLE.S b/vm/mterp/armv5te/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..de36691
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_dadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..744b2ab
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_dadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_FLOAT.S b/vm/mterp/armv5te/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..badf1f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..70f1fe8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT.S b/vm/mterp/armv5te/OP_ADD_INT.S
new file mode 100644
index 0000000..97cb5fe
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S b/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..c424f0c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_LIT16.S b/vm/mterp/armv5te/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..f5a1603
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_LIT8.S b/vm/mterp/armv5te/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..93e57d3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_LONG.S b/vm/mterp/armv5te/OP_ADD_LONG.S
new file mode 100644
index 0000000..b30cd05
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"adds    r0, r0, r2", "instr":"adc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S b/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..ff34ed5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"adds    r0, r0, r2", "instr":"adc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_AGET.S b/vm/mterp/armv5te/OP_AGET.S
new file mode 100644
index 0000000..8d8ed58
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET.S
@@ -0,0 +1,27 @@
+%default { "load":"ldr", "shift":"2" }
+%verify "executed"
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #$shift     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $load   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_AGET_BOOLEAN.S b/vm/mterp/armv5te/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..ccce848
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_AGET_BYTE.S b/vm/mterp/armv5te/OP_AGET_BYTE.S
new file mode 100644
index 0000000..c290922
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrsb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_AGET_CHAR.S b/vm/mterp/armv5te/OP_AGET_CHAR.S
new file mode 100644
index 0000000..78c40a0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_AGET_OBJECT.S b/vm/mterp/armv5te/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..9d64585
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S"
diff --git a/vm/mterp/armv5te/OP_AGET_SHORT.S b/vm/mterp/armv5te/OP_AGET_SHORT.S
new file mode 100644
index 0000000..77951e6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrsh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_AGET_WIDE.S b/vm/mterp/armv5te/OP_AGET_WIDE.S
new file mode 100644
index 0000000..6f641dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_WIDE.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .L${opcode}_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_AND_INT.S b/vm/mterp/armv5te/OP_AND_INT.S
new file mode 100644
index 0000000..2a0022e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_2ADDR.S b/vm/mterp/armv5te/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..24203aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_LIT16.S b/vm/mterp/armv5te/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..a06084d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_LIT8.S b/vm/mterp/armv5te/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..bd309bb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_LONG.S b/vm/mterp/armv5te/OP_AND_LONG.S
new file mode 100644
index 0000000..c76bae8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"and     r0, r0, r2", "instr":"and     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S b/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..6b7c830
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"and     r0, r0, r2", "instr":"and     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_APUT.S b/vm/mterp/armv5te/OP_APUT.S
new file mode 100644
index 0000000..741aadd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT.S
@@ -0,0 +1,27 @@
+%default { "store":"str", "shift":"2" }
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #$shift     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $store  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_APUT_BOOLEAN.S b/vm/mterp/armv5te/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..d46650d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_APUT_BYTE.S b/vm/mterp/armv5te/OP_APUT_BYTE.S
new file mode 100644
index 0000000..d46650d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_APUT_CHAR.S b/vm/mterp/armv5te/OP_APUT_CHAR.S
new file mode 100644
index 0000000..2ff31be
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_APUT_OBJECT.S b/vm/mterp/armv5te/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..95aec3a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_OBJECT.S
@@ -0,0 +1,55 @@
+%verify "executed"
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .L${opcode}_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+%break
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.L${opcode}_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .L${opcode}_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .L${opcode}_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.L${opcode}_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.L${opcode}_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
diff --git a/vm/mterp/armv5te/OP_APUT_SHORT.S b/vm/mterp/armv5te/OP_APUT_SHORT.S
new file mode 100644
index 0000000..2ff31be
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_APUT_WIDE.S b/vm/mterp/armv5te/OP_APUT_WIDE.S
new file mode 100644
index 0000000..cc9f332
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_WIDE.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .L${opcode}_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_ARRAY_LENGTH.S b/vm/mterp/armv5te/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..3a6faf3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ARRAY_LENGTH.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    and     r2, r2, #15                 @ r2<- A
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_BREAKPOINT.S b/vm/mterp/armv5te/OP_BREAKPOINT.S
new file mode 100644
index 0000000..b4ea333
--- /dev/null
+++ b/vm/mterp/armv5te/OP_BREAKPOINT.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
diff --git a/vm/mterp/armv5te/OP_CHECK_CAST.S b/vm/mterp/armv5te/OP_CHECK_CAST.S
new file mode 100644
index 0000000..57df60e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CHECK_CAST.S
@@ -0,0 +1,68 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .L${opcode}_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .L${opcode}_resolve         @ not resolved, do it now
+.L${opcode}_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .L${opcode}_fullcheck       @ no, do full check
+.L${opcode}_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.L${opcode}_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .L${opcode}_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .L${opcode}_resolved        @ pick up where we left off
diff --git a/vm/mterp/armv5te/OP_CMPG_DOUBLE.S b/vm/mterp/armv5te/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..a18c186
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_CMPL_DOUBLE.S" { "naninst":"mov     r1, #1" }
diff --git a/vm/mterp/armv5te/OP_CMPG_FLOAT.S b/vm/mterp/armv5te/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..3f87470
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_CMPL_FLOAT.S" { "naninst":"mov     r1, #1" }
diff --git a/vm/mterp/armv5te/OP_CMPL_DOUBLE.S b/vm/mterp/armv5te/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..01a63f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPL_DOUBLE.S
@@ -0,0 +1,48 @@
+%default { "naninst":"mvn     r1, #0" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r9, r0, #255                @ r9<- BB
+    mov     r10, r0, lsr #8             @ r10<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[BB]
+    add     r10, rFP, r10, lsl #2       @ r10<- &fp[CC]
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r10, {r2-r3}                @ r2/r3<- vCC/vCC+1
+    bl      __aeabi_cdcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.L${opcode}_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    ldmia   r10, {r0-r1}                @ reverse order
+    ldmia   r9, {r2-r3}
+    bl      __aeabi_cdcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .L${opcode}_finish
+    $naninst                            @ r1<- 1 or -1 for NaN
+    b       .L${opcode}_finish
diff --git a/vm/mterp/armv5te/OP_CMPL_FLOAT.S b/vm/mterp/armv5te/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..657f0dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPL_FLOAT.S
@@ -0,0 +1,115 @@
+%default { "naninst":"mvn     r1, #0" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ copy to arg registers
+    mov     r1, r10
+    bl      __aeabi_cfcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.L${opcode}_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    mov     r1, r9                      @ reverse order
+    mov     r0, r10
+    bl      __aeabi_cfcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .L${opcode}_finish
+    $naninst                            @ r1<- 1 or -1 for NaN
+    b       .L${opcode}_finish
+
+
+#if 0       /* "clasic" form */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpeq              @ r0<- (vBB == vCC)
+    cmp     r0, #0                      @ equal?
+    movne   r1, #0                      @ yes, result is 0
+    bne     ${opcode}_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmplt              @ r0<- (vBB < vCC)
+    cmp     r0, #0                      @ less than?
+    b       ${opcode}_continue
+@%break
+
+${opcode}_continue:
+    mvnne   r1, #0                      @ yes, result is -1
+    bne     ${opcode}_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpgt              @ r0<- (vBB > vCC)
+    cmp     r0, #0                      @ greater than?
+    beq     ${opcode}_nan               @ no, must be NaN
+    mov     r1, #1                      @ yes, result is 1
+    @ fall through to _finish
+
+${opcode}_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * This is expected to be uncommon, so we double-branch (once to here,
+     * again back to _finish).
+     */
+${opcode}_nan:
+    $naninst                            @ r1<- 1 or -1 for NaN
+    b       ${opcode}_finish
+
+#endif
diff --git a/vm/mterp/armv5te/OP_CMP_LONG.S b/vm/mterp/armv5te/OP_CMP_LONG.S
new file mode 100644
index 0000000..084a3f2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMP_LONG.S
@@ -0,0 +1,60 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .L${opcode}_less            @ signed compare on high part
+    bgt     .L${opcode}_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .L${opcode}_greater         @ unsigned compare on low part
+    bne     .L${opcode}_less
+    b       .L${opcode}_finish          @ equal; r1 already holds 0
+%break
+
+.L${opcode}_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.L${opcode}_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST.S b/vm/mterp/armv5te/OP_CONST.S
new file mode 100644
index 0000000..a813c52
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_16.S b/vm/mterp/armv5te/OP_CONST_16.S
new file mode 100644
index 0000000..b5654a3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_4.S b/vm/mterp/armv5te/OP_CONST_4.S
new file mode 100644
index 0000000..6dd53af
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_CLASS.S b/vm/mterp/armv5te/OP_CONST_CLASS.S
new file mode 100644
index 0000000..018e1e0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_CLASS.S
@@ -0,0 +1,35 @@
+%verify "executed"
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .L${opcode}_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_HIGH16.S b/vm/mterp/armv5te/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..4536d3a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_HIGH16.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_STRING.S b/vm/mterp/armv5te/OP_CONST_STRING.S
new file mode 100644
index 0000000..0d889dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_STRING.S
@@ -0,0 +1,34 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .L${opcode}_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S b/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..6ca6bd1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,36 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .L${opcode}_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE.S b/vm/mterp/armv5te/OP_CONST_WIDE.S
new file mode 100644
index 0000000..b724264
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_16.S b/vm/mterp/armv5te/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..e3e4149
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_32.S b/vm/mterp/armv5te/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..a86e042
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_32.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S b/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..11bf518
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_DIV_DOUBLE.S b/vm/mterp/armv5te/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..2fb085b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_ddiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..2dcef28
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_ddiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_FLOAT.S b/vm/mterp/armv5te/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..8fc2fa9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fdiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e2d57dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fdiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT.S b/vm/mterp/armv5te/OP_DIV_INT.S
new file mode 100644
index 0000000..1e326d9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S b/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..22c8380
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_LIT16.S b/vm/mterp/armv5te/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..574594f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_LIT8.S b/vm/mterp/armv5te/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..8e56a4a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_LONG.S b/vm/mterp/armv5te/OP_DIV_LONG.S
new file mode 100644
index 0000000..ec5db4a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S b/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..4651383
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..f0b1a33
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopNarrower.S" {"instr":"bl      __aeabi_d2f"}
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S b/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..3e0a26b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,54 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv5te/unopNarrower.S" {"instr":"bl      __aeabi_d2iz"}
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl      d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r2, #0x80000000             @ maxint, as a double (low word)
+    mov     r2, r2, asr #9              @  0xffc00000
+    sub     sp, sp, #4                  @ align for EABI
+    mvn     r3, #0xbe000000             @ maxint, as a double (high word)
+    sub     r3, r3, #0x00200000         @  0x41dfffff
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (0x7fffffff)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc1000000             @ minint, as a double (high word)
+    add     r3, r3, #0x00e00000         @  0xc1e00000
+    mov     r2, #0                      @ minint, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    beq     1f                          @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2iz                @ convert double to int
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+#endif
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S b/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..ff0fd2e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,53 @@
+%verify "executed"
+@include "armv5te/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+%include "armv5te/unopWide.S" {"instr":"bl      d2l_doconv"}
+
+%break
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
diff --git a/vm/mterp/armv5te/OP_EXECUTE_INLINE.S b/vm/mterp/armv5te/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..ca71de1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE.S
@@ -0,0 +1,99 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .L${opcode}_debugmode       @ yes - take slow path
+.L${opcode}_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.L${opcode}_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .L${opcode}_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.L${opcode}_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .L${opcode}_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.L${opcode}_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
diff --git a/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..d9e35b8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,94 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .L${opcode}_debugmode       @ yes - take slow path
+.L${opcode}_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.L${opcode}_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .L${opcode}_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.L${opcode}_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .L${opcode}_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.L${opcode}_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
diff --git a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..de39958
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,109 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .L${opcode}_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .L${opcode}_continue
+%break
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.L${opcode}_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     $isrange
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .L${opcode}_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     $isrange
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.L${opcode}_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_${opcode}
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_${opcode}:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
diff --git a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..a2273c4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S b/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..a0d8399
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..b235e61
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2d"}
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_INT.S b/vm/mterp/armv5te/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..c9cb957
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_INT.S
@@ -0,0 +1,40 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv5te/unop.S" {"instr":"bl      __aeabi_f2iz"}
+
+#if 0
+@include "armv5te/unop.S" {"instr":"bl      f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x4f000000             @ (float)maxint
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (7fffffff)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xcf000000             @ (float)minint
+    bl      __aeabi_fcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    ldmeqfd sp!, {r4, pc}               @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2iz                @ convert float to int
+    ldmfd   sp!, {r4, pc}
+#endif
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S b/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..e42e1a4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+@include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+%include "armv5te/unopWider.S" {"instr":"bl      f2l_doconv"}
+
+%break
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
diff --git a/vm/mterp/armv5te/OP_GOTO.S b/vm/mterp/armv5te/OP_GOTO.S
new file mode 100644
index 0000000..7feca7b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO.S
@@ -0,0 +1,22 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_GOTO_16.S b/vm/mterp/armv5te/OP_GOTO_16.S
new file mode 100644
index 0000000..8b1f1bd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO_16.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_GOTO_32.S b/vm/mterp/armv5te/OP_GOTO_32.S
new file mode 100644
index 0000000..6202d7e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO_32.S
@@ -0,0 +1,29 @@
+%verify "executed"
+%verify "forward, backward, self"
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IF_EQ.S b/vm/mterp/armv5te/OP_IF_EQ.S
new file mode 100644
index 0000000..ecd2c55
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv5te/OP_IF_EQZ.S b/vm/mterp/armv5te/OP_IF_EQZ.S
new file mode 100644
index 0000000..2011a03
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv5te/OP_IF_GE.S b/vm/mterp/armv5te/OP_IF_GE.S
new file mode 100644
index 0000000..7424939
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv5te/OP_IF_GEZ.S b/vm/mterp/armv5te/OP_IF_GEZ.S
new file mode 100644
index 0000000..2cae521
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv5te/OP_IF_GT.S b/vm/mterp/armv5te/OP_IF_GT.S
new file mode 100644
index 0000000..553a74a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv5te/OP_IF_GTZ.S b/vm/mterp/armv5te/OP_IF_GTZ.S
new file mode 100644
index 0000000..c82911f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv5te/OP_IF_LE.S b/vm/mterp/armv5te/OP_IF_LE.S
new file mode 100644
index 0000000..dd99709
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv5te/OP_IF_LEZ.S b/vm/mterp/armv5te/OP_IF_LEZ.S
new file mode 100644
index 0000000..382c34d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv5te/OP_IF_LT.S b/vm/mterp/armv5te/OP_IF_LT.S
new file mode 100644
index 0000000..34dc445
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv5te/OP_IF_LTZ.S b/vm/mterp/armv5te/OP_IF_LTZ.S
new file mode 100644
index 0000000..6a0432f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv5te/OP_IF_NE.S b/vm/mterp/armv5te/OP_IF_NE.S
new file mode 100644
index 0000000..950c916
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv5te/OP_IF_NEZ.S b/vm/mterp/armv5te/OP_IF_NEZ.S
new file mode 100644
index 0000000..226549c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv5te/OP_IGET.S b/vm/mterp/armv5te/OP_IGET.S
new file mode 100644
index 0000000..a81467c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET.S
@@ -0,0 +1,47 @@
+%default { "load":"ldr", "barrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    $load   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    $barrier                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_BOOLEAN.S b/vm/mterp/armv5te/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..0fbd0aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_BOOLEAN.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"1" }
diff --git a/vm/mterp/armv5te/OP_IGET_BYTE.S b/vm/mterp/armv5te/OP_IGET_BYTE.S
new file mode 100644
index 0000000..ef8fdfc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_BYTE.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"2" }
diff --git a/vm/mterp/armv5te/OP_IGET_CHAR.S b/vm/mterp/armv5te/OP_IGET_CHAR.S
new file mode 100644
index 0000000..b88b017
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_CHAR.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"3" }
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT.S b/vm/mterp/armv5te/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..e5ca05f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S"
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S b/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..727b2aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET_QUICK.S"
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..acf9ac0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IGET_QUICK.S b/vm/mterp/armv5te/OP_IGET_QUICK.S
new file mode 100644
index 0000000..c19f870
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_SHORT.S b/vm/mterp/armv5te/OP_IGET_SHORT.S
new file mode 100644
index 0000000..a1b60b1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_SHORT.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"4" }
diff --git a/vm/mterp/armv5te/OP_IGET_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..acf9ac0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE.S b/vm/mterp/armv5te/OP_IGET_WIDE.S
new file mode 100644
index 0000000..c73edfd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE.S
@@ -0,0 +1,49 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     $volatile
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..b32e429
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    and     r2, r2, #15
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..face363
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_INSTANCE_OF.S b/vm/mterp/armv5te/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..73911b1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INSTANCE_OF.S
@@ -0,0 +1,85 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .L${opcode}_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .L${opcode}_resolve         @ not resolved, do it now
+.L${opcode}_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .L${opcode}_trivial         @ yes, trivial finish
+    b       .L${opcode}_fullcheck       @ no, do full check
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.L${opcode}_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to ${opcode}_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.L${opcode}_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.L${opcode}_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b ${opcode}_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .L${opcode}_resolved        @ pick up where we left off
diff --git a/vm/mterp/armv5te/OP_INT_TO_BYTE.S b/vm/mterp/armv5te/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..cf1c981
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov     r0, r0, asl #24", "instr":"mov     r0, r0, asr #24"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_CHAR.S b/vm/mterp/armv5te/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..568cf08
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov     r0, r0, asl #16", "instr":"mov     r0, r0, lsr #16"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S b/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..8b00b64
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"bl      __aeabi_i2d"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_FLOAT.S b/vm/mterp/armv5te/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..53d49df
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"bl      __aeabi_i2f"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_LONG.S b/vm/mterp/armv5te/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..b744439
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"mov     r1, r0, asr #31"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_SHORT.S b/vm/mterp/armv5te/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..b6deb85
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov     r0, r0, asl #16", "instr":"mov     r0, r0, asr #16"}
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..7167a2b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT.S
@@ -0,0 +1,46 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .L${opcode}_resolve         @ not resolved, do it now
+.L${opcode}_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethod${routine}   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+%break
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..0b799e5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S b/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..5ed35a8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,27 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!$isrange)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..f1eb27d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S
new file mode 100644
index 0000000..f97c4e3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S
@@ -0,0 +1,44 @@
+%default { "cccc":"2" }
+%verify "executed"
+%verify "finalizable class"
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, ${cccc})                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .L${opcode}_setFinal        @ yes, go
+.L${opcode}_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .L${opcode}_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(${cccc}+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+%break
+
+.L${opcode}_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .L${opcode}_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.L${opcode}_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
diff --git a/vm/mterp/armv5te/OP_INVOKE_STATIC.S b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..a89db03
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
@@ -0,0 +1,54 @@
+%default { "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethod${routine} @ yes, continue on
+    b       .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethod${routine}     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethod${routine}     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethod${routine}     @ whew, finally!
+#else
+    bne     common_invokeMethod${routine}     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
diff --git a/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..92b9ca5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_STATIC.S" { "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER.S b/vm/mterp/armv5te/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..b1d1411
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER.S
@@ -0,0 +1,60 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .L${opcode}_continue        @ resolved, continue on
+    b       .L${opcode}_resolve         @ do resolve now
+%break
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.L${opcode}_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .L${opcode}_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethod${routine} @ continue on
+
+.L${opcode}_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.L${opcode}_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..4848b7e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,25 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..77d43cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..0a0f737
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..58b0a42
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,45 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .L${opcode}_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+%break
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.L${opcode}_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..4b425da
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!$isrange)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..d257f2b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..4f9ca50
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_IPUT.S b/vm/mterp/armv5te/OP_IPUT.S
new file mode 100644
index 0000000..e0aa269
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT.S
@@ -0,0 +1,48 @@
+%default { "store":"str", "postbarrier":"@ no-op ", "prebarrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $prebarrier                        @ releasing store
+    $store  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    $postbarrier
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S b/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..9ac68ca
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"1" }
diff --git a/vm/mterp/armv5te/OP_IPUT_BYTE.S b/vm/mterp/armv5te/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..3871999
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"2" }
diff --git a/vm/mterp/armv5te/OP_IPUT_CHAR.S b/vm/mterp/armv5te/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..60e136c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"3" }
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT.S b/vm/mterp/armv5te/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..8795971
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT.S
@@ -0,0 +1,51 @@
+%default { "postbarrier":"@ no-op ", "prebarrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $prebarrier                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    $postbarrier
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..7bf9b21
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..317c5b2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT_OBJECT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IPUT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..ad76eca
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_SHORT.S b/vm/mterp/armv5te/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..4844575
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"4" }
diff --git a/vm/mterp/armv5te/OP_IPUT_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..1a7a098
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE.S b/vm/mterp/armv5te/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..ec787f0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE.S
@@ -0,0 +1,46 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     $volatile
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..b7dd703
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A(+)
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..944811b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S b/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..842a8ff
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"instr":"bl      __aeabi_l2d"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S b/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..ba250cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopNarrower.S" {"instr":"bl      __aeabi_l2f"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_INT.S b/vm/mterp/armv5te/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..4509546
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+%include "armv5te/OP_MOVE.S"
diff --git a/vm/mterp/armv5te/OP_MONITOR_ENTER.S b/vm/mterp/armv5te/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..ba5a144
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MONITOR_ENTER.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MONITOR_EXIT.S b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..9f36f0e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
@@ -0,0 +1,26 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
diff --git a/vm/mterp/armv5te/OP_MOVE.S b/vm/mterp/armv5te/OP_MOVE.S
new file mode 100644
index 0000000..efeddf4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_16.S b/vm/mterp/armv5te/OP_MOVE_16.S
new file mode 100644
index 0000000..3c08759
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S b/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..e2fc66f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_FROM16.S b/vm/mterp/armv5te/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..fdcc64e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT.S b/vm/mterp/armv5te/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..0cd09df
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S b/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..9f0f1a9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_16.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..c331a82
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT.S b/vm/mterp/armv5te/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..72377f8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..a04cdb4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S b/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..ea80de8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE.S b/vm/mterp/armv5te/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..0811c95
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r2, r2, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE_16.S b/vm/mterp/armv5te/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..2b3e99f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S b/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..81ae7dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MUL_DOUBLE.S b/vm/mterp/armv5te/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..69a10f9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_dmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..2201414
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_dmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_FLOAT.S b/vm/mterp/armv5te/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..13e64e0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..d97e80f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT.S b/vm/mterp/armv5te/OP_MUL_INT.S
new file mode 100644
index 0000000..252114b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binop.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S b/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..a5a3754
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binop2addr.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_LIT16.S b/vm/mterp/armv5te/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..1a28d8b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binopLit16.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_LIT8.S b/vm/mterp/armv5te/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..e714ed9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_LIT8.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binopLit8.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_LONG.S b/vm/mterp/armv5te/OP_MUL_LONG.S
new file mode 100644
index 0000000..3a7aac1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_LONG.S
@@ -0,0 +1,41 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S b/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..a561dc9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,26 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_NEG_DOUBLE.S b/vm/mterp/armv5te/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..d371016
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"instr":"add     r1, r1, #0x80000000"}
diff --git a/vm/mterp/armv5te/OP_NEG_FLOAT.S b/vm/mterp/armv5te/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..81b439c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"add     r0, r0, #0x80000000"}
diff --git a/vm/mterp/armv5te/OP_NEG_INT.S b/vm/mterp/armv5te/OP_NEG_INT.S
new file mode 100644
index 0000000..2f57540
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"rsb     r0, r0, #0"}
diff --git a/vm/mterp/armv5te/OP_NEG_LONG.S b/vm/mterp/armv5te/OP_NEG_LONG.S
new file mode 100644
index 0000000..215f056
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"preinstr":"rsbs    r0, r0, #0", "instr":"rsc     r1, r1, #0"}
diff --git a/vm/mterp/armv5te/OP_NEW_ARRAY.S b/vm/mterp/armv5te/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..eca1ac6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEW_ARRAY.S
@@ -0,0 +1,61 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .L${opcode}_finish          @ resolved, continue
+    b       .L${opcode}_resolve         @ do resolve now
+%break
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to ${opcode}_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.L${opcode}_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_NEW_INSTANCE.S b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..a03d111
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
@@ -0,0 +1,101 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .L${opcode}_resolve         @ no, resolve it now
+.L${opcode}_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .L${opcode}_needinit        @ no, init class now
+.L${opcode}_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .L${opcode}_finish          @ continue
+%break
+
+    .balign 32                          @ minimize cache lines
+.L${opcode}_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .L${opcode}_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.L${opcode}_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.L${opcode}_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .L${opcode}_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.L${opcode}_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .L${opcode}_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
diff --git a/vm/mterp/armv5te/OP_NOP.S b/vm/mterp/armv5te/OP_NOP.S
new file mode 100644
index 0000000..ff4cf5a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOP.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
diff --git a/vm/mterp/armv5te/OP_NOT_INT.S b/vm/mterp/armv5te/OP_NOT_INT.S
new file mode 100644
index 0000000..7281cbe
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"mvn     r0, r0"}
diff --git a/vm/mterp/armv5te/OP_NOT_LONG.S b/vm/mterp/armv5te/OP_NOT_LONG.S
new file mode 100644
index 0000000..fe741dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"preinstr":"mvn     r0, r0", "instr":"mvn     r1, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT.S b/vm/mterp/armv5te/OP_OR_INT.S
new file mode 100644
index 0000000..f0e5b2d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_2ADDR.S b/vm/mterp/armv5te/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..3b2cf7f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_LIT16.S b/vm/mterp/armv5te/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..9098c76
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_LIT8.S b/vm/mterp/armv5te/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..b69e315
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_LONG.S b/vm/mterp/armv5te/OP_OR_LONG.S
new file mode 100644
index 0000000..43110a2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"orr     r0, r0, r2", "instr":"orr     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..97d5f5b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"orr     r0, r0, r2", "instr":"orr     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_PACKED_SWITCH.S b/vm/mterp/armv5te/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..6fa03c1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_PACKED_SWITCH.S
@@ -0,0 +1,35 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      $func                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_REM_DOUBLE.S b/vm/mterp/armv5te/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..54e0214
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_DOUBLE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv5te/binopWide.S" {"instr":"bl      fmod"}
diff --git a/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..9808c56
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv5te/binopWide2addr.S" {"instr":"bl      fmod"}
diff --git a/vm/mterp/armv5te/OP_REM_FLOAT.S b/vm/mterp/armv5te/OP_REM_FLOAT.S
new file mode 100644
index 0000000..09cba5c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_FLOAT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv5te/binop.S" {"instr":"bl      fmodf"}
diff --git a/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..83af133
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv5te/binop2addr.S" {"instr":"bl      fmodf"}
diff --git a/vm/mterp/armv5te/OP_REM_INT.S b/vm/mterp/armv5te/OP_REM_INT.S
new file mode 100644
index 0000000..fbe9ad3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_2ADDR.S b/vm/mterp/armv5te/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..42337c7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_LIT16.S b/vm/mterp/armv5te/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..396e890
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binopLit16.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_LIT8.S b/vm/mterp/armv5te/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..906c5c4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_LIT8.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binopLit8.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_LONG.S b/vm/mterp/armv5te/OP_REM_LONG.S
new file mode 100644
index 0000000..d703047
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_LONG.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S b/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..b62f093
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_RETURN.S b/vm/mterp/armv5te/OP_RETURN.S
new file mode 100644
index 0000000..5f7350a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_OBJECT.S b/vm/mterp/armv5te/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..7956b45
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_RETURN.S"
diff --git a/vm/mterp/armv5te/OP_RETURN_VOID.S b/vm/mterp/armv5te/OP_RETURN_VOID.S
new file mode 100644
index 0000000..e09ebb0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_VOID.S
@@ -0,0 +1,2 @@
+%verify "executed"
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_VOID_BARRIER.S b/vm/mterp/armv5te/OP_RETURN_VOID_BARRIER.S
new file mode 100644
index 0000000..1f51b62
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_VOID_BARRIER.S
@@ -0,0 +1,3 @@
+%verify "executed"
+    SMP_DMB_ST
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_WIDE.S b/vm/mterp/armv5te/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..c185077
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RSUB_INT.S b/vm/mterp/armv5te/OP_RSUB_INT.S
new file mode 100644
index 0000000..8113456
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "armv5te/binopLit16.S" {"instr":"rsb     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S b/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..5d5e0fb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"rsb     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SGET.S b/vm/mterp/armv5te/OP_SGET.S
new file mode 100644
index 0000000..2e2453a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET.S
@@ -0,0 +1,50 @@
+%default { "barrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    $barrier                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish
diff --git a/vm/mterp/armv5te/OP_SGET_BOOLEAN.S b/vm/mterp/armv5te/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_BYTE.S b/vm/mterp/armv5te/OP_SGET_BYTE.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_CHAR.S b/vm/mterp/armv5te/OP_SGET_CHAR.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_OBJECT.S b/vm/mterp/armv5te/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..8b9c103
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SGET_SHORT.S b/vm/mterp/armv5te/OP_SGET_SHORT.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..8b9c103
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SGET_WIDE.S b/vm/mterp/armv5te/OP_SGET_WIDE.S
new file mode 100644
index 0000000..f79ec30
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_WIDE.S
@@ -0,0 +1,55 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if $volatile
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
diff --git a/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..b852348
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT.S b/vm/mterp/armv5te/OP_SHL_INT.S
new file mode 100644
index 0000000..7470344
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S b/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..15b6579
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT_LIT8.S b/vm/mterp/armv5te/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..4da9a0f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_LONG.S b/vm/mterp/armv5te/OP_SHL_LONG.S
new file mode 100644
index 0000000..b48ca5e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..42a0904
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHR_INT.S b/vm/mterp/armv5te/OP_SHR_INT.S
new file mode 100644
index 0000000..586f294
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S b/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..8b97195
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_INT_LIT8.S b/vm/mterp/armv5te/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..465b61f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_LONG.S b/vm/mterp/armv5te/OP_SHR_LONG.S
new file mode 100644
index 0000000..e6489a0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..9414ffb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SPARSE_SWITCH.S b/vm/mterp/armv5te/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..2447286
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/armv5te/OP_SPUT.S b/vm/mterp/armv5te/OP_SPUT.S
new file mode 100644
index 0000000..986f06a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT.S
@@ -0,0 +1,51 @@
+%default { "prebarrier":"@ no-op", "postbarrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $prebarrier                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    $postbarrier
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
diff --git a/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S b/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_BYTE.S b/vm/mterp/armv5te/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_CHAR.S b/vm/mterp/armv5te/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT.S b/vm/mterp/armv5te/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..77938d0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT.S
@@ -0,0 +1,59 @@
+%default { "postbarrier":"@ no-op ", "prebarrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $prebarrier                        @ releasing store
+    b       .L${opcode}_end
+%break
+
+
+.L${opcode}_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    $postbarrier
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
+
diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..a9d69bb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT_OBJECT.S"  {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SPUT_SHORT.S b/vm/mterp/armv5te/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..1b8dd25
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SPUT_WIDE.S b/vm/mterp/armv5te/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..d0f65e6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_WIDE.S
@@ -0,0 +1,57 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if $volatile
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
diff --git a/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..a88de85
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_SUB_DOUBLE.S b/vm/mterp/armv5te/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..fed41d2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_dsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d8d51a7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_dsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_FLOAT.S b/vm/mterp/armv5te/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..651669c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e7d27c7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_INT.S b/vm/mterp/armv5te/OP_SUB_INT.S
new file mode 100644
index 0000000..f25e643
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"sub     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S b/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..56ff9bc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"sub     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SUB_LONG.S b/vm/mterp/armv5te/OP_SUB_LONG.S
new file mode 100644
index 0000000..cfdcc88
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"subs    r0, r0, r2", "instr":"sbc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..4fd6610
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"subs    r0, r0, r2", "instr":"sbc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_THROW.S b/vm/mterp/armv5te/OP_THROW.S
new file mode 100644
index 0000000..6e157b4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_THROW.S
@@ -0,0 +1,14 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
diff --git a/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..afe9fd8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,13 @@
+%verify executed
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
diff --git a/vm/mterp/armv5te/OP_UNUSED_3E.S b/vm/mterp/armv5te/OP_UNUSED_3E.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_3F.S b/vm/mterp/armv5te/OP_UNUSED_3F.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_40.S b/vm/mterp/armv5te/OP_UNUSED_40.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_41.S b/vm/mterp/armv5te/OP_UNUSED_41.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_42.S b/vm/mterp/armv5te/OP_UNUSED_42.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_43.S b/vm/mterp/armv5te/OP_UNUSED_43.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_73.S b/vm/mterp/armv5te/OP_UNUSED_73.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_79.S b/vm/mterp/armv5te/OP_UNUSED_79.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_7A.S b/vm/mterp/armv5te/OP_UNUSED_7A.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_FF.S b/vm/mterp/armv5te/OP_UNUSED_FF.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_USHR_INT.S b/vm/mterp/armv5te/OP_USHR_INT.S
new file mode 100644
index 0000000..a72c2cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S b/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..8ace5f9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_INT_LIT8.S b/vm/mterp/armv5te/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..9d038c5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_LONG.S b/vm/mterp/armv5te/OP_USHR_LONG.S
new file mode 100644
index 0000000..d9ae338
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..27592a4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_XOR_INT.S b/vm/mterp/armv5te/OP_XOR_INT.S
new file mode 100644
index 0000000..81bfa00
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S b/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..ead63c5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_LIT16.S b/vm/mterp/armv5te/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..fcb4abb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_LIT8.S b/vm/mterp/armv5te/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..5ef0711
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_LONG.S b/vm/mterp/armv5te/OP_XOR_LONG.S
new file mode 100644
index 0000000..29a0075
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"eor     r0, r0, r2", "instr":"eor     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..7dc71c9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"eor     r0, r0, r2", "instr":"eor     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/alt_stub.S b/vm/mterp/armv5te/alt_stub.S
new file mode 100644
index 0000000..4ab5f9f
--- /dev/null
+++ b/vm/mterp/armv5te/alt_stub.S
@@ -0,0 +1,18 @@
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (${opnum} * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
diff --git a/vm/mterp/armv5te/bincmp.S b/vm/mterp/armv5te/bincmp.S
new file mode 100644
index 0000000..12a2c8c
--- /dev/null
+++ b/vm/mterp/armv5te/bincmp.S
@@ -0,0 +1,30 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    mov${revcmp} r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/binop.S b/vm/mterp/armv5te/binop.S
new file mode 100644
index 0000000..d169ed6
--- /dev/null
+++ b/vm/mterp/armv5te/binop.S
@@ -0,0 +1,35 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
diff --git a/vm/mterp/armv5te/binop2addr.S b/vm/mterp/armv5te/binop2addr.S
new file mode 100644
index 0000000..061242a
--- /dev/null
+++ b/vm/mterp/armv5te/binop2addr.S
@@ -0,0 +1,33 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv5te/binopLit16.S b/vm/mterp/armv5te/binopLit16.S
new file mode 100644
index 0000000..df49929
--- /dev/null
+++ b/vm/mterp/armv5te/binopLit16.S
@@ -0,0 +1,30 @@
+%default {"result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv5te/binopLit8.S b/vm/mterp/armv5te/binopLit8.S
new file mode 100644
index 0000000..2addd26
--- /dev/null
+++ b/vm/mterp/armv5te/binopLit8.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if $chkzero
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
diff --git a/vm/mterp/armv5te/binopWide.S b/vm/mterp/armv5te/binopWide.S
new file mode 100644
index 0000000..71fd3fe
--- /dev/null
+++ b/vm/mterp/armv5te/binopWide.S
@@ -0,0 +1,38 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if $chkzero
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {$result0,$result1}     @ vAA/vAA+1<- $result0/$result1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
diff --git a/vm/mterp/armv5te/binopWide2addr.S b/vm/mterp/armv5te/binopWide2addr.S
new file mode 100644
index 0000000..3fd5747
--- /dev/null
+++ b/vm/mterp/armv5te/binopWide2addr.S
@@ -0,0 +1,35 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if $chkzero
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {$result0,$result1}     @ vAA/vAA+1<- $result0/$result1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
diff --git a/vm/mterp/armv5te/debug.cpp b/vm/mterp/armv5te/debug.cpp
new file mode 100644
index 0000000..570e432
--- /dev/null
+++ b/vm/mterp/armv5te/debug.cpp
@@ -0,0 +1,82 @@
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
diff --git a/vm/mterp/armv5te/entry.S b/vm/mterp/armv5te/entry.S
new file mode 100644
index 0000000..46b26a6
--- /dev/null
+++ b/vm/mterp/armv5te/entry.S
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S
new file mode 100644
index 0000000..2980fa7
--- /dev/null
+++ b/vm/mterp/armv5te/footer.S
@@ -0,0 +1,1232 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
diff --git a/vm/mterp/armv5te/header.S b/vm/mterp/armv5te/header.S
new file mode 100644
index 0000000..b3c8f0a
--- /dev/null
+++ b/vm/mterp/armv5te/header.S
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #${handler_size_bits}
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
diff --git a/vm/mterp/armv5te/platform.S b/vm/mterp/armv5te/platform.S
new file mode 100644
index 0000000..ff3150b
--- /dev/null
+++ b/vm/mterp/armv5te/platform.S
@@ -0,0 +1,17 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB_ST
+.endm
diff --git a/vm/mterp/armv5te/stub.S b/vm/mterp/armv5te/stub.S
new file mode 100644
index 0000000..767427b
--- /dev/null
+++ b/vm/mterp/armv5te/stub.S
@@ -0,0 +1,8 @@
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF()            @ only need to export these two
+    mov     r0, rSELF               @ self is first arg to function
+    bl      dvmMterp_${opcode}      @ call
+    LOAD_PC_FP_FROM_SELF()          @ retrieve updated values
+    FETCH_INST()                    @ load next instruction from rPC
+    GET_INST_OPCODE(ip)             @ ...trim down to just the opcode
+    GOTO_OPCODE(ip)                 @ ...and jump to the handler
diff --git a/vm/mterp/armv5te/unop.S b/vm/mterp/armv5te/unop.S
new file mode 100644
index 0000000..12d8206
--- /dev/null
+++ b/vm/mterp/armv5te/unop.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/armv5te/unopNarrower.S b/vm/mterp/armv5te/unopNarrower.S
new file mode 100644
index 0000000..f1ad902
--- /dev/null
+++ b/vm/mterp/armv5te/unopNarrower.S
@@ -0,0 +1,24 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/armv5te/unopWide.S b/vm/mterp/armv5te/unopWide.S
new file mode 100644
index 0000000..0805fdf
--- /dev/null
+++ b/vm/mterp/armv5te/unopWide.S
@@ -0,0 +1,22 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
diff --git a/vm/mterp/armv5te/unopWider.S b/vm/mterp/armv5te/unopWider.S
new file mode 100644
index 0000000..df1baea
--- /dev/null
+++ b/vm/mterp/armv5te/unopWider.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/armv5te/unused.S b/vm/mterp/armv5te/unused.S
new file mode 100644
index 0000000..0194f58
--- /dev/null
+++ b/vm/mterp/armv5te/unused.S
@@ -0,0 +1 @@
+    bl      common_abort
diff --git a/vm/mterp/armv5te/zcmp.S b/vm/mterp/armv5te/zcmp.S
new file mode 100644
index 0000000..bd63fe4
--- /dev/null
+++ b/vm/mterp/armv5te/zcmp.S
@@ -0,0 +1,27 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    mov${revcmp} r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6/OP_INT_TO_BYTE.S b/vm/mterp/armv6/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..40d8a5c
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"sxtb    r0, r0"}
diff --git a/vm/mterp/armv6/OP_INT_TO_CHAR.S b/vm/mterp/armv6/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..3f0fdad
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"uxth    r0, r0"}
diff --git a/vm/mterp/armv6/OP_INT_TO_SHORT.S b/vm/mterp/armv6/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..82274c4
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"sxth    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d81ece9
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_dadd"}
diff --git a/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..ec6cdf1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fadd"}
diff --git a/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..af271cb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S b/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..f66b1d4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..0e3a901
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"adds    r0, r0, r2", "instr":"adc     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S b/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..e7b716d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_AND_INT_LIT16.S b/vm/mterp/armv6t2/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..77bb06b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..b77fbd2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"and     r0, r0, r2", "instr":"and     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S b/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..05991be
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_CONST_4.S b/vm/mterp/armv6t2/OP_CONST_4.S
new file mode 100644
index 0000000..8ec67d7
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_CONST_4.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..a3b7ffb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_ddiv"}
diff --git a/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..125230c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fdiv"}
diff --git a/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..8e58043
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S b/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..b4df053
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..cbd9c2d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..bdbb2fb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopNarrower.S" {"instr":"bl      __aeabi_d2f"}
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..f66dc5f
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,54 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv6t2/unopNarrower.S" {"instr":"bl      __aeabi_d2iz"}
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl      d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r2, #0x80000000             @ maxint, as a double (low word)
+    mov     r2, r2, asr #9              @  0xffc00000
+    sub     sp, sp, #4                  @ align for EABI
+    mvn     r3, #0xbe000000             @ maxint, as a double (high word)
+    sub     r3, r3, #0x00200000         @  0x41dfffff
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (0x7fffffff)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc1000000             @ minint, as a double (high word)
+    add     r3, r3, #0x00e00000         @  0xc1e00000
+    mov     r2, #0                      @ minint, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    beq     1f                          @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2iz                @ convert double to int
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+#endif
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..ac751de
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,53 @@
+%verify "executed"
+@include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+%include "armv6t2/unopWide.S" {"instr":"bl      d2l_doconv"}
+
+%break
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..64ca64c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2d"}
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S b/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..4ba28e7
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S
@@ -0,0 +1,40 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv6t2/unop.S" {"instr":"bl      __aeabi_f2iz"}
+
+#if 0
+@include "armv6t2/unop.S" {"instr":"bl      f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x4f000000             @ (float)maxint
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (7fffffff)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xcf000000             @ (float)minint
+    bl      __aeabi_fcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    ldmeqfd sp!, {r4, pc}               @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2iz                @ convert float to int
+    ldmfd   sp!, {r4, pc}
+#endif
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S b/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..168e338
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+@include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+%include "armv6t2/unopWider.S" {"instr":"bl      f2l_doconv"}
+
+%break
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
diff --git a/vm/mterp/armv6t2/OP_IF_EQ.S b/vm/mterp/armv6t2/OP_IF_EQ.S
new file mode 100644
index 0000000..d14b10b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv6t2/OP_IF_GE.S b/vm/mterp/armv6t2/OP_IF_GE.S
new file mode 100644
index 0000000..e6c518d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv6t2/OP_IF_GT.S b/vm/mterp/armv6t2/OP_IF_GT.S
new file mode 100644
index 0000000..6e89b3c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv6t2/OP_IF_LE.S b/vm/mterp/armv6t2/OP_IF_LE.S
new file mode 100644
index 0000000..0be9f60
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv6t2/OP_IF_LT.S b/vm/mterp/armv6t2/OP_IF_LT.S
new file mode 100644
index 0000000..cea79b1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv6t2/OP_IF_NE.S b/vm/mterp/armv6t2/OP_IF_NE.S
new file mode 100644
index 0000000..ad1f936
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv6t2/OP_IGET.S b/vm/mterp/armv6t2/OP_IGET.S
new file mode 100644
index 0000000..1cf41fc
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET.S
@@ -0,0 +1,45 @@
+%default { "load":"ldr", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    $load   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_QUICK.S b/vm/mterp/armv6t2/OP_IGET_QUICK.S
new file mode 100644
index 0000000..1e913c3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_WIDE.S b/vm/mterp/armv6t2/OP_IGET_WIDE.S
new file mode 100644
index 0000000..3e826dd
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_WIDE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..067d40d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_INT_TO_BYTE.S b/vm/mterp/armv6t2/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..27f92e6
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"sxtb    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_CHAR.S b/vm/mterp/armv6t2/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..db1eaa3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"uxth    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..38a2ef2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_i2d"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S b/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..7407a73
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"bl      __aeabi_i2f"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_LONG.S b/vm/mterp/armv6t2/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..e4d4221
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"mov     r1, r0, asr #31"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_SHORT.S b/vm/mterp/armv6t2/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..6426a9f
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"sxth    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_IPUT.S b/vm/mterp/armv6t2/OP_IPUT.S
new file mode 100644
index 0000000..0b219c0
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT.S
@@ -0,0 +1,45 @@
+%default { "store":"str", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    ubfx    r1, rINST, #8, #4           @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $store  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_QUICK.S b/vm/mterp/armv6t2/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..ad87b55
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick, iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_WIDE.S b/vm/mterp/armv6t2/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..f4ddb43
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_WIDE.S
@@ -0,0 +1,39 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..09f7a8e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..f04f917
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_l2d"}
diff --git a/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S b/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..eaf983b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopNarrower.S" {"instr":"bl      __aeabi_l2f"}
diff --git a/vm/mterp/armv6t2/OP_MOVE.S b/vm/mterp/armv6t2/OP_MOVE.S
new file mode 100644
index 0000000..3047158
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MOVE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    ubfx    r0, rINST, #8, #4           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv6t2/OP_MOVE_WIDE.S b/vm/mterp/armv6t2/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..adc2c95
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MOVE_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..b2b1297
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_dmul"}
diff --git a/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..a48a3a0
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fmul"}
diff --git a/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..e822fce
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv6t2/binop2addr.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S b/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..a07e540
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv6t2/binopLit16.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..1526a1e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,25 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_NEG_DOUBLE.S b/vm/mterp/armv6t2/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..52ef346
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"instr":"add     r1, r1, #0x80000000"}
diff --git a/vm/mterp/armv6t2/OP_NEG_FLOAT.S b/vm/mterp/armv6t2/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..34672d3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"add     r0, r0, #0x80000000"}
diff --git a/vm/mterp/armv6t2/OP_NEG_INT.S b/vm/mterp/armv6t2/OP_NEG_INT.S
new file mode 100644
index 0000000..98fb1b3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"rsb     r0, r0, #0"}
diff --git a/vm/mterp/armv6t2/OP_NEG_LONG.S b/vm/mterp/armv6t2/OP_NEG_LONG.S
new file mode 100644
index 0000000..22f45fd
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"preinstr":"rsbs    r0, r0, #0", "instr":"rsc     r1, r1, #0"}
diff --git a/vm/mterp/armv6t2/OP_NOT_INT.S b/vm/mterp/armv6t2/OP_NOT_INT.S
new file mode 100644
index 0000000..5ce758e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"mvn     r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_NOT_LONG.S b/vm/mterp/armv6t2/OP_NOT_LONG.S
new file mode 100644
index 0000000..ac7e875
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"preinstr":"mvn     r0, r0", "instr":"mvn     r1, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..b13b90c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_INT_LIT16.S b/vm/mterp/armv6t2/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..87db288
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..a1891e5
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"orr     r0, r0, r2", "instr":"orr     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..48e4cc3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      fmod"}
diff --git a/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..8273df1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv6t2/binop2addr.S" {"instr":"bl      fmodf"}
diff --git a/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S b/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..be4951d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_REM_INT_LIT16.S b/vm/mterp/armv6t2/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..ba66b48
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv6t2/binopLit16.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..c663f78
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_RSUB_INT.S b/vm/mterp/armv6t2/OP_RSUB_INT.S
new file mode 100644
index 0000000..761259c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "armv6t2/binopLit16.S" {"instr":"rsb     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..c6959b2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..502481e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..ce0a2ce
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..501b3f2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..631187b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_dsub"}
diff --git a/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..13ee1b4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fsub"}
diff --git a/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..a3bd5e5
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"sub     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..46dda45
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"subs    r0, r0, r2", "instr":"sbc     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..5d5808e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..a1fcef4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..49c82d6
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S b/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..6fe178d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..8d5ba2b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"eor     r0, r0, r2", "instr":"eor     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/bincmp.S b/vm/mterp/armv6t2/bincmp.S
new file mode 100644
index 0000000..8d8c48d
--- /dev/null
+++ b/vm/mterp/armv6t2/bincmp.S
@@ -0,0 +1,29 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    mov${revcmp} r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/binop2addr.S b/vm/mterp/armv6t2/binop2addr.S
new file mode 100644
index 0000000..b402156
--- /dev/null
+++ b/vm/mterp/armv6t2/binop2addr.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv6t2/binopLit16.S b/vm/mterp/armv6t2/binopLit16.S
new file mode 100644
index 0000000..1f67524
--- /dev/null
+++ b/vm/mterp/armv6t2/binopLit16.S
@@ -0,0 +1,29 @@
+%default {"result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv6t2/binopWide2addr.S b/vm/mterp/armv6t2/binopWide2addr.S
new file mode 100644
index 0000000..0b4691f
--- /dev/null
+++ b/vm/mterp/armv6t2/binopWide2addr.S
@@ -0,0 +1,34 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if $chkzero
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {$result0,$result1}     @ vAA/vAA+1<- $result0/$result1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
diff --git a/vm/mterp/armv6t2/unop.S b/vm/mterp/armv6t2/unop.S
new file mode 100644
index 0000000..58465fe
--- /dev/null
+++ b/vm/mterp/armv6t2/unop.S
@@ -0,0 +1,20 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
diff --git a/vm/mterp/armv6t2/unopNarrower.S b/vm/mterp/armv6t2/unopNarrower.S
new file mode 100644
index 0000000..224e8e7
--- /dev/null
+++ b/vm/mterp/armv6t2/unopNarrower.S
@@ -0,0 +1,23 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/armv6t2/unopWide.S b/vm/mterp/armv6t2/unopWide.S
new file mode 100644
index 0000000..e0a303e
--- /dev/null
+++ b/vm/mterp/armv6t2/unopWide.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/armv6t2/unopWider.S b/vm/mterp/armv6t2/unopWider.S
new file mode 100644
index 0000000..7ec221b
--- /dev/null
+++ b/vm/mterp/armv6t2/unopWider.S
@@ -0,0 +1,20 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/armv7-a/platform.S b/vm/mterp/armv7-a/platform.S
new file mode 100644
index 0000000..96ff2c2
--- /dev/null
+++ b/vm/mterp/armv7-a/platform.S
@@ -0,0 +1,31 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    dmb     st
+#else
+    /* not SMP */
+#endif
+.endm
diff --git a/vm/mterp/c/OP_ADD_DOUBLE.cpp b/vm/mterp/c/OP_ADD_DOUBLE.cpp
new file mode 100644
index 0000000..571aeb8
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..af952cb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_FLOAT.cpp b/vm/mterp/c/OP_ADD_FLOAT.cpp
new file mode 100644
index 0000000..dab7d33
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..a068fd0
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT.cpp b/vm/mterp/c/OP_ADD_INT.cpp
new file mode 100644
index 0000000..bfaa590
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_2ADDR.cpp b/vm/mterp/c/OP_ADD_INT_2ADDR.cpp
new file mode 100644
index 0000000..dfd3289
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_LIT16.cpp b/vm/mterp/c/OP_ADD_INT_LIT16.cpp
new file mode 100644
index 0000000..442ab40
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_LIT8.cpp b/vm/mterp/c/OP_ADD_INT_LIT8.cpp
new file mode 100644
index 0000000..1455599
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_LONG.cpp b/vm/mterp/c/OP_ADD_LONG.cpp
new file mode 100644
index 0000000..25d1f47
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_LONG_2ADDR.cpp b/vm/mterp/c/OP_ADD_LONG_2ADDR.cpp
new file mode 100644
index 0000000..4ae71bb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AGET.cpp b/vm/mterp/c/OP_AGET.cpp
new file mode 100644
index 0000000..766beaf
--- /dev/null
+++ b/vm/mterp/c/OP_AGET.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BOOLEAN.cpp b/vm/mterp/c/OP_AGET_BOOLEAN.cpp
new file mode 100644
index 0000000..d63bc10
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BYTE.cpp b/vm/mterp/c/OP_AGET_BYTE.cpp
new file mode 100644
index 0000000..61ecc05
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_CHAR.cpp b/vm/mterp/c/OP_AGET_CHAR.cpp
new file mode 100644
index 0000000..55e16ef
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_OBJECT.cpp b/vm/mterp/c/OP_AGET_OBJECT.cpp
new file mode 100644
index 0000000..903637c
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_SHORT.cpp b/vm/mterp/c/OP_AGET_SHORT.cpp
new file mode 100644
index 0000000..176b4a6
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_WIDE.cpp b/vm/mterp/c/OP_AGET_WIDE.cpp
new file mode 100644
index 0000000..e7974cb
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT.cpp b/vm/mterp/c/OP_AND_INT.cpp
new file mode 100644
index 0000000..3cf31cb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_2ADDR.cpp b/vm/mterp/c/OP_AND_INT_2ADDR.cpp
new file mode 100644
index 0000000..9f69292
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_LIT16.cpp b/vm/mterp/c/OP_AND_INT_LIT16.cpp
new file mode 100644
index 0000000..19f825b
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_LIT8.cpp b/vm/mterp/c/OP_AND_INT_LIT8.cpp
new file mode 100644
index 0000000..c0e1315
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_LONG.cpp b/vm/mterp/c/OP_AND_LONG.cpp
new file mode 100644
index 0000000..1c638fb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_LONG_2ADDR.cpp b/vm/mterp/c/OP_AND_LONG_2ADDR.cpp
new file mode 100644
index 0000000..23c464d
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_APUT.cpp b/vm/mterp/c/OP_APUT.cpp
new file mode 100644
index 0000000..07d3e04
--- /dev/null
+++ b/vm/mterp/c/OP_APUT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BOOLEAN.cpp b/vm/mterp/c/OP_APUT_BOOLEAN.cpp
new file mode 100644
index 0000000..fc69147
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BYTE.cpp b/vm/mterp/c/OP_APUT_BYTE.cpp
new file mode 100644
index 0000000..45aeb0b
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_CHAR.cpp b/vm/mterp/c/OP_APUT_CHAR.cpp
new file mode 100644
index 0000000..1553c27
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_OBJECT.cpp b/vm/mterp/c/OP_APUT_OBJECT.cpp
new file mode 100644
index 0000000..0730b72
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_OBJECT.cpp
@@ -0,0 +1,38 @@
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+    {
+        ArrayObject* arrayObj;
+        Object* obj;
+        u2 arrayInfo;
+        EXPORT_PC();
+        vdst = INST_AA(inst);       /* AA: source value */
+        arrayInfo = FETCH(1);
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */
+        vsrc2 = arrayInfo >> 8;     /* CC: index */
+        ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!checkForNull((Object*) arrayObj))
+            GOTO_exceptionThrown();
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+            dvmThrowArrayIndexOutOfBoundsException(
+                arrayObj->length, GET_REGISTER(vsrc2));
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+                ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->clazz->descriptor, arrayObj);
+                dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+        dvmSetObjectArrayElement(arrayObj,
+                                 GET_REGISTER(vsrc2),
+                                 (Object *)GET_REGISTER(vdst));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_APUT_SHORT.cpp b/vm/mterp/c/OP_APUT_SHORT.cpp
new file mode 100644
index 0000000..a72b5ea
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_WIDE.cpp b/vm/mterp/c/OP_APUT_WIDE.cpp
new file mode 100644
index 0000000..39c8cfa
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_ARRAY_LENGTH.cpp b/vm/mterp/c/OP_ARRAY_LENGTH.cpp
new file mode 100644
index 0000000..0d5a933
--- /dev/null
+++ b/vm/mterp/c/OP_ARRAY_LENGTH.cpp
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+    {
+        ArrayObject* arrayObj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        ILOGV("|array-length v%d,v%d  (%p)", vdst, vsrc1, arrayObj);
+        if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+            GOTO_exceptionThrown();
+        /* verifier guarantees this is an array reference */
+        SET_REGISTER(vdst, arrayObj->length);
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_BREAKPOINT.cpp b/vm/mterp/c/OP_BREAKPOINT.cpp
new file mode 100644
index 0000000..3041190
--- /dev/null
+++ b/vm/mterp/c/OP_BREAKPOINT.cpp
@@ -0,0 +1,24 @@
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_CHECK_CAST.cpp b/vm/mterp/c/OP_CHECK_CAST.cpp
new file mode 100644
index 0000000..2c4a304
--- /dev/null
+++ b/vm/mterp/c/OP_CHECK_CAST.cpp
@@ -0,0 +1,31 @@
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                clazz = dvmResolveClass(curMethod->clazz, ref, false);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            if (!dvmInstanceof(obj->clazz, clazz)) {
+                dvmThrowClassCastException(obj->clazz, clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CMPG_DOUBLE.cpp b/vm/mterp/c/OP_CMPG_DOUBLE.cpp
new file mode 100644
index 0000000..3f4082c
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPG_FLOAT.cpp b/vm/mterp/c/OP_CMPG_FLOAT.cpp
new file mode 100644
index 0000000..0bba49e
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPL_DOUBLE.cpp b/vm/mterp/c/OP_CMPL_DOUBLE.cpp
new file mode 100644
index 0000000..4da18b4
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPL_FLOAT.cpp b/vm/mterp/c/OP_CMPL_FLOAT.cpp
new file mode 100644
index 0000000..7916193
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
diff --git a/vm/mterp/c/OP_CMP_LONG.cpp b/vm/mterp/c/OP_CMP_LONG.cpp
new file mode 100644
index 0000000..a0e412c
--- /dev/null
+++ b/vm/mterp/c/OP_CMP_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
diff --git a/vm/mterp/c/OP_CONST.cpp b/vm/mterp/c/OP_CONST.cpp
new file mode 100644
index 0000000..e281a51
--- /dev/null
+++ b/vm/mterp/c/OP_CONST.cpp
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_16.cpp b/vm/mterp/c/OP_CONST_16.cpp
new file mode 100644
index 0000000..f58f50c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER(vdst, (s2) vsrc1);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_4.cpp b/vm/mterp/c/OP_CONST_4.cpp
new file mode 100644
index 0000000..800ef9a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_4.cpp
@@ -0,0 +1,11 @@
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+    {
+        s4 tmp;
+
+        vdst = INST_A(inst);
+        tmp = (s4) (INST_B(inst) << 28) >> 28;  // sign extend 4-bit value
+        ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_CLASS.cpp b/vm/mterp/c/OP_CONST_CLASS.cpp
new file mode 100644
index 0000000..9c60a27
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_CLASS.cpp
@@ -0,0 +1,18 @@
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            EXPORT_PC();
+            clazz = dvmResolveClass(curMethod->clazz, ref, true);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) clazz);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_HIGH16.cpp b/vm/mterp/c/OP_CONST_HIGH16.cpp
new file mode 100644
index 0000000..26b22f4
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_HIGH16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+    SET_REGISTER(vdst, vsrc1 << 16);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_STRING.cpp b/vm/mterp/c/OP_CONST_STRING.cpp
new file mode 100644
index 0000000..748119a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING.cpp
@@ -0,0 +1,18 @@
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+    {
+        StringObject* strObj;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+        strObj = dvmDexGetResolvedString(methodClassDex, ref);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, ref);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_STRING_JUMBO.cpp b/vm/mterp/c/OP_CONST_STRING_JUMBO.cpp
new file mode 100644
index 0000000..435b34c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING_JUMBO.cpp
@@ -0,0 +1,20 @@
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+    {
+        StringObject* strObj;
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+        strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, tmp);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE.cpp b/vm/mterp/c/OP_CONST_WIDE.cpp
new file mode 100644
index 0000000..ccb3955
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE.cpp
@@ -0,0 +1,14 @@
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+    {
+        u8 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u8)FETCH(2) << 16;
+        tmp |= (u8)FETCH(3) << 32;
+        tmp |= (u8)FETCH(4) << 48;
+        ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, tmp);
+    }
+    FINISH(5);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_16.cpp b/vm/mterp/c/OP_CONST_WIDE_16.cpp
new file mode 100644
index 0000000..da69f37
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_32.cpp b/vm/mterp/c/OP_CONST_WIDE_32.cpp
new file mode 100644
index 0000000..ad4acbb
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_32.cpp
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, (s4) tmp);
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_HIGH16.cpp b/vm/mterp/c/OP_CONST_WIDE_HIGH16.cpp
new file mode 100644
index 0000000..bcc0664
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_HIGH16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+    SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_DIV_DOUBLE.cpp b/vm/mterp/c/OP_DIV_DOUBLE.cpp
new file mode 100644
index 0000000..d6e4b55
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..85a1523
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_FLOAT.cpp b/vm/mterp/c/OP_DIV_FLOAT.cpp
new file mode 100644
index 0000000..2c5049b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..cd7b4d9
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT.cpp b/vm/mterp/c/OP_DIV_INT.cpp
new file mode 100644
index 0000000..af6e8c6
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_2ADDR.cpp b/vm/mterp/c/OP_DIV_INT_2ADDR.cpp
new file mode 100644
index 0000000..80c0da7
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_LIT16.cpp b/vm/mterp/c/OP_DIV_INT_LIT16.cpp
new file mode 100644
index 0000000..4e8d901
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_LIT8.cpp b/vm/mterp/c/OP_DIV_INT_LIT8.cpp
new file mode 100644
index 0000000..eec5389
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_LONG.cpp b/vm/mterp/c/OP_DIV_LONG.cpp
new file mode 100644
index 0000000..557b56b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_LONG_2ADDR.cpp b/vm/mterp/c/OP_DIV_LONG_2ADDR.cpp
new file mode 100644
index 0000000..00e0e6c
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_FLOAT.cpp b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.cpp
new file mode 100644
index 0000000..152e5fd
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_INT.cpp b/vm/mterp/c/OP_DOUBLE_TO_INT.cpp
new file mode 100644
index 0000000..e210b92
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_INT.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_LONG.cpp b/vm/mterp/c/OP_DOUBLE_TO_LONG.cpp
new file mode 100644
index 0000000..44d548c
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_LONG.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_EXECUTE_INLINE.cpp b/vm/mterp/c/OP_EXECUTE_INLINE.cpp
new file mode 100644
index 0000000..288ccc9
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE.cpp
@@ -0,0 +1,59 @@
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+    {
+        /*
+         * This has the same form as other method calls, but we ignore
+         * the 5th argument (vA).  This is chiefly because the first four
+         * arguments to a function on ARM are in registers.
+         *
+         * We only set the arguments that are actually used, leaving
+         * the rest uninitialized.  We're assuming that, if the method
+         * needs them, they'll be specified in the call.
+         *
+         * However, this annoys gcc when optimizations are enabled,
+         * causing a "may be used uninitialized" warning.  Quieting
+         * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+         * on empty method).  Note that valgrind is perfectly happy
+         * either way as the uninitialiezd values are never actually
+         * used.
+         */
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_B(inst);       /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* 0-4 register indices */
+        ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+            vsrc1, ref, vdst);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst >> 12);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst & 0x0f);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.cpp b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.cpp
new file mode 100644
index 0000000..467f0e9
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.cpp
@@ -0,0 +1,43 @@
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_FILLED_NEW_ARRAY.cpp b/vm/mterp/c/OP_FILLED_NEW_ARRAY.cpp
new file mode 100644
index 0000000..fad7dbb
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+    GOTO_invoke(filledNewArray, false);
+OP_END
diff --git a/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.cpp b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.cpp
new file mode 100644
index 0000000..06c3a79
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+    GOTO_invoke(filledNewArray, true);
+OP_END
diff --git a/vm/mterp/c/OP_FILL_ARRAY_DATA.cpp b/vm/mterp/c/OP_FILL_ARRAY_DATA.cpp
new file mode 100644
index 0000000..678bb32
--- /dev/null
+++ b/vm/mterp/c/OP_FILL_ARRAY_DATA.cpp
@@ -0,0 +1,27 @@
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA)   /*vAA, +BBBBBBBB*/
+    {
+        const u2* arrayData;
+        s4 offset;
+        ArrayObject* arrayObj;
+
+        EXPORT_PC();
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+        arrayData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (arrayData < curMethod->insns ||
+            arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            dvmThrowInternalError("bad fill array data");
+            GOTO_exceptionThrown();
+        }
+#endif
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+            GOTO_exceptionThrown();
+        }
+        FINISH(3);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_DOUBLE.cpp b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.cpp
new file mode 100644
index 0000000..ea5e7a6
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_INT.cpp b/vm/mterp/c/OP_FLOAT_TO_INT.cpp
new file mode 100644
index 0000000..15522f8
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_INT.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_LONG.cpp b/vm/mterp/c/OP_FLOAT_TO_LONG.cpp
new file mode 100644
index 0000000..03bd30d
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_LONG.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_GOTO.cpp b/vm/mterp/c/OP_GOTO.cpp
new file mode 100644
index 0000000..0e10384
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO.cpp
@@ -0,0 +1,11 @@
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+    vdst = INST_AA(inst);
+    if ((s1)vdst < 0)
+        ILOGV("|goto -0x%02x", -((s1)vdst));
+    else
+        ILOGV("|goto +0x%02x", ((s1)vdst));
+    ILOGV("> branch taken");
+    if ((s1)vdst < 0)
+        PERIODIC_CHECKS((s1)vdst);
+    FINISH((s1)vdst);
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_16.cpp b/vm/mterp/c/OP_GOTO_16.cpp
new file mode 100644
index 0000000..f0541fd
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_16.cpp
@@ -0,0 +1,14 @@
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+    {
+        s4 offset = (s2) FETCH(1);          /* sign-extend next code unit */
+
+        if (offset < 0)
+            ILOGV("|goto/16 -0x%04x", -offset);
+        else
+            ILOGV("|goto/16 +0x%04x", offset);
+        ILOGV("> branch taken");
+        if (offset < 0)
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_32.cpp b/vm/mterp/c/OP_GOTO_32.cpp
new file mode 100644
index 0000000..1b1815c
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_32.cpp
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+    {
+        s4 offset = FETCH(1);               /* low-order 16 bits */
+        offset |= ((s4) FETCH(2)) << 16;    /* high-order 16 bits */
+
+        if (offset < 0)
+            ILOGV("|goto/32 -0x%08x", -offset);
+        else
+            ILOGV("|goto/32 +0x%08x", offset);
+        ILOGV("> branch taken");
+        if (offset <= 0)    /* allowed to branch to self */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQ.cpp b/vm/mterp/c/OP_IF_EQ.cpp
new file mode 100644
index 0000000..2c3b9b5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQZ.cpp b/vm/mterp/c/OP_IF_EQZ.cpp
new file mode 100644
index 0000000..d2dd1aa
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GE.cpp b/vm/mterp/c/OP_IF_GE.cpp
new file mode 100644
index 0000000..8aa85c4
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GEZ.cpp b/vm/mterp/c/OP_IF_GEZ.cpp
new file mode 100644
index 0000000..8c4b78a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GEZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GT.cpp b/vm/mterp/c/OP_IF_GT.cpp
new file mode 100644
index 0000000..d35eb29
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GTZ.cpp b/vm/mterp/c/OP_IF_GTZ.cpp
new file mode 100644
index 0000000..63a0073
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GTZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LE.cpp b/vm/mterp/c/OP_IF_LE.cpp
new file mode 100644
index 0000000..f4b213a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LEZ.cpp b/vm/mterp/c/OP_IF_LEZ.cpp
new file mode 100644
index 0000000..1d57a50
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LEZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LT.cpp b/vm/mterp/c/OP_IF_LT.cpp
new file mode 100644
index 0000000..0233892
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LTZ.cpp b/vm/mterp/c/OP_IF_LTZ.cpp
new file mode 100644
index 0000000..b4b9be2
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LTZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NE.cpp b/vm/mterp/c/OP_IF_NE.cpp
new file mode 100644
index 0000000..8da70a5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NEZ.cpp b/vm/mterp/c/OP_IF_NEZ.cpp
new file mode 100644
index 0000000..209e836
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NEZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IGET.cpp b/vm/mterp/c/OP_IGET.cpp
new file mode 100644
index 0000000..c6333e5
--- /dev/null
+++ b/vm/mterp/c/OP_IGET.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BOOLEAN.cpp b/vm/mterp/c/OP_IGET_BOOLEAN.cpp
new file mode 100644
index 0000000..a5a47be
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BYTE.cpp b/vm/mterp/c/OP_IGET_BYTE.cpp
new file mode 100644
index 0000000..647f311
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_CHAR.cpp b/vm/mterp/c/OP_IGET_CHAR.cpp
new file mode 100644
index 0000000..9a8adb0
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT.cpp b/vm/mterp/c/OP_IGET_OBJECT.cpp
new file mode 100644
index 0000000..03c9e50
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT_QUICK.cpp b/vm/mterp/c/OP_IGET_OBJECT_QUICK.cpp
new file mode 100644
index 0000000..2ac3a54
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..3577552
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_QUICK.cpp b/vm/mterp/c/OP_IGET_QUICK.cpp
new file mode 100644
index 0000000..b5724cc
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_SHORT.cpp b/vm/mterp/c/OP_IGET_SHORT.cpp
new file mode 100644
index 0000000..3e77789
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_VOLATILE.cpp b/vm/mterp/c/OP_IGET_VOLATILE.cpp
new file mode 100644
index 0000000..7a3be56
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE.cpp b/vm/mterp/c/OP_IGET_WIDE.cpp
new file mode 100644
index 0000000..cb1fcca
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE_QUICK.cpp b/vm/mterp/c/OP_IGET_WIDE_QUICK.cpp
new file mode 100644
index 0000000..adb4fc1
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..a080823
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_INSTANCE_OF.cpp b/vm/mterp/c/OP_INSTANCE_OF.cpp
new file mode 100644
index 0000000..8b8f9d3
--- /dev/null
+++ b/vm/mterp/c/OP_INSTANCE_OF.cpp
@@ -0,0 +1,30 @@
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);   /* object to check */
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj == NULL) {
+            SET_REGISTER(vdst, 0);
+        } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNullExportPC(obj, fp, pc))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                EXPORT_PC();
+                clazz = dvmResolveClass(curMethod->clazz, ref, true);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+        }
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_BYTE.cpp b/vm/mterp/c/OP_INT_TO_BYTE.cpp
new file mode 100644
index 0000000..ea75747
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_CHAR.cpp b/vm/mterp/c/OP_INT_TO_CHAR.cpp
new file mode 100644
index 0000000..45ae0df
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_DOUBLE.cpp b/vm/mterp/c/OP_INT_TO_DOUBLE.cpp
new file mode 100644
index 0000000..624c702
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_FLOAT.cpp b/vm/mterp/c/OP_INT_TO_FLOAT.cpp
new file mode 100644
index 0000000..fd15199
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_LONG.cpp b/vm/mterp/c/OP_INT_TO_LONG.cpp
new file mode 100644
index 0000000..8bc4223
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_SHORT.cpp b/vm/mterp/c/OP_INT_TO_SHORT.cpp
new file mode 100644
index 0000000..0f06739
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT.cpp b/vm/mterp/c/OP_INVOKE_DIRECT.cpp
new file mode 100644
index 0000000..58cfe5b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.cpp b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.cpp
new file mode 100644
index 0000000..9877bbe
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeDirect, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_INTERFACE.cpp b/vm/mterp/c/OP_INVOKE_INTERFACE.cpp
new file mode 100644
index 0000000..9c639d5
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.cpp b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.cpp
new file mode 100644
index 0000000..6244c9e
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_OBJECT_INIT_RANGE.cpp b/vm/mterp/c/OP_INVOKE_OBJECT_INIT_RANGE.cpp
new file mode 100644
index 0000000..a22446f
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_OBJECT_INIT_RANGE.cpp
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_STATIC.cpp b/vm/mterp/c/OP_INVOKE_STATIC.cpp
new file mode 100644
index 0000000..81f3d62
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_STATIC_RANGE.cpp b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.cpp
new file mode 100644
index 0000000..3fc4c35
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeStatic, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER.cpp b/vm/mterp/c/OP_INVOKE_SUPER.cpp
new file mode 100644
index 0000000..e7baea4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_QUICK.cpp b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.cpp
new file mode 100644
index 0000000..b66e033
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuperQuick, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.cpp b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.cpp
new file mode 100644
index 0000000..879497b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuperQuick, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_RANGE.cpp b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.cpp
new file mode 100644
index 0000000..724e3a0
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuper, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL.cpp
new file mode 100644
index 0000000..29a4560
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.cpp
new file mode 100644
index 0000000..244fed4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtualQuick, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp
new file mode 100644
index 0000000..9adb4ad
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtualQuick, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.cpp
new file mode 100644
index 0000000..94671ae
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtual, true);
+OP_END
diff --git a/vm/mterp/c/OP_IPUT.cpp b/vm/mterp/c/OP_IPUT.cpp
new file mode 100644
index 0000000..9d503ef
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BOOLEAN.cpp b/vm/mterp/c/OP_IPUT_BOOLEAN.cpp
new file mode 100644
index 0000000..7fe4929
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BYTE.cpp b/vm/mterp/c/OP_IPUT_BYTE.cpp
new file mode 100644
index 0000000..8a49fb7
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_CHAR.cpp b/vm/mterp/c/OP_IPUT_CHAR.cpp
new file mode 100644
index 0000000..b2812c2
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT.cpp b/vm/mterp/c/OP_IPUT_OBJECT.cpp
new file mode 100644
index 0000000..dbfb5ab
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT.cpp
@@ -0,0 +1,13 @@
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible.  In practice, many popular VMs don't
+ * do this because it slows down a very common operation.  It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT_QUICK.cpp b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.cpp
new file mode 100644
index 0000000..8670188
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..cce63c1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_QUICK.cpp b/vm/mterp/c/OP_IPUT_QUICK.cpp
new file mode 100644
index 0000000..483b9b1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_SHORT.cpp b/vm/mterp/c/OP_IPUT_SHORT.cpp
new file mode 100644
index 0000000..0a63ebc
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_VOLATILE.cpp b/vm/mterp/c/OP_IPUT_VOLATILE.cpp
new file mode 100644
index 0000000..814379e
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE.cpp b/vm/mterp/c/OP_IPUT_WIDE.cpp
new file mode 100644
index 0000000..bb4926c
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE_QUICK.cpp b/vm/mterp/c/OP_IPUT_WIDE_QUICK.cpp
new file mode 100644
index 0000000..691630b
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..d888b4a
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_DOUBLE.cpp b/vm/mterp/c/OP_LONG_TO_DOUBLE.cpp
new file mode 100644
index 0000000..91b5eb2
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_FLOAT.cpp b/vm/mterp/c/OP_LONG_TO_FLOAT.cpp
new file mode 100644
index 0000000..ff1f5fb
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_INT.cpp b/vm/mterp/c/OP_LONG_TO_INT.cpp
new file mode 100644
index 0000000..87c9a2e
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_MONITOR_ENTER.cpp b/vm/mterp/c/OP_MONITOR_ENTER.cpp
new file mode 100644
index 0000000..976aab3
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_ENTER.cpp
@@ -0,0 +1,16 @@
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+    {
+        Object* obj;
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-enter v%d %s(0x%08x)",
+            vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+        ILOGV("+ locking %p %s", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC */
+        dvmLockObject(self, obj);
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MONITOR_EXIT.cpp b/vm/mterp/c/OP_MONITOR_EXIT.cpp
new file mode 100644
index 0000000..c26585d
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_EXIT.cpp
@@ -0,0 +1,30 @@
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+    {
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-exit v%d %s(0x%08x)",
+            vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /*
+             * The exception needs to be processed at the *following*
+             * instruction, not the current instruction (see the Dalvik
+             * spec).  Because we're jumping to an exception handler,
+             * we're not actually at risk of skipping an instruction
+             * by doing so.
+             */
+            ADJUST_PC(1);           /* monitor-exit width is 1 */
+            GOTO_exceptionThrown();
+        }
+        ILOGV("+ unlocking %p %s", obj, obj->clazz->descriptor);
+        if (!dvmUnlockObject(self, obj)) {
+            assert(dvmCheckException(self));
+            ADJUST_PC(1);
+            GOTO_exceptionThrown();
+        }
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE.cpp b/vm/mterp/c/OP_MOVE.cpp
new file mode 100644
index 0000000..6666199
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE.cpp
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_16.cpp b/vm/mterp/c/OP_MOVE_16.cpp
new file mode 100644
index 0000000..53af5d5
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_16.cpp
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_EXCEPTION.cpp b/vm/mterp/c/OP_MOVE_EXCEPTION.cpp
new file mode 100644
index 0000000..86587ca
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_EXCEPTION.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-exception v%d", vdst);
+    assert(self->exception != NULL);
+    SET_REGISTER(vdst, (u4)self->exception);
+    dvmClearException(self);
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_FROM16.cpp b/vm/mterp/c/OP_MOVE_FROM16.cpp
new file mode 100644
index 0000000..59fc285
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_FROM16.cpp
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_OBJECT.cpp b/vm/mterp/c/OP_MOVE_OBJECT.cpp
new file mode 100644
index 0000000..579095f
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE.cpp"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_16.cpp b/vm/mterp/c/OP_MOVE_OBJECT_16.cpp
new file mode 100644
index 0000000..89cfb77
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_16.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE_16.cpp"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_FROM16.cpp b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.cpp
new file mode 100644
index 0000000..9451b9e
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE_FROM16.cpp"
diff --git a/vm/mterp/c/OP_MOVE_RESULT.cpp b/vm/mterp/c/OP_MOVE_RESULT.cpp
new file mode 100644
index 0000000..ddf535b
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE($opcode /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_RESULT_OBJECT.cpp b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.cpp
new file mode 100644
index 0000000..08c5c02
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE_RESULT.cpp"
diff --git a/vm/mterp/c/OP_MOVE_RESULT_WIDE.cpp b/vm/mterp/c/OP_MOVE_RESULT_WIDE.cpp
new file mode 100644
index 0000000..f6ec8d9
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_WIDE.cpp
@@ -0,0 +1,6 @@
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+    SET_REGISTER_WIDE(vdst, retval.j);
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE.cpp b/vm/mterp/c/OP_MOVE_WIDE.cpp
new file mode 100644
index 0000000..9ee323d
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE.cpp
@@ -0,0 +1,10 @@
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+    /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+     * "move-wide v6, v7" and "move-wide v7, v6" */
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE_16.cpp b/vm/mterp/c/OP_MOVE_WIDE_16.cpp
new file mode 100644
index 0000000..e3d0e16
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_16.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE_FROM16.cpp b/vm/mterp/c/OP_MOVE_WIDE_FROM16.cpp
new file mode 100644
index 0000000..cdbaa2e
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_FROM16.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move-wide/from16 v%d,v%d  (v%d=0x%08llx)", vdst, vsrc1,
+        vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_MUL_DOUBLE.cpp b/vm/mterp/c/OP_MUL_DOUBLE.cpp
new file mode 100644
index 0000000..3e65efa
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..905b6a7
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_FLOAT.cpp b/vm/mterp/c/OP_MUL_FLOAT.cpp
new file mode 100644
index 0000000..310495c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..03623ca
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT.cpp b/vm/mterp/c/OP_MUL_INT.cpp
new file mode 100644
index 0000000..b723a29
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_2ADDR.cpp b/vm/mterp/c/OP_MUL_INT_2ADDR.cpp
new file mode 100644
index 0000000..f7a179c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_LIT16.cpp b/vm/mterp/c/OP_MUL_INT_LIT16.cpp
new file mode 100644
index 0000000..3a34dbc
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_LIT8.cpp b/vm/mterp/c/OP_MUL_INT_LIT8.cpp
new file mode 100644
index 0000000..2ca0036
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_LONG.cpp b/vm/mterp/c/OP_MUL_LONG.cpp
new file mode 100644
index 0000000..768a2ad
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_LONG_2ADDR.cpp b/vm/mterp/c/OP_MUL_LONG_2ADDR.cpp
new file mode 100644
index 0000000..1469a22
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_DOUBLE.cpp b/vm/mterp/c/OP_NEG_DOUBLE.cpp
new file mode 100644
index 0000000..805082c
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_FLOAT.cpp b/vm/mterp/c/OP_NEG_FLOAT.cpp
new file mode 100644
index 0000000..00e14f5
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_INT.cpp b/vm/mterp/c/OP_NEG_INT.cpp
new file mode 100644
index 0000000..9b97bef
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
diff --git a/vm/mterp/c/OP_NEG_LONG.cpp b/vm/mterp/c/OP_NEG_LONG.cpp
new file mode 100644
index 0000000..52d553a
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_NEW_ARRAY.cpp b/vm/mterp/c/OP_NEW_ARRAY.cpp
new file mode 100644
index 0000000..6d6771a
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_ARRAY.cpp
@@ -0,0 +1,35 @@
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        s4 length;
+
+        EXPORT_PC();
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);       /* length reg */
+        ref = FETCH(1);
+        ILOGV("|new-array v%d,v%d,class@0x%04x  (%d elements)",
+            vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+        length = (s4) GET_REGISTER(vsrc1);
+        if (length < 0) {
+            dvmThrowNegativeArraySizeException(length);
+            GOTO_exceptionThrown();
+        }
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newArray);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_NEW_INSTANCE.cpp b/vm/mterp/c/OP_NEW_INSTANCE.cpp
new file mode 100644
index 0000000..b0b9c18
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_INSTANCE.cpp
@@ -0,0 +1,48 @@
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* newObj;
+
+        EXPORT_PC();
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            clazz = dvmResolveClass(curMethod->clazz, ref, false);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+            GOTO_exceptionThrown();
+
+#if defined(WITH_JIT)
+        /*
+         * The JIT needs dvmDexGetResolvedClass() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (!dvmDexGetResolvedClass(methodClassDex, ref))) {
+            /* Class initialization is still ongoing - end the trace */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage(gDvm.exInstantiationError,
+        //        clazz->descriptor);
+        //    GOTO_exceptionThrown();
+        //}
+        newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        if (newObj == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newObj);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_NOP.cpp b/vm/mterp/c/OP_NOP.cpp
new file mode 100644
index 0000000..d9fd744
--- /dev/null
+++ b/vm/mterp/c/OP_NOP.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_NOT_INT.cpp b/vm/mterp/c/OP_NOT_INT.cpp
new file mode 100644
index 0000000..e585f62
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
diff --git a/vm/mterp/c/OP_NOT_LONG.cpp b/vm/mterp/c/OP_NOT_LONG.cpp
new file mode 100644
index 0000000..4fb393a
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT.cpp b/vm/mterp/c/OP_OR_INT.cpp
new file mode 100644
index 0000000..19e397b
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_2ADDR.cpp b/vm/mterp/c/OP_OR_INT_2ADDR.cpp
new file mode 100644
index 0000000..5d5fde0
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_LIT16.cpp b/vm/mterp/c/OP_OR_INT_LIT16.cpp
new file mode 100644
index 0000000..5a682fa
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_LIT8.cpp b/vm/mterp/c/OP_OR_INT_LIT8.cpp
new file mode 100644
index 0000000..40b9837
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_LONG.cpp b/vm/mterp/c/OP_OR_LONG.cpp
new file mode 100644
index 0000000..62f4dd3
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_LONG_2ADDR.cpp b/vm/mterp/c/OP_OR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..03a7c65
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_PACKED_SWITCH.cpp b/vm/mterp/c/OP_PACKED_SWITCH.cpp
new file mode 100644
index 0000000..c820e80
--- /dev/null
+++ b/vm/mterp/c/OP_PACKED_SWITCH.cpp
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|packed-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_REM_DOUBLE.cpp b/vm/mterp/c/OP_REM_DOUBLE.cpp
new file mode 100644
index 0000000..343e25e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE.cpp
@@ -0,0 +1,13 @@
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_DOUBLE(vdst,
+            fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_REM_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..392eacf
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_DOUBLE(vdst,
+        fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_REM_FLOAT.cpp b/vm/mterp/c/OP_REM_FLOAT.cpp
new file mode 100644
index 0000000..9604b30
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT.cpp
@@ -0,0 +1,13 @@
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_FLOAT(vdst,
+            fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_REM_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_REM_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..87bb31e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT_2ADDR.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_FLOAT(vdst,
+        fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT.cpp b/vm/mterp/c/OP_REM_INT.cpp
new file mode 100644
index 0000000..0e3efe6
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_2ADDR.cpp b/vm/mterp/c/OP_REM_INT_2ADDR.cpp
new file mode 100644
index 0000000..5801f35
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_LIT16.cpp b/vm/mterp/c/OP_REM_INT_LIT16.cpp
new file mode 100644
index 0000000..a4dabe8
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_LIT8.cpp b/vm/mterp/c/OP_REM_INT_LIT8.cpp
new file mode 100644
index 0000000..2bc88be
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_LONG.cpp b/vm/mterp/c/OP_REM_LONG.cpp
new file mode 100644
index 0000000..fb2ba71
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_LONG_2ADDR.cpp b/vm/mterp/c/OP_REM_LONG_2ADDR.cpp
new file mode 100644
index 0000000..3049770
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_RETURN.cpp b/vm/mterp/c/OP_RETURN.cpp
new file mode 100644
index 0000000..89d3b3b
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE($opcode /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_OBJECT.cpp b/vm/mterp/c/OP_RETURN_OBJECT.cpp
new file mode 100644
index 0000000..d8bae43
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_OBJECT.cpp
@@ -0,0 +1 @@
+%include "c/OP_RETURN.cpp"
diff --git a/vm/mterp/c/OP_RETURN_VOID.cpp b/vm/mterp/c/OP_RETURN_VOID.cpp
new file mode 100644
index 0000000..7431f60
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_VOID.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_VOID_BARRIER.cpp b/vm/mterp/c/OP_RETURN_VOID_BARRIER.cpp
new file mode 100644
index 0000000..312402e
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_VOID_BARRIER.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_WIDE.cpp b/vm/mterp/c/OP_RETURN_WIDE.cpp
new file mode 100644
index 0000000..a27bfd4
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_WIDE.cpp
@@ -0,0 +1,6 @@
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return-wide v%d", vsrc1);
+    retval.j = GET_REGISTER_WIDE(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RSUB_INT.cpp b/vm/mterp/c/OP_RSUB_INT.cpp
new file mode 100644
index 0000000..336ca55
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT.cpp
@@ -0,0 +1,10 @@
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+    {
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        vsrc2 = FETCH(1);
+        ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_RSUB_INT_LIT8.cpp b/vm/mterp/c/OP_RSUB_INT_LIT8.cpp
new file mode 100644
index 0000000..742854b
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT_LIT8.cpp
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+    {
+        u2 litInfo;
+        vdst = INST_AA(inst);
+        litInfo = FETCH(1);
+        vsrc1 = litInfo & 0xff;
+        vsrc2 = litInfo >> 8;
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_SGET.cpp b/vm/mterp/c/OP_SGET.cpp
new file mode 100644
index 0000000..5297cd7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BOOLEAN.cpp b/vm/mterp/c/OP_SGET_BOOLEAN.cpp
new file mode 100644
index 0000000..7c5d45e
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BYTE.cpp b/vm/mterp/c/OP_SGET_BYTE.cpp
new file mode 100644
index 0000000..b37cab4
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_CHAR.cpp b/vm/mterp/c/OP_SGET_CHAR.cpp
new file mode 100644
index 0000000..7ede5ec
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_OBJECT.cpp b/vm/mterp/c/OP_SGET_OBJECT.cpp
new file mode 100644
index 0000000..9f3b63d
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..0a9049f
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_SHORT.cpp b/vm/mterp/c/OP_SGET_SHORT.cpp
new file mode 100644
index 0000000..cd1fe4c
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_VOLATILE.cpp b/vm/mterp/c/OP_SGET_VOLATILE.cpp
new file mode 100644
index 0000000..6713a54
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_WIDE.cpp b/vm/mterp/c/OP_SGET_WIDE.cpp
new file mode 100644
index 0000000..817c6e7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..26a67bf
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT.cpp b/vm/mterp/c/OP_SHL_INT.cpp
new file mode 100644
index 0000000..e32af49
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT_2ADDR.cpp b/vm/mterp/c/OP_SHL_INT_2ADDR.cpp
new file mode 100644
index 0000000..c5f5399
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT_LIT8.cpp b/vm/mterp/c/OP_SHL_INT_LIT8.cpp
new file mode 100644
index 0000000..009d14e
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_LONG.cpp b/vm/mterp/c/OP_SHL_LONG.cpp
new file mode 100644
index 0000000..f6b502a
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_LONG_2ADDR.cpp b/vm/mterp/c/OP_SHL_LONG_2ADDR.cpp
new file mode 100644
index 0000000..b8a9954
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT.cpp b/vm/mterp/c/OP_SHR_INT.cpp
new file mode 100644
index 0000000..3834824
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT_2ADDR.cpp b/vm/mterp/c/OP_SHR_INT_2ADDR.cpp
new file mode 100644
index 0000000..c76c178
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT_LIT8.cpp b/vm/mterp/c/OP_SHR_INT_LIT8.cpp
new file mode 100644
index 0000000..e2657d7
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_LONG.cpp b/vm/mterp/c/OP_SHR_LONG.cpp
new file mode 100644
index 0000000..357a666
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_LONG_2ADDR.cpp b/vm/mterp/c/OP_SHR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..43e27ea
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SPARSE_SWITCH.cpp b/vm/mterp/c/OP_SPARSE_SWITCH.cpp
new file mode 100644
index 0000000..9ca16ad
--- /dev/null
+++ b/vm/mterp/c/OP_SPARSE_SWITCH.cpp
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|sparse-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_SPUT.cpp b/vm/mterp/c/OP_SPUT.cpp
new file mode 100644
index 0000000..286e64c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BOOLEAN.cpp b/vm/mterp/c/OP_SPUT_BOOLEAN.cpp
new file mode 100644
index 0000000..55ceb11
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BYTE.cpp b/vm/mterp/c/OP_SPUT_BYTE.cpp
new file mode 100644
index 0000000..d242fe1
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_CHAR.cpp b/vm/mterp/c/OP_SPUT_CHAR.cpp
new file mode 100644
index 0000000..18a2f06
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_OBJECT.cpp b/vm/mterp/c/OP_SPUT_OBJECT.cpp
new file mode 100644
index 0000000..fb223d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..38d6c0d
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_SHORT.cpp b/vm/mterp/c/OP_SPUT_SHORT.cpp
new file mode 100644
index 0000000..c6cd8d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_VOLATILE.cpp b/vm/mterp/c/OP_SPUT_VOLATILE.cpp
new file mode 100644
index 0000000..7899d05
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_WIDE.cpp b/vm/mterp/c/OP_SPUT_WIDE.cpp
new file mode 100644
index 0000000..0c74651
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..bdf552c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_DOUBLE.cpp b/vm/mterp/c/OP_SUB_DOUBLE.cpp
new file mode 100644
index 0000000..64a112d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..5870400
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_FLOAT.cpp b/vm/mterp/c/OP_SUB_FLOAT.cpp
new file mode 100644
index 0000000..96c5fbd
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..802935c
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_INT.cpp b/vm/mterp/c/OP_SUB_INT.cpp
new file mode 100644
index 0000000..2c1006d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_INT_2ADDR.cpp b/vm/mterp/c/OP_SUB_INT_2ADDR.cpp
new file mode 100644
index 0000000..328ed33
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_LONG.cpp b/vm/mterp/c/OP_SUB_LONG.cpp
new file mode 100644
index 0000000..bace11a
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_LONG_2ADDR.cpp b/vm/mterp/c/OP_SUB_LONG_2ADDR.cpp
new file mode 100644
index 0000000..f234dd4
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_THROW.cpp b/vm/mterp/c/OP_THROW.cpp
new file mode 100644
index 0000000..6fde0cc
--- /dev/null
+++ b/vm/mterp/c/OP_THROW.cpp
@@ -0,0 +1,24 @@
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+    {
+        Object* obj;
+
+        /*
+         * We don't create an exception here, but the process of searching
+         * for a catch block can do class lookups and throw exceptions.
+         * We need to update the saved PC.
+         */
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|throw v%d  (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+        obj = (Object*) GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /* will throw a null pointer exception */
+            LOGVV("Bad exception");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
diff --git a/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.cpp b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.cpp
new file mode 100644
index 0000000..85cc8fb
--- /dev/null
+++ b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+    EXPORT_PC();
+    vsrc1 = INST_AA(inst);
+    ref = FETCH(1);             /* class/field/method ref */
+    dvmThrowVerificationError(curMethod, vsrc1, ref);
+    GOTO_exceptionThrown();
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_3E.cpp b/vm/mterp/c/OP_UNUSED_3E.cpp
new file mode 100644
index 0000000..9ecf8e3
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3E.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_3F.cpp b/vm/mterp/c/OP_UNUSED_3F.cpp
new file mode 100644
index 0000000..9d1d68d
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3F.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_40.cpp b/vm/mterp/c/OP_UNUSED_40.cpp
new file mode 100644
index 0000000..f73a59c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_40.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_41.cpp b/vm/mterp/c/OP_UNUSED_41.cpp
new file mode 100644
index 0000000..38747e6
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_41.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_42.cpp b/vm/mterp/c/OP_UNUSED_42.cpp
new file mode 100644
index 0000000..154d293
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_42.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_43.cpp b/vm/mterp/c/OP_UNUSED_43.cpp
new file mode 100644
index 0000000..c7e702c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_43.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_73.cpp b/vm/mterp/c/OP_UNUSED_73.cpp
new file mode 100644
index 0000000..85aa95f
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_73.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_79.cpp b/vm/mterp/c/OP_UNUSED_79.cpp
new file mode 100644
index 0000000..1fa86e9
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_79.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_7A.cpp b/vm/mterp/c/OP_UNUSED_7A.cpp
new file mode 100644
index 0000000..beab006
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_7A.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_FF.cpp b/vm/mterp/c/OP_UNUSED_FF.cpp
new file mode 100644
index 0000000..1348aec
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_FF.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    ALOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT.cpp b/vm/mterp/c/OP_USHR_INT.cpp
new file mode 100644
index 0000000..7596c94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT_2ADDR.cpp b/vm/mterp/c/OP_USHR_INT_2ADDR.cpp
new file mode 100644
index 0000000..5fa2b94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT_LIT8.cpp b/vm/mterp/c/OP_USHR_INT_LIT8.cpp
new file mode 100644
index 0000000..0d325d7
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_LONG.cpp b/vm/mterp/c/OP_USHR_LONG.cpp
new file mode 100644
index 0000000..9b7e757
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_LONG_2ADDR.cpp b/vm/mterp/c/OP_USHR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..4ac0598
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT.cpp b/vm/mterp/c/OP_XOR_INT.cpp
new file mode 100644
index 0000000..d591909
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_2ADDR.cpp b/vm/mterp/c/OP_XOR_INT_2ADDR.cpp
new file mode 100644
index 0000000..7d32879
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_LIT16.cpp b/vm/mterp/c/OP_XOR_INT_LIT16.cpp
new file mode 100644
index 0000000..c204b79
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_LIT8.cpp b/vm/mterp/c/OP_XOR_INT_LIT8.cpp
new file mode 100644
index 0000000..f01773a
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_LONG.cpp b/vm/mterp/c/OP_XOR_LONG.cpp
new file mode 100644
index 0000000..d3dbc4c
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_LONG_2ADDR.cpp b/vm/mterp/c/OP_XOR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..e7a50f4
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/gotoTargets.cpp b/vm/mterp/c/gotoTargets.cpp
new file mode 100644
index 0000000..452bee8
--- /dev/null
+++ b/vm/mterp/c/gotoTargets.cpp
@@ -0,0 +1,971 @@
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
diff --git a/vm/mterp/c/header.cpp b/vm/mterp/c/header.cpp
new file mode 100644
index 0000000..709e4c5
--- /dev/null
+++ b/vm/mterp/c/header.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
diff --git a/vm/mterp/c/opcommon.cpp b/vm/mterp/c/opcommon.cpp
new file mode 100644
index 0000000..a49ed86
--- /dev/null
+++ b/vm/mterp/c/opcommon.cpp
@@ -0,0 +1,647 @@
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
diff --git a/vm/mterp/common/FindInterface.h b/vm/mterp/common/FindInterface.h
new file mode 100644
index 0000000..23e6f0e
--- /dev/null
+++ b/vm/mterp/common/FindInterface.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+
+extern "C" {
+
+/*
+ * Look up an interface on a class using the cache.
+ *
+ * This function used to be defined in mterp/c/header.c, but it is now used by
+ * the JIT compiler as well so it is separated into its own header file to
+ * avoid potential out-of-sync changes in the future.
+ */
+INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+    u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+{
+#define ATOMIC_CACHE_CALC \
+    dvmInterpFindInterfaceMethod(thisClass, methodIdx, method, methodClassDex)
+#define ATOMIC_CACHE_NULL_ALLOWED false
+
+    return (Method*) ATOMIC_CACHE_LOOKUP(methodClassDex->pInterfaceCache,
+                DEX_INTERFACE_CACHE_SIZE, thisClass, methodIdx);
+
+#undef ATOMIC_CACHE_CALC
+}
+
+}
diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h
new file mode 100644
index 0000000..80b36fc
--- /dev/null
+++ b/vm/mterp/common/asm-constants.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2008 The Android Open Source Project
+ *
+ * Constants used by the assembler and verified by the C compiler.
+ */
+
+#if defined(ASM_DEF_VERIFY)
+  /*
+   * Generate C fragments that verify values; assumes "bool failed" exists.
+   * These are all constant expressions, so on success these will compile
+   * down to nothing.
+   */
+# define MTERP_OFFSET(_name, _type, _field, _offset)                        \
+    if (OFFSETOF_MEMBER(_type, _field) != _offset) {                        \
+        ALOGE("Bad asm offset %s (%d), should be %d",                        \
+            #_name, _offset, OFFSETOF_MEMBER(_type, _field));               \
+        failed = true;                                                      \
+    }
+# define MTERP_SIZEOF(_name, _type, _size)                                  \
+    if (sizeof(_type) != (_size)) {                                         \
+        ALOGE("Bad asm sizeof %s (%d), should be %d",                        \
+            #_name, (_size), sizeof(_type));                                \
+        failed = true;                                                      \
+    }
+# define MTERP_CONSTANT(_name, _value)                                      \
+    if ((_name) != (_value)) {                                              \
+        ALOGE("Bad asm constant %s (%d), should be %d",                      \
+            #_name, (_value), (_name));                                     \
+        failed = true;                                                      \
+    }
+#else
+  /* generate constant labels for the assembly output */
+# define MTERP_OFFSET(name, type, field, offset)    name = offset
+# define MTERP_SIZEOF(name, type, size)             name = size
+# define MTERP_CONSTANT(name, value)                name = value
+#endif
+
+/*
+ * Platform dependencies.  Some platforms require 64-bit alignment of 64-bit
+ * data structures.  Some versions of gcc will hold small enumerated types
+ * in a char instead of an int.
+ */
+#if defined(__ARM_EABI__) || defined(__mips__)
+# define MTERP_NO_UNALIGN_64
+#endif
+#if defined(HAVE_SHORT_ENUMS)
+# define MTERP_SMALL_ENUM   1
+#else
+# define MTERP_SMALL_ENUM   4
+#endif
+
+/*
+ * This file must only contain the following kinds of statements:
+ *
+ *  MTERP_OFFSET(name, StructType, fieldname, offset)
+ *
+ *   Declares that the expected offset of StructType.fieldname is "offset".
+ *   This will break whenever the contents of StructType are rearranged.
+ *
+ *  MTERP_SIZEOF(name, Type, size)
+ *
+ *   Declares that the expected size of Type is "size".
+ *
+ *  MTERP_CONSTANT(name, value)
+ *
+ *   Declares that the expected value of "name" is "value".  Useful for
+ *   enumerations and defined constants that are inaccessible to the
+ *   assembly source.  (Note this assumes you will use the same name in
+ *   both C and assembly, which is good practice.)
+ *
+ * In all cases the "name" field is the label you will use in the assembler.
+ *
+ * The "value" field must always be an actual number, not a symbol, unless
+ * you are sure that the symbol's value will be visible to both C and
+ * assembly sources.  There may be restrictions on the possible range of
+ * values (which are usually provided as immediate operands), so it's best
+ * to restrict numbers assuming a signed 8-bit field.
+ *
+ * On the assembly side, these just become "name=value" constants.  On the
+ * C side, these turn into assertions that cause the VM to abort if the
+ * values are incorrect.
+ */
+
+/* DvmDex fields */
+MTERP_OFFSET(offDvmDex_pResStrings,     DvmDex, pResStrings, 8)
+MTERP_OFFSET(offDvmDex_pResClasses,     DvmDex, pResClasses, 12)
+MTERP_OFFSET(offDvmDex_pResMethods,     DvmDex, pResMethods, 16)
+MTERP_OFFSET(offDvmDex_pResFields,      DvmDex, pResFields, 20)
+MTERP_OFFSET(offDvmDex_pInterfaceCache, DvmDex, pInterfaceCache, 24)
+
+/* StackSaveArea fields */
+#ifdef EASY_GDB
+MTERP_OFFSET(offStackSaveArea_prevSave, StackSaveArea, prevSave, 0)
+MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 4)
+MTERP_OFFSET(offStackSaveArea_savedPc,  StackSaveArea, savedPc, 8)
+MTERP_OFFSET(offStackSaveArea_method,   StackSaveArea, method, 12)
+MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 16)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+                                        StackSaveArea, xtra.localRefCookie, 16)
+MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 20)
+MTERP_SIZEOF(sizeofStackSaveArea,       StackSaveArea, 24)
+#else
+MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 0)
+MTERP_OFFSET(offStackSaveArea_savedPc,  StackSaveArea, savedPc, 4)
+MTERP_OFFSET(offStackSaveArea_method,   StackSaveArea, method, 8)
+MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 12)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+                                        StackSaveArea, xtra.localRefCookie, 12)
+MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 16)
+MTERP_SIZEOF(sizeofStackSaveArea,       StackSaveArea, 20)
+#endif
+
+  /* ShadowSpace fields */
+#if defined(WITH_JIT) && defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offShadowSpace_startPC,     ShadowSpace, startPC, 0)
+MTERP_OFFSET(offShadowSpace_fp,          ShadowSpace, fp, 4)
+MTERP_OFFSET(offShadowSpace_method,      ShadowSpace, method, 8)
+MTERP_OFFSET(offShadowSpace_methodClassDex, ShadowSpace, methodClassDex, 12)
+MTERP_OFFSET(offShadowSpace_retval,      ShadowSpace, retval, 16)
+MTERP_OFFSET(offShadowSpace_interpStackEnd, ShadowSpace, interpStackEnd, 24)
+MTERP_OFFSET(offShadowSpace_jitExitState,ShadowSpace, jitExitState, 28)
+MTERP_OFFSET(offShadowSpace_svState,     ShadowSpace, selfVerificationState, 32)
+MTERP_OFFSET(offShadowSpace_shadowFP,    ShadowSpace, shadowFP, 40)
+#endif
+
+/* InstField fields */
+MTERP_OFFSET(offInstField_byteOffset,   InstField, byteOffset, 16)
+
+/* Field fields */
+MTERP_OFFSET(offField_clazz,            Field, clazz, 0)
+
+/* StaticField fields */
+MTERP_OFFSET(offStaticField_value,      StaticField, value, 16)
+
+/* Method fields */
+MTERP_OFFSET(offMethod_clazz,           Method, clazz, 0)
+MTERP_OFFSET(offMethod_accessFlags,     Method, accessFlags, 4)
+MTERP_OFFSET(offMethod_methodIndex,     Method, methodIndex, 8)
+MTERP_OFFSET(offMethod_registersSize,   Method, registersSize, 10)
+MTERP_OFFSET(offMethod_outsSize,        Method, outsSize, 12)
+MTERP_OFFSET(offMethod_name,            Method, name, 16)
+MTERP_OFFSET(offMethod_insns,           Method, insns, 32)
+MTERP_OFFSET(offMethod_nativeFunc,      Method, nativeFunc, 40)
+
+/* InlineOperation fields -- code assumes "func" offset is zero, do not alter */
+MTERP_OFFSET(offInlineOperation_func,   InlineOperation, func, 0)
+
+/* Thread fields */
+MTERP_OFFSET(offThread_pc,                Thread, interpSave.pc, 0)
+MTERP_OFFSET(offThread_curFrame,          Thread, interpSave.curFrame, 4)
+MTERP_OFFSET(offThread_method,            Thread, interpSave.method, 8)
+MTERP_OFFSET(offThread_methodClassDex,    Thread, interpSave.methodClassDex, 12)
+/* make sure all JValue union members are stored at the same offset */
+MTERP_OFFSET(offThread_retval,            Thread, interpSave.retval, 16)
+#ifdef HAVE_BIG_ENDIAN
+MTERP_OFFSET(offThread_retval_z,          Thread, interpSave.retval.z, 19)
+#else
+MTERP_OFFSET(offThread_retval_z,          Thread, interpSave.retval.z, 16)
+#endif
+MTERP_OFFSET(offThread_retval_i,          Thread, interpSave.retval.i, 16)
+MTERP_OFFSET(offThread_retval_j,          Thread, interpSave.retval.j, 16)
+MTERP_OFFSET(offThread_retval_l,          Thread, interpSave.retval.l, 16)
+MTERP_OFFSET(offThread_bailPtr,           Thread, interpSave.bailPtr, 24)
+MTERP_OFFSET(offThread_threadId,          Thread, threadId, 36)
+
+//40
+MTERP_OFFSET(offThread_subMode, \
+                               Thread, interpBreak.ctl.subMode, 40)
+MTERP_OFFSET(offThread_breakFlags, \
+                               Thread, interpBreak.ctl.breakFlags, 42)
+MTERP_OFFSET(offThread_curHandlerTable, \
+                               Thread, interpBreak.ctl.curHandlerTable, 44)
+MTERP_OFFSET(offThread_suspendCount,      Thread, suspendCount, 48);
+MTERP_OFFSET(offThread_dbgSuspendCount,   Thread, dbgSuspendCount, 52);
+MTERP_OFFSET(offThread_cardTable,         Thread, cardTable, 56)
+MTERP_OFFSET(offThread_interpStackEnd,    Thread, interpStackEnd, 60)
+MTERP_OFFSET(offThread_exception,         Thread, exception, 68)
+MTERP_OFFSET(offThread_debugIsMethodEntry, Thread, debugIsMethodEntry, 72)
+MTERP_OFFSET(offThread_interpStackSize,   Thread, interpStackSize, 76)
+MTERP_OFFSET(offThread_stackOverflowed,   Thread, stackOverflowed, 80)
+MTERP_OFFSET(offThread_mainHandlerTable,  Thread, mainHandlerTable, 88)
+MTERP_OFFSET(offThread_singleStepCount,   Thread, singleStepCount, 96)
+
+#ifdef WITH_JIT
+MTERP_OFFSET(offThread_jitToInterpEntries,Thread, jitToInterpEntries, 100)
+MTERP_OFFSET(offThread_inJitCodeCache,    Thread, inJitCodeCache, 124)
+MTERP_OFFSET(offThread_pJitProfTable,     Thread, pJitProfTable, 128)
+MTERP_OFFSET(offThread_jitThreshold,      Thread, jitThreshold, 132)
+MTERP_OFFSET(offThread_jitResumeNPC,      Thread, jitResumeNPC, 136)
+MTERP_OFFSET(offThread_jitResumeNSP,      Thread, jitResumeNSP, 140)
+MTERP_OFFSET(offThread_jitResumeDPC,      Thread, jitResumeDPC, 144)
+MTERP_OFFSET(offThread_jitState,          Thread, jitState, 148)
+MTERP_OFFSET(offThread_icRechainCount,    Thread, icRechainCount, 152)
+MTERP_OFFSET(offThread_pProfileCountdown, Thread, pProfileCountdown, 156)
+MTERP_OFFSET(offThread_callsiteClass,     Thread, callsiteClass, 160)
+MTERP_OFFSET(offThread_methodToCall,      Thread, methodToCall, 164)
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.segmentState.all, 168)
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offThread_shadowSpace,       Thread, shadowSpace, 188)
+#endif
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.segmentState.all, 100)
+#endif
+
+/* Object fields */
+MTERP_OFFSET(offObject_clazz,           Object, clazz, 0)
+MTERP_OFFSET(offObject_lock,            Object, lock, 4)
+
+/* Lock shape */
+MTERP_CONSTANT(LW_LOCK_OWNER_SHIFT, 3)
+MTERP_CONSTANT(LW_HASH_STATE_SHIFT, 1)
+
+/* ArrayObject fields */
+MTERP_OFFSET(offArrayObject_length,     ArrayObject, length, 8)
+#ifdef MTERP_NO_UNALIGN_64
+MTERP_OFFSET(offArrayObject_contents,   ArrayObject, contents, 16)
+#else
+MTERP_OFFSET(offArrayObject_contents,   ArrayObject, contents, 12)
+#endif
+
+/* String fields */
+MTERP_CONSTANT(STRING_FIELDOFF_VALUE,     8)
+MTERP_CONSTANT(STRING_FIELDOFF_HASHCODE, 12)
+MTERP_CONSTANT(STRING_FIELDOFF_OFFSET,   16)
+MTERP_CONSTANT(STRING_FIELDOFF_COUNT,    20)
+
+#if defined(WITH_JIT)
+/*
+ * Reasons for the non-chaining interpreter entry points
+ * Enums defined in vm/Globals.h
+ */
+MTERP_CONSTANT(kInlineCacheMiss,        0)
+MTERP_CONSTANT(kCallsiteInterpreted,    1)
+MTERP_CONSTANT(kSwitchOverflow,         2)
+MTERP_CONSTANT(kHeavyweightMonitor,     3)
+
+/* Size of callee save area */
+MTERP_CONSTANT(JIT_CALLEE_SAVE_DOUBLE_COUNT,   8)
+#endif
+
+/* ClassObject fields */
+MTERP_OFFSET(offClassObject_descriptor, ClassObject, descriptor, 24)
+MTERP_OFFSET(offClassObject_accessFlags, ClassObject, accessFlags, 32)
+MTERP_OFFSET(offClassObject_pDvmDex,    ClassObject, pDvmDex, 40)
+MTERP_OFFSET(offClassObject_status,     ClassObject, status, 44)
+MTERP_OFFSET(offClassObject_super,      ClassObject, super, 72)
+MTERP_OFFSET(offClassObject_vtableCount, ClassObject, vtableCount, 112)
+MTERP_OFFSET(offClassObject_vtable,     ClassObject, vtable, 116)
+
+#if defined(WITH_JIT)
+MTERP_CONSTANT(kJitNot,                 0)
+MTERP_CONSTANT(kJitTSelectRequest,      1)
+MTERP_CONSTANT(kJitTSelectRequestHot,   2)
+MTERP_CONSTANT(kJitSelfVerification,    3)
+MTERP_CONSTANT(kJitTSelect,             4)
+MTERP_CONSTANT(kJitTSelectEnd,          5)
+MTERP_CONSTANT(kJitDone,                6)
+
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_CONSTANT(kSVSIdle, 0)
+MTERP_CONSTANT(kSVSStart, 1)
+MTERP_CONSTANT(kSVSPunt, 2)
+MTERP_CONSTANT(kSVSSingleStep, 3)
+MTERP_CONSTANT(kSVSNoProfile, 4)
+MTERP_CONSTANT(kSVSTraceSelect, 5)
+MTERP_CONSTANT(kSVSNormal, 6)
+MTERP_CONSTANT(kSVSNoChain, 7)
+MTERP_CONSTANT(kSVSBackwardBranch, 8)
+MTERP_CONSTANT(kSVSDebugInterp, 9)
+#endif
+#endif
+
+/* ClassStatus enumeration */
+MTERP_SIZEOF(sizeofClassStatus,         ClassStatus, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(CLASS_INITIALIZED,   7)
+
+/* MethodType enumeration */
+MTERP_SIZEOF(sizeofMethodType,          MethodType, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(METHOD_DIRECT,       1)
+MTERP_CONSTANT(METHOD_STATIC,       2)
+MTERP_CONSTANT(METHOD_VIRTUAL,      3)
+MTERP_CONSTANT(METHOD_INTERFACE,    4)
+
+/* ClassObject constants */
+MTERP_CONSTANT(ACC_PRIVATE,         0x0002)
+MTERP_CONSTANT(ACC_STATIC,          0x0008)
+MTERP_CONSTANT(ACC_NATIVE,          0x0100)
+MTERP_CONSTANT(ACC_INTERFACE,       0x0200)
+MTERP_CONSTANT(ACC_ABSTRACT,        0x0400)
+MTERP_CONSTANT(CLASS_ISFINALIZABLE, 1<<31)
+
+/* flags for dvmMalloc */
+MTERP_CONSTANT(ALLOC_DONT_TRACK,    0x01)
+
+/* for GC */
+MTERP_CONSTANT(GC_CARD_SHIFT, 7)
+
+/* opcode number */
+MTERP_CONSTANT(OP_MOVE_EXCEPTION,   0x0d)
+MTERP_CONSTANT(OP_INVOKE_DIRECT_RANGE, 0x76)
+
+/* flags for interpBreak */
+MTERP_CONSTANT(kSubModeNormal,          0x0000)
+MTERP_CONSTANT(kSubModeMethodTrace,     0x0001)
+MTERP_CONSTANT(kSubModeEmulatorTrace,   0x0002)
+MTERP_CONSTANT(kSubModeInstCounting,    0x0004)
+MTERP_CONSTANT(kSubModeDebuggerActive,  0x0008)
+MTERP_CONSTANT(kSubModeSuspendPending,  0x0010)
+MTERP_CONSTANT(kSubModeCallbackPending, 0x0020)
+MTERP_CONSTANT(kSubModeCountedStep,     0x0040)
+MTERP_CONSTANT(kSubModeJitTraceBuild,   0x4000)
+MTERP_CONSTANT(kSubModeJitSV,           0x8000)
+MTERP_CONSTANT(kSubModeDebugProfile,    0x000f)
+
+MTERP_CONSTANT(kInterpNoBreak,            0x00)
+MTERP_CONSTANT(kInterpSingleStep,         0x01)
+MTERP_CONSTANT(kInterpSafePoint,          0x02)
+
+MTERP_CONSTANT(DBG_METHOD_ENTRY,          0x04)
+MTERP_CONSTANT(DBG_METHOD_EXIT,           0x08)
+
+#if defined(__thumb__)
+# define PCREL_REF(sym,label) sym-(label+4)
+#else
+# define PCREL_REF(sym,label) sym-(label+8)
+#endif
diff --git a/vm/mterp/common/jit-config.h b/vm/mterp/common/jit-config.h
new file mode 100644
index 0000000..8cc32e3
--- /dev/null
+++ b/vm/mterp/common/jit-config.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if __ARM_ARCH_5TE__
+#define JIT_PROF_SIZE_LOG_2     9
+#else
+#define JIT_PROF_SIZE_LOG_2     11
+#endif
+
+#define JIT_PROF_SIZE           (1 << JIT_PROF_SIZE_LOG_2)
diff --git a/vm/mterp/common/mips-defines.h b/vm/mterp/common/mips-defines.h
new file mode 100644
index 0000000..1e11a30
--- /dev/null
+++ b/vm/mterp/common/mips-defines.h
@@ -0,0 +1,3 @@
+#define fcc0    $fcc0
+#define fcc1    $fcc1
+
diff --git a/vm/mterp/config-allstubs b/vm/mterp/config-allstubs
new file mode 100644
index 0000000..9df7b12
--- /dev/null
+++ b/vm/mterp/config-allstubs
@@ -0,0 +1,47 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for "allstubs" target.  This is structured like the
+# assembly interpreters, but consists entirely of C stubs, making it
+# a handy if inefficient way to exercise all of the C handlers.  The
+# handler-style command should match the target assembly interpreter.
+#
+
+#handler-style jump-table
+handler-style computed-goto
+handler-size 64
+
+# C file header and basic definitions
+import c/header.cpp
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# common defs for the C opcodes
+import c/opcommon.cpp
+
+# opcode list; argument to op-start is default directory
+op-start c
+    # use nothing but C stubs
+op-end
+
+# arch-specific entry point to interpreter
+import cstubs/entry.cpp
+
+# "helper" code
+import c/gotoTargets.cpp
+
+# finish
+import cstubs/enddefs.cpp
diff --git a/vm/mterp/config-armv5te b/vm/mterp/config-armv5te
new file mode 100644
index 0000000..5861f8b
--- /dev/null
+++ b/vm/mterp/config-armv5te
@@ -0,0 +1,58 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    #op OP_FILL_ARRAY_DATA c
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-armv5te-vfp b/vm/mterp/config-armv5te-vfp
new file mode 100644
index 0000000..160913c
--- /dev/null
+++ b/vm/mterp/config-armv5te-vfp
@@ -0,0 +1,108 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE targets with VFP support.
+#
+# This is just ARMv5TE with replacements for the handlers that can benefit
+# from floating-point instructions.  Essentially all float/double
+# operations except for "remainder" and conversions to/from 64-bit ints.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    op OP_ADD_DOUBLE arm-vfp
+    op OP_ADD_DOUBLE_2ADDR arm-vfp
+    op OP_ADD_FLOAT arm-vfp
+    op OP_ADD_FLOAT_2ADDR arm-vfp
+    op OP_CMPG_DOUBLE arm-vfp
+    op OP_CMPG_FLOAT arm-vfp
+    op OP_CMPL_DOUBLE arm-vfp
+    op OP_CMPL_FLOAT arm-vfp
+    op OP_DIV_DOUBLE arm-vfp
+    op OP_DIV_DOUBLE_2ADDR arm-vfp
+    op OP_DIV_FLOAT arm-vfp
+    op OP_DIV_FLOAT_2ADDR arm-vfp
+    op OP_DOUBLE_TO_FLOAT arm-vfp
+    op OP_DOUBLE_TO_INT arm-vfp
+    op OP_FLOAT_TO_DOUBLE arm-vfp
+    op OP_FLOAT_TO_INT arm-vfp
+    op OP_INT_TO_DOUBLE arm-vfp
+    op OP_INT_TO_FLOAT arm-vfp
+    op OP_MUL_DOUBLE arm-vfp
+    op OP_MUL_DOUBLE_2ADDR arm-vfp
+    op OP_MUL_FLOAT arm-vfp
+    op OP_MUL_FLOAT_2ADDR arm-vfp
+    op OP_SUB_DOUBLE arm-vfp
+    op OP_SUB_DOUBLE_2ADDR arm-vfp
+    op OP_SUB_FLOAT arm-vfp
+    op OP_SUB_FLOAT_2ADDR arm-vfp
+
+    # use trivial integer operation
+    #op OP_NEG_DOUBLE armv5te
+    #op OP_NEG_FLOAT armv5te
+
+    # use __aeabi_* functions
+    #op OP_DOUBLE_TO_LONG armv5te
+    #op OP_FLOAT_TO_LONG armv5te
+    #op OP_LONG_TO_DOUBLE armv5te
+    #op OP_LONG_TO_FLOAT armv5te
+
+    # no "remainder" op in vfp or libgcc.a; use libc function
+    #op OP_REM_DOUBLE armv5te
+    #op OP_REM_DOUBLE_2ADDR armv5te
+    #op OP_REM_FLOAT armv5te
+    #op OP_REM_FLOAT_2ADDR armv5te
+
+    # experiment, unrelated to vfp
+    #op OP_INT_TO_BYTE armv6
+    #op OP_INT_TO_CHAR armv6
+    #op OP_INT_TO_SHORT armv6
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-armv7-a b/vm/mterp/config-armv7-a
new file mode 100644
index 0000000..eadaeca
--- /dev/null
+++ b/vm/mterp/config-armv7-a
@@ -0,0 +1,172 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv7-A targets.
+#
+# This target includes Thumb-2 and Thumb2-EE support, as well as VFPLite.
+#
+# The difference in performance between this and ARMv5TE appears to be
+# negligible on a Cortex-A8 CPU, so this is really just an experiment.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    # handlers that take advantage of >= ARMv6T2 instructions
+    op OP_ADD_DOUBLE_2ADDR armv6t2
+    op OP_ADD_FLOAT_2ADDR armv6t2
+    op OP_ADD_INT_2ADDR armv6t2
+    op OP_ADD_INT_LIT16 armv6t2
+    op OP_ADD_LONG_2ADDR armv6t2
+    op OP_AND_INT_2ADDR armv6t2
+    op OP_AND_INT_LIT16 armv6t2
+    op OP_AND_LONG_2ADDR armv6t2
+    op OP_ARRAY_LENGTH armv6t2
+    op OP_CONST_4 armv6t2
+    op OP_DIV_DOUBLE_2ADDR armv6t2
+    op OP_DIV_FLOAT_2ADDR armv6t2
+    op OP_DIV_INT_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)
+#
+# Add this if you see linker failures for stuff like "dvmMterp_exceptionThrown".
+##import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-armv7-a-neon b/vm/mterp/config-armv7-a-neon
new file mode 100644
index 0000000..5f91365
--- /dev/null
+++ b/vm/mterp/config-armv7-a-neon
@@ -0,0 +1,170 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv7-A targets.
+#
+# This target includes Thumb-2 and Thumb2-EE support, as well as VFPLite.
+#
+# The difference in performance between this and ARMv5TE appears to be
+# negligible on a Cortex-A8 CPU, so this is really just an experiment.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    # handlers that take advantage of >= ARMv6T2 instructions
+    op OP_ADD_DOUBLE_2ADDR armv6t2
+    op OP_ADD_FLOAT_2ADDR armv6t2
+    op OP_ADD_INT_2ADDR armv6t2
+    op OP_ADD_INT_LIT16 armv6t2
+    op OP_ADD_LONG_2ADDR armv6t2
+    op OP_AND_INT_2ADDR armv6t2
+    op OP_AND_INT_LIT16 armv6t2
+    op OP_AND_LONG_2ADDR armv6t2
+    op OP_ARRAY_LENGTH armv6t2
+    op OP_CONST_4 armv6t2
+    op OP_DIV_DOUBLE_2ADDR armv6t2
+    op OP_DIV_FLOAT_2ADDR armv6t2
+    op OP_DIV_INT_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.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-mips b/vm/mterp/config-mips
new file mode 100644
index 0000000..0dc80e8
--- /dev/null
+++ b/vm/mterp/config-mips
@@ -0,0 +1,65 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for MIPS architecture targets.
+#
+
+handler-style computed-goto
+handler-size 128
+# Need to specify split-ops to generate alt-ops at the end after
+# importing other files.
+split-ops
+
+# source for the instruction table stub
+asm-stub mips/stub.S
+
+# source for alternate entry stub
+asm-alt-stub mips/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import mips/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import mips/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import mips/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start mips
+
+# OP_BREAKPOINT needs explicit testing
+    op OP_BREAKPOINT c
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import mips/footer.S
+import mips/debug.cpp
+alt-ops
diff --git a/vm/mterp/config-portable b/vm/mterp/config-portable
new file mode 100644
index 0000000..8f696b7
--- /dev/null
+++ b/vm/mterp/config-portable
@@ -0,0 +1,42 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for the portable interpreter.
+#
+
+handler-style all-c
+
+# C file header and basic definitions
+import c/header.cpp
+
+# C pre-processor defines for stub C instructions
+import portable/stubdefs.cpp
+
+# common defs for the C opcodes
+import c/opcommon.cpp
+
+# entry point
+import portable/entry.cpp
+
+# opcode list; argument to op-start is default directory
+op-start c
+    # concatenate all C implementations
+op-end
+
+# "helper" code
+import c/gotoTargets.cpp
+
+# finish
+import portable/enddefs.cpp
diff --git a/vm/mterp/config-x86 b/vm/mterp/config-x86
new file mode 100644
index 0000000..9e5fa43
--- /dev/null
+++ b/vm/mterp/config-x86
@@ -0,0 +1,61 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for "desktop" targets.
+#
+
+handler-style jump-table
+
+# source for the instruction table stub
+asm-stub x86/stub.S
+
+# source for alternate entry stub
+asm-alt-stub x86/alt_stub.S
+
+# C file header and basic definitions
+import c/header.cpp
+import x86/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# common defs for the C opcodes
+import c/opcommon.cpp
+
+# opcode list; argument to op-start is default directory
+op-start x86
+    # stub -- need native impl
+    op OP_EXECUTE_INLINE_RANGE c
+    op OP_IGET_WIDE_VOLATILE c
+    op OP_IPUT_WIDE_VOLATILE c
+    op OP_SGET_WIDE_VOLATILE c
+    op OP_SPUT_WIDE_VOLATILE c
+    op OP_RETURN_VOID_BARRIER c
+    op OP_INVOKE_OBJECT_INIT_RANGE c
+op-end
+
+# arch-specific entry point to interpreter
+import x86/entry.S
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+# (asm code is currently calling into dvmMterp_exceptionThrown)
+import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import x86/footer.S
diff --git a/vm/mterp/cstubs/enddefs.cpp b/vm/mterp/cstubs/enddefs.cpp
new file mode 100644
index 0000000..cac74bf
--- /dev/null
+++ b/vm/mterp/cstubs/enddefs.cpp
@@ -0,0 +1,9 @@
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
diff --git a/vm/mterp/cstubs/entry.cpp b/vm/mterp/cstubs/entry.cpp
new file mode 100644
index 0000000..90b6cea
--- /dev/null
+++ b/vm/mterp/cstubs/entry.cpp
@@ -0,0 +1,61 @@
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) (const void*) dvmMterp_##_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlers)
+
+#undef H
+#define H(_op) #_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlerNames)
+
+#include <setjmp.h>
+
+/*
+ * C mterp entry point.  This just calls the various C fallbacks, making
+ * this a slow but portable interpeter.
+ *
+ * This is only used for the "allstubs" variant.
+ */
+void dvmMterpStdRun(Thread* self)
+{
+    jmp_buf jmpBuf;
+
+    self->interpSave.bailPtr = &jmpBuf;
+
+    /* We exit via a longjmp */
+    if (setjmp(jmpBuf)) {
+        LOGVV("mterp threadid=%d returning", dvmThreadSelf()->threadId);
+        return;
+    }
+
+    /* run until somebody longjmp()s out */
+    while (true) {
+        typedef void (*Handler)(Thread* self);
+
+        u2 inst = /*self->interpSave.*/pc[0];
+        /*
+         * In mterp, dvmCheckBefore is handled via the altHandlerTable,
+         * while in the portable interpreter it is part of the handler
+         * FINISH code.  For allstubs, we must do an explicit check
+         * in the interpretation loop.
+         */
+        if (self->interpBreak.ctl.subMode) {
+            dvmCheckBefore(pc, fp, self);
+        }
+        Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+        (void) gDvmMterpHandlerNames;   /* avoid gcc "defined but not used" */
+        LOGVV("handler %p %s",
+            handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+        (*handler)(self);
+    }
+}
+
+/*
+ * C mterp exit point.  Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(Thread* self)
+{
+    jmp_buf* pJmpBuf = (jmp_buf*) self->interpSave.bailPtr;
+    longjmp(*pJmpBuf, 1);
+}
diff --git a/vm/mterp/cstubs/stubdefs.cpp b/vm/mterp/cstubs/stubdefs.cpp
new file mode 100644
index 0000000..58b4559
--- /dev/null
+++ b/vm/mterp/cstubs/stubdefs.cpp
@@ -0,0 +1,136 @@
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
diff --git a/vm/mterp/gen-mterp.py b/vm/mterp/gen-mterp.py
new file mode 100755
index 0000000..f60d8c6
--- /dev/null
+++ b/vm/mterp/gen-mterp.py
@@ -0,0 +1,634 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Using instructions from an architecture-specific config file, generate C
+# and assembly source files for the Dalvik interpreter.
+#
+
+import sys, string, re, time
+from string import Template
+
+interp_defs_file = "../../libdex/DexOpcodes.h" # need opcode list
+kNumPackedOpcodes = 256 # TODO: Derive this from DexOpcodes.h.
+
+splitops = False
+verbose = False
+handler_size_bits = -1000
+handler_size_bytes = -1000
+in_op_start = 0             # 0=not started, 1=started, 2=ended
+in_alt_op_start = 0         # 0=not started, 1=started, 2=ended
+default_op_dir = None
+default_alt_stub = None
+opcode_locations = {}
+alt_opcode_locations = {}
+asm_stub_text = []
+label_prefix = ".L"         # use ".L" to hide labels from gdb
+alt_label_prefix = ".L_ALT" # use ".L" to hide labels from gdb
+style = None                # interpreter style
+generate_alt_table = False
+
+# Exception class.
+class DataParseError(SyntaxError):
+    "Failure when parsing data file"
+
+#
+# Set any omnipresent substitution values.
+#
+def getGlobalSubDict():
+    return { "handler_size_bits":handler_size_bits,
+             "handler_size_bytes":handler_size_bytes }
+
+#
+# Parse arch config file --
+# Set interpreter style.
+#
+def setHandlerStyle(tokens):
+    global style
+    if len(tokens) != 2:
+        raise DataParseError("handler-style requires one argument")
+    style = tokens[1]
+    if style != "computed-goto" and style != "jump-table" and style != "all-c":
+        raise DataParseError("handler-style (%s) invalid" % style)
+
+#
+# Parse arch config file --
+# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
+# log2(handler_size_bytes).  Throws an exception if "bytes" is not 0 or
+# a power of two.
+#
+def setHandlerSize(tokens):
+    global handler_size_bits, handler_size_bytes
+    if style != "computed-goto":
+        print "Warning: handler-size valid only for computed-goto interpreters"
+    if len(tokens) != 2:
+        raise DataParseError("handler-size requires one argument")
+    if handler_size_bits != -1000:
+        raise DataParseError("handler-size may only be set once")
+
+    # compute log2(n), and make sure n is 0 or a power of 2
+    handler_size_bytes = bytes = int(tokens[1])
+    bits = -1
+    while bytes > 0:
+        bytes //= 2     # halve with truncating division
+        bits += 1
+
+    if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
+        raise DataParseError("handler-size (%d) must be power of 2" \
+                % orig_bytes)
+    handler_size_bits = bits
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def importFile(tokens):
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    source = tokens[1]
+    if source.endswith(".cpp"):
+        appendSourceFile(tokens[1], getGlobalSubDict(), c_fp, None)
+    elif source.endswith(".S"):
+        appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
+    else:
+        raise DataParseError("don't know how to import %s (expecting .cpp/.S)"
+                % source)
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def setAsmStub(tokens):
+    global asm_stub_text
+    if style == "all-c":
+        print "Warning: asm-stub ignored for all-c interpreter"
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    try:
+        stub_fp = open(tokens[1])
+        asm_stub_text = stub_fp.readlines()
+    except IOError, err:
+        stub_fp.close()
+        raise DataParseError("unable to load asm-stub: %s" % str(err))
+    stub_fp.close()
+
+#
+# Parse arch config file --
+# Record location of default alt stub
+#
+def setAsmAltStub(tokens):
+    global default_alt_stub, generate_alt_table
+    if style == "all-c":
+        print "Warning: asm-alt-stub ingored for all-c interpreter"
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    default_alt_stub = tokens[1]
+    generate_alt_table = True
+
+#
+# Parse arch config file --
+# Start of opcode list.
+#
+def opStart(tokens):
+    global in_op_start
+    global default_op_dir
+    if len(tokens) != 2:
+        raise DataParseError("opStart takes a directory name argument")
+    if in_op_start != 0:
+        raise DataParseError("opStart can only be specified once")
+    default_op_dir = tokens[1]
+    in_op_start = 1
+
+#
+# Parse arch config file --
+# Set location of a single alt opcode's source file.
+#
+def altEntry(tokens):
+    global generate_alt_table
+    if len(tokens) != 3:
+        raise DataParseError("alt requires exactly two arguments")
+    if in_op_start != 1:
+        raise DataParseError("alt statements must be between opStart/opEnd")
+    try:
+        index = opcodes.index(tokens[1])
+    except ValueError:
+        raise DataParseError("unknown opcode %s" % tokens[1])
+    if alt_opcode_locations.has_key(tokens[1]):
+        print "Note: alt overrides earlier %s (%s -> %s)" \
+                % (tokens[1], alt_opcode_locations[tokens[1]], tokens[2])
+    alt_opcode_locations[tokens[1]] = tokens[2]
+    generate_alt_table = True
+
+#
+# Parse arch config file --
+# Set location of a single opcode's source file.
+#
+def opEntry(tokens):
+    #global opcode_locations
+    if len(tokens) != 3:
+        raise DataParseError("op requires exactly two arguments")
+    if in_op_start != 1:
+        raise DataParseError("op statements must be between opStart/opEnd")
+    try:
+        index = opcodes.index(tokens[1])
+    except ValueError:
+        raise DataParseError("unknown opcode %s" % tokens[1])
+    if opcode_locations.has_key(tokens[1]):
+        print "Note: op overrides earlier %s (%s -> %s)" \
+                % (tokens[1], opcode_locations[tokens[1]], tokens[2])
+    opcode_locations[tokens[1]] = tokens[2]
+
+#
+# Emit jump table
+#
+def emitJmpTable(start_label, prefix):
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .text\n")
+    asm_fp.write("%s:\n" % start_label)
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+        dict = getGlobalSubDict()
+        dict.update({ "opcode":op, "opnum":i })
+        asm_fp.write("    .long " + prefix + \
+                     "_%(opcode)s /* 0x%(opnum)02x */\n" % dict)
+
+#
+# Parse arch config file --
+# End of opcode list; emit instruction blocks.
+#
+def opEnd(tokens):
+    global in_op_start
+    if len(tokens) != 1:
+        raise DataParseError("opEnd takes no arguments")
+    if in_op_start != 1:
+        raise DataParseError("opEnd must follow opStart, and only appear once")
+    in_op_start = 2
+
+    loadAndEmitOpcodes()
+    if splitops == False:
+        if generate_alt_table:
+            loadAndEmitAltOpcodes()
+            if style == "jump-table":
+                emitJmpTable("dvmAsmInstructionStart", label_prefix);
+                emitJmpTable("dvmAsmAltInstructionStart", alt_label_prefix);
+
+def genaltop(tokens):
+    if in_op_start != 2:
+       raise DataParseError("alt-op can be specified only after op-end")
+    if len(tokens) != 1:
+        raise DataParseError("opEnd takes no arguments")
+    if generate_alt_table:
+        loadAndEmitAltOpcodes()
+        if style == "jump-table":
+            emitJmpTable("dvmAsmInstructionStart", label_prefix);
+            emitJmpTable("dvmAsmAltInstructionStart", alt_label_prefix);
+
+
+#
+# Extract an ordered list of instructions from the VM sources.  We use the
+# "goto table" definition macro, which has exactly kNumPackedOpcodes
+# entries.
+#
+def getOpcodeList():
+    opcodes = []
+    opcode_fp = open(interp_defs_file)
+    opcode_re = re.compile(r"^\s*H\(OP_(\w+)\),.*", re.DOTALL)
+    for line in opcode_fp:
+        match = opcode_re.match(line)
+        if not match:
+            continue
+        opcodes.append("OP_" + match.group(1))
+    opcode_fp.close()
+
+    if len(opcodes) != kNumPackedOpcodes:
+        print "ERROR: found %d opcodes in Interp.h (expected %d)" \
+                % (len(opcodes), kNumPackedOpcodes)
+        raise SyntaxError, "bad opcode count"
+    return opcodes
+
+def emitAlign():
+    if style == "computed-goto":
+        asm_fp.write("    .balign %d\n" % handler_size_bytes)
+
+#
+# Load and emit opcodes for all kNumPackedOpcodes instructions.
+#
+def loadAndEmitOpcodes():
+    sister_list = []
+    assert len(opcodes) == kNumPackedOpcodes
+    need_dummy_start = False
+    if style == "jump-table":
+        start_label = "dvmAsmInstructionStartCode"
+        end_label = "dvmAsmInstructionEndCode"
+    else:
+        start_label = "dvmAsmInstructionStart"
+        end_label = "dvmAsmInstructionEnd"
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .type   %s, %%function\n" % start_label)
+    asm_fp.write("%s = " % start_label + label_prefix + "_OP_NOP\n")
+    asm_fp.write("    .text\n\n")
+
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+
+        if opcode_locations.has_key(op):
+            location = opcode_locations[op]
+        else:
+            location = default_op_dir
+
+        if location == "c":
+            loadAndEmitC(location, i)
+            if len(asm_stub_text) == 0:
+                need_dummy_start = True
+        else:
+            loadAndEmitAsm(location, i, sister_list)
+
+    # For a 100% C implementation, there are no asm handlers or stubs.  We
+    # need to have the dvmAsmInstructionStart label point at OP_NOP, and it's
+    # too annoying to try to slide it in after the alignment psuedo-op, so
+    # we take the low road and just emit a dummy OP_NOP here.
+    if need_dummy_start:
+        emitAlign()
+        asm_fp.write(label_prefix + "_OP_NOP:   /* dummy */\n");
+
+    emitAlign()
+    asm_fp.write("    .size   %s, .-%s\n" % (start_label, start_label))
+    asm_fp.write("    .global %s\n" % end_label)
+    asm_fp.write("%s:\n" % end_label)
+
+    if style == "computed-goto":
+        emitSectionComment("Sister implementations", asm_fp)
+        asm_fp.write("    .global dvmAsmSisterStart\n")
+        asm_fp.write("    .type   dvmAsmSisterStart, %function\n")
+        asm_fp.write("    .text\n")
+        asm_fp.write("    .balign 4\n")
+        asm_fp.write("dvmAsmSisterStart:\n")
+        asm_fp.writelines(sister_list)
+        asm_fp.write("\n    .size   dvmAsmSisterStart, .-dvmAsmSisterStart\n")
+        asm_fp.write("    .global dvmAsmSisterEnd\n")
+        asm_fp.write("dvmAsmSisterEnd:\n\n")
+
+#
+# Load an alternate entry stub
+#
+def loadAndEmitAltStub(source, opindex):
+    op = opcodes[opindex]
+    if verbose:
+        print " alt emit %s --> stub" % source
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+
+    emitAsmHeader(asm_fp, dict, alt_label_prefix)
+    appendSourceFile(source, dict, asm_fp, None)
+
+#
+# Load and emit alternate opcodes for all kNumPackedOpcodes instructions.
+#
+def loadAndEmitAltOpcodes():
+    assert len(opcodes) == kNumPackedOpcodes
+    if style == "jump-table":
+        start_label = "dvmAsmAltInstructionStartCode"
+        end_label = "dvmAsmAltInstructionEndCode"
+    else:
+        start_label = "dvmAsmAltInstructionStart"
+        end_label = "dvmAsmAltInstructionEnd"
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .type   %s, %%function\n" % start_label)
+    asm_fp.write("    .text\n\n")
+    asm_fp.write("%s = " % start_label + label_prefix + "_ALT_OP_NOP\n")
+
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+        if alt_opcode_locations.has_key(op):
+            source = "%s/ALT_%s.S" % (alt_opcode_locations[op], op)
+        else:
+            source = default_alt_stub
+        loadAndEmitAltStub(source, i)
+
+    emitAlign()
+    asm_fp.write("    .size   %s, .-%s\n" % (start_label, start_label))
+    asm_fp.write("    .global %s\n" % end_label)
+    asm_fp.write("%s:\n" % end_label)
+
+#
+# Load a C fragment and emit it, then output an assembly stub.
+#
+def loadAndEmitC(location, opindex):
+    op = opcodes[opindex]
+    source = "%s/%s.cpp" % (location, op)
+    if verbose:
+        print " emit %s --> C++" % source
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+
+    appendSourceFile(source, dict, c_fp, None)
+
+    if len(asm_stub_text) != 0:
+        emitAsmStub(asm_fp, dict)
+
+#
+# Load an assembly fragment and emit it.
+#
+def loadAndEmitAsm(location, opindex, sister_list):
+    op = opcodes[opindex]
+    source = "%s/%s.S" % (location, op)
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+    if verbose:
+        print " emit %s --> asm" % source
+
+    emitAsmHeader(asm_fp, dict, label_prefix)
+    appendSourceFile(source, dict, asm_fp, sister_list)
+
+#
+# Output the alignment directive and label for an assembly piece.
+#
+def emitAsmHeader(outfp, dict, prefix):
+    outfp.write("/* ------------------------------ */\n")
+    # The alignment directive ensures that the handler occupies
+    # at least the correct amount of space.  We don't try to deal
+    # with overflow here.
+    emitAlign()
+    # Emit a label so that gdb will say the right thing.  We prepend an
+    # underscore so the symbol name doesn't clash with the Opcode enum.
+    outfp.write(prefix + "_%(opcode)s: /* 0x%(opnum)02x */\n" % dict)
+
+#
+# Output a generic instruction stub that updates the "glue" struct and
+# calls the C implementation.
+#
+def emitAsmStub(outfp, dict):
+    emitAsmHeader(outfp, dict, label_prefix)
+    for line in asm_stub_text:
+        templ = Template(line)
+        outfp.write(templ.substitute(dict))
+
+#
+# Append the file specified by "source" to the open "outfp".  Each line will
+# be template-replaced using the substitution dictionary "dict".
+#
+# If the first line of the file starts with "%" it is taken as a directive.
+# A "%include" line contains a filename and, optionally, a Python-style
+# dictionary declaration with substitution strings.  (This is implemented
+# with recursion.)
+#
+# If "sister_list" is provided, and we find a line that contains only "&",
+# all subsequent lines from the file will be appended to sister_list instead
+# of copied to the output.
+#
+# This may modify "dict".
+#
+def appendSourceFile(source, dict, outfp, sister_list):
+    outfp.write("/* File: %s */\n" % source)
+    infp = open(source, "r")
+    in_sister = False
+    for line in infp:
+        if line.startswith("%include"):
+            # Parse the "include" line
+            tokens = line.strip().split(' ', 2)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%include in %s" % source)
+
+            alt_source = tokens[1].strip("\"")
+            if alt_source == source:
+                raise DataParseError("self-referential %%include in %s"
+                        % source)
+
+            new_dict = dict.copy()
+            if len(tokens) == 3:
+                new_dict.update(eval(tokens[2]))
+            #print " including src=%s dict=%s" % (alt_source, new_dict)
+            appendSourceFile(alt_source, new_dict, outfp, sister_list)
+            continue
+
+        elif line.startswith("%default"):
+            # copy keywords into dictionary
+            tokens = line.strip().split(' ', 1)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%default in %s" % source)
+            defaultValues = eval(tokens[1])
+            for entry in defaultValues:
+                dict.setdefault(entry, defaultValues[entry])
+            continue
+
+        elif line.startswith("%verify"):
+            # more to come, someday
+            continue
+
+        elif line.startswith("%break") and sister_list != None:
+            # allow more than one %break, ignoring all following the first
+            if style == "computed-goto" and not in_sister:
+                in_sister = True
+                sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
+            continue
+
+        # perform keyword substitution if a dictionary was provided
+        if dict != None:
+            templ = Template(line)
+            try:
+                subline = templ.substitute(dict)
+            except KeyError, err:
+                raise DataParseError("keyword substitution failed in %s: %s"
+                        % (source, str(err)))
+            except:
+                print "ERROR: substitution failed: " + line
+                raise
+        else:
+            subline = line
+
+        # write output to appropriate file
+        if in_sister:
+            sister_list.append(subline)
+        else:
+            outfp.write(subline)
+    outfp.write("\n")
+    infp.close()
+
+#
+# Emit a C-style section header comment.
+#
+def emitSectionComment(str, fp):
+    equals = "========================================" \
+             "==================================="
+
+    fp.write("\n/*\n * %s\n *  %s\n * %s\n */\n" %
+        (equals, str, equals))
+
+
+#
+# ===========================================================================
+# "main" code
+#
+
+#
+# Check args.
+#
+if len(sys.argv) != 3:
+    print "Usage: %s target-arch output-dir" % sys.argv[0]
+    sys.exit(2)
+
+target_arch = sys.argv[1]
+output_dir = sys.argv[2]
+
+#
+# Extract opcode list.
+#
+opcodes = getOpcodeList()
+#for op in opcodes:
+#    print "  %s" % op
+
+#
+# Open config file.
+#
+try:
+    config_fp = open("config-%s" % target_arch)
+except:
+    print "Unable to open config file 'config-%s'" % target_arch
+    sys.exit(1)
+
+#
+# Open and prepare output files.
+#
+try:
+    c_fp = open("%s/InterpC-%s.cpp" % (output_dir, target_arch), "w")
+    asm_fp = open("%s/InterpAsm-%s.S" % (output_dir, target_arch), "w")
+except:
+    print "Unable to open output files"
+    print "Make sure directory '%s' exists and existing files are writable" \
+            % output_dir
+    # Ideally we'd remove the files to avoid confusing "make", but if they
+    # failed to open we probably won't be able to remove them either.
+    sys.exit(1)
+
+print "Generating %s, %s" % (c_fp.name, asm_fp.name)
+
+file_header = """/*
+ * This file was generated automatically by gen-mterp.py for '%s'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+""" % (target_arch)
+
+c_fp.write(file_header)
+asm_fp.write(file_header)
+
+#
+# Process the config file.
+#
+failed = False
+try:
+    for line in config_fp:
+        line = line.strip()         # remove CRLF, leading spaces
+        tokens = line.split(' ')    # tokenize
+        #print "%d: %s" % (len(tokens), tokens)
+        if len(tokens[0]) == 0:
+            #print "  blank"
+            pass
+        elif tokens[0][0] == '#':
+            #print "  comment"
+            pass
+        else:
+            if tokens[0] == "handler-size":
+                setHandlerSize(tokens)
+            elif tokens[0] == "import":
+                importFile(tokens)
+            elif tokens[0] == "asm-stub":
+                setAsmStub(tokens)
+            elif tokens[0] == "asm-alt-stub":
+                setAsmAltStub(tokens)
+            elif tokens[0] == "op-start":
+                opStart(tokens)
+            elif tokens[0] == "op-end":
+                opEnd(tokens)
+            elif tokens[0] == "alt":
+                altEntry(tokens)
+            elif tokens[0] == "op":
+                opEntry(tokens)
+            elif tokens[0] == "handler-style":
+                setHandlerStyle(tokens)
+            elif tokens[0] == "alt-ops":
+                genaltop(tokens)
+            elif tokens[0] == "split-ops":
+                splitops = True
+            else:
+                raise DataParseError, "unrecognized command '%s'" % tokens[0]
+            if style == None:
+                print "tokens[0] = %s" % tokens[0]
+                raise DataParseError, "handler-style must be first command"
+except DataParseError, err:
+    print "Failed: " + str(err)
+    # TODO: remove output files so "make" doesn't get confused
+    failed = True
+    c_fp.close()
+    asm_fp.close()
+    c_fp = asm_fp = None
+
+config_fp.close()
+
+#
+# Done!
+#
+if c_fp:
+    c_fp.close()
+if asm_fp:
+    asm_fp.close()
+
+sys.exit(failed)
diff --git a/vm/mterp/mips/OP_ADD_DOUBLE.S b/vm/mterp/mips/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..1d5cebc
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__adddf3)", "instr_f":"add.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..499961f
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__adddf3)", "instr_f":"add.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_FLOAT.S b/vm/mterp/mips/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..18c94f4
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__addsf3)", "instr_f":"add.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/mips/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..0a39770
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__addsf3)", "instr_f":"add.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_INT.S b/vm/mterp/mips/OP_ADD_INT.S
new file mode 100644
index 0000000..dcbbb7e
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_INT_2ADDR.S b/vm/mterp/mips/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..8bb3b0c
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_INT_LIT16.S b/vm/mterp/mips/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..de45f81
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_INT_LIT8.S b/vm/mterp/mips/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..feaaaa2
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_LONG.S b/vm/mterp/mips/OP_ADD_LONG.S
new file mode 100644
index 0000000..d57e1cf
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_LONG.S
@@ -0,0 +1,10 @@
+%verify "executed"
+/*
+ *  The compiler generates the following sequence for
+ *  [v1 v0] =  [a1 a0] + [a3 a2];
+ *    addu v0,a2,a0
+ *    addu a1,a3,a1
+ *    sltu v1,v0,a2
+ *    addu v1,v1,a1
+ */
+%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" }
diff --git a/vm/mterp/mips/OP_ADD_LONG_2ADDR.S b/vm/mterp/mips/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..6a87119
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,5 @@
+%verify "executed"
+/*
+ *See OP_ADD_LONG.S for details
+ */
+%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" }
diff --git a/vm/mterp/mips/OP_AGET.S b/vm/mterp/mips/OP_AGET.S
new file mode 100644
index 0000000..e1b182a
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET.S
@@ -0,0 +1,31 @@
+%default { "load":"lw", "shift":"2" }
+%verify "executed"
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if $shift
+    EASN(a0, a0, a1, $shift)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $load a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
diff --git a/vm/mterp/mips/OP_AGET_BOOLEAN.S b/vm/mterp/mips/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..d38c466
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lbu", "shift":"0" }
diff --git a/vm/mterp/mips/OP_AGET_BYTE.S b/vm/mterp/mips/OP_AGET_BYTE.S
new file mode 100644
index 0000000..2c0b0be
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lb", "shift":"0" }
diff --git a/vm/mterp/mips/OP_AGET_CHAR.S b/vm/mterp/mips/OP_AGET_CHAR.S
new file mode 100644
index 0000000..9146b97
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lhu", "shift":"1" }
diff --git a/vm/mterp/mips/OP_AGET_OBJECT.S b/vm/mterp/mips/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..16d500d
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S"
diff --git a/vm/mterp/mips/OP_AGET_SHORT.S b/vm/mterp/mips/OP_AGET_SHORT.S
new file mode 100644
index 0000000..ba4c939
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lh", "shift":"1" }
diff --git a/vm/mterp/mips/OP_AGET_WIDE.S b/vm/mterp/mips/OP_AGET_WIDE.S
new file mode 100644
index 0000000..896ea4f
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_WIDE.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64_off(a2, a3, a0, offArrayObject_contents)
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a2, a3, rOBJ)                  #  vAA/vAA+1 <- a2/a3
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_AND_INT.S b/vm/mterp/mips/OP_AND_INT.S
new file mode 100644
index 0000000..721129b
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_INT_2ADDR.S b/vm/mterp/mips/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..4563705
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_INT_LIT16.S b/vm/mterp/mips/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..81c0a04
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_INT_LIT8.S b/vm/mterp/mips/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..61c1c9d
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_LONG.S b/vm/mterp/mips/OP_AND_LONG.S
new file mode 100644
index 0000000..8249617
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_AND_LONG_2ADDR.S b/vm/mterp/mips/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..f9bf88f
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide2addr.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_APUT.S b/vm/mterp/mips/OP_APUT.S
new file mode 100644
index 0000000..7839b69
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT.S
@@ -0,0 +1,27 @@
+%default { "store":"sw", "shift":"2" }
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if $shift
+    EASN(a0, a0, a1, $shift)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    $store a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_APUT_BOOLEAN.S b/vm/mterp/mips/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..eeb9747
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sb", "shift":"0" }
diff --git a/vm/mterp/mips/OP_APUT_BYTE.S b/vm/mterp/mips/OP_APUT_BYTE.S
new file mode 100644
index 0000000..eeb9747
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sb", "shift":"0" }
diff --git a/vm/mterp/mips/OP_APUT_CHAR.S b/vm/mterp/mips/OP_APUT_CHAR.S
new file mode 100644
index 0000000..4c57fb1
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sh", "shift":"1" }
diff --git a/vm/mterp/mips/OP_APUT_OBJECT.S b/vm/mterp/mips/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..1d5b06e
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_OBJECT.S
@@ -0,0 +1,50 @@
+%verify "executed"
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t1)                            #  t1 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(rINST, a2)                    #  rINST <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    GET_VREG(rBIX, t1)                     #  rBIX <- vAA
+    # null array object?
+    beqz      rINST, common_errNullObject  #  yes, bail
+
+    LOAD_base_offArrayObject_length(a3, rINST) #  a3 <- arrayObj->length
+    EAS2(rOBJ, rINST, a1)                  #  rOBJ <- arrayObj + index*width
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  rBIX = vAA (obj)
+     *  rOBJ = offset into array (vBB + vCC * width)
+     */
+    bnez      rBIX, .L${opcode}_checks     #  yes, skip type checks
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sw        rBIX, offArrayObject_contents(rOBJ) #  vBB[vCC] <- vAA
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+%break
+.L${opcode}_checks:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    JAL(dvmCanPutArrayElement)             #  test object type vs. array type
+    beqz      v0, .L${opcode}_throw        #  okay ?
+    lw        a2, offThread_cardTable(rSELF)
+    srl       t1, rINST, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)
+    b         .L${opcode}_finish           #  yes, skip type checks
+.L${opcode}_throw:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    EXPORT_PC()
+    JAL(dvmThrowArrayStoreExceptionIncompatibleElement)
+    b         common_exceptionThrown
diff --git a/vm/mterp/mips/OP_APUT_SHORT.S b/vm/mterp/mips/OP_APUT_SHORT.S
new file mode 100644
index 0000000..4c57fb1
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sh", "shift":"1" }
diff --git a/vm/mterp/mips/OP_APUT_WIDE.S b/vm/mterp/mips/OP_APUT_WIDE.S
new file mode 100644
index 0000000..0046cd5
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_WIDE.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t0 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a2, a3, rOBJ)                   #  a2/a3 <- vAA/vAA+1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64_off(a2, a3, a0, offArrayObject_contents) #  a2/a3 <- vBB[vCC]
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_ARRAY_LENGTH.S b/vm/mterp/mips/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..9416011
--- /dev/null
+++ b/vm/mterp/mips/OP_ARRAY_LENGTH.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+    GET_OPB(a1)                            #  a1 <- B
+    GET_OPA4(a2)                           #  a2 <- A+
+    GET_VREG(a0, a1)                       #  a0 <- vB (object ref)
+    # is object null?
+    beqz      a0, common_errNullObject     #  yup, fail
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- array length
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a3, a2, t0)              #  vA <- length
+
diff --git a/vm/mterp/mips/OP_BREAKPOINT.S b/vm/mterp/mips/OP_BREAKPOINT.S
new file mode 100644
index 0000000..3624810
--- /dev/null
+++ b/vm/mterp/mips/OP_BREAKPOINT.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    move    a0, rPC
+    JAL(dvmGetOriginalOpcode)           # (rPC)
+    FETCH(rINST, 0)                     # reload OP_BREAKPOINT + rest of inst
+    lw      a1, offThread_mainHandlerTable(rSELF)
+    and     rINST, 0xff00
+    or      rINST, rINST, a0
+    GOTO_OPCODE_BASE(a1, a0)
diff --git a/vm/mterp/mips/OP_CHECK_CAST.S b/vm/mterp/mips/OP_CHECK_CAST.S
new file mode 100644
index 0000000..f29a51f
--- /dev/null
+++ b/vm/mterp/mips/OP_CHECK_CAST.S
@@ -0,0 +1,71 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    # check-cast vAA, class                /* BBBB */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- object
+    LOAD_rSELF_methodClassDex(a0)          #  a0 <- pDvmDex
+    LOAD_base_offDvmDex_pResClasses(a0, a0) #  a0 <- pDvmDex->pResClasses
+    # is object null?
+    beqz      rOBJ, .L${opcode}_okay       #  null obj, cast always succeeds
+    LOAD_eas2(a1, a0, a2)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .L${opcode}_resolve      #  not resolved, do it now
+.L${opcode}_resolved:
+    # same class (trivial success)?
+    bne       a0, a1, .L${opcode}_fullcheck #  no, do full check
+.L${opcode}_okay:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0 holds obj->clazz
+     *  a1 holds class resolved from BBBB
+     *  rOBJ holds object
+     */
+.L${opcode}_fullcheck:
+    move      rBIX,a1                      #  avoid ClassObject getting clobbered
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    # failed?
+    bnez      v0, .L${opcode}_okay         #  no, success
+    b         .L${opcode}_castfailure
+%break
+
+.L${opcode}_castfailure:
+    # A cast has failed. We need to throw a ClassCastException with the
+    # class of the object that failed to be cast.
+    EXPORT_PC()                            #  about to throw
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    move      a1,rBIX                      #  r1<- desired class
+    JAL(dvmThrowClassCastException)
+    b         common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a2   holds BBBB
+     *  rOBJ holds object
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      a1, a2                       #  a1 <- BBBB
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    b         .L${opcode}_resolved         #  pick up where we left off
diff --git a/vm/mterp/mips/OP_CMPG_DOUBLE.S b/vm/mterp/mips/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..8e740e3
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_CMPL_DOUBLE.S" { "naninst":"li rTEMP, 1" }
diff --git a/vm/mterp/mips/OP_CMPG_FLOAT.S b/vm/mterp/mips/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..2c4e97b
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_CMPL_FLOAT.S" { "naninst":"li rTEMP, 1" }
diff --git a/vm/mterp/mips/OP_CMPL_DOUBLE.S b/vm/mterp/mips/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..2c824b3
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPL_DOUBLE.S
@@ -0,0 +1,70 @@
+%default { "naninst":"li rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       rOBJ, a0, 255                #  s0 <- BB
+    srl       rBIX, a0, 8                  #  t0 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s0 <- &fp[BB]
+    EAS2(rBIX, rFP, rBIX)                  #  t0 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__eqdf2)                           #  cmp <=: C clear if <, Z set if eq
+    li        rTEMP, 0
+    beqz      v0, ${opcode}_finish
+
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__ltdf2)
+    li        rTEMP, -1
+    bltz      v0, ${opcode}_finish
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    b         ${opcode}_continue
+#else
+    LOAD64_F(ft0, ft0f, rOBJ)
+    LOAD64_F(ft1, ft1f, rBIX)
+    c.olt.d   fcc0, ft0, ft1
+    li        rTEMP, -1
+    bc1t      fcc0, ${opcode}_finish
+    c.olt.d   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, ${opcode}_finish
+    c.eq.d    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, ${opcode}_finish
+    b         ${opcode}_nan
+#endif
+%break
+
+${opcode}_nan:
+    $naninst
+    b         ${opcode}_finish
+
+#ifdef SOFT_FLOAT
+${opcode}_continue:
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__gtdf2)                           #  fallthru
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    blez      v0, ${opcode}_nan            #  fall thru for finish
+#endif
+
+${opcode}_finish:
+    GET_OPA(rOBJ)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(rTEMP, rOBJ, t0)         #  vAA <- rTEMP
diff --git a/vm/mterp/mips/OP_CMPL_FLOAT.S b/vm/mterp/mips/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..01db920
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPL_FLOAT.S
@@ -0,0 +1,82 @@
+%default { "naninst":"li rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8
+#ifdef SOFT_FLOAT
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- vBB
+    GET_VREG(rBIX, a3)                     #  rBIX <- vCC
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__eqsf2)                           #  a0 <- (vBB == vCC)
+    li        rTEMP, 0                     # set rTEMP to 0
+    beqz      v0, ${opcode}_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__ltsf2)                           #  a0 <- (vBB < vCC)
+    li        rTEMP, -1
+    bltz      v0, ${opcode}_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    b         ${opcode}_continue
+#else
+    GET_VREG_F(ft0, a2)
+    GET_VREG_F(ft1, a3)
+    c.olt.s   fcc0, ft0, ft1               # Is ft0 < ft1
+    li        rTEMP, -1
+    bc1t      fcc0, ${opcode}_finish
+    c.olt.s   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, ${opcode}_finish
+    c.eq.s    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, ${opcode}_finish
+    b         ${opcode}_nan
+
+#endif
+
+%break
+
+${opcode}_nan:
+    $naninst
+    b         ${opcode}_finish
+
+#ifdef SOFT_FLOAT
+${opcode}_continue:
+    JAL(__gtsf2)                           #  v0 <- (vBB > vCC)
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    bgtz      v0, ${opcode}_finish
+    b         ${opcode}_nan
+#endif
+
+${opcode}_finish:
+    GET_OPA(t0)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rTEMP, t0)                    #  vAA <- rTEMP
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)
diff --git a/vm/mterp/mips/OP_CMP_LONG.S b/vm/mterp/mips/OP_CMP_LONG.S
new file mode 100644
index 0000000..fcdfce7
--- /dev/null
+++ b/vm/mterp/mips/OP_CMP_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;	# (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;	# (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, a3)                     #  a2/a3 <- vCC/vCC+1
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    slt       t0, a1, a3                   #  compare hi
+    sgt       t1, a1, a3
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0)
+    bnez      v0, .L${opcode}_finish
+    # at this point x.hi==y.hi
+    sltu      t0, a0, a2                   #  compare lo
+    sgtu      t1, a0, a2
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0) for [< > =]
+
+.L${opcode}_finish:
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST.S b/vm/mterp/mips/OP_CONST.S
new file mode 100644
index 0000000..309b52a
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    # const vAA,                           /* +BBBBbbbb */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a1, a1, 16
+    or        a0, a1, a0                   #  a0 <- BBBBbbbb
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
diff --git a/vm/mterp/mips/OP_CONST_16.S b/vm/mterp/mips/OP_CONST_16.S
new file mode 100644
index 0000000..69732f4
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    # const/16 vAA,                        /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
diff --git a/vm/mterp/mips/OP_CONST_4.S b/vm/mterp/mips/OP_CONST_4.S
new file mode 100644
index 0000000..833e373
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    # const/4 vA,                          /* +B */
+    sll       a1, rINST, 16                #  a1 <- Bxxx0000
+    GET_OPA(a0)                            #  a0 <- A+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sra       a1, a1, 28                   #  a1 <- sssssssB (sign-extended)
+    and       a0, a0, 15
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    SET_VREG_GOTO(a1, a0, t0)              #  fp[A] <- a1
+
diff --git a/vm/mterp/mips/OP_CONST_CLASS.S b/vm/mterp/mips/OP_CONST_CLASS.S
new file mode 100644
index 0000000..f63d7c3
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_CLASS.S
@@ -0,0 +1,31 @@
+%verify "executed"
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+    # const/class vAA, Class               /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- dvmDex->pResClasses
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResClasses[BBBB]
+
+    bnez      v0, .L${opcode}_resolve      #  v0!=0 => resolved-ok
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  a1: BBBB (Class ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- Class reference
+    # failed==0?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.L${opcode}_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
diff --git a/vm/mterp/mips/OP_CONST_HIGH16.S b/vm/mterp/mips/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..04c6d5d
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_HIGH16.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    # const/high16 vAA,                    /* +BBBB0000 */
+    FETCH(a0, 1)                           #  a0 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a0, a0, 16                   #  a0 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
diff --git a/vm/mterp/mips/OP_CONST_STRING.S b/vm/mterp/mips/OP_CONST_STRING.S
new file mode 100644
index 0000000..f59b1d6
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_STRING.S
@@ -0,0 +1,33 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    # const/string vAA, String             /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    # not yet resolved?
+    bnez      v0, .L${opcode}_resolve
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1:   BBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.L${opcode}_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
+
+
+
diff --git a/vm/mterp/mips/OP_CONST_STRING_JUMBO.S b/vm/mterp/mips/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..0c3d0bd
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    # const/string vAA, String             /* BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    sll       a1, a1, 16
+    or        a1, a1, a0                   #  a1 <- BBBBbbbb
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    bnez      v0, .L${opcode}_resolve
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1: BBBBBBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.L${opcode}_resolve:
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t1)            #  vAA <- v0
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE.S b/vm/mterp/mips/OP_CONST_WIDE.S
new file mode 100644
index 0000000..ba1c462
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    # const-wide vAA,                      /* +HHHHhhhhBBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (low middle)
+    FETCH(a2, 3)                           #  a2 <- hhhh (high middle)
+    sll       a1, 16 #
+    or        a0, a1                       #  a0 <- BBBBbbbb (low word)
+    FETCH(a3, 4)                           #  a3 <- HHHH (high)
+    GET_OPA(t1)                            #  t1 <- AA
+    sll       a3, 16
+    or        a1, a3, a2                   #  a1 <- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)                  #  advance rPC, load rINST
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, t1)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE_16.S b/vm/mterp/mips/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..d431529
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE_16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    # const-wide/16 vAA,                   /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE_32.S b/vm/mterp/mips/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..9cb9a3f
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE_32.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    # const-wide/32 vAA,                   /* +BBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- 0000bbbb (low)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_S(a2, 2)                         #  a2 <- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a2, a2, 16
+    or        a0, a0, a2                   #  a0 <- BBBBbbbb
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE_HIGH16.S b/vm/mterp/mips/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..c56cd26
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    # const-wide/high16 vAA,               /* +BBBB000000000000 */
+    FETCH(a1, 1)                           #  a1 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    li        a0, 0                        #  a0 <- 00000000
+    sll       a1, 16                       #  a1 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_DIV_DOUBLE.S b/vm/mterp/mips/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..a7e0302
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__divdf3)", "instr_f":"div.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..18e28d7
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__divdf3)", "instr_f":"div.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_FLOAT.S b/vm/mterp/mips/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..59bb8d6
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__divsf3)", "instr_f":"div.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/mips/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..a0a546f
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__divsf3)", "instr_f":"div.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_INT.S b/vm/mterp/mips/OP_DIV_INT.S
new file mode 100644
index 0000000..b845475
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_INT_2ADDR.S b/vm/mterp/mips/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..1f13ad8
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_INT_LIT16.S b/vm/mterp/mips/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..d75d210
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_INT_LIT8.S b/vm/mterp/mips/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..384eb0d
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_LONG.S b/vm/mterp/mips/OP_DIV_LONG.S
new file mode 100644
index 0000000..bb39d2a
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_LONG.S
@@ -0,0 +1,6 @@
+%verify "executed"
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide.S" { "arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_DIV_LONG_2ADDR.S b/vm/mterp/mips/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..8e751b6
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,6 @@
+%verify "executed"
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide2addr.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide2addr.S" {"arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/mips/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..f1e04ea
--- /dev/null
+++ b/vm/mterp/mips/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopNarrower.S" {"instr":"JAL(__truncdfsf2)", "instr_f":"cvt.s.d fv0, fa0"}
diff --git a/vm/mterp/mips/OP_DOUBLE_TO_INT.S b/vm/mterp/mips/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..33199c4
--- /dev/null
+++ b/vm/mterp/mips/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,80 @@
+%verify "executed"
+%include "mips/unopNarrower.S" {"instr":"b d2i_doconv", "instr_f":"b d2i_doconv"}
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rTEMP as global to hold arguments (they are not bound to a global var)
+ */
+%break
+
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)                           #  is arg >= maxint?
+
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .L${opcode}_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                           #  is arg <= minint?
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .L${opcode}_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    move      rARG2, rBIX                  #  compare against self
+    move      rARG3, rTEMP
+    JAL(__nedf2)                           #  is arg == self?
+
+    move      t0, v0                       #  zero == no
+    li        v0, 0
+    bnez      t0, .L${opcode}_set_vreg     #  return zero for NaN
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    JAL(__fixdfsi)                         #  convert double to int
+    b         .L${opcode}_set_vreg
+#else
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    l.s       fv0, .LDOUBLE_TO_INT_maxret
+    bc1t      .L${opcode}_set_vreg_f
+
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    l.s       fv0, .LDOUBLE_TO_INT_minret
+    bc1t      .L${opcode}_set_vreg_f
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .L${opcode}_set_vreg_f
+
+    trunc.w.d  fv0, fa0
+    b         .L${opcode}_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword 0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword 0xc1e0000000000000              #  minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word 0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word 0x80000000
diff --git a/vm/mterp/mips/OP_DOUBLE_TO_LONG.S b/vm/mterp/mips/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..153d557
--- /dev/null
+++ b/vm/mterp/mips/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,76 @@
+%verify "executed"
+%include "mips/unflopWide.S" {"instr":"b d2l_doconv", "st_result":"STORE64(rRESULT0, rRESULT1, rOBJ)"}
+%break
+
+d2l_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bgez      t1, .L${opcode}_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    blez      t1, .L${opcode}_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    move      rARG2, rBIX
+    move      rARG3, rTEMP
+    JAL(__nedf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .L${opcode}_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    JAL(__fixdfdi)
+
+#else
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .L${opcode}_set_vreg
+
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .L${opcode}_set_vreg
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .L${opcode}_set_vreg
+    JAL(__fixdfdi)
+#endif
+    b         .L${opcode}_set_vreg
+
+
+.LDOUBLE_TO_LONG_max:
+    .dword 0x43e0000000000000              #  maxlong, as a double (high word)
+.LDOUBLE_TO_LONG_min:
+    .dword 0xc3e0000000000000              #  minlong, as a double (high word)
+.LDOUBLE_TO_LONG_ret_max:
+    .dword 0x7fffffffffffffff
+.LDOUBLE_TO_LONG_ret_min:
+    .dword 0x8000000000000000
diff --git a/vm/mterp/mips/OP_EXECUTE_INLINE.S b/vm/mterp/mips/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..34f23ea
--- /dev/null
+++ b/vm/mterp/mips/OP_EXECUTE_INLINE.S
@@ -0,0 +1,104 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                         #  rBIX <- BBBB
+    EXPORT_PC()                            #  can throw
+    and       a2, kSubModeDebugProfile     #  Any going on?
+    bnez      a2, .L${opcode}_debugmode    #  yes - take slow path
+.L${opcode}_resume:
+    addu      a1, rSELF, offThread_retval  #  a1 <- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.L${opcode}_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    # test boolean result of inline
+    beqz      v0, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+%break
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LW pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.L${opcode}_continue:
+    FETCH(rINST, 2)                        #  rINST <- FEDC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    and       t0, rINST, 0xf000            #  isolate F
+    ESRN(t1, rFP, t0, 10)
+    lw        a3, 0(t1)                    #  a3 <- vF (shift right 12, left 2)
+3:
+    and       t0, rINST, 0x0f00            #  isolate E
+    ESRN(t1, rFP, t0, 6)
+    lw        a2, 0(t1)                    #  a2 <- vE
+2:
+    and       t0, rINST, 0x00f0            #  isolate D
+    ESRN(t1, rFP, t0, 2)
+    lw        a1, 0(t1)                    #  a1 <- vD
+1:
+    and       t0, rINST, 0x000f            #  isolate C
+    EASN(t1, rFP, t0, 2)
+    lw        a0, 0(t1)                    #  a0 <- vC
+0:
+    la        rINST, gDvmInlineOpsTable    #  table of InlineOperation
+    EAS4(t1, rINST, rBIX)                  #  t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                           #  sizeof=16, "func" is first entry
+    # (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.L${opcode}_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .L${opcode}_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.L${opcode}_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rINST, v0                    #  save result of inline
+    move      a0, rOBJ                     #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rINST, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/mips/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..fd964f7
--- /dev/null
+++ b/vm/mterp/mips/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,92 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                       # rBIX<- BBBB
+    EXPORT_PC()                          # can throw
+    and       a2, kSubModeDebugProfile   # Any going on?
+    bnez      a2, .L${opcode}_debugmode  # yes - take slow path
+.L${opcode}_resume:
+    addu      a1, rSELF, offThread_retval # a1<- &self->retval
+    GET_OPA(a0)
+    sw        a1, STACK_OFFSET_ARG04(sp)  # push &self->retval
+    BAL(.L${opcode}_continue)             # make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)     #  restore gp
+    beqz      v0, common_exceptionThrown  # returned false, handle exception
+    FETCH_ADVANCE_INST(3)                 # advance rPC, load rINST
+    GET_INST_OPCODE(t0)                   # extract opcode from rINST
+    GOTO_OPCODE(t0)                       # jump to next instruction
+
+%break
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *  ra = return addr, above  [DO NOT JAL out of here w/o preserving ra]
+     */
+.L${opcode}_continue:
+    FETCH(rOBJ, 2)                       # rOBJ <- CCCC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    add       t0, rOBJ, 3
+    GET_VREG(a3, t0)
+3:
+    add       t0, rOBJ, 2
+    GET_VREG(a2, t0)
+2:
+    add       t0, rOBJ, 1
+    GET_VREG(a1, t0)
+1:
+    GET_VREG(a0, rOBJ)
+0:
+    la        rOBJ, gDvmInlineOpsTable      # table of InlineOperation
+    EAS4(t1, rOBJ, rBIX)                    # t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                            # sizeof=16, "func" is first entry
+    # not reached
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.L${opcode}_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .L${opcode}_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPA(a0)                            #  a0 <- A
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    move      rINST, rOBJ                  #  rINST<- method
+    BAL(.L${opcode}_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rOBJ, v0                     #  save result of inline
+    move      a0, rINST                    #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rOBJ, common_exceptionThrown #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_FILLED_NEW_ARRAY.S b/vm/mterp/mips/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..2cb225d
--- /dev/null
+++ b/vm/mterp/mips/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,120 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, type       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    EXPORT_PC()                            #  need for resolve and alloc
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+    GET_OPA(rOBJ)                          #  rOBJ <- AA or BA
+    # already resolved?
+    bnez      a0, .L${opcode}_continue     #  yes, continue on
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .L${opcode}_continue
+%break
+
+    /*
+     * On entry:
+     *  a0 holds array class
+     *  rOBJ holds AA or BA
+     */
+.L${opcode}_continue:
+    LOAD_base_offClassObject_descriptor(a3, a0) #  a3 <- arrayClass->descriptor
+    li        a2, ALLOC_DONT_TRACK         #  a2 <- alloc flags
+    lbu       rINST, 1(a3)                 #  rINST <- descriptor[1]
+    .if $isrange
+    move      a1, rOBJ                     #  a1 <- AA (length)
+    .else
+    srl       a1, rOBJ, 4                  #  rOBJ <- B (length)
+    .endif
+    seq       t0, rINST, 'I'               #  array of ints?
+    seq       t1, rINST, 'L'               #  array of objects?
+    or        t0, t1
+    seq       t1, rINST, '['               #  array of arrays?
+    or        t0, t1
+    move      rBIX, a1                     #  save length in rBIX
+    beqz      t0, .L${opcode}_notimpl      #  no, not handled yet
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(arClass, length, flags)
+    # null return?
+    beqz      v0, common_exceptionThrown   #  alloc failed, handle exception
+
+    FETCH(a1, 2)                           #  a1 <- FEDC or CCCC
+    sw        v0, offThread_retval(rSELF)  #  retval.l <- new array
+    sw        rINST, (offThread_retval+4)(rSELF) #  retval.h <- type
+    addu      a0, v0, offArrayObject_contents #  a0 <- newArray->contents
+    subu      rBIX, rBIX, 1                #  length--, check for neg
+    FETCH_ADVANCE_INST(3)                  #  advance to next instr, load rINST
+    bltz      rBIX, 2f                     #  was zero, bail
+
+    # copy values from registers into the array
+    # a0=array, a1=CCCC/FEDC, t0=length (from AA or B), rOBJ=AA/BA
+    move      t0, rBIX
+    .if $isrange
+    EAS2(a2, rFP, a1)                      #  a2 <- &fp[CCCC]
+1:
+    lw        a3, 0(a2)                    #  a3 <- *a2++
+    addu      a2, 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, (a0)                     #  *contents++ = vX
+    addu      a0, 4
+    bgez      t0, 1b
+
+    # continue at 2
+    .else
+    slt       t1, t0, 4                    #  length was initially 5?
+    and       a2, rOBJ, 15                 #  a2 <- A
+    bnez      t1, 1f                       #  <= 4 args, branch
+    GET_VREG(a3, a2)                       #  a3 <- vA
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 16(a0)                   #  contents[4] = vA
+1:
+    and       a2, a1, 15                   #  a2 <- F/E/D/C
+    GET_VREG(a3, a2)                       #  a3 <- vF/vE/vD/vC
+    srl       a1, a1, 4                    #  a1 <- next reg in low 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 0(a0)                    #  *contents++ = vX
+    addu      a0, a0, 4
+    bgez      t0, 1b
+    # continue at 2
+    .endif
+
+2:
+    lw        a0, offThread_retval(rSELF)  #  a0 <- object
+    lw        a1, (offThread_retval+4)(rSELF) #  a1 <- type
+    seq       t1, a1, 'I'                  #  Is int array?
+    bnez      t1, 3f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t3, a0, GC_CARD_SHIFT
+    addu      t2, a2, t3
+    sb        a2, (t2)
+3:
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    GOTO_OPCODE(t0)                        #  execute it
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.L${opcode}_notimpl:
+    la        a0, .LstrFilledNewArrayNotImpl
+    JAL(dvmThrowInternalError)
+    b         common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
diff --git a/vm/mterp/mips/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/mips/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..9611796
--- /dev/null
+++ b/vm/mterp/mips/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/mips/OP_FILL_ARRAY_DATA.S b/vm/mterp/mips/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..7a97799
--- /dev/null
+++ b/vm/mterp/mips/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a1, a1, 16                   #  a1 <- BBBBbbbb
+    or        a1, a0, a1                   #  a1 <- BBBBbbbb
+    GET_VREG(a0, a3)                       #  a0 <- vAA (array object)
+    EAS1(a1, rPC, a1)                      #  a1 <- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC()
+    JAL(dvmInterpHandleFillArrayData)      #  fill the array with predefined data
+    # 0 means an exception is thrown
+    beqz      v0, common_exceptionThrown   #  has exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/mips/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..1e2120d
--- /dev/null
+++ b/vm/mterp/mips/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflopWider.S" {"instr":"JAL(__extendsfdf2)", "instr_f":"cvt.d.s fv0, fa0"}
diff --git a/vm/mterp/mips/OP_FLOAT_TO_INT.S b/vm/mterp/mips/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..166d685
--- /dev/null
+++ b/vm/mterp/mips/OP_FLOAT_TO_INT.S
@@ -0,0 +1,63 @@
+%verify "executed"
+%include "mips/unflop.S" {"instr":"b f2i_doconv", "instr_f":"b f2i_doconv"}
+%break
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x4f000000               #  (float)maxint
+    move      rBIX, a0
+    JAL(__gesf2)                           #  is arg >= maxint?
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX                     #  recover arg
+    li        a1, 0xcf000000               #  (float)minint
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .L${opcode}_set_vreg
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        v0, 0                        #  return zero for NaN
+    bnez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfsi)
+    b         .L${opcode}_set_vreg
+#else
+    l.s       fa1, .LFLOAT_TO_INT_max
+    c.ole.s   fcc0, fa1, fa0
+    l.s       fv0, .LFLOAT_TO_INT_ret_max
+    bc1t      .L${opcode}_set_vreg_f
+
+    l.s       fa1, .LFLOAT_TO_INT_min
+    c.ole.s   fcc0, fa0, fa1
+    l.s       fv0, .LFLOAT_TO_INT_ret_min
+    bc1t      .L${opcode}_set_vreg_f
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .L${opcode}_set_vreg_f
+
+    trunc.w.s  fv0, fa0
+    b         .L${opcode}_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+    .word 0x4f000000
+.LFLOAT_TO_INT_min:
+    .word 0xcf000000
+.LFLOAT_TO_INT_ret_max:
+    .word 0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+    .word 0x80000000
+
diff --git a/vm/mterp/mips/OP_FLOAT_TO_LONG.S b/vm/mterp/mips/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..3e76027
--- /dev/null
+++ b/vm/mterp/mips/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,65 @@
+%verify "executed"
+%include "mips/unflopWider.S" {"instr":"b f2l_doconv", "instr_f":"b f2l_doconv", "st_result":"STORE64(rRESULT0, rRESULT1, rOBJ)"}
+%break
+
+f2l_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x5f000000
+    move      rBIX, a0
+    JAL(__gesf2)
+
+    move      t0, v0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bgez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    li        a1, 0xdf000000
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    blez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfdi)
+
+#else
+    l.s       fa1, .LLONG_TO_max
+    c.ole.s   fcc0, fa1, fa0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bc1t      .L${opcode}_set_vreg
+
+    l.s       fa1, .LLONG_TO_min
+    c.ole.s   fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    bc1t      .L${opcode}_set_vreg
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .L${opcode}_set_vreg
+
+    JAL(__fixsfdi)
+#endif
+
+    b         .L${opcode}_set_vreg
+
+.LLONG_TO_max:
+    .word 0x5f000000
+
+.LLONG_TO_min:
+    .word 0xdf000000
diff --git a/vm/mterp/mips/OP_GOTO.S b/vm/mterp/mips/OP_GOTO.S
new file mode 100644
index 0000000..27c20e3
--- /dev/null
+++ b/vm/mterp/mips/OP_GOTO.S
@@ -0,0 +1,23 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    sll       a0, rINST, 16                #  a0 <- AAxx0000
+    sra       a1, a0, 24                   #  a1 <- ssssssAA (sign-extended)
+    addu      a2, a1, a1                   #  a2 <- byte offset
+    /* If backwards branch refresh rBASE */
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) check for trace hotness
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_GOTO_16.S b/vm/mterp/mips/OP_GOTO_16.S
new file mode 100644
index 0000000..22c29da
--- /dev/null
+++ b/vm/mterp/mips/OP_GOTO_16.S
@@ -0,0 +1,21 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(a0, 1)                         #  a0 <- ssssAAAA (sign-extended)
+    addu      a1, a0, a0                   #  a1 <- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) hot trace head?
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_GOTO_32.S b/vm/mterp/mips/OP_GOTO_32.S
new file mode 100644
index 0000000..84598c2
--- /dev/null
+++ b/vm/mterp/mips/OP_GOTO_32.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "forward, backward, self"
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(a0, 1)                           #  a0 <- aaaa (lo)
+    FETCH(a1, 2)                           #  a1 <- AAAA (hi)
+    sll       a1, a1, 16
+    or        a0, a0, a1                   #  a0 <- AAAAaaaa
+    addu      a1, a0, a0                   #  a1 <- byte offset
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    blez      a1, common_testUpdateProfile # (a0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgtz      a0, 2f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+2:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_IF_EQ.S b/vm/mterp/mips/OP_IF_EQ.S
new file mode 100644
index 0000000..183ec1b
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/mips/OP_IF_EQZ.S b/vm/mterp/mips/OP_IF_EQZ.S
new file mode 100644
index 0000000..5587291
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/mips/OP_IF_GE.S b/vm/mterp/mips/OP_IF_GE.S
new file mode 100644
index 0000000..19bc86f
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/mips/OP_IF_GEZ.S b/vm/mterp/mips/OP_IF_GEZ.S
new file mode 100644
index 0000000..5d4fa0f
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/mips/OP_IF_GT.S b/vm/mterp/mips/OP_IF_GT.S
new file mode 100644
index 0000000..8335bd3
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/mips/OP_IF_GTZ.S b/vm/mterp/mips/OP_IF_GTZ.S
new file mode 100644
index 0000000..3c70c35
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/mips/OP_IF_LE.S b/vm/mterp/mips/OP_IF_LE.S
new file mode 100644
index 0000000..c1524f9
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/mips/OP_IF_LEZ.S b/vm/mterp/mips/OP_IF_LEZ.S
new file mode 100644
index 0000000..fa930aa
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/mips/OP_IF_LT.S b/vm/mterp/mips/OP_IF_LT.S
new file mode 100644
index 0000000..fbda8bc
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/mips/OP_IF_LTZ.S b/vm/mterp/mips/OP_IF_LTZ.S
new file mode 100644
index 0000000..e93dd62
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/mips/OP_IF_NE.S b/vm/mterp/mips/OP_IF_NE.S
new file mode 100644
index 0000000..c484ede
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/mips/OP_IF_NEZ.S b/vm/mterp/mips/OP_IF_NEZ.S
new file mode 100644
index 0000000..24cbb6b
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/mips/OP_IGET.S b/vm/mterp/mips/OP_IGET.S
new file mode 100644
index 0000000..ba4fada
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET.S
@@ -0,0 +1,49 @@
+%default { "load":"lw", "barrier":"     # noop", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.L${opcode}_finish:
+    #BAL(common_squeak${sqnum})
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    $load a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+    $barrier                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_BOOLEAN.S b/vm/mterp/mips/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..4f32dbf
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_BYTE.S b/vm/mterp/mips/OP_IGET_BYTE.S
new file mode 100644
index 0000000..f699e87
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_CHAR.S b/vm/mterp/mips/OP_IGET_CHAR.S
new file mode 100644
index 0000000..cb3a03b
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_OBJECT.S b/vm/mterp/mips/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..4f32dbf
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_OBJECT_QUICK.S b/vm/mterp/mips/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..e4f7d00
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET_QUICK.S"
diff --git a/vm/mterp/mips/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..30c6774
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IGET_QUICK.S b/vm/mterp/mips/OP_IGET_QUICK.S
new file mode 100644
index 0000000..4490796
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1 #
+    lw        a0, 0(t0)                    #  a0 <- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_SHORT.S b/vm/mterp/mips/OP_IGET_SHORT.S
new file mode 100644
index 0000000..f699e87
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_VOLATILE.S b/vm/mterp/mips/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..30c6774
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IGET_WIDE.S b/vm/mterp/mips/OP_IGET_WIDE.S
new file mode 100644
index 0000000..2cdf80c
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_WIDE.S
@@ -0,0 +1,49 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Wide 32-bit instance field get.
+     */
+    # iget-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test return code
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    beqz      rOBJ, common_errNullObject   #  object was null
+    GET_OPA4(a2)                           #  a2 <- A+
+    addu      rOBJ, rOBJ, a3               #  form address
+    .if $volatile
+    vLOAD64(a0, a1, rOBJ)                  #  a0/a1 <- obj.field (64-bit align ok)
+    .else
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- obj.field (64-bit align ok)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)                      #  a3 <- &fp[A]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_WIDE_QUICK.S b/vm/mterp/mips/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..f4d8fdb
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    # iget-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1                   #  t0 <- a3 + a1
+    LOAD64(a0, a1, t0)                     #  a0 <- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_WIDE_VOLATILE.S b/vm/mterp/mips/OP_IGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..1804fb1
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_INSTANCE_OF.S b/vm/mterp/mips/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..f296d44
--- /dev/null
+++ b/vm/mterp/mips/OP_INSTANCE_OF.S
@@ -0,0 +1,82 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    # instance-of vA, vB, class            /* CCCC */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- pDvmDex
+    # is object null?
+    beqz      a0, .L${opcode}_store        #  null obj, not an instance, store a0
+    FETCH(a3, 1)                           #  a3 <- CCCC
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- pDvmDex->pResClasses
+    LOAD_eas2(a1, a2, a3)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .L${opcode}_resolve      #  not resolved, do it now
+.L${opcode}_resolved:                   #  a0=obj->clazz, a1=resolved class
+    # same class (trivial success)?
+    beq       a0, a1, .L${opcode}_trivial  #  yes, trivial finish
+    b         .L${opcode}_fullcheck        #  no, do full check
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  rOBJ holds A
+     */
+.L${opcode}_trivial:
+    li        a0, 1                        #  indicate success
+    # fall thru
+    /*
+     * a0   holds boolean result
+     * rOBJ holds A
+     */
+.L${opcode}_store:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, rOBJ)                     #  vA <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0   holds obj->clazz
+     *  a1   holds class resolved from BBBB
+     *  rOBJ holds A
+     */
+.L${opcode}_fullcheck:
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    move      a0, v0                       #  fall through to ${opcode}_store
+    b         .L${opcode}_store
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a3   holds BBBB
+     *  rOBJ holds A
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    move      a1, a3                       #  a1 <- BBBB
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    b         .L${opcode}_resolved         #  pick up where we left off
+
diff --git a/vm/mterp/mips/OP_INT_TO_BYTE.S b/vm/mterp/mips/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..e9edb97
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"preinstr":"sll a0, a0, 24", "instr":"sra a0, a0, 24"}
diff --git a/vm/mterp/mips/OP_INT_TO_CHAR.S b/vm/mterp/mips/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..5da74da
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"preinstr":"", "instr":"and a0, 0xffff"}
diff --git a/vm/mterp/mips/OP_INT_TO_DOUBLE.S b/vm/mterp/mips/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..5ee4813
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflopWider.S" {"instr":"JAL(__floatsidf)", "instr_f":"cvt.d.w fv0, fa0"}
diff --git a/vm/mterp/mips/OP_INT_TO_FLOAT.S b/vm/mterp/mips/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..9cf7c48
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflop.S" {"instr":"JAL(__floatsisf)", "instr_f":"cvt.s.w fv0, fa0"}
diff --git a/vm/mterp/mips/OP_INT_TO_LONG.S b/vm/mterp/mips/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..5691ea5
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopWider.S" {"instr":"sra a1, a0, 31"}
diff --git a/vm/mterp/mips/OP_INT_TO_SHORT.S b/vm/mterp/mips/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..d1fc349
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"preinstr":"sll a0, 16", "instr":"sra a0, 16"}
diff --git a/vm/mterp/mips/OP_INVOKE_DIRECT.S b/vm/mterp/mips/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..9bbf334
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_DIRECT.S
@@ -0,0 +1,42 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+    .if (!$isrange)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    # already resolved?
+    bnez      a0, 1f                       #  resolved, call the function
+
+    lw        a3, offThread_method(rSELF)  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_DIRECT            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+
+1:
+    bnez      rOBJ, common_invokeMethod${routine} #  a0=method, rOBJ="this"
+    b         common_errNullObject         #  yes, throw exception
+
+
+
diff --git a/vm/mterp/mips/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/mips/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..ef88011
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_INTERFACE.S b/vm/mterp/mips/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..0924093
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,28 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(a2, 2)                           #  a2 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!$isrange)
+    and       a2, 15                       #  a2 <- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- first arg ("this")
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- methodClassDex
+    LOAD_rSELF_method(a2)                  #  a2 <- method
+    # null obj?
+    beqz      rOBJ, common_errNullObject   #  yes, fail
+    LOAD_base_offObject_clazz(a0, rOBJ)      #  a0 <- thisPtr->clazz
+    JAL(dvmFindInterfaceMethodInCache)     #  v0 <- call(class, ref, method, dex)
+    move      a0, v0
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         common_invokeMethod${routine} #  (a0=method, rOBJ="this")
diff --git a/vm/mterp/mips/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/mips/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..6257c8a
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_OBJECT_INIT_RANGE.S b/vm/mterp/mips/OP_INVOKE_OBJECT_INIT_RANGE.S
new file mode 100644
index 0000000..705be9f
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_OBJECT_INIT_RANGE.S
@@ -0,0 +1,44 @@
+%default { "cccc":"2" }
+%verify "executed"
+%verify "finalizable class"
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(a1, ${cccc})                  # a1<- CCCC
+    GET_VREG(a0, a1)                    # a0<- "this" ptr
+    # check for NULL
+    beqz    a0, common_errNullObject    # export PC and throw NPE
+    LOAD_base_offObject_clazz(a1, a0)   # a1<- obj->clazz
+    LOAD_base_offClassObject_accessFlags(a2, a1) # a2<- clazz->accessFlags
+    and     a2, CLASS_ISFINALIZABLE     # is this class finalizable?
+    beqz    a2, .L${opcode}_finish      # no, go
+
+.L${opcode}_setFinal:
+    EXPORT_PC()                         # can throw
+    JAL(dvmSetFinalizable)              # call dvmSetFinalizable(obj)
+    LOAD_offThread_exception(a0, rSELF)	# a0<- self->exception
+    # exception pending?
+    bnez    a0, common_exceptionThrown  # yes, handle it
+
+.L${opcode}_finish:
+    lhu     a1, offThread_subMode(rSELF)
+    and     a1, kSubModeDebuggerActive  # debugger active?
+    bnez    a1, .L${opcode}_debugger    # Yes - skip optimization
+    FETCH_ADVANCE_INST(${cccc}+1)       # advance to next instr, load rINST
+    GET_INST_OPCODE(t0)                 # t0<- opcode from rINST
+    GOTO_OPCODE(t0)                     # execute it
+
+%break
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.L${opcode}_debugger:
+    lw      a1, offThread_mainHandlerTable(rSELF)
+    li      t0, OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(a1, t0)            # execute it
diff --git a/vm/mterp/mips/OP_INVOKE_STATIC.S b/vm/mterp/mips/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..ba2d7cc
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_STATIC.S
@@ -0,0 +1,54 @@
+%default { "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    li      rOBJ, 0                        #  null "this" in delay slot
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX<- &resolved_metherToCall
+#endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, common_invokeMethod${routine} #  yes, continue on
+    b         .L${opcode}_resolve
+%break
+
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_STATIC            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * rBIX: &resolved_methodToCall
+     */
+    lhu       a2, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  null, handle exception
+    and       a2, kSubModeJitTraceBuild    #  trace under construction?
+    beqz      a2, common_invokeMethod${routine} #  no, (a0=method, rOBJ="this")
+    lw        a1, 0(rBIX)                  #  reload resolved method
+    # finished resloving?
+    bnez      a1, common_invokeMethod${routine} #  yes, (a0=method, rOBJ="this")
+    move      rBIX, a0                     #  preserve method
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    move      a0, rBIX
+    b         common_invokeMethod${routine} #  whew, finally!
+#else
+    # got null?
+    bnez      v0, common_invokeMethod${routine} #  (a0=method, rOBJ="this")
+    b         common_exceptionThrown       #  yes, handle exception
+#endif
diff --git a/vm/mterp/mips/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/mips/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..9b45216
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_STATIC.S" { "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER.S b/vm/mterp/mips/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..6b44380
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER.S
@@ -0,0 +1,60 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    .if (!$isrange)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this" ptr
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    # null "this"?
+    LOAD_rSELF_method(t1)                  #  t1 <- current method
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    # cmp a0, 0; already resolved?
+    LOAD_base_offMethod_clazz(rBIX, t1)    #  rBIX <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    bnez      a0, .L${opcode}_continue     #  resolved, continue on
+
+    move      a0, rBIX                     #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .L${opcode}_continue
+%break
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX = method->clazz
+     */
+.L${opcode}_continue:
+    LOAD_base_offClassObject_super(a1, rBIX) #  a1 <- method->clazz->super
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    LOAD_base_offClassObject_vtableCount(a3, a1) #  a3 <- super->vtableCount
+    EXPORT_PC()                            #  must export for invoke
+    # compare (methodIndex, vtableCount)
+    bgeu      a2, a3, .L${opcode}_nsm      #  method not present in superclass
+    LOAD_base_offClassObject_vtable(a1, a1) #  a1 <- ...clazz->super->vtable
+    LOAD_eas2(a0, a1, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethod${routine} #  continue on
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  a0 = resolved base method
+     */
+.L${opcode}_nsm:
+    LOAD_base_offMethod_name(a1, a0)       #  a1 <- method name
+    b         common_errNoSuchMethod
+
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..eb5465a
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,26 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    .if (!$isrange)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offMethod_clazz(a2, a2)      #  a2 <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    LOAD_base_offClassObject_super(a2, a2) #  a2 <- method->clazz->super
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this"
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- ...clazz->super->vtable
+    # is "this" null ?
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- super->vtable[BBBB]
+    beqz      rOBJ, common_errNullObject   #  "this" is null, throw exception
+    b         common_invokeMethod${routine} #  (a0=method, rOBJ="this")
+
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..ade7bba
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/mips/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..7821d31
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..9f6d2c3
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,48 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    .if (!$isrange)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, .L${opcode}_continue     #  yes, continue on
+
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    bnez      v0, .L${opcode}_continue     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+%break
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX= C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.L${opcode}_continue:
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a3, rOBJ)    #  a3 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a3, a3) #  a3 <- thisPtr->clazz->vtable
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethod${routine} #  (a0=method, rOBJ="this")
+
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..1952b70
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(a3, 2)                           #  a3 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!$isrange)
+    and       a3, a3, 15                   #  a3 <- C (or stays CCCC)
+    .endif
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- vC ("this" ptr)
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a2, rOBJ)    #  a2 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- thisPtr->clazz->vtable
+    EXPORT_PC()                            #  invoke must export
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- vtable[BBBB]
+    b         common_invokeMethod${routine} #  (a0=method, r9="this")
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..8048895
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..5f86b4b
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_IPUT.S b/vm/mterp/mips/OP_IPUT.S
new file mode 100644
index 0000000..626cc92
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT.S
@@ -0,0 +1,50 @@
+%default { "store":"sw","postbarrier":"    #  noop", "prebarrier":"    #  noop", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish       #  yes, finish up
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    #BAL(common_squeak${sqnum})
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+    $prebarrier                            #  releasing store
+    $store a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+    $postbarrier
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_BOOLEAN.S b/vm/mterp/mips/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_BYTE.S b/vm/mterp/mips/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_CHAR.S b/vm/mterp/mips/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_OBJECT.S b/vm/mterp/mips/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..0382fa8
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_OBJECT.S
@@ -0,0 +1,56 @@
+%default { "store":"sw", "postbarrier":"    #  noop", "prebarrier":"    #  noop", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish       #  yes, finish up
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    #BAL(common_squeak${sqnum})
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      t2, rOBJ, a3                 #  form address
+    $prebarrier                            #  releasing store
+    $store a0, (t2)                        #  obj.field (32 bits) <- a0
+    $postbarrier
+    beqz      a0, 1f                       #  stored a null reference?
+    srl       t1, rOBJ, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)                     #  mark card if not
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/mips/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..eb0afb4
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,21 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    beqz      a0, 1f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t1, a3, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, 0(t2)
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..8320a7d
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT_OBJECT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IPUT_QUICK.S b/vm/mterp/mips/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..8976265
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick, iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_SHORT.S b/vm/mterp/mips/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_VOLATILE.S b/vm/mterp/mips/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..4cb365f
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IPUT_WIDE.S b/vm/mterp/mips/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..b8d9690
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_WIDE.S
@@ -0,0 +1,48 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    # iput-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish       #  yes, finish up
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    GET_OPA4(a2)                           #  a2 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- fp[A]
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    addu      a2, rOBJ, a3                 #  form address
+    .if $volatile
+    JAL(dvmQuasiAtomicSwap64Sync)          # stores r0/r1 into addr r2
+#    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .else
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_WIDE_QUICK.S b/vm/mterp/mips/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..f86c403
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    # iput-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPA4(a0)                           #  a0 <- A(+)
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a2, a1)                       #  a2 <- fp[B], the object pointer
+    EAS2(a3, rFP, a0)                      #  a3 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[A]
+    # check object for null
+    beqz      a2, common_errNullObject     #  object was null
+    FETCH(a3, 1)                           #  a3 <- field byte offset
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      a2, a2, a3                   #  obj.field (64 bits, aligned) <- a0/a1
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_WIDE_VOLATILE.S b/vm/mterp/mips/OP_IPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..784be66
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_LONG_TO_DOUBLE.S b/vm/mterp/mips/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..fad9ec0
--- /dev/null
+++ b/vm/mterp/mips/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflopWide.S" {"instr":"JAL(__floatdidf)", "ld_arg":"LOAD64(rARG0, rARG1, a3)"}
diff --git a/vm/mterp/mips/OP_LONG_TO_FLOAT.S b/vm/mterp/mips/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..86a143a
--- /dev/null
+++ b/vm/mterp/mips/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopNarrower.S" {"instr":"JAL(__floatdisf)", "instr_f":"JAL(__floatdisf)", "load":"LOAD64(rARG0, rARG1, a3)"}
diff --git a/vm/mterp/mips/OP_LONG_TO_INT.S b/vm/mterp/mips/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..fe8f865
--- /dev/null
+++ b/vm/mterp/mips/OP_LONG_TO_INT.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef HAVE_BIG_ENDIAN
+    addu      a1, a1, 1
+#endif
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
diff --git a/vm/mterp/mips/OP_MONITOR_ENTER.S b/vm/mterp/mips/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..1f5541e
--- /dev/null
+++ b/vm/mterp/mips/OP_MONITOR_ENTER.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    move      a0, rSELF                    #  a0 <- self
+    EXPORT_PC()                            #  export PC so we can grab stack trace
+    # null object?
+    beqz      a1, common_errNullObject     #  null object, throw an exception
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    JAL(dvmLockObject)                     #  call(self, obj)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MONITOR_EXIT.S b/vm/mterp/mips/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..fc671cb
--- /dev/null
+++ b/vm/mterp/mips/OP_MONITOR_EXIT.S
@@ -0,0 +1,26 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EXPORT_PC()                            #  before fetch: export the PC
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    # null object?
+    beqz      a1, 1f
+    move      a0, rSELF                    #  a0 <- self
+    JAL(dvmUnlockObject)                   #  v0 <- success for unlock(self, obj)
+    # failed?
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    beqz      v0, common_exceptionThrown   #  yes, exception is pending
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    b         common_errNullObject
diff --git a/vm/mterp/mips/OP_MOVE.S b/vm/mterp/mips/OP_MOVE.S
new file mode 100644
index 0000000..dbf7ea4
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
diff --git a/vm/mterp/mips/OP_MOVE_16.S b/vm/mterp/mips/OP_MOVE_16.S
new file mode 100644
index 0000000..8410b93
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(a1, 2)                           #  a1 <- BBBB
+    FETCH(a0, 1)                           #  a0 <- AAAA
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AAAA] <- a2 and jump
+
diff --git a/vm/mterp/mips/OP_MOVE_EXCEPTION.S b/vm/mterp/mips/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..1040155
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* move-exception vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    LOAD_offThread_exception(a3, rSELF)    #  a3 <- dvmGetException bypass
+    li        a1, 0                        #  a1 <- 0
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    SET_VREG(a3, a2)                       #  fp[AA] <- exception obj
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE_offThread_exception(a1, rSELF)   #  dvmClearException bypass
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_FROM16.S b/vm/mterp/mips/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..d018140
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    GET_OPA(a0)                            #  a0 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AA] <- a2
+
diff --git a/vm/mterp/mips/OP_MOVE_OBJECT.S b/vm/mterp/mips/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..7150ed5
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE.S"
diff --git a/vm/mterp/mips/OP_MOVE_OBJECT_16.S b/vm/mterp/mips/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..c3dfae0
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE_16.S"
diff --git a/vm/mterp/mips/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/mips/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..1ec1ae9
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/mips/OP_MOVE_RESULT.S b/vm/mterp/mips/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..05f40fa
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_RESULT.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_rSELF_retval(a0)                  #  a0 <- self->retval.i
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a2, t0)              #  fp[AA] <- a0
+
diff --git a/vm/mterp/mips/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/mips/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..74aa091
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/mips/OP_MOVE_RESULT_WIDE.S b/vm/mterp/mips/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..8a548d1
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* move-result-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- retval.j
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_WIDE.S b/vm/mterp/mips/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..7470061
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[B]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_WIDE_16.S b/vm/mterp/mips/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..bdd9f26
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_WIDE_16.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 2)                           #  a3 <- BBBB
+    FETCH(a2, 1)                           #  a2 <- AAAA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AAAA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[AAAA] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_WIDE_FROM16.S b/vm/mterp/mips/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..44251f4
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 1)                           #  a3 <- BBBB
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MUL_DOUBLE.S b/vm/mterp/mips/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..565ca57
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__muldf3)", "instr_f":"mul.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..8d1dac1
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__muldf3)", "instr_f":"mul.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_FLOAT.S b/vm/mterp/mips/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..af9bb3b
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__mulsf3)", "instr_f":"mul.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/mips/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..726e8a4
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__mulsf3)", "instr_f":"mul.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_INT.S b/vm/mterp/mips/OP_MUL_INT.S
new file mode 100644
index 0000000..d9d6d2a
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_INT_2ADDR.S b/vm/mterp/mips/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..bbf4d77
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_INT_LIT16.S b/vm/mterp/mips/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..654e76d
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_INT_LIT8.S b/vm/mterp/mips/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..c0278ae
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_LONG.S b/vm/mterp/mips/OP_MUL_LONG.S
new file mode 100644
index 0000000..c16a230
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_LONG.S
@@ -0,0 +1,41 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       t0, a0, 255                  #  a2 <- BB
+    srl       t1, a0, 8                    #  a3 <- CC
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[BB]
+    LOAD64(a0, a1, t0)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t1, rFP, t1)                      #  t0 <- &fp[CC]
+    LOAD64(a2, a3, t1)                     #  a2/a3 <- vCC/vCC+1
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t0, a2, a1                   #  t0= a2a1
+    addu      v1, v1, t1                   #  v1+= hi(a2a0)
+    addu      v1, v1, t0                   #  v1= a3a0 + a2a1;
+
+    GET_OPA(a0)                            #  a0 <- AA
+    EAS2(a0, rFP, a0)                      #  a0 <- &fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    b         .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, a0)                    #  vAA::vAA+1 <- v0(low) :: v1(high)
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MUL_LONG_2ADDR.S b/vm/mterp/mips/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..85de7be
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * See comments in OP_MUL_LONG.S
+     */
+    /* mul-long/2addr vA, vB */
+    GET_OPA4(t0)                           #  t0 <- A+
+
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[A]
+    LOAD64(a0, a1, t0)                     #  vAA.low / high
+
+    GET_OPB(t1)                            #  t1 <- B
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[B]
+    LOAD64(a2, a3, t1)                     #  vBB.low / high
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t2, a2, a1                   #  t2= a2a1
+    addu      v1, v1, t1                   #  v1= a3a0 + hi(a2a0)
+    addu      v1, v1, t2                   #  v1= v1 + a2a1;
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    # vAA <- v0 (low)
+    STORE64(v0, v1, t0)                    #  vAA+1 <- v1 (high)
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_NEG_DOUBLE.S b/vm/mterp/mips/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..5707c65
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopWide.S" {"instr":"addu a1, a1, 0x80000000"}
diff --git a/vm/mterp/mips/OP_NEG_FLOAT.S b/vm/mterp/mips/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..7e25e55
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"instr":"addu a0, a0, 0x80000000"}
diff --git a/vm/mterp/mips/OP_NEG_INT.S b/vm/mterp/mips/OP_NEG_INT.S
new file mode 100644
index 0000000..da87a6a
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"instr":"negu a0, a0"}
diff --git a/vm/mterp/mips/OP_NEG_LONG.S b/vm/mterp/mips/OP_NEG_LONG.S
new file mode 100644
index 0000000..a562987
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_LONG.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%include "mips/unopWide.S" {"result0":"v0", "result1":"v1", "preinstr":"negu v0, a0", "instr":"negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0"}
+
diff --git a/vm/mterp/mips/OP_NEW_ARRAY.S b/vm/mterp/mips/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..5d01794
--- /dev/null
+++ b/vm/mterp/mips/OP_NEW_ARRAY.S
@@ -0,0 +1,61 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    FETCH(a2, 1)                           #  a2 <- CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    GET_VREG(a1, a0)                       #  a1 <- vB (array length)
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- resolved class
+    # check length
+    bltz      a1, common_errNegativeArraySize #  negative length, bail - len in a1
+    EXPORT_PC()                            #  req'd for resolve, alloc
+    # already resolved?
+    beqz      a0, .L${opcode}_resolve
+
+    /*
+     * Finish allocation.
+     *
+     *  a0 holds class
+     *  a1 holds array length
+     */
+.L${opcode}_finish:
+    li        a2, ALLOC_DONT_TRACK         #  don't track in local refs table
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(clazz, length, flags)
+    GET_OPA4(a2)                           #  a2 <- A+
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(v0, a2)                       #  vA <- v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+%break
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  a1 holds array length
+     *  a2 holds class ref CCCC
+     */
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      rOBJ, a1                     #  rOBJ <- length (save)
+    move      a1, a2                       #  a1 <- CCCC
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a1, rOBJ                     #  a1 <- length (restore)
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a0, v0
+    b         .L${opcode}_finish           #  continue with ${opcode}_finish
+
+
diff --git a/vm/mterp/mips/OP_NEW_INSTANCE.S b/vm/mterp/mips/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..ca946ad
--- /dev/null
+++ b/vm/mterp/mips/OP_NEW_INSTANCE.S
@@ -0,0 +1,106 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+    /*
+     * Create a new instance of a class.
+     */
+    # new-instance vAA, class              /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX <- &resolved_class
+#endif
+    EXPORT_PC()                            #  req'd for init, resolve, alloc
+    # already resolved?
+    beqz      a0, .L${opcode}_resolve      #  no, resolve it now
+.L${opcode}_resolved:                      #  a0=class
+    lbu       a1, offClassObject_status(a0) #  a1 <- ClassStatus enum
+    # has class been initialized?
+    li        t0, CLASS_INITIALIZED
+    move      rOBJ, a0                     #  save a0
+    bne       a1, t0, .L${opcode}_needinit #  no, init class now
+
+.L${opcode}_initialized:                   #  a0=class
+    LOAD_base_offClassObject_accessFlags(a3, a0) #  a3 <- clazz->accessFlags
+    li        a1, ALLOC_DONT_TRACK         #  flags for alloc call
+    # a0=class
+    JAL(dvmAllocObject)                    #  v0 <- new object
+    GET_OPA(a3)                            #  a3 <- AA
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    lhu       a1, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    and       a1, kSubModeJitTraceBuild    #  under construction?
+    bnez      a1, .L${opcode}_jitCheck
+#else
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+#endif
+    b         .L${opcode}_continue
+
+%break
+
+.L${opcode}_continue:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(v0, a3)                       #  vAA <- v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * v0: new object
+     * a3: vAA
+     */
+.L${opcode}_jitCheck:
+    lw        a1, 0(rBIX)                  #  reload resolved class
+    # okay?
+    bnez      a1, .L${opcode}_continue     #  yes, finish
+    move      rOBJ, v0                     #  preserve new object
+    move      rBIX, a3                     #  preserve vAA
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(rOBJ, rBIX)                   #  vAA <- new object
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  a0 holds class object
+     */
+.L${opcode}_needinit:
+    JAL(dvmInitClass)                      #  initialize class
+    move      a0, rOBJ                     #  restore a0
+    # check boolean result
+    bnez      v0, .L${opcode}_initialized  #  success, continue
+    b         common_exceptionThrown       #  failed, deal with init exception
+
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a1 holds BBBB
+     */
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    move      a0, v0
+    # got null?
+    bnez      v0, .L${opcode}_resolved     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
diff --git a/vm/mterp/mips/OP_NOP.S b/vm/mterp/mips/OP_NOP.S
new file mode 100644
index 0000000..38a5eb4
--- /dev/null
+++ b/vm/mterp/mips/OP_NOP.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    FETCH_ADVANCE_INST(1)                  #  advance to next instr, load rINST
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)                        #  execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type dalvik_inst, @function
+dalvik_inst:
+    .ent dalvik_inst
+    .end dalvik_inst
+#endif
+
diff --git a/vm/mterp/mips/OP_NOT_INT.S b/vm/mterp/mips/OP_NOT_INT.S
new file mode 100644
index 0000000..3402d19
--- /dev/null
+++ b/vm/mterp/mips/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"instr":"not a0, a0"}
diff --git a/vm/mterp/mips/OP_NOT_LONG.S b/vm/mterp/mips/OP_NOT_LONG.S
new file mode 100644
index 0000000..8947c4e
--- /dev/null
+++ b/vm/mterp/mips/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopWide.S" {"preinstr":"not a0, a0", "instr":"not a1, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT.S b/vm/mterp/mips/OP_OR_INT.S
new file mode 100644
index 0000000..683242f
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT_2ADDR.S b/vm/mterp/mips/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..e63835b
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT_LIT16.S b/vm/mterp/mips/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..c12495d
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT_LIT8.S b/vm/mterp/mips/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..f2ac2d0
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_LONG.S b/vm/mterp/mips/OP_OR_LONG.S
new file mode 100644
index 0000000..8b080f6
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_OR_LONG_2ADDR.S b/vm/mterp/mips/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..ef37dbf
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide2addr.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_PACKED_SWITCH.S b/vm/mterp/mips/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..add1dac
--- /dev/null
+++ b/vm/mterp/mips/OP_PACKED_SWITCH.S
@@ -0,0 +1,34 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       t0, a1, 16
+    or        a0, a0, t0                   #  a0 <- BBBBbbbb
+    GET_VREG(a1, a3)                       #  a1 <- vAA
+    EAS1(a0, rPC, a0)                      #  a0 <- PC + BBBBbbbb*2
+    JAL($func)                             #  a0 <- code-unit branch offset
+    addu      a1, v0, v0                   #  a1 <- byte offset
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bnez      a0, common_updateProfile
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_REM_DOUBLE.S b/vm/mterp/mips/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..4329ed3
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(fmod)", "instr_f":"JAL(fmod)"}
diff --git a/vm/mterp/mips/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..97cd893
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(fmod)", "instr_f":"JAL(fmod)"}
diff --git a/vm/mterp/mips/OP_REM_FLOAT.S b/vm/mterp/mips/OP_REM_FLOAT.S
new file mode 100644
index 0000000..e68cfb5
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(fmodf)", "instr_f":"JAL(fmodf)"}
diff --git a/vm/mterp/mips/OP_REM_FLOAT_2ADDR.S b/vm/mterp/mips/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..f78cbb3
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(fmodf)", "instr_f":"JAL(fmodf)"}
diff --git a/vm/mterp/mips/OP_REM_INT.S b/vm/mterp/mips/OP_REM_INT.S
new file mode 100644
index 0000000..f1dcf37
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_INT_2ADDR.S b/vm/mterp/mips/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..85d616b
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_INT_LIT16.S b/vm/mterp/mips/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..1f31442
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_INT_LIT8.S b/vm/mterp/mips/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..4b5bb82
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_LONG.S b/vm/mterp/mips/OP_REM_LONG.S
new file mode 100644
index 0000000..d76221a
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_LONG.S
@@ -0,0 +1,7 @@
+%verify "executed"
+/* ldivmod returns quotient in a0/a1 and remainder in a2/a3 */
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide.S" { "arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_REM_LONG_2ADDR.S b/vm/mterp/mips/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..be194a5
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,6 @@
+%verify "executed"
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide2addr.S" {"arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_RETURN.S b/vm/mterp/mips/OP_RETURN.S
new file mode 100644
index 0000000..acc01cf
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a0, a2)                       #  a0 <- vAA
+    sw        a0, offThread_retval(rSELF)  #  retval.i <- vAA
+    b         common_returnFromMethod
+
diff --git a/vm/mterp/mips/OP_RETURN_OBJECT.S b/vm/mterp/mips/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..4459668
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_RETURN.S"
diff --git a/vm/mterp/mips/OP_RETURN_VOID.S b/vm/mterp/mips/OP_RETURN_VOID.S
new file mode 100644
index 0000000..781f835
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_VOID.S
@@ -0,0 +1,3 @@
+%verify "executed"
+    b         common_returnFromMethod
+
diff --git a/vm/mterp/mips/OP_RETURN_VOID_BARRIER.S b/vm/mterp/mips/OP_RETURN_VOID_BARRIER.S
new file mode 100644
index 0000000..4cb5b9b
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_VOID_BARRIER.S
@@ -0,0 +1,3 @@
+%verify "executed"
+    SMP_DMB
+    b         common_returnFromMethod
diff --git a/vm/mterp/mips/OP_RETURN_WIDE.S b/vm/mterp/mips/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..bd93d6a
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vAA/vAA+1
+    STORE64(a0, a1, a3)                    #  retval <- a0/a1
+    b         common_returnFromMethod
+
diff --git a/vm/mterp/mips/OP_RSUB_INT.S b/vm/mterp/mips/OP_RSUB_INT.S
new file mode 100644
index 0000000..03918ea
--- /dev/null
+++ b/vm/mterp/mips/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "mips/binopLit16.S" {"instr":"subu a0, a1, a0"}
diff --git a/vm/mterp/mips/OP_RSUB_INT_LIT8.S b/vm/mterp/mips/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..75d3d40
--- /dev/null
+++ b/vm/mterp/mips/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"subu a0, a1, a0"}
diff --git a/vm/mterp/mips/OP_SGET.S b/vm/mterp/mips/OP_SGET.S
new file mode 100644
index 0000000..80e1913
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET.S
@@ -0,0 +1,50 @@
+%default { "barrier":"                  #  no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .L${opcode}_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .L${opcode}_finish            # resume
+%break
+
+.L${opcode}_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+    $barrier                               #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
diff --git a/vm/mterp/mips/OP_SGET_BOOLEAN.S b/vm/mterp/mips/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_BYTE.S b/vm/mterp/mips/OP_SGET_BYTE.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_CHAR.S b/vm/mterp/mips/OP_SGET_CHAR.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_OBJECT.S b/vm/mterp/mips/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..d880f97
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SGET_SHORT.S b/vm/mterp/mips/OP_SGET_SHORT.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_VOLATILE.S b/vm/mterp/mips/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..d880f97
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SGET_WIDE.S b/vm/mterp/mips/OP_SGET_WIDE.S
new file mode 100644
index 0000000..0e72992
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_WIDE.S
@@ -0,0 +1,58 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SGET handler.
+     */
+    # sget-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in v0.
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+
+    b        .L${opcode}_finish            # resume
+%break
+
+.L${opcode}_finish:
+    GET_OPA(a1)                            #  a1 <- AA
+    .if $volatile
+    vLOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .else
+    LOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[AA]
+    STORE64(a2, a3, a1)                    #  vAA/vAA+1 <- a2/a3
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
diff --git a/vm/mterp/mips/OP_SGET_WIDE_VOLATILE.S b/vm/mterp/mips/OP_SGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..ca2fce4
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_SHL_INT.S b/vm/mterp/mips/OP_SHL_INT.S
new file mode 100644
index 0000000..9981dec
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"preinstr":"and a1, a1, 31", "instr":"sll a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHL_INT_2ADDR.S b/vm/mterp/mips/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..0ac0a8f
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"preinstr":"and a1, a1, 31", "instr":"sll a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHL_INT_LIT8.S b/vm/mterp/mips/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..1110037
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"preinstr":"and a1, a1, 31", "instr":"sll a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHL_LONG.S b/vm/mterp/mips/OP_SHL_LONG.S
new file mode 100644
index 0000000..817ac2f
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_LONG.S
@@ -0,0 +1,33 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t2)                            #  t2 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SHL_LONG_2ADDR.S b/vm/mterp/mips/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..1191427
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(rOBJ, rFP, t2)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SHR_INT.S b/vm/mterp/mips/OP_SHR_INT.S
new file mode 100644
index 0000000..c5911e7
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"preinstr":"and a1, a1, 31", "instr":"sra a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHR_INT_2ADDR.S b/vm/mterp/mips/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..b979e9f
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"preinstr":"and a1, a1, 31", "instr":"sra a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHR_INT_LIT8.S b/vm/mterp/mips/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..6124619
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"preinstr":"and a1, a1, 31", "instr":"sra a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHR_LONG.S b/vm/mterp/mips/OP_SHR_LONG.S
new file mode 100644
index 0000000..6906978
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_LONG.S
@@ -0,0 +1,33 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t3)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    STORE64(v0, v1, t3)                    #  vAA/VAA+1 <- v0/v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SHR_LONG_2ADDR.S b/vm/mterp/mips/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..439923e
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[A]
+    LOAD64(a0, a1, t2)                     #  a0/a1 <- vAA/vAA+1
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SPARSE_SWITCH.S b/vm/mterp/mips/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..32067de
--- /dev/null
+++ b/vm/mterp/mips/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/mips/OP_SPUT.S b/vm/mterp/mips/OP_SPUT.S
new file mode 100644
index 0000000..722a12f
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT.S
@@ -0,0 +1,50 @@
+%default { "postbarrier":"#  no-op", "prebarrier":"#  no-op" }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .L${opcode}_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .L${opcode}_finish            # resume
+%break
+
+.L${opcode}_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    $prebarrier                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    $postbarrier
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_SPUT_BOOLEAN.S b/vm/mterp/mips/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_BYTE.S b/vm/mterp/mips/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_CHAR.S b/vm/mterp/mips/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_OBJECT.S b/vm/mterp/mips/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..0fd3db3
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_OBJECT.S
@@ -0,0 +1,56 @@
+%default { "postbarrier":"#  no-op", "prebarrier":"#  no-op" }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .L${opcode}_finish       #  is resolved entry null?
+
+    /* Continuation if the field has not yet been resolved.
+     * a1:  BBBB field ref
+     * rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b       .L${opcode}_finish             # resume
+
+%break
+.L${opcode}_finish:                        #  field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    lw        t1, offField_clazz(a0)       #  t1 <- field->clazz
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    $prebarrier                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    $postbarrier
+    beqz      a1, 1f
+    srl       t2, t1, GC_CARD_SHIFT
+    addu      t3, a2, t2
+    sb        a2, (t3)
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..8b6dc14
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT_OBJECT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SPUT_SHORT.S b/vm/mterp/mips/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_VOLATILE.S b/vm/mterp/mips/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..9e1f1a5
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SPUT_WIDE.S b/vm/mterp/mips/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..3e1d042
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_WIDE.S
@@ -0,0 +1,58 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SPUT handler.
+     */
+    # sput-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    GET_OPA(t0)                            #  t0 <- AA
+    LOAD_eas2(a2, rBIX, a1)                #  a2 <- resolved StaticField ptr
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ<- &fp[AA]
+    # is resolved entry null?
+    beqz      a2, .L${opcode}_resolve      #  yes, do resolve
+.L${opcode}_finish:                        #  field ptr in a2, AA in rOBJ
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    .if $volatile
+    addu    a2, offStaticField_value       #  a2<- pointer to data
+    JAL(dvmQuasiAtomicSwap64Sync)          #  stores a0/a1 into addr a2
+    .else
+    STORE64_off(a0, a1, a2, offStaticField_value) #  field <- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rOBJ:  &fp[AA]
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in a2.
+     */
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    # success ?
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    move      a2, v0
+    b         .L${opcode}_finish           # resume
diff --git a/vm/mterp/mips/OP_SPUT_WIDE_VOLATILE.S b/vm/mterp/mips/OP_SPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..359b37f
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_SUB_DOUBLE.S b/vm/mterp/mips/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..3b6fa6d
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__subdf3)", "instr_f":"sub.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..cdd973e
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__subdf3)", "instr_f":"sub.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_FLOAT.S b/vm/mterp/mips/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..9096267
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__subsf3)", "instr_f":"sub.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/mips/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..143b7e6
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__subsf3)", "instr_f":"sub.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_INT.S b/vm/mterp/mips/OP_SUB_INT.S
new file mode 100644
index 0000000..aaa6a7b
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"subu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SUB_INT_2ADDR.S b/vm/mterp/mips/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..0032229
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"subu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SUB_LONG.S b/vm/mterp/mips/OP_SUB_LONG.S
new file mode 100644
index 0000000..700d4ea
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_LONG.S
@@ -0,0 +1,10 @@
+%verify "executed"
+/*
+ * For little endian the code sequence looks as follows:
+ *    subu    v0,a0,a2
+ *    subu    v1,a1,a3
+ *    sltu    a0,a0,v0
+ *    subu    v1,v1,a0
+ */
+%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" }
+
diff --git a/vm/mterp/mips/OP_SUB_LONG_2ADDR.S b/vm/mterp/mips/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..9b12d69
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,5 @@
+%verify "executed"
+/*
+ * See comments in OP_SUB_LONG.S
+ */
+%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" }
diff --git a/vm/mterp/mips/OP_THROW.S b/vm/mterp/mips/OP_THROW.S
new file mode 100644
index 0000000..b879b29
--- /dev/null
+++ b/vm/mterp/mips/OP_THROW.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (exception object)
+    EXPORT_PC()                            #  exception handler can throw
+    # null object?
+    beqz      a1, common_errNullObject     #  yes, throw an NPE instead
+    # bypass dvmSetException, just store it
+    STORE_offThread_exception(a1, rSELF)   #  thread->exception <- obj
+    b         common_exceptionThrown
+
diff --git a/vm/mterp/mips/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/mips/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..a68b256
--- /dev/null
+++ b/vm/mterp/mips/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,15 @@
+%verify executed
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    EXPORT_PC()                            #  export the PC
+    GET_OPA(a1)                            #  a1 <- AA
+    JAL(dvmThrowVerificationError)         #  always throws
+    b         common_exceptionThrown       #  handle exception
+
diff --git a/vm/mterp/mips/OP_UNUSED_3E.S b/vm/mterp/mips/OP_UNUSED_3E.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_3F.S b/vm/mterp/mips/OP_UNUSED_3F.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_40.S b/vm/mterp/mips/OP_UNUSED_40.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_41.S b/vm/mterp/mips/OP_UNUSED_41.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_42.S b/vm/mterp/mips/OP_UNUSED_42.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_43.S b/vm/mterp/mips/OP_UNUSED_43.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_73.S b/vm/mterp/mips/OP_UNUSED_73.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_79.S b/vm/mterp/mips/OP_UNUSED_79.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_7A.S b/vm/mterp/mips/OP_UNUSED_7A.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E3.S b/vm/mterp/mips/OP_UNUSED_E3.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E3.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E4.S b/vm/mterp/mips/OP_UNUSED_E4.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E4.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E5.S b/vm/mterp/mips/OP_UNUSED_E5.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E5.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E6.S b/vm/mterp/mips/OP_UNUSED_E6.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E6.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E7.S b/vm/mterp/mips/OP_UNUSED_E7.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E7.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E8.S b/vm/mterp/mips/OP_UNUSED_E8.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E8.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E9.S b/vm/mterp/mips/OP_UNUSED_E9.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E9.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EA.S b/vm/mterp/mips/OP_UNUSED_EA.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EA.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EB.S b/vm/mterp/mips/OP_UNUSED_EB.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EB.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EC.S b/vm/mterp/mips/OP_UNUSED_EC.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EC.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_ED.S b/vm/mterp/mips/OP_UNUSED_ED.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_ED.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EF.S b/vm/mterp/mips/OP_UNUSED_EF.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EF.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_F1.S b/vm/mterp/mips/OP_UNUSED_F1.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_F1.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FC.S b/vm/mterp/mips/OP_UNUSED_FC.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FC.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FD.S b/vm/mterp/mips/OP_UNUSED_FD.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FD.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FE.S b/vm/mterp/mips/OP_UNUSED_FE.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FE.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FF.S b/vm/mterp/mips/OP_UNUSED_FF.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_USHR_INT.S b/vm/mterp/mips/OP_USHR_INT.S
new file mode 100644
index 0000000..7b474b6
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"preinstr":"and a1, a1, 31", "instr":"srl a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_USHR_INT_2ADDR.S b/vm/mterp/mips/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..71b5e36
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"preinstr":"and a1, a1, 31", "instr":"srl a0, a0, a1 "}
diff --git a/vm/mterp/mips/OP_USHR_INT_LIT8.S b/vm/mterp/mips/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..7dbe863
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"preinstr":"and a1, a1, 31", "instr":"srl a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_USHR_LONG.S b/vm/mterp/mips/OP_USHR_LONG.S
new file mode 100644
index 0000000..acd9d15
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- v0/v1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_USHR_LONG_2ADDR.S b/vm/mterp/mips/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..103cc98
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    GET_OPA4(t3)                           #  t3 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[A]
+    LOAD64(a0, a1, t3)                     #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t3)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_XOR_INT.S b/vm/mterp/mips/OP_XOR_INT.S
new file mode 100644
index 0000000..6551e75
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_INT_2ADDR.S b/vm/mterp/mips/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..f93b782
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_INT_LIT16.S b/vm/mterp/mips/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..add8ef2
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_INT_LIT8.S b/vm/mterp/mips/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..31fa360
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_LONG.S b/vm/mterp/mips/OP_XOR_LONG.S
new file mode 100644
index 0000000..1f07c84
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_XOR_LONG_2ADDR.S b/vm/mterp/mips/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..dade7a9
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide2addr.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"}
diff --git a/vm/mterp/mips/alt_stub.S b/vm/mterp/mips/alt_stub.S
new file mode 100644
index 0000000..edf71a7
--- /dev/null
+++ b/vm/mterp/mips/alt_stub.S
@@ -0,0 +1,20 @@
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (${opnum} * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
diff --git a/vm/mterp/mips/bincmp.S b/vm/mterp/mips/bincmp.S
new file mode 100644
index 0000000..e2398d0
--- /dev/null
+++ b/vm/mterp/mips/bincmp.S
@@ -0,0 +1,35 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    b${revcmp} a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/binflop.S b/vm/mterp/mips/binflop.S
new file mode 100644
index 0000000..6b02707
--- /dev/null
+++ b/vm/mterp/mips/binflop.S
@@ -0,0 +1,44 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if $chkzero
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    $instr_f                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
diff --git a/vm/mterp/mips/binflop2addr.S b/vm/mterp/mips/binflop2addr.S
new file mode 100644
index 0000000..c20a1c6
--- /dev/null
+++ b/vm/mterp/mips/binflop2addr.S
@@ -0,0 +1,45 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if $chkzero
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    $instr_f
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/mips/binflopWide.S b/vm/mterp/mips/binflopWide.S
new file mode 100644
index 0000000..ad61680
--- /dev/null
+++ b/vm/mterp/mips/binflopWide.S
@@ -0,0 +1,52 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if $chkzero
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if $chkzero
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    $instr_f
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
diff --git a/vm/mterp/mips/binflopWide2addr.S b/vm/mterp/mips/binflopWide2addr.S
new file mode 100644
index 0000000..aacd482
--- /dev/null
+++ b/vm/mterp/mips/binflopWide2addr.S
@@ -0,0 +1,46 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if $chkzero
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if $chkzero
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    $instr_f
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
diff --git a/vm/mterp/mips/binop.S b/vm/mterp/mips/binop.S
new file mode 100644
index 0000000..8bbe0fb
--- /dev/null
+++ b/vm/mterp/mips/binop.S
@@ -0,0 +1,34 @@
+%default {"preinstr":"", "result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 11-14 instructions */
+
diff --git a/vm/mterp/mips/binop2addr.S b/vm/mterp/mips/binop2addr.S
new file mode 100644
index 0000000..acca20d
--- /dev/null
+++ b/vm/mterp/mips/binop2addr.S
@@ -0,0 +1,30 @@
+%default {"preinstr":"", "result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/mips/binopLit16.S b/vm/mterp/mips/binopLit16.S
new file mode 100644
index 0000000..74b4533
--- /dev/null
+++ b/vm/mterp/mips/binopLit16.S
@@ -0,0 +1,30 @@
+%default {"result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if $chkzero
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/mips/binopLit8.S b/vm/mterp/mips/binopLit8.S
new file mode 100644
index 0000000..c3d7464
--- /dev/null
+++ b/vm/mterp/mips/binopLit8.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 10-12 instructions */
+
diff --git a/vm/mterp/mips/binopWide.S b/vm/mterp/mips/binopWide.S
new file mode 100644
index 0000000..3e47ab9
--- /dev/null
+++ b/vm/mterp/mips/binopWide.S
@@ -0,0 +1,38 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64($arg0, $arg1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64($arg2, $arg3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if $chkzero
+    or        t0, $arg2, $arg3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vAA/vAA+1 <- $result0/$result1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
diff --git a/vm/mterp/mips/binopWide2addr.S b/vm/mterp/mips/binopWide2addr.S
new file mode 100644
index 0000000..7494604
--- /dev/null
+++ b/vm/mterp/mips/binopWide2addr.S
@@ -0,0 +1,34 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64($arg2, $arg3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64($arg0, $arg1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if $chkzero
+    or        t0, $arg2, $arg3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vAA/vAA+1 <- $result0/$result1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
diff --git a/vm/mterp/mips/debug.cpp b/vm/mterp/mips/debug.cpp
new file mode 100644
index 0000000..0de6b67
--- /dev/null
+++ b/vm/mterp/mips/debug.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose MIPS registers, along with some other info.
+ *
+ */
+void dvmMterpDumpMipsRegs(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3)
+{
+    register uint32_t rPC       asm("s0");
+    register uint32_t rFP       asm("s1");
+    register uint32_t rSELF     asm("s2");
+    register uint32_t rIBASE    asm("s3");
+    register uint32_t rINST     asm("s4");
+    register uint32_t rOBJ      asm("s5");
+    register uint32_t rBIX      asm("s6");
+    register uint32_t rTEMP	asm("s7");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: a0=%08x a1=%08x a2=%08x a3=%08x\n", a0, a1, a2, a3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rIBASE=%08x\n",
+        rPC, rFP, rSELF, rIBASE);
+    printf("    : rINST=%08x rOBJ=%08x rBIX=%08x rTEMP=%08x \n", rINST, rOBJ, rBIX, rTEMP);
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->signature);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
diff --git a/vm/mterp/mips/entry.S b/vm/mterp/mips/entry.S
new file mode 100644
index 0000000..8a1b61a
--- /dev/null
+++ b/vm/mterp/mips/entry.S
@@ -0,0 +1,107 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align 2
+    .global dvmMterpStdRun
+    .ent dvmMterpStdRun
+    .frame sp, STACK_SIZE, ra
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+
+dvmMterpStdRun:
+    .set noreorder
+    .cpload t9
+    .set reorder
+/* Save to the stack. Frame size = STACK_SIZE */
+    STACK_STORE_FULL()
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+    .cprestore STACK_OFFSET_GP
+
+    addu      fp, sp, STACK_SIZE           #  Move Frame Pointer to the base of frame
+    /* save stack pointer, add magic word for debuggerd */
+    sw        sp, offThread_bailPtr(a0)      # Save SP
+
+    /* set up "named" registers, figure out entry point */
+    move      rSELF, a0                    #  set rSELF
+    LOAD_PC_FROM_SELF()
+    LOAD_FP_FROM_SELF()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()                           #  load rINST from rPC
+    sw        zero, offThread_inJitCodeCache(rSELF)
+#if !defined(WITH_SELF_VERIFICATION)
+    bnez      a0, common_updateProfile     # profiling is enabled
+#else
+    lw       a2, offThread_shadowSpace(rSELF) # to find out the jit exit state
+    beqz     a0, 1f                        # profiling is disabled
+    lw       a3, offShadowSpace_jitExitState(a2) # jit exit state
+    li	     t0, kSVSTraceSelect
+    bne      a3, t0, 2f
+    li       a2, kJitTSelectRequestHot     # ask for trace selection
+    b        common_selectTrace            # go build the trace
+2:
+    li       a4, kSVSNoProfile
+    beq      a3, a4, 1f                    # don't profile the next instruction?
+    b        common_updateProfile          # collect profiles
+#endif
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                           #  load rINST from rPC
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+.Lbad_arg:
+    la        a0, .LstrBadEntryPoint
+    #a1 holds value of entryPoint
+    JAL(printf)
+    JAL(dvmAbort)
+
+    .end dvmMterpStdRun
+
+    .global dvmMterpStdBail
+    .ent dvmMterpStdBail
+
+/* Restore the stack pointer and all the registers stored at sp from the save
+ * point established  on entry. Return to whoever called dvmMterpStdRun.
+ *
+ * On entry:
+ *   a0    Thread* self
+ */
+dvmMterpStdBail:
+    lw        sp, offThread_bailPtr(a0)      #  Restore sp
+    STACK_LOAD_FULL()
+    jr        ra
+
+    .end dvmMterpStdBail
diff --git a/vm/mterp/mips/footer.S b/vm/mterp/mips/footer.S
new file mode 100644
index 0000000..54da6c3
--- /dev/null
+++ b/vm/mterp/mips/footer.S
@@ -0,0 +1,1173 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      rBIX, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    b       jitSVShadowRunStart                 # resume as if cache hit
+                                                # expects resume addr in rBIX
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    li        a2, kSVSPunt                 #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    move      rPC, a0                      # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a2, kSVSSingleStep           #  a2 <- interpreter entry point
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoProfile            #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSBackwardBranch       #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSNormal               #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoChain              #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+#else                                   /*  WITH_SELF_VERIFICATION */
+
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      a0, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    jr      a0                                  # resume translation
+
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0
+#if defined(WITH_JIT_TUNING)
+    move      a0, ra
+    JAL(dvmBumpPunt)
+#endif
+    EXPORT_PC()
+    sw        zero, offThread_inJitCodeCache(rSELF) # Back to the interp land
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    rPC <= Dalvik PC of instrucion to interpret
+ *    a1 <= Dalvik PC of resume instruction
+ *    ra <= resume point in translation
+ */
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0                       # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        sp, offThread_jitResumeNSP(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a1, 1
+    sw        a1, offThread_singleStepCount(rSELF) # just step once
+    move      a0, rSELF
+    li        a1, kSubModeCountedStep
+    JAL(dvmEnableSubMode)                   # (self, subMode)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    move      a1, rPC                      # arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 2f                       # 0 means translation does not exist
+    jr        a0
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # @ (pc, self)
+    sw        v0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    beqz      v0, 2f
+    move      a0, v0
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/* No translation, so request one if profiling isn't disabled */
+2:
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()
+    li        t0, kJitTSelectRequestHot
+    movn      a2, t0, a0                   #  ask for trace selection
+    bnez      a0, common_selectTrace
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNormal)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)           # @ (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    beqz      a0, toInterpreter            #  go if not, otherwise do chain
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, footer235
+
+    jr        a0                           #  continue native execution if so
+footer235:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 1f
+    jr        a0                           #  continue native execution if so
+1:
+#endif                                  /*  WITH_SELF_VERIFICATION */
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+
+toInterpreter:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    lw        a0, offThread_pJitProfTable(rSELF)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    # NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+
+common_testUpdateProfile:
+
+    beqz      a0, 4f
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    srl       a3, rPC, 12                  #  cheap, but fast hash function
+    xor       a3, a3, rPC
+    andi      a3, a3, JIT_PROF_SIZE-1      #  eliminate excess bits
+    addu      t1, a0, a3
+    lbu       a1, (t1)                     #  get counter
+    GET_INST_OPCODE(t0)
+    subu      a1, a1, 1                    #  decrement counter
+    sb        a1, (t1)                     #  and store it
+    beqz      a1, 1f
+    GOTO_OPCODE(t0)                        #  if not threshold, fallthrough otherwise
+1:
+    /* Looks good, reset the counter */
+    lw        a1, offThread_jitThreshold(rSELF)
+    sb        a1, (t1)
+    EXPORT_PC()
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        v0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+
+#if !defined(WITH_SELF_VERIFICATION)
+    li        t0, kJitTSelectRequest       #  ask for trace selection
+    movz      a2, t0, a0
+    beqz      a0, common_selectTrace
+    jr        a0                           #  jump to the translation
+#else
+
+    bne       a0, zero, skip_ask_for_trace_selection
+    li        a2, kJitTSelectRequest       #  ask for trace selection
+    j         common_selectTrace
+
+skip_ask_for_trace_selection:
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    move      rBIX, a0                     #  save target
+    jal       dvmCompilerGetInterpretTemplate
+    # special case?
+    bne       v0, rBIX, jitSVShadowRunStart  #  set up self verification shadow space
+    # Need to clear the inJitCodeCache flag
+    sw        zero, offThread_inJitCodeCache(rSELF) #  back to the interp land
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+
+common_selectTrace:
+    lhu        a0, offThread_subMode(rSELF)
+    andi       a0, (kSubModeJitTraceBuild | kSubModeJitSV)
+    bnez       a0, 3f                      # already doing JIT work, continue
+    sw         a2, offThread_jitState(rSELF)
+    move       a0, rSELF
+
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+
+    EXPORT_PC()
+    SAVE_PC_TO_SELF()
+    SAVE_FP_TO_SELF()
+    JAL(dvmJitCheckTraceRequest)
+3:
+    FETCH_INST()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+4:
+    GET_INST_OPCODE(t0)                    # extract opcode from rINST
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    move      a0, rPC                      #  r0 <- program counter
+    move      a1, rFP                      #  r1 <- frame pointer
+    move      a2, rSELF                    #  r2 <- InterpState pointer
+    move      a3, rBIX                     #  r3 <- target translation
+    jal       dvmSelfVerificationSaveState #  save registers to shadow space
+    lw        rFP, offShadowSpace_shadowFP(v0) #  rFP <- fp in shadow space
+    jr        rBIX                         #  jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    move      a1, rFP                      #  pass ending fp
+    move      a3, rSELF                    #  pass self ptr for convenience
+    jal       dvmSelfVerificationRestoreState #  restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()                 #  restore pc, fp
+    lw        a1, offShadowSpace_svState(a0) #  get self verification state
+    beq       a1, zero, 1f                 #  check for punt condition
+
+    # Setup SV single-stepping
+    move      a0, rSELF
+    li        a1, kSubModeJitSV
+    JAL(dvmEnableSubMode)                  # (self, subMode)
+    li        a2, kJitSelfVerification     #  ask for self verification
+    sw        a2, offThread_jitState(rSELF)
+    # Intentional fallthrough
+
+1:
+    # exit to interpreter without check
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+    .ent common_gotoBail
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                   # export state to "thread"
+    move      a0, rSELF                    # a0 <- self ptr
+    b         dvmMterpStdBail              # call(self, changeInterp)
+    .end common_gotoBail
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    beqz    rOBJ, 1f
+    lw      rOBJ, offObject_clazz(rOBJ)
+1:
+    sw      a0, offThread_methodToCall(rSELF)
+    sw      rOBJ, offThread_callsiteClass(rSELF)
+    jr      ra
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPA(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)              #  rBIX <- stack save area
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)                           #  a1 <- CCCC
+.LinvokeRangeArgs:
+    # a0=methodToCall, a1=CCCC, a2=count, rBIX=outs
+    # (very few methods have > 10 args; could unroll for common cases)
+    EAS2(a3, rFP, a1)
+    sll       t0, a2, 2
+    subu      rBIX, rBIX, t0
+
+1:
+    lw        a1, 0(a3)
+    addu      a3, a3, 4
+    subu      a2, a2, 1
+    sw        a1, 0(rBIX)
+    addu      rBIX, 4
+    bnez      a2, 1b
+    b         .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", "rOBJ is this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPB(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)
+
+    # a0=methodToCall, a1=GFED, a2=count,
+.LinvokeNonRange:
+    beq       a2, 0, 0f
+    beq       a2, 1, 1f
+    beq       a2, 2, 2f
+    beq       a2, 3, 3f
+    beq       a2, 4, 4f
+    beq       a2, 5, 5f
+
+5:
+    and       t0, rINST, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+4:
+    and       t0, a1, 0xf000
+    ESRN(t2, rFP, t0, 10)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+3:
+    and       t0, a1, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+2:
+    and       t0, a1, 0x00f0
+    ESRN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+1:
+    and       t0, a1, 0x000f
+    EASN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+0:
+    #fall through .LinvokeArgsDone
+
+
+.LinvokeArgsDone:                          #  a0=methodToCall
+    lhu       rOBJ, offMethod_registersSize(a0)
+    lhu       a3, offMethod_outsSize(a0)
+    lw        a2, offMethod_insns(a0)
+    lw        rINST, offMethod_clazz(a0)
+    # find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(a1, rFP)              # a1 <- stack save area
+    sll       t0, rOBJ, 2                    #  a1 <- newFp (old savearea - regsSize)
+    subu      a1, a1, t0
+    SAVEAREA_FROM_FP(rBIX, a1)
+    lw        rOBJ, offThread_interpStackEnd(rSELF) #  t3 <- interpStackEnd
+    sll       t2, a3, 2
+    subu      t0, rBIX, t2
+    lhu       ra, offThread_subMode(rSELF)
+    lw        a3, offMethod_accessFlags(a0) #  a3 <- methodToCall->accessFlags
+    bltu      t0, rOBJ, .LstackOverflow      #  yes, this frame will overflow stack
+
+
+    # set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(t0, rFP)
+    sw        t0, offStackSaveArea_prevSave(rBIX)
+#endif
+    sw        rFP, (offStackSaveArea_prevFrame)(rBIX)
+    sw        rPC, (offStackSaveArea_savedPc)(rBIX)
+#if defined(WITH_JIT)
+    sw        zero, (offStackSaveArea_returnAddr)(rBIX)
+#endif
+    sw        a0, (offStackSaveArea_method)(rBIX)
+    # Profiling?
+    bnez       ra, 2f
+1:
+    and       t2, a3, ACC_NATIVE
+    bnez      t2, .LinvokeNative
+    lhu       rOBJ, (a2)           # rOBJ -< load Inst from New PC
+    lw        a3, offClassObject_pDvmDex(rINST)
+    move      rPC, a2              # Publish new rPC
+    # Update state values for the new method
+    # a0=methodToCall, a1=newFp, a3=newMethodClass, rOBJ=newINST
+    sw        a0, offThread_method(rSELF)
+    sw        a3, offThread_methodClassDex(rSELF)
+    li        a2, 1
+    sw        a2, offThread_debugIsMethodEntry(rSELF)
+
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    move      rFP, a1                    # fp = newFp
+    GET_PREFETCHED_OPCODE(t0, rOBJ)      # extract prefetched opcode from rOBJ
+    move      rINST, rOBJ                # publish new rINST
+    sw        a1, offThread_curFrame(rSELF)
+    bnez      a0, common_updateProfile
+    GOTO_OPCODE(t0)
+#else
+    move      rFP, a1
+    GET_PREFETCHED_OPCODE(t0, rOBJ)
+    move      rINST, rOBJ
+    sw        a1, offThread_curFrame(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+2:
+    # Profiling - record method entry.  a0: methodToCall
+    STACK_STORE(a0, 0)
+    STACK_STORE(a1, 4)
+    STACK_STORE(a2, 8)
+    STACK_STORE(a3, 12)
+    sw       rPC, offThread_pc(rSELF)          # update interpSave.pc
+    move     a1, a0
+    move     a0, rSELF
+    JAL(dvmReportInvoke)
+    STACK_LOAD(a3, 12)                         # restore a0-a3
+    STACK_LOAD(a2, 8)
+    STACK_LOAD(a1, 4)
+    STACK_LOAD(a0, 0)
+    b        1b
+.LinvokeNative:
+    # Prep for the native call
+    # a0=methodToCall, a1=newFp, rBIX=newSaveArea
+    lhu       ra, offThread_subMode(rSELF)
+    lw        t3, offThread_jniLocal_topCookie(rSELF)
+    sw        a1, offThread_curFrame(rSELF)
+    sw        t3, offStackSaveArea_localRefCookie(rBIX) # newFp->localRefCookie=top
+    move      a2, a0
+    move      a0, a1
+    addu      a1, rSELF, offThread_retval
+    move      a3, rSELF
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b         .Lskip
+    .ent dalvik_mterp
+dalvik_mterp:
+    STACK_STORE_FULL()
+.Lskip:
+#endif
+    bnez      ra, 11f                          # Any special SubModes active?
+    lw        t9, offMethod_nativeFunc(a2)
+    jalr      t9
+    lw        gp, STACK_OFFSET_GP(sp)
+7:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw        a0, offStackSaveArea_localRefCookie(rBIX)
+    lw        a1, offThread_exception(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    sw        a0, offThread_jniLocal_topCookie(rSELF)    # new top <- old top
+    bnez      a1, common_exceptionThrown
+
+    FETCH_ADVANCE_INST(3)
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+11:
+    # a0=newFp, a1=&retval, a2=methodToCall, a3=self, ra=subModes
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    move      a0, a2                    # a0 <- methodToCall
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPreNativeInvoke)       # (methodToCall, self, fp)
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Call the native method
+    lw       t9, offMethod_nativeFunc(a2)      # t9<-methodToCall->nativeFunc
+    jalr     t9
+    lw       gp, STACK_OFFSET_GP(sp)
+
+    # Restore the pre-call arguments
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Finish up any post-invoke subMode requirements
+    move      a0, a2
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPostNativeInvoke)      # (methodToCall, self, fp)
+    b         7b
+
+
+.LstackOverflow:       # a0=methodToCall
+    move      a1, a0                    #  a1 <- methodToCall
+    move      a0, rSELF                 # a0 <- self
+    JAL(dvmHandleStackOverflow)         #  dvmHandleStackOverflow(self, methodToCall)
+    b         common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .end dalvik_mterp
+#endif
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    lhu       t0, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(a0, rFP)
+    lw        rOBJ, offStackSaveArea_savedPc(a0) # rOBJ = saveArea->savedPc
+    bnez      t0, 19f
+14:
+    lw        rFP, offStackSaveArea_prevFrame(a0) # fp = saveArea->prevFrame
+    lw        a2, (offStackSaveArea_method - sizeofStackSaveArea)(rFP)
+                                               # a2<- method we're returning to
+    # is this a break frame?
+    beqz      a2, common_gotoBail              # break frame, bail out completely
+
+    lw        rBIX, offMethod_clazz(a2)        # rBIX<- method->clazz
+    lw        rIBASE, offThread_curHandlerTable(rSELF) # refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, rOBJ, 3)      # advance rOBJ, update new rINST
+    sw        a2, offThread_method(rSELF)      # self->method = newSave->method
+    lw        a1, offClassObject_pDvmDex(rBIX) # r1<- method->clazz->pDvmDex
+    sw        rFP, offThread_curFrame(rSELF)   # curFrame = fp
+#if defined(WITH_JIT)
+    lw         rBIX, offStackSaveArea_returnAddr(a0)
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    sw         rBIX, offThread_inJitCodeCache(rSELF) # may return to JIT'ed land
+    beqz       rBIX, 15f                       # caller is compiled code
+    move       t9, rBIX
+    jalr       t9
+    lw         gp, STACK_OFFSET_GP(sp)
+15:
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    GOTO_OPCODE(t0)                            # jump to next instruction
+#else
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+19:
+    # Handle special actions
+    # On entry, a0: StackSaveArea
+    lw         a1, offStackSaveArea_prevFrame(a0) # a1<- prevFP
+    sw         rPC, offThread_pc(rSELF)        # update interpSave.pc
+    sw         a1, offThread_curFrame(rSELF)   # update interpSave.curFrame
+    move       a0, rSELF
+    JAL(dvmReportReturn)
+    SAVEAREA_FROM_FP(a0, rFP)                  # restore StackSaveArea
+    b          14b
+
+    .if 0
+    /*
+     * Return handling, calls through "glue code".
+     */
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                       # export state
+    move       a0, rSELF                       # arg to function
+    JAL(dvmMterp_returnFromMethod)
+    b          common_resumeAfterGlueCall
+    .endif
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+    .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+    move     a0, rSELF
+    JAL(dvmCheckSuspendPending)
+    lw       rOBJ, offThread_exception(rSELF)
+    move     a1, rSELF
+    move     a0, rOBJ
+    JAL(dvmAddTrackedAlloc)
+    lhu      a2, offThread_subMode(rSELF)
+    sw       zero, offThread_exception(rSELF)
+
+    # Special subMode?
+    bnez     a2, 7f                     # any special subMode handling needed?
+8:
+    /* set up args and a local for "&fp" */
+    sw       rFP, 20(sp)                 #  store rFP => tmp
+    addu     t0, sp, 20                  #  compute &tmp
+    sw       t0, STACK_OFFSET_ARG04(sp)  #  save it in arg4 as per ABI
+    li       a3, 0                       #  a3 <- false
+    lw       a1, offThread_method(rSELF)
+    move     a0, rSELF
+    lw       a1, offMethod_insns(a1)
+    move     a2, rOBJ
+    subu     a1, rPC, a1
+    sra      a1, a1, 1
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    JAL(dvmFindCatchBlock)           # call(self, relPc, exc, scan?, &fp)
+    lw        rFP, 20(sp)            # retrieve the updated rFP
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    move      a0, v0
+    bltz      v0, .LnotCaughtLocally
+
+    /* fix earlier stack overflow if necessary; Preserve a0 */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 1f
+    move      rBIX, a0
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)
+    move      a0, rBIX
+
+1:
+
+/* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(a1, rFP)           # a1<- new save area
+    lw        a1, offStackSaveArea_method(a1)
+    sw        a1, offThread_method(rSELF)
+    lw        a2, offMethod_clazz(a1)
+    lw        a3, offMethod_insns(a1)
+    lw        a2, offClassObject_pDvmDex(a2)
+    EAS1(rPC, a3, a0)
+    sw        a2, offThread_methodClassDex(rSELF)
+
+    /* release the tracked alloc on the exception */
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+
+    /* restore the exception if the handler wants it */
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    bne       t0, OP_MOVE_EXCEPTION, 2f
+    sw        rOBJ, offThread_exception(rSELF)
+2:
+    GOTO_OPCODE(t0)
+
+    # Manage debugger bookkeeping
+7:
+    sw        rPC, offThread_pc(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmReportExceptionThrow)
+    b         8b
+
+.LnotCaughtLocally:                     #  rOBJ = exception
+    /* fix stack overflow if necessary */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 3f
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)           #  dvmCleanupStackOverflow(self, exception)
+
+3:
+    # may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    lw        a0, offThread_method(rSELF)
+    lw        a1, offMethod_insns(a0)
+    subu      a1, rPC, a1
+    sra       a1, a1, 1
+    JAL(dvmLineNumFromPC)
+    sw        v0, 20(sp)
+    # dvmGetMethodSourceFile(method)
+    lw        a0, offThread_method(rSELF)
+    JAL(dvmGetMethodSourceFile)
+    sw        v0, 16(sp)
+    # exception->clazz->descriptor
+    lw        a3, offObject_clazz(rOBJ)
+    lw        a3, offClassObject_descriptor(a3)
+    la        a2, .LstrExceptionNotCaughtLocally
+    la        a1, .LstrLogTag
+    li        a0, 3
+    JAL(__android_log_print)
+#endif
+    sw        rOBJ, offThread_exception(rSELF)
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+    b         common_gotoBail
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_TO_SELF()                # export state
+    SAVE_FP_TO_SELF()
+    move     a0, rSELF               # arg to function
+    JAL(dvmMterp_exceptionThrown)
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     rBIX: &dvmDex->pResFields[field]
+     *     a0:  field pointer (must preserve)
+     */
+common_verifyField:
+     lhu     a3, offThread_subMode(rSELF)
+     andi    a3, kSubModeJitTraceBuild
+     bnez    a3, 1f                 # Not building trace, continue
+     jr      ra
+1:
+     lw      a1, (rBIX)
+     beqz    a1, 2f                 # resolution complete ?
+     jr      ra
+2:
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(ra, 16)
+    move    a0, rSELF
+    move    a1, rPC
+    JAL(dvmJitEndTraceSelect)        #(self,pc) end trace before this inst)
+    SCRATCH_LOAD(a0, 0)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(ra, 16)
+    jr      ra                       # return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()           #  pull rPC and rFP out of thread
+    lw      rIBASE, offThread_curHandlerTable(rSELF) # refresh
+    FETCH_INST()                     #  load rINST from rPC
+    GET_INST_OPCODE(t0)              #  extract opcode from rINST
+    GOTO_OPCODE(t0)                  #  jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use a1
+ * and a3 because those just happen to be the registers all our callers are
+ * using. We move a3 before calling the C function, but a1 happens to match.
+ * a1: index
+ * a3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    move      a0, a3
+    JAL(dvmThrowArrayIndexOutOfBoundsException)
+    b         common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    la     a0, .LstrDivideByZero
+    JAL(dvmThrowArithmeticException)
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in a1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    move    a0, a1                                # arg0 <- len
+    JAL(dvmThrowNegativeArraySizeException)    # (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in a1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    move     a0, a1
+    JAL(dvmThrowNoSuchMethodError)
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    li      a0, 0
+    JAL(dvmThrowNullPointerException)
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will be in ra. Use a jal to jump here.
+ */
+common_abort:
+    lw      zero,-4(zero)            #  generate SIGSEGV
+
+/*
+ * Spit out a "we were here", preserving all registers.
+ */
+    .macro SQUEAK num
+common_squeak\num:
+    STACK_STORE_RA();
+    la        a0, .LstrSqueak
+    LOAD_IMM(a1, \num);
+    JAL(printf);
+    STACK_LOAD_RA();
+    RETURN;
+    .endm
+
+    SQUEAK 0
+    SQUEAK 1
+    SQUEAK 2
+    SQUEAK 3
+    SQUEAK 4
+    SQUEAK 5
+
+/*
+ * Spit out the number in a0, preserving registers.
+ */
+common_printNum:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrSqueak
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    STACK_STORE_RA()
+    la        a0, .LstrNewline
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+    /*
+     * Print the 32-bit quantity in a0 as a hex value, preserving registers.
+     */
+common_printHex:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrPrintHex
+    JAL(printf)
+    STACK_LOAD_RA()
+RETURN;
+
+/*
+ * Print the 64-bit quantity in a0-a1, preserving registers.
+ */
+common_printLong:
+    STACK_STORE_RA()
+    MOVE_REG(a3, a1)
+    MOVE_REG(a2, a0)
+    la        a0, .LstrPrintLong
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN;
+
+/*
+ * Print full method info.  Pass the Method* in a0.  Preserves regs.
+ */
+common_printMethod:
+    STACK_STORE_RA()
+    JAL(dvmMterpPrintMethod)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if 0
+common_dumpRegs:
+    STACK_STORE_RA()
+    JAL(dvmMterpDumpMipsRegs)
+    STACK_LOAD_RA()
+    RETURN
+    .endif
+
+/*
+ * Zero-terminated ASCII string data.
+ */
+    .data
+
+.LstrBadEntryPoint:
+    .asciiz "Bad entry point %d\n"
+.LstrDivideByZero:
+    .asciiz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciiz "filled-new-array only implemented for 'int'"
+.LstrLogTag:
+    .asciiz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciiz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciiz "\n"
+.LstrSqueak:
+    .asciiz "<%d>"
+.LstrPrintHex:
+    .asciiz "<0x%x>"
+.LstrPrintLong:
+    .asciiz "<%lld>"
diff --git a/vm/mterp/mips/header.S b/vm/mterp/mips/header.S
new file mode 100644
index 0000000..0f03599
--- /dev/null
+++ b/vm/mterp/mips/header.S
@@ -0,0 +1,345 @@
+#include "../common/asm-constants.h"
+#include "../common/mips-defines.h"
+#include <asm/regdef.h>
+#include <asm/fpregdef.h>
+
+#ifdef __mips_hard_float
+#define HARD_FLOAT
+#else
+#define SOFT_FLOAT
+#endif
+
+#if (__mips==32) && (__mips_isa_rev>=2)
+#define MIPS32R2
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		self (Thread) pointer
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+
+/* single-purpose registers, given names for clarity */
+#define rPC s0
+#define rFP s1
+#define rSELF s2
+#define rIBASE s3
+#define rINST s4
+#define rOBJ s5
+#define rBIX s6
+#define rTEMP s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0 a0
+#define rARG1 a1
+#define rARG2 a2
+#define rARG3 a3
+#define rRESULT0 v0
+#define rRESULT1 v1
+#else
+#define rARG0 a1
+#define rARG1 a0
+#define rARG2 a3
+#define rARG3 a2
+#define rRESULT0 v1
+#define rRESULT1 v0
+#endif
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_SELF() lw rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF() sw rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF() lw rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF() sw rFP, offThread_curFrame(rSELF)
+#define LOAD_PC_FP_FROM_SELF() \
+	LOAD_PC_FROM_SELF();   \
+	LOAD_FP_FROM_SELF()
+#define SAVE_PC_FP_TO_SELF()   \
+	SAVE_PC_TO_SELF();     \
+	SAVE_FP_TO_SELF()
+
+#define EXPORT_PC() \
+    sw        rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+    subu      rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST() lhu rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \
+    addu      rPC, rPC, ((_count) * 2)
+
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+    lhu       _dreg, ((_count)*2)(_sreg) ;            \
+    addu      _sreg, _sreg, (_count)*2
+
+#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \
+    lhu       rINST, (rPC)
+
+#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC)
+#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+
+#define GET_PREFETCHED_OPCODE(dreg, sreg)   andi     dreg, sreg, 255
+
+#define GOTO_OPCODE(rd) sll rd, rd, ${handler_size_bits}; \
+    addu      rd, rIBASE, rd; \
+    jr        rd
+
+#define GOTO_OPCODE_BASE(_base, rd)  sll rd, rd, ${handler_size_bits}; \
+    addu      rd, _base, rd; \
+    jr        rd
+
+#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix)
+
+#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix) STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \
+    sll       dst, dst, ${handler_size_bits}; \
+    addu      dst, rIBASE, dst; \
+    sll       t8, rix, 2; \
+    addu      t8, t8, rFP; \
+    jr        dst; \
+    sw        rd, 0(t8); \
+    .set reorder
+
+#define SET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; s.s rd, (AT); .set at
+
+
+#define GET_OPA(rd) srl rd, rINST, 8
+#ifndef MIPS32R2
+#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf
+#else
+#define GET_OPA4(rd) ext rd, rINST, 8, 4
+#endif
+#define GET_OPB(rd) srl rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd, off) lw rd, offThread_##off## (rSELF)
+
+#define LOAD_rSELF_method(rd) LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd) LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd) LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd) LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd) LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd) LOAD_rSELF_OFF(rd, bailPtr)
+#define LOAD_rSELF_SelfSuspendCount(rd) LOAD_rSELF_OFF(rd, SelfSuspendCount)
+
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd, rbase, roff, rshift) .set noat; \
+    sll       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define EAS1(rd, rbase, roff) EASN(rd, rbase, roff, 1)
+#define EAS2(rd, rbase, roff) EASN(rd, rbase, roff, 2)
+#define EAS3(rd, rbase, roff) EASN(rd, rbase, roff, 3)
+#define EAS4(rd, rbase, roff) EASN(rd, rbase, roff, 4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd, rbase, roff, rshift) .set noat; \
+    srl       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; lw rd, 0(AT); .set at
+
+#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; sw rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase)
+#define LOADu2_RB_OFF(rd, rbase, off) lhu rd, off(rbase)
+#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \
+    s.s       rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \
+    l.s       rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, (off+4)(rbase); \
+    s.s       rhi, (off)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, (off+4)(rbase); \
+    l.s       rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0)
+#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0)
+
+#define vSTORE64(rlo, rhi, rbase) vSTORE64_off(rlo, rhi, rbase, 0)
+#define vLOAD64(rlo, rhi, rbase) vLOAD64_off(rlo, rhi, rbase, 0)
+
+#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0)
+#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0)
+
+#define STORE64_lo(rd, rbase) sw rd, 0(rbase)
+#define STORE64_hi(rd, rbase) sw rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd, rbase) LOAD_RB_OFF(rd, rbase, offThread_exception)
+#define LOAD_base_offArrayObject_length(rd, rbase) LOAD_RB_OFF(rd, rbase, offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd, rbase) LOAD_RB_OFF(rd, rbase, offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd, rbase) LOAD_RB_OFF(rd, rbase, offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_name)
+#define LOAD_base_offObject_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd, rbase) LOADu2_RB_OFF(rd, rbase, offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd, rbase) STORE_RB_OFF(rd, rbase, offThread_exception)
+
+
+#define STACK_STORE(rd, off) sw rd, off(sp)
+#define STACK_LOAD(rd, off) lw rd, off(sp)
+#define CREATE_STACK(n) subu sp, sp, n
+#define DELETE_STACK(n) addu sp, sp, n
+
+#define SAVE_RA(offset) STACK_STORE(ra, offset)
+#define LOAD_RA(offset) STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest, addr) la dest, addr
+#define LOAD_IMM(dest, imm) li dest, imm
+#define MOVE_REG(dest, src) move dest, src
+#define RETURN jr ra
+#define STACK_SIZE 128
+
+#define STACK_OFFSET_ARG04 16
+#define STACK_OFFSET_ARG05 20
+#define STACK_OFFSET_ARG06 24
+#define STACK_OFFSET_ARG07 28
+#define STACK_OFFSET_SCR   32
+#define STACK_OFFSET_SCRMX 80
+#define STACK_OFFSET_GP    84
+#define STACK_OFFSET_rFP   112
+
+#define JAL(n) jal n
+#define BAL(n) bal n
+
+#define STACK_STORE_RA() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(gp, STACK_OFFSET_GP); \
+    STACK_STORE(ra, 124)
+
+#define STACK_STORE_S0() STACK_STORE_RA(); \
+    STACK_STORE(s0, 116)
+
+#define STACK_STORE_S0S1() STACK_STORE_S0(); \
+    STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define STACK_LOAD_RA() STACK_LOAD(ra, 124); \
+    STACK_LOAD(gp, STACK_OFFSET_GP); \
+    DELETE_STACK(STACK_SIZE)
+
+#define STACK_LOAD_S0() STACK_LOAD(s0, 116); \
+    STACK_LOAD_RA()
+
+#define STACK_LOAD_S0S1() STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD_S0()
+
+#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(ra, 124); \
+    STACK_STORE(fp, 120); \
+    STACK_STORE(s0, 116); \
+    STACK_STORE(s1, STACK_OFFSET_rFP); \
+    STACK_STORE(s2, 108); \
+    STACK_STORE(s3, 104); \
+    STACK_STORE(s4, 100); \
+    STACK_STORE(s5, 96); \
+    STACK_STORE(s6, 92); \
+    STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \
+    STACK_LOAD(s7, 88); \
+    STACK_LOAD(s6, 92); \
+    STACK_LOAD(s5, 96); \
+    STACK_LOAD(s4, 100); \
+    STACK_LOAD(s3, 104); \
+    STACK_LOAD(s2, 108); \
+    STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD(s0, 116); \
+    STACK_LOAD(fp, 120); \
+    STACK_LOAD(ra, 124); \
+    DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
diff --git a/vm/mterp/mips/platform.S b/vm/mterp/mips/platform.S
new file mode 100644
index 0000000..ec1e3ee
--- /dev/null
+++ b/vm/mterp/mips/platform.S
@@ -0,0 +1,32 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro SMP_DMB
+#if ANDROID_SMP != 0
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    // FIXME: Is this really needed?
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
diff --git a/vm/mterp/mips/stub.S b/vm/mterp/mips/stub.S
new file mode 100644
index 0000000..fad2238
--- /dev/null
+++ b/vm/mterp/mips/stub.S
@@ -0,0 +1,10 @@
+    /* (stub) */
+    SAVE_PC_TO_SELF()            # only need to export PC and FP
+    SAVE_FP_TO_SELF()
+    move        a0, rSELF        # self is first arg to function
+    JAL(dvmMterp_${opcode})      # call
+    LOAD_PC_FROM_SELF()          # retrieve updated values
+    LOAD_FP_FROM_SELF()
+    FETCH_INST()                 # load next instruction from rPC
+    GET_INST_OPCODE(t0)          # ...trim down to just the opcode
+    GOTO_OPCODE(t0)              # ...and jump to the handler
diff --git a/vm/mterp/mips/unflop.S b/vm/mterp/mips/unflop.S
new file mode 100644
index 0000000..9018bc9
--- /dev/null
+++ b/vm/mterp/mips/unflop.S
@@ -0,0 +1,32 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t0 <- A+
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    $preinstr                              #  optional op
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef SOFT_FLOAT
+    $instr                                 #  a0 <- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vAA <- result0
+#else
+    $instr_f
+
+.L${opcode}_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)
+#endif
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/mips/unflopWide.S b/vm/mterp/mips/unflopWide.S
new file mode 100644
index 0000000..3411c2e
--- /dev/null
+++ b/vm/mterp/mips/unflopWide.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "ld_arg":"LOAD64_F(fa0, fa0f, a3)", "st_result":"STORE64_F(fv0, fv0f, rOBJ)"}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be a MIPS instruction or a function call.
+     *
+     * long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  t1 <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vAA
+#else
+    $ld_arg
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  a0/a1 <- op, a2-a3 changed
+
+.L${opcode}_set_vreg:
+#ifdef SOFT_FLOAT
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vAA <- a0/a1
+#else
+    $st_result                             #  vAA <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
diff --git a/vm/mterp/mips/unflopWider.S b/vm/mterp/mips/unflopWider.S
new file mode 100644
index 0000000..f6d5718
--- /dev/null
+++ b/vm/mterp/mips/unflopWider.S
@@ -0,0 +1,33 @@
+%default {"preinstr":"", "st_result":"STORE64_F(fv0, fv0f, rOBJ)"}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    $instr_f
+
+.L${opcode}_set_vreg:
+    $st_result                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/mips/unop.S b/vm/mterp/mips/unop.S
new file mode 100644
index 0000000..52a8f0a
--- /dev/null
+++ b/vm/mterp/mips/unop.S
@@ -0,0 +1,19 @@
+%default {"preinstr":"", "result0":"a0"}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
diff --git a/vm/mterp/mips/unopNarrower.S b/vm/mterp/mips/unopNarrower.S
new file mode 100644
index 0000000..85a94b7
--- /dev/null
+++ b/vm/mterp/mips/unopNarrower.S
@@ -0,0 +1,37 @@
+%default {"preinstr":"", "load":"LOAD64_F(fa0, fa0f, a3)"}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    $load
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  a0 <- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    $instr_f
+
+.L${opcode}_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/mips/unopWide.S b/vm/mterp/mips/unopWide.S
new file mode 100644
index 0000000..00e4e17
--- /dev/null
+++ b/vm/mterp/mips/unopWide.S
@@ -0,0 +1,22 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1"}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
diff --git a/vm/mterp/mips/unopWider.S b/vm/mterp/mips/unopWider.S
new file mode 100644
index 0000000..f601c11
--- /dev/null
+++ b/vm/mterp/mips/unopWider.S
@@ -0,0 +1,20 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1"}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vA/vA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/mips/unused.S b/vm/mterp/mips/unused.S
new file mode 100644
index 0000000..d91dafb
--- /dev/null
+++ b/vm/mterp/mips/unused.S
@@ -0,0 +1,2 @@
+    BAL(common_abort)
+
diff --git a/vm/mterp/mips/zcmp.S b/vm/mterp/mips/zcmp.S
new file mode 100644
index 0000000..aaac52d
--- /dev/null
+++ b/vm/mterp/mips/zcmp.S
@@ -0,0 +1,33 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    b${revcmp} a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/out/InterpAsm-allstubs.S b/vm/mterp/out/InterpAsm-allstubs.S
new file mode 100644
index 0000000..779fd5f
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-allstubs.S
@@ -0,0 +1,34 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'allstubs'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+    .balign 64
+.L_OP_NOP:   /* dummy */
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
new file mode 100644
index 0000000..a173c72
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -0,0 +1,16877 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB_ST
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r2, r2, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv5te/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    and     r2, r2, #15                 @ r2<- A
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/OP_IF_EQ.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv5te/OP_IF_NE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv5te/OP_IF_LT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv5te/OP_IF_GE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv5te/OP_IF_GT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv5te/OP_IF_LE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/OP_NEG_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/OP_NOT_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/OP_NEG_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/OP_NOT_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/OP_NEG_FLOAT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/OP_NEG_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/OP_INT_TO_LONG.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitos  s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitod  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/OP_LONG_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/OP_LONG_TO_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2d                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizs s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/OP_FLOAT_TO_LONG.S */
+@include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtds  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/OP_DOUBLE_TO_LONG.S */
+@include "armv5te/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtsd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/OP_INT_TO_BYTE.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #24                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #24                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/OP_INT_TO_CHAR.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, lsr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/OP_INT_TO_SHORT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: arm-vfp/OP_ADD_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: arm-vfp/OP_SUB_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: arm-vfp/OP_MUL_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: arm-vfp/OP_DIV_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: arm-vfp/OP_ADD_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    faddd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: arm-vfp/OP_SUB_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: arm-vfp/OP_MUL_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuld   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: arm-vfp/OP_DIV_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/OP_ADD_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/OP_SUB_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/OP_DIV_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/OP_AND_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/OP_OR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/OP_XOR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/OP_SHL_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/OP_SHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/OP_USHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/OP_ADD_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/OP_SUB_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/OP_DIV_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/OP_AND_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/OP_OR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/OP_XOR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: arm-vfp/OP_ADD_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: arm-vfp/OP_SUB_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: arm-vfp/OP_MUL_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: arm-vfp/OP_DIV_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: arm-vfp/OP_ADD_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    faddd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: arm-vfp/OP_SUB_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fsubd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: arm-vfp/OP_MUL_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fmuld   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: arm-vfp/OP_DIV_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fdivd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/OP_ADD_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/OP_DIV_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/OP_AND_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/OP_OR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/OP_XOR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    and     r2, r2, #15
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/OP_IPUT_QUICK.S */
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A(+)
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     0
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     0
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
new file mode 100644
index 0000000..7b6c9d1
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -0,0 +1,17335 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB_ST
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r2, r2, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv5te/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    and     r2, r2, #15                 @ r2<- A
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ copy to arg registers
+    mov     r1, r10
+    bl      __aeabi_cfcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPL_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/OP_CMPG_FLOAT.S */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ copy to arg registers
+    mov     r1, r10
+    bl      __aeabi_cfcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPG_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r9, r0, #255                @ r9<- BB
+    mov     r10, r0, lsr #8             @ r10<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[BB]
+    add     r10, rFP, r10, lsl #2       @ r10<- &fp[CC]
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r10, {r2-r3}                @ r2/r3<- vCC/vCC+1
+    bl      __aeabi_cdcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPL_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_DOUBLE_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/OP_CMPG_DOUBLE.S */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r9, r0, #255                @ r9<- BB
+    mov     r10, r0, lsr #8             @ r10<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[BB]
+    add     r10, rFP, r10, lsl #2       @ r10<- &fp[CC]
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r10, {r2-r3}                @ r2/r3<- vCC/vCC+1
+    bl      __aeabi_cdcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPG_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_DOUBLE_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/OP_IF_EQ.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv5te/OP_IF_NE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv5te/OP_IF_LT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv5te/OP_IF_GE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv5te/OP_IF_GT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv5te/OP_IF_LE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/OP_NEG_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/OP_NOT_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/OP_NEG_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/OP_NOT_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/OP_NEG_FLOAT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/OP_NEG_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/OP_INT_TO_LONG.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/OP_INT_TO_FLOAT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_i2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/OP_INT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_i2d                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/OP_LONG_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/OP_LONG_TO_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2d                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/OP_FLOAT_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_f2iz                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+#if 0
+@include "armv5te/unop.S" {"instr":"bl      f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x4f000000             @ (float)maxint
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (7fffffff)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xcf000000             @ (float)minint
+    bl      __aeabi_fcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    ldmeqfd sp!, {r4, pc}               @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2iz                @ convert float to int
+    ldmfd   sp!, {r4, pc}
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/OP_FLOAT_TO_LONG.S */
+@include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/OP_FLOAT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_f2d                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/OP_DOUBLE_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_d2iz                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl      d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r2, #0x80000000             @ maxint, as a double (low word)
+    mov     r2, r2, asr #9              @  0xffc00000
+    sub     sp, sp, #4                  @ align for EABI
+    mvn     r3, #0xbe000000             @ maxint, as a double (high word)
+    sub     r3, r3, #0x00200000         @  0x41dfffff
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (0x7fffffff)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc1000000             @ minint, as a double (high word)
+    add     r3, r3, #0x00e00000         @  0xc1e00000
+    mov     r2, #0                      @ minint, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    beq     1f                          @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2iz                @ convert double to int
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/OP_DOUBLE_TO_LONG.S */
+@include "armv5te/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/OP_DOUBLE_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_d2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/OP_INT_TO_BYTE.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #24                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #24                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/OP_INT_TO_CHAR.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, lsr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/OP_INT_TO_SHORT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/OP_ADD_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fadd                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/OP_SUB_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fsub                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/OP_MUL_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fmul                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/OP_DIV_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fdiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/OP_ADD_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dadd                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/OP_SUB_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dsub                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/OP_MUL_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dmul                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/OP_DIV_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ddiv                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/OP_ADD_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/OP_SUB_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/OP_DIV_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/OP_AND_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/OP_OR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/OP_XOR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/OP_SHL_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/OP_SHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/OP_USHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/OP_ADD_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/OP_SUB_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/OP_DIV_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/OP_AND_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/OP_OR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/OP_XOR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/OP_ADD_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fadd                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/OP_SUB_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fsub                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/OP_MUL_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fmul                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/OP_DIV_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fdiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/OP_ADD_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dadd                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/OP_SUB_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dsub                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/OP_MUL_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dmul                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/OP_DIV_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ddiv                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/OP_ADD_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/OP_DIV_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/OP_AND_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/OP_OR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/OP_XOR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    and     r2, r2, #15
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/OP_IPUT_QUICK.S */
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A(+)
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPL_FLOAT_gt_or_nan:
+    mov     r1, r9                      @ reverse order
+    mov     r0, r10
+    bl      __aeabi_cfcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPL_FLOAT_finish
+    mvn     r1, #0                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPL_FLOAT_finish
+
+
+#if 0       /* "clasic" form */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpeq              @ r0<- (vBB == vCC)
+    cmp     r0, #0                      @ equal?
+    movne   r1, #0                      @ yes, result is 0
+    bne     OP_CMPL_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmplt              @ r0<- (vBB < vCC)
+    cmp     r0, #0                      @ less than?
+    b       OP_CMPL_FLOAT_continue
+@%break
+
+OP_CMPL_FLOAT_continue:
+    mvnne   r1, #0                      @ yes, result is -1
+    bne     OP_CMPL_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpgt              @ r0<- (vBB > vCC)
+    cmp     r0, #0                      @ greater than?
+    beq     OP_CMPL_FLOAT_nan               @ no, must be NaN
+    mov     r1, #1                      @ yes, result is 1
+    @ fall through to _finish
+
+OP_CMPL_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * This is expected to be uncommon, so we double-branch (once to here,
+     * again back to _finish).
+     */
+OP_CMPL_FLOAT_nan:
+    mvn     r1, #0                            @ r1<- 1 or -1 for NaN
+    b       OP_CMPL_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPG_FLOAT */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPG_FLOAT_gt_or_nan:
+    mov     r1, r9                      @ reverse order
+    mov     r0, r10
+    bl      __aeabi_cfcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPG_FLOAT_finish
+    mov     r1, #1                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPG_FLOAT_finish
+
+
+#if 0       /* "clasic" form */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpeq              @ r0<- (vBB == vCC)
+    cmp     r0, #0                      @ equal?
+    movne   r1, #0                      @ yes, result is 0
+    bne     OP_CMPG_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmplt              @ r0<- (vBB < vCC)
+    cmp     r0, #0                      @ less than?
+    b       OP_CMPG_FLOAT_continue
+@%break
+
+OP_CMPG_FLOAT_continue:
+    mvnne   r1, #0                      @ yes, result is -1
+    bne     OP_CMPG_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpgt              @ r0<- (vBB > vCC)
+    cmp     r0, #0                      @ greater than?
+    beq     OP_CMPG_FLOAT_nan               @ no, must be NaN
+    mov     r1, #1                      @ yes, result is 1
+    @ fall through to _finish
+
+OP_CMPG_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * This is expected to be uncommon, so we double-branch (once to here,
+     * again back to _finish).
+     */
+OP_CMPG_FLOAT_nan:
+    mov     r1, #1                            @ r1<- 1 or -1 for NaN
+    b       OP_CMPG_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPL_DOUBLE */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPL_DOUBLE_gt_or_nan:
+    ldmia   r10, {r0-r1}                @ reverse order
+    ldmia   r9, {r2-r3}
+    bl      __aeabi_cdcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPL_DOUBLE_finish
+    mvn     r1, #0                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPL_DOUBLE_finish
+
+/* continuation for OP_CMPG_DOUBLE */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPG_DOUBLE_gt_or_nan:
+    ldmia   r10, {r0-r1}                @ reverse order
+    ldmia   r9, {r2-r3}
+    bl      __aeabi_cdcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPG_DOUBLE_finish
+    mov     r1, #1                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPG_DOUBLE_finish
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     0
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     0
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv7-a-neon.S b/vm/mterp/out/InterpAsm-armv7-a-neon.S
new file mode 100644
index 0000000..c3419c2
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a-neon.S
@@ -0,0 +1,16814 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv7-a/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    dmb     st
+#else
+    /* not SMP */
+#endif
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv6t2/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    ubfx    r0, rINST, #8, #4           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv6t2/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv6t2/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv6t2/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv6t2/OP_IF_EQ.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv6t2/OP_IF_NE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv6t2/OP_IF_LT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv6t2/OP_IF_GE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv6t2/OP_IF_GT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv6t2/OP_IF_LE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv6t2/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv6t2/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv6t2/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv6t2/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv6t2/OP_NEG_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv6t2/OP_NOT_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv6t2/OP_NEG_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv6t2/OP_NOT_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv6t2/OP_NEG_FLOAT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv6t2/OP_NEG_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv6t2/OP_INT_TO_LONG.S */
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitos  s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitod  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv6t2/OP_LONG_TO_FLOAT.S */
+/* File: armv6t2/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv6t2/OP_LONG_TO_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2d                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizs s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv6t2/OP_FLOAT_TO_LONG.S */
+@include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtds  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv6t2/OP_DOUBLE_TO_LONG.S */
+@include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtsd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv6t2/OP_INT_TO_BYTE.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxtb    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv6t2/OP_INT_TO_CHAR.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    uxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv6t2/OP_INT_TO_SHORT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: 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, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv6t2/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv6t2/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv6t2/OP_IPUT_QUICK.S */
+    /* For: iput-quick, iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv6t2/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    ubfx    r1, rINST, #8, #4           @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S
new file mode 100644
index 0000000..2542245
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -0,0 +1,16814 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv7-a/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    dmb     st
+#else
+    /* not SMP */
+#endif
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv6t2/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    ubfx    r0, rINST, #8, #4           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv6t2/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv6t2/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv6t2/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv6t2/OP_IF_EQ.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv6t2/OP_IF_NE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv6t2/OP_IF_LT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv6t2/OP_IF_GE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv6t2/OP_IF_GT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv6t2/OP_IF_LE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv6t2/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv6t2/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv6t2/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv6t2/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv6t2/OP_NEG_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv6t2/OP_NOT_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv6t2/OP_NEG_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv6t2/OP_NOT_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv6t2/OP_NEG_FLOAT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv6t2/OP_NEG_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv6t2/OP_INT_TO_LONG.S */
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitos  s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitod  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv6t2/OP_LONG_TO_FLOAT.S */
+/* File: armv6t2/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv6t2/OP_LONG_TO_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2d                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizs s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv6t2/OP_FLOAT_TO_LONG.S */
+@include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtds  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv6t2/OP_DOUBLE_TO_LONG.S */
+@include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtsd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv6t2/OP_INT_TO_BYTE.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxtb    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv6t2/OP_INT_TO_CHAR.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    uxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv6t2/OP_INT_TO_SHORT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: 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, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv6t2/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv6t2/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv6t2/OP_IPUT_QUICK.S */
+    /* For: iput-quick, iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv6t2/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    ubfx    r1, rINST, #8, #4           @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-mips.S b/vm/mterp/out/InterpAsm-mips.S
new file mode 100644
index 0000000..3e1c670
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-mips.S
@@ -0,0 +1,18586 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'mips'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: mips/header.S */
+#include "../common/asm-constants.h"
+#include "../common/mips-defines.h"
+#include <asm/regdef.h>
+#include <asm/fpregdef.h>
+
+#ifdef __mips_hard_float
+#define HARD_FLOAT
+#else
+#define SOFT_FLOAT
+#endif
+
+#if (__mips==32) && (__mips_isa_rev>=2)
+#define MIPS32R2
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		self (Thread) pointer
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+
+/* single-purpose registers, given names for clarity */
+#define rPC s0
+#define rFP s1
+#define rSELF s2
+#define rIBASE s3
+#define rINST s4
+#define rOBJ s5
+#define rBIX s6
+#define rTEMP s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0 a0
+#define rARG1 a1
+#define rARG2 a2
+#define rARG3 a3
+#define rRESULT0 v0
+#define rRESULT1 v1
+#else
+#define rARG0 a1
+#define rARG1 a0
+#define rARG2 a3
+#define rARG3 a2
+#define rRESULT0 v1
+#define rRESULT1 v0
+#endif
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_SELF() lw rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF() sw rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF() lw rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF() sw rFP, offThread_curFrame(rSELF)
+#define LOAD_PC_FP_FROM_SELF() \
+	LOAD_PC_FROM_SELF();   \
+	LOAD_FP_FROM_SELF()
+#define SAVE_PC_FP_TO_SELF()   \
+	SAVE_PC_TO_SELF();     \
+	SAVE_FP_TO_SELF()
+
+#define EXPORT_PC() \
+    sw        rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+    subu      rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST() lhu rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \
+    addu      rPC, rPC, ((_count) * 2)
+
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+    lhu       _dreg, ((_count)*2)(_sreg) ;            \
+    addu      _sreg, _sreg, (_count)*2
+
+#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \
+    lhu       rINST, (rPC)
+
+#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC)
+#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+
+#define GET_PREFETCHED_OPCODE(dreg, sreg)   andi     dreg, sreg, 255
+
+#define GOTO_OPCODE(rd) sll rd, rd, 7; \
+    addu      rd, rIBASE, rd; \
+    jr        rd
+
+#define GOTO_OPCODE_BASE(_base, rd)  sll rd, rd, 7; \
+    addu      rd, _base, rd; \
+    jr        rd
+
+#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix)
+
+#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix) STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \
+    sll       dst, dst, 7; \
+    addu      dst, rIBASE, dst; \
+    sll       t8, rix, 2; \
+    addu      t8, t8, rFP; \
+    jr        dst; \
+    sw        rd, 0(t8); \
+    .set reorder
+
+#define SET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; s.s rd, (AT); .set at
+
+
+#define GET_OPA(rd) srl rd, rINST, 8
+#ifndef MIPS32R2
+#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf
+#else
+#define GET_OPA4(rd) ext rd, rINST, 8, 4
+#endif
+#define GET_OPB(rd) srl rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd, off) lw rd, offThread_##off## (rSELF)
+
+#define LOAD_rSELF_method(rd) LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd) LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd) LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd) LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd) LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd) LOAD_rSELF_OFF(rd, bailPtr)
+#define LOAD_rSELF_SelfSuspendCount(rd) LOAD_rSELF_OFF(rd, SelfSuspendCount)
+
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd, rbase, roff, rshift) .set noat; \
+    sll       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define EAS1(rd, rbase, roff) EASN(rd, rbase, roff, 1)
+#define EAS2(rd, rbase, roff) EASN(rd, rbase, roff, 2)
+#define EAS3(rd, rbase, roff) EASN(rd, rbase, roff, 3)
+#define EAS4(rd, rbase, roff) EASN(rd, rbase, roff, 4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd, rbase, roff, rshift) .set noat; \
+    srl       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; lw rd, 0(AT); .set at
+
+#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; sw rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase)
+#define LOADu2_RB_OFF(rd, rbase, off) lhu rd, off(rbase)
+#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \
+    s.s       rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \
+    l.s       rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, (off+4)(rbase); \
+    s.s       rhi, (off)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, (off+4)(rbase); \
+    l.s       rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0)
+#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0)
+
+#define vSTORE64(rlo, rhi, rbase) vSTORE64_off(rlo, rhi, rbase, 0)
+#define vLOAD64(rlo, rhi, rbase) vLOAD64_off(rlo, rhi, rbase, 0)
+
+#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0)
+#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0)
+
+#define STORE64_lo(rd, rbase) sw rd, 0(rbase)
+#define STORE64_hi(rd, rbase) sw rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd, rbase) LOAD_RB_OFF(rd, rbase, offThread_exception)
+#define LOAD_base_offArrayObject_length(rd, rbase) LOAD_RB_OFF(rd, rbase, offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd, rbase) LOAD_RB_OFF(rd, rbase, offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd, rbase) LOAD_RB_OFF(rd, rbase, offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_name)
+#define LOAD_base_offObject_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd, rbase) LOADu2_RB_OFF(rd, rbase, offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd, rbase) STORE_RB_OFF(rd, rbase, offThread_exception)
+
+
+#define STACK_STORE(rd, off) sw rd, off(sp)
+#define STACK_LOAD(rd, off) lw rd, off(sp)
+#define CREATE_STACK(n) subu sp, sp, n
+#define DELETE_STACK(n) addu sp, sp, n
+
+#define SAVE_RA(offset) STACK_STORE(ra, offset)
+#define LOAD_RA(offset) STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest, addr) la dest, addr
+#define LOAD_IMM(dest, imm) li dest, imm
+#define MOVE_REG(dest, src) move dest, src
+#define RETURN jr ra
+#define STACK_SIZE 128
+
+#define STACK_OFFSET_ARG04 16
+#define STACK_OFFSET_ARG05 20
+#define STACK_OFFSET_ARG06 24
+#define STACK_OFFSET_ARG07 28
+#define STACK_OFFSET_SCR   32
+#define STACK_OFFSET_SCRMX 80
+#define STACK_OFFSET_GP    84
+#define STACK_OFFSET_rFP   112
+
+#define JAL(n) jal n
+#define BAL(n) bal n
+
+#define STACK_STORE_RA() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(gp, STACK_OFFSET_GP); \
+    STACK_STORE(ra, 124)
+
+#define STACK_STORE_S0() STACK_STORE_RA(); \
+    STACK_STORE(s0, 116)
+
+#define STACK_STORE_S0S1() STACK_STORE_S0(); \
+    STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define STACK_LOAD_RA() STACK_LOAD(ra, 124); \
+    STACK_LOAD(gp, STACK_OFFSET_GP); \
+    DELETE_STACK(STACK_SIZE)
+
+#define STACK_LOAD_S0() STACK_LOAD(s0, 116); \
+    STACK_LOAD_RA()
+
+#define STACK_LOAD_S0S1() STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD_S0()
+
+#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(ra, 124); \
+    STACK_STORE(fp, 120); \
+    STACK_STORE(s0, 116); \
+    STACK_STORE(s1, STACK_OFFSET_rFP); \
+    STACK_STORE(s2, 108); \
+    STACK_STORE(s3, 104); \
+    STACK_STORE(s4, 100); \
+    STACK_STORE(s5, 96); \
+    STACK_STORE(s6, 92); \
+    STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \
+    STACK_LOAD(s7, 88); \
+    STACK_LOAD(s6, 92); \
+    STACK_LOAD(s5, 96); \
+    STACK_LOAD(s4, 100); \
+    STACK_LOAD(s3, 104); \
+    STACK_LOAD(s2, 108); \
+    STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD(s0, 116); \
+    STACK_LOAD(fp, 120); \
+    STACK_LOAD(ra, 124); \
+    DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: mips/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro SMP_DMB
+#if ANDROID_SMP != 0
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    // FIXME: Is this really needed?
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
+
+/* File: mips/entry.S */
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align 2
+    .global dvmMterpStdRun
+    .ent dvmMterpStdRun
+    .frame sp, STACK_SIZE, ra
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+
+dvmMterpStdRun:
+    .set noreorder
+    .cpload t9
+    .set reorder
+/* Save to the stack. Frame size = STACK_SIZE */
+    STACK_STORE_FULL()
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+    .cprestore STACK_OFFSET_GP
+
+    addu      fp, sp, STACK_SIZE           #  Move Frame Pointer to the base of frame
+    /* save stack pointer, add magic word for debuggerd */
+    sw        sp, offThread_bailPtr(a0)      # Save SP
+
+    /* set up "named" registers, figure out entry point */
+    move      rSELF, a0                    #  set rSELF
+    LOAD_PC_FROM_SELF()
+    LOAD_FP_FROM_SELF()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()                           #  load rINST from rPC
+    sw        zero, offThread_inJitCodeCache(rSELF)
+#if !defined(WITH_SELF_VERIFICATION)
+    bnez      a0, common_updateProfile     # profiling is enabled
+#else
+    lw       a2, offThread_shadowSpace(rSELF) # to find out the jit exit state
+    beqz     a0, 1f                        # profiling is disabled
+    lw       a3, offShadowSpace_jitExitState(a2) # jit exit state
+    li	     t0, kSVSTraceSelect
+    bne      a3, t0, 2f
+    li       a2, kJitTSelectRequestHot     # ask for trace selection
+    b        common_selectTrace            # go build the trace
+2:
+    li       a4, kSVSNoProfile
+    beq      a3, a4, 1f                    # don't profile the next instruction?
+    b        common_updateProfile          # collect profiles
+#endif
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                           #  load rINST from rPC
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+.Lbad_arg:
+    la        a0, .LstrBadEntryPoint
+    #a1 holds value of entryPoint
+    JAL(printf)
+    JAL(dvmAbort)
+
+    .end dvmMterpStdRun
+
+    .global dvmMterpStdBail
+    .ent dvmMterpStdBail
+
+/* Restore the stack pointer and all the registers stored at sp from the save
+ * point established  on entry. Return to whoever called dvmMterpStdRun.
+ *
+ * On entry:
+ *   a0    Thread* self
+ */
+dvmMterpStdBail:
+    lw        sp, offThread_bailPtr(a0)      #  Restore sp
+    STACK_LOAD_FULL()
+    jr        ra
+
+    .end dvmMterpStdBail
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NOP: /* 0x00 */
+/* File: mips/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)                  #  advance to next instr, load rINST
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)                        #  execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type dalvik_inst, @function
+dalvik_inst:
+    .ent dalvik_inst
+    .end dalvik_inst
+#endif
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE: /* 0x01 */
+/* File: mips/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: mips/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    GET_OPA(a0)                            #  a0 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AA] <- a2
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_16: /* 0x03 */
+/* File: mips/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(a1, 2)                           #  a1 <- BBBB
+    FETCH(a0, 1)                           #  a0 <- AAAA
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AAAA] <- a2 and jump
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: mips/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[B]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: mips/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 1)                           #  a3 <- BBBB
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: mips/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 2)                           #  a3 <- BBBB
+    FETCH(a2, 1)                           #  a2 <- AAAA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AAAA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[AAAA] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: mips/OP_MOVE_OBJECT.S */
+/* File: mips/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: mips/OP_MOVE_OBJECT_FROM16.S */
+/* File: mips/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    GET_OPA(a0)                            #  a0 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AA] <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: mips/OP_MOVE_OBJECT_16.S */
+/* File: mips/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(a1, 2)                           #  a1 <- BBBB
+    FETCH(a0, 1)                           #  a0 <- AAAA
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AAAA] <- a2 and jump
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: mips/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_rSELF_retval(a0)                  #  a0 <- self->retval.i
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a2, t0)              #  fp[AA] <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: mips/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- retval.j
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: mips/OP_MOVE_RESULT_OBJECT.S */
+/* File: mips/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_rSELF_retval(a0)                  #  a0 <- self->retval.i
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a2, t0)              #  fp[AA] <- a0
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: mips/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    LOAD_offThread_exception(a3, rSELF)    #  a3 <- dvmGetException bypass
+    li        a1, 0                        #  a1 <- 0
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    SET_VREG(a3, a2)                       #  fp[AA] <- exception obj
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE_offThread_exception(a1, rSELF)   #  dvmClearException bypass
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: mips/OP_RETURN_VOID.S */
+    b         common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN: /* 0x0f */
+/* File: mips/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a0, a2)                       #  a0 <- vAA
+    sw        a0, offThread_retval(rSELF)  #  retval.i <- vAA
+    b         common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: mips/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vAA/vAA+1
+    STORE64(a0, a1, a3)                    #  retval <- a0/a1
+    b         common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: mips/OP_RETURN_OBJECT.S */
+/* File: mips/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a0, a2)                       #  a0 <- vAA
+    sw        a0, offThread_retval(rSELF)  #  retval.i <- vAA
+    b         common_returnFromMethod
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_4: /* 0x12 */
+/* File: mips/OP_CONST_4.S */
+    # const/4 vA,                          /* +B */
+    sll       a1, rINST, 16                #  a1 <- Bxxx0000
+    GET_OPA(a0)                            #  a0 <- A+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sra       a1, a1, 28                   #  a1 <- sssssssB (sign-extended)
+    and       a0, a0, 15
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    SET_VREG_GOTO(a1, a0, t0)              #  fp[A] <- a1
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_16: /* 0x13 */
+/* File: mips/OP_CONST_16.S */
+    # const/16 vAA,                        /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST: /* 0x14 */
+/* File: mips/OP_CONST.S */
+    # const vAA,                           /* +BBBBbbbb */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a1, a1, 16
+    or        a0, a1, a0                   #  a0 <- BBBBbbbb
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: mips/OP_CONST_HIGH16.S */
+    # const/high16 vAA,                    /* +BBBB0000 */
+    FETCH(a0, 1)                           #  a0 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a0, a0, 16                   #  a0 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: mips/OP_CONST_WIDE_16.S */
+    # const-wide/16 vAA,                   /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: mips/OP_CONST_WIDE_32.S */
+    # const-wide/32 vAA,                   /* +BBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- 0000bbbb (low)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_S(a2, 2)                         #  a2 <- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a2, a2, 16
+    or        a0, a0, a2                   #  a0 <- BBBBbbbb
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: mips/OP_CONST_WIDE.S */
+    # const-wide vAA,                      /* +HHHHhhhhBBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (low middle)
+    FETCH(a2, 3)                           #  a2 <- hhhh (high middle)
+    sll       a1, 16 #
+    or        a0, a1                       #  a0 <- BBBBbbbb (low word)
+    FETCH(a3, 4)                           #  a3 <- HHHH (high)
+    GET_OPA(t1)                            #  t1 <- AA
+    sll       a3, 16
+    or        a1, a3, a2                   #  a1 <- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)                  #  advance rPC, load rINST
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, t1)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: mips/OP_CONST_WIDE_HIGH16.S */
+    # const-wide/high16 vAA,               /* +BBBB000000000000 */
+    FETCH(a1, 1)                           #  a1 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    li        a0, 0                        #  a0 <- 00000000
+    sll       a1, 16                       #  a1 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: mips/OP_CONST_STRING.S */
+    # const/string vAA, String             /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    # not yet resolved?
+    bnez      v0, .LOP_CONST_STRING_resolve
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1:   BBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.LOP_CONST_STRING_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: mips/OP_CONST_STRING_JUMBO.S */
+    # const/string vAA, String             /* BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    sll       a1, a1, 16
+    or        a1, a1, a0                   #  a1 <- BBBBbbbb
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    bnez      v0, .LOP_CONST_STRING_JUMBO_resolve
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1: BBBBBBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.LOP_CONST_STRING_JUMBO_resolve:
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t1)            #  vAA <- v0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: mips/OP_CONST_CLASS.S */
+    # const/class vAA, Class               /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- dvmDex->pResClasses
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResClasses[BBBB]
+
+    bnez      v0, .LOP_CONST_CLASS_resolve      #  v0!=0 => resolved-ok
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  a1: BBBB (Class ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- Class reference
+    # failed==0?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.LOP_CONST_CLASS_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: mips/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    move      a0, rSELF                    #  a0 <- self
+    EXPORT_PC()                            #  export PC so we can grab stack trace
+    # null object?
+    beqz      a1, common_errNullObject     #  null object, throw an exception
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    JAL(dvmLockObject)                     #  call(self, obj)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: mips/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EXPORT_PC()                            #  before fetch: export the PC
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    # null object?
+    beqz      a1, 1f
+    move      a0, rSELF                    #  a0 <- self
+    JAL(dvmUnlockObject)                   #  v0 <- success for unlock(self, obj)
+    # failed?
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    beqz      v0, common_exceptionThrown   #  yes, exception is pending
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    b         common_errNullObject
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: mips/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    # check-cast vAA, class                /* BBBB */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- object
+    LOAD_rSELF_methodClassDex(a0)          #  a0 <- pDvmDex
+    LOAD_base_offDvmDex_pResClasses(a0, a0) #  a0 <- pDvmDex->pResClasses
+    # is object null?
+    beqz      rOBJ, .LOP_CHECK_CAST_okay       #  null obj, cast always succeeds
+    LOAD_eas2(a1, a0, a2)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .LOP_CHECK_CAST_resolve      #  not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    # same class (trivial success)?
+    bne       a0, a1, .LOP_CHECK_CAST_fullcheck #  no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0 holds obj->clazz
+     *  a1 holds class resolved from BBBB
+     *  rOBJ holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    move      rBIX,a1                      #  avoid ClassObject getting clobbered
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    # failed?
+    bnez      v0, .LOP_CHECK_CAST_okay         #  no, success
+    b         .LOP_CHECK_CAST_castfailure
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: mips/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    # instance-of vA, vB, class            /* CCCC */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- pDvmDex
+    # is object null?
+    beqz      a0, .LOP_INSTANCE_OF_store        #  null obj, not an instance, store a0
+    FETCH(a3, 1)                           #  a3 <- CCCC
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- pDvmDex->pResClasses
+    LOAD_eas2(a1, a2, a3)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .LOP_INSTANCE_OF_resolve      #  not resolved, do it now
+.LOP_INSTANCE_OF_resolved:                   #  a0=obj->clazz, a1=resolved class
+    # same class (trivial success)?
+    beq       a0, a1, .LOP_INSTANCE_OF_trivial  #  yes, trivial finish
+    b         .LOP_INSTANCE_OF_fullcheck        #  no, do full check
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  rOBJ holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    li        a0, 1                        #  indicate success
+    # fall thru
+    /*
+     * a0   holds boolean result
+     * rOBJ holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, rOBJ)                     #  vA <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: mips/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    GET_OPB(a1)                            #  a1 <- B
+    GET_OPA4(a2)                           #  a2 <- A+
+    GET_VREG(a0, a1)                       #  a0 <- vB (object ref)
+    # is object null?
+    beqz      a0, common_errNullObject     #  yup, fail
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- array length
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a3, a2, t0)              #  vA <- length
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: mips/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    # new-instance vAA, class              /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX <- &resolved_class
+#endif
+    EXPORT_PC()                            #  req'd for init, resolve, alloc
+    # already resolved?
+    beqz      a0, .LOP_NEW_INSTANCE_resolve      #  no, resolve it now
+.LOP_NEW_INSTANCE_resolved:                      #  a0=class
+    lbu       a1, offClassObject_status(a0) #  a1 <- ClassStatus enum
+    # has class been initialized?
+    li        t0, CLASS_INITIALIZED
+    move      rOBJ, a0                     #  save a0
+    bne       a1, t0, .LOP_NEW_INSTANCE_needinit #  no, init class now
+
+.LOP_NEW_INSTANCE_initialized:                   #  a0=class
+    LOAD_base_offClassObject_accessFlags(a3, a0) #  a3 <- clazz->accessFlags
+    li        a1, ALLOC_DONT_TRACK         #  flags for alloc call
+    # a0=class
+    JAL(dvmAllocObject)                    #  v0 <- new object
+    GET_OPA(a3)                            #  a3 <- AA
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    lhu       a1, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    and       a1, kSubModeJitTraceBuild    #  under construction?
+    bnez      a1, .LOP_NEW_INSTANCE_jitCheck
+#else
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+#endif
+    b         .LOP_NEW_INSTANCE_continue
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: mips/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    FETCH(a2, 1)                           #  a2 <- CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    GET_VREG(a1, a0)                       #  a1 <- vB (array length)
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- resolved class
+    # check length
+    bltz      a1, common_errNegativeArraySize #  negative length, bail - len in a1
+    EXPORT_PC()                            #  req'd for resolve, alloc
+    # already resolved?
+    beqz      a0, .LOP_NEW_ARRAY_resolve
+
+    /*
+     * Finish allocation.
+     *
+     *  a0 holds class
+     *  a1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    li        a2, ALLOC_DONT_TRACK         #  don't track in local refs table
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(clazz, length, flags)
+    GET_OPA4(a2)                           #  a2 <- A+
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(v0, a2)                       #  vA <- v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: mips/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, type       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    EXPORT_PC()                            #  need for resolve and alloc
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+    GET_OPA(rOBJ)                          #  rOBJ <- AA or BA
+    # already resolved?
+    bnez      a0, .LOP_FILLED_NEW_ARRAY_continue     #  yes, continue on
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: mips/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: mips/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, type       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    EXPORT_PC()                            #  need for resolve and alloc
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+    GET_OPA(rOBJ)                          #  rOBJ <- AA or BA
+    # already resolved?
+    bnez      a0, .LOP_FILLED_NEW_ARRAY_RANGE_continue     #  yes, continue on
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: mips/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a1, a1, 16                   #  a1 <- BBBBbbbb
+    or        a1, a0, a1                   #  a1 <- BBBBbbbb
+    GET_VREG(a0, a3)                       #  a0 <- vAA (array object)
+    EAS1(a1, rPC, a1)                      #  a1 <- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC()
+    JAL(dvmInterpHandleFillArrayData)      #  fill the array with predefined data
+    # 0 means an exception is thrown
+    beqz      v0, common_exceptionThrown   #  has exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_THROW: /* 0x27 */
+/* File: mips/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (exception object)
+    EXPORT_PC()                            #  exception handler can throw
+    # null object?
+    beqz      a1, common_errNullObject     #  yes, throw an NPE instead
+    # bypass dvmSetException, just store it
+    STORE_offThread_exception(a1, rSELF)   #  thread->exception <- obj
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_GOTO: /* 0x28 */
+/* File: mips/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    sll       a0, rINST, 16                #  a0 <- AAxx0000
+    sra       a1, a0, 24                   #  a1 <- ssssssAA (sign-extended)
+    addu      a2, a1, a1                   #  a2 <- byte offset
+    /* If backwards branch refresh rBASE */
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) check for trace hotness
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_GOTO_16: /* 0x29 */
+/* File: mips/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(a0, 1)                         #  a0 <- ssssAAAA (sign-extended)
+    addu      a1, a0, a0                   #  a1 <- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) hot trace head?
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_GOTO_32: /* 0x2a */
+/* File: mips/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(a0, 1)                           #  a0 <- aaaa (lo)
+    FETCH(a1, 2)                           #  a1 <- AAAA (hi)
+    sll       a1, a1, 16
+    or        a0, a0, a1                   #  a0 <- AAAAaaaa
+    addu      a1, a0, a0                   #  a1 <- byte offset
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    blez      a1, common_testUpdateProfile # (a0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgtz      a0, 2f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+2:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: mips/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       t0, a1, 16
+    or        a0, a0, t0                   #  a0 <- BBBBbbbb
+    GET_VREG(a1, a3)                       #  a1 <- vAA
+    EAS1(a0, rPC, a0)                      #  a0 <- PC + BBBBbbbb*2
+    JAL(dvmInterpHandlePackedSwitch)                             #  a0 <- code-unit branch offset
+    addu      a1, v0, v0                   #  a1 <- byte offset
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bnez      a0, common_updateProfile
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: mips/OP_SPARSE_SWITCH.S */
+/* File: mips/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       t0, a1, 16
+    or        a0, a0, t0                   #  a0 <- BBBBbbbb
+    GET_VREG(a1, a3)                       #  a1 <- vAA
+    EAS1(a0, rPC, a0)                      #  a0 <- PC + BBBBbbbb*2
+    JAL(dvmInterpHandleSparseSwitch)                             #  a0 <- code-unit branch offset
+    addu      a1, v0, v0                   #  a1 <- byte offset
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bnez      a0, common_updateProfile
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: mips/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8
+#ifdef SOFT_FLOAT
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- vBB
+    GET_VREG(rBIX, a3)                     #  rBIX <- vCC
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__eqsf2)                           #  a0 <- (vBB == vCC)
+    li        rTEMP, 0                     # set rTEMP to 0
+    beqz      v0, OP_CMPL_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__ltsf2)                           #  a0 <- (vBB < vCC)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPL_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    b         OP_CMPL_FLOAT_continue
+#else
+    GET_VREG_F(ft0, a2)
+    GET_VREG_F(ft1, a3)
+    c.olt.s   fcc0, ft0, ft1               # Is ft0 < ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPL_FLOAT_finish
+    c.olt.s   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPL_FLOAT_finish
+    c.eq.s    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPL_FLOAT_finish
+    b         OP_CMPL_FLOAT_nan
+
+#endif
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: mips/OP_CMPG_FLOAT.S */
+/* File: mips/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8
+#ifdef SOFT_FLOAT
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- vBB
+    GET_VREG(rBIX, a3)                     #  rBIX <- vCC
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__eqsf2)                           #  a0 <- (vBB == vCC)
+    li        rTEMP, 0                     # set rTEMP to 0
+    beqz      v0, OP_CMPG_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__ltsf2)                           #  a0 <- (vBB < vCC)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPG_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    b         OP_CMPG_FLOAT_continue
+#else
+    GET_VREG_F(ft0, a2)
+    GET_VREG_F(ft1, a3)
+    c.olt.s   fcc0, ft0, ft1               # Is ft0 < ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPG_FLOAT_finish
+    c.olt.s   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPG_FLOAT_finish
+    c.eq.s    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPG_FLOAT_finish
+    b         OP_CMPG_FLOAT_nan
+
+#endif
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: mips/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       rOBJ, a0, 255                #  s0 <- BB
+    srl       rBIX, a0, 8                  #  t0 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s0 <- &fp[BB]
+    EAS2(rBIX, rFP, rBIX)                  #  t0 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__eqdf2)                           #  cmp <=: C clear if <, Z set if eq
+    li        rTEMP, 0
+    beqz      v0, OP_CMPL_DOUBLE_finish
+
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__ltdf2)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPL_DOUBLE_finish
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    b         OP_CMPL_DOUBLE_continue
+#else
+    LOAD64_F(ft0, ft0f, rOBJ)
+    LOAD64_F(ft1, ft1f, rBIX)
+    c.olt.d   fcc0, ft0, ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPL_DOUBLE_finish
+    c.olt.d   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPL_DOUBLE_finish
+    c.eq.d    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPL_DOUBLE_finish
+    b         OP_CMPL_DOUBLE_nan
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: mips/OP_CMPG_DOUBLE.S */
+/* File: mips/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       rOBJ, a0, 255                #  s0 <- BB
+    srl       rBIX, a0, 8                  #  t0 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s0 <- &fp[BB]
+    EAS2(rBIX, rFP, rBIX)                  #  t0 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__eqdf2)                           #  cmp <=: C clear if <, Z set if eq
+    li        rTEMP, 0
+    beqz      v0, OP_CMPG_DOUBLE_finish
+
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__ltdf2)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPG_DOUBLE_finish
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    b         OP_CMPG_DOUBLE_continue
+#else
+    LOAD64_F(ft0, ft0f, rOBJ)
+    LOAD64_F(ft1, ft1f, rBIX)
+    c.olt.d   fcc0, ft0, ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPG_DOUBLE_finish
+    c.olt.d   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPG_DOUBLE_finish
+    c.eq.d    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPG_DOUBLE_finish
+    b         OP_CMPG_DOUBLE_nan
+#endif
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: mips/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;	# (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;	# (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, a3)                     #  a2/a3 <- vCC/vCC+1
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    slt       t0, a1, a3                   #  compare hi
+    sgt       t1, a1, a3
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0)
+    bnez      v0, .LOP_CMP_LONG_finish
+    # at this point x.hi==y.hi
+    sltu      t0, a0, a2                   #  compare lo
+    sgtu      t1, a0, a2
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0) for [< > =]
+
+.LOP_CMP_LONG_finish:
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_EQ: /* 0x32 */
+/* File: mips/OP_IF_EQ.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    bne a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_NE: /* 0x33 */
+/* File: mips/OP_IF_NE.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    beq a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LT: /* 0x34 */
+/* File: mips/OP_IF_LT.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    bge a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GE: /* 0x35 */
+/* File: mips/OP_IF_GE.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    blt a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GT: /* 0x36 */
+/* File: mips/OP_IF_GT.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    ble a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LE: /* 0x37 */
+/* File: mips/OP_IF_LE.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    bgt a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: mips/OP_IF_EQZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    bne a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: mips/OP_IF_NEZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    beq a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: mips/OP_IF_LTZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    bge a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: mips/OP_IF_GEZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    blt a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: mips/OP_IF_GTZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    ble a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: mips/OP_IF_LEZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    bgt a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: mips/OP_UNUSED_3E.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: mips/OP_UNUSED_3F.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: mips/OP_UNUSED_40.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: mips/OP_UNUSED_41.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: mips/OP_UNUSED_42.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: mips/OP_UNUSED_43.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET: /* 0x44 */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 2
+    EASN(a0, a0, a1, 2)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lw a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: mips/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64_off(a2, a3, a0, offArrayObject_contents)
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a2, a3, rOBJ)                  #  vAA/vAA+1 <- a2/a3
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: mips/OP_AGET_OBJECT.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 2
+    EASN(a0, a0, a1, 2)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lw a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: mips/OP_AGET_BOOLEAN.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lbu a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: mips/OP_AGET_BYTE.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lb a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: mips/OP_AGET_CHAR.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lhu a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: mips/OP_AGET_SHORT.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lh a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT: /* 0x4b */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 2
+    EASN(a0, a0, a1, 2)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sw a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: mips/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t0 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a2, a3, rOBJ)                   #  a2/a3 <- vAA/vAA+1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64_off(a2, a3, a0, offArrayObject_contents) #  a2/a3 <- vBB[vCC]
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: mips/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t1)                            #  t1 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(rINST, a2)                    #  rINST <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    GET_VREG(rBIX, t1)                     #  rBIX <- vAA
+    # null array object?
+    beqz      rINST, common_errNullObject  #  yes, bail
+
+    LOAD_base_offArrayObject_length(a3, rINST) #  a3 <- arrayObj->length
+    EAS2(rOBJ, rINST, a1)                  #  rOBJ <- arrayObj + index*width
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  rBIX = vAA (obj)
+     *  rOBJ = offset into array (vBB + vCC * width)
+     */
+    bnez      rBIX, .LOP_APUT_OBJECT_checks     #  yes, skip type checks
+.LOP_APUT_OBJECT_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sw        rBIX, offArrayObject_contents(rOBJ) #  vBB[vCC] <- vAA
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: mips/OP_APUT_BOOLEAN.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sb a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: mips/OP_APUT_BYTE.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sb a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: mips/OP_APUT_CHAR.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sh a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: mips/OP_APUT_SHORT.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sh a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET: /* 0x52 */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_finish
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: mips/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    # iget-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_WIDE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test return code
+    move      a0, v0
+    bnez      v0, .LOP_IGET_WIDE_finish
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: mips/OP_IGET_OBJECT.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_OBJECT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_OBJECT_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: mips/OP_IGET_BOOLEAN.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_BOOLEAN_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_BOOLEAN_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: mips/OP_IGET_BYTE.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_BYTE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_BYTE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: mips/OP_IGET_CHAR.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_CHAR_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_CHAR_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: mips/OP_IGET_SHORT.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_SHORT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_SHORT_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT: /* 0x59 */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: mips/OP_IPUT_WIDE.S */
+    # iput-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_WIDE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_WIDE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: mips/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_OBJECT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_OBJECT_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: mips/OP_IPUT_BOOLEAN.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_BOOLEAN_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_BOOLEAN_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: mips/OP_IPUT_BYTE.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_BYTE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_BYTE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: mips/OP_IPUT_CHAR.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_CHAR_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_CHAR_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: mips/OP_IPUT_SHORT.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_SHORT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_SHORT_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET: /* 0x60 */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_finish            # resume
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: mips/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    # sget-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_SGET_WIDE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in v0.
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+
+    b        .LOP_SGET_WIDE_finish            # resume
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: mips/OP_SGET_OBJECT.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_OBJECT_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_OBJECT_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: mips/OP_SGET_BOOLEAN.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_BOOLEAN_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_BOOLEAN_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: mips/OP_SGET_BYTE.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_BYTE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_BYTE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: mips/OP_SGET_CHAR.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_CHAR_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_CHAR_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: mips/OP_SGET_SHORT.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_SHORT_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_SHORT_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT: /* 0x67 */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_finish            # resume
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: mips/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    # sput-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    GET_OPA(t0)                            #  t0 <- AA
+    LOAD_eas2(a2, rBIX, a1)                #  a2 <- resolved StaticField ptr
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ<- &fp[AA]
+    # is resolved entry null?
+    beqz      a2, .LOP_SPUT_WIDE_resolve      #  yes, do resolve
+.LOP_SPUT_WIDE_finish:                        #  field ptr in a2, AA in rOBJ
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    .if 0
+    addu    a2, offStaticField_value       #  a2<- pointer to data
+    JAL(dvmQuasiAtomicSwap64Sync)          #  stores a0/a1 into addr a2
+    .else
+    STORE64_off(a0, a1, a2, offStaticField_value) #  field <- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: mips/OP_SPUT_OBJECT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_OBJECT_finish       #  is resolved entry null?
+
+    /* Continuation if the field has not yet been resolved.
+     * a1:  BBBB field ref
+     * rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b       .LOP_SPUT_OBJECT_finish             # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: mips/OP_SPUT_BOOLEAN.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_BOOLEAN_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_BOOLEAN_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: mips/OP_SPUT_BYTE.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_BYTE_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_BYTE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: mips/OP_SPUT_CHAR.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_CHAR_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_CHAR_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: mips/OP_SPUT_SHORT.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_SHORT_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_SHORT_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: mips/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    .if (!0)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, .LOP_INVOKE_VIRTUAL_continue     #  yes, continue on
+
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    bnez      v0, .LOP_INVOKE_VIRTUAL_continue     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: mips/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    .if (!0)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this" ptr
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    # null "this"?
+    LOAD_rSELF_method(t1)                  #  t1 <- current method
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    # cmp a0, 0; already resolved?
+    LOAD_base_offMethod_clazz(rBIX, t1)    #  rBIX <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    bnez      a0, .LOP_INVOKE_SUPER_continue     #  resolved, continue on
+
+    move      a0, rBIX                     #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_INVOKE_SUPER_continue
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: mips/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+    .if (!0)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    # already resolved?
+    bnez      a0, 1f                       #  resolved, call the function
+
+    lw        a3, offThread_method(rSELF)  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_DIRECT            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+
+1:
+    bnez      rOBJ, common_invokeMethodNoRange #  a0=method, rOBJ="this"
+    b         common_errNullObject         #  yes, throw exception
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: mips/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    li      rOBJ, 0                        #  null "this" in delay slot
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX<- &resolved_metherToCall
+#endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, common_invokeMethodNoRange #  yes, continue on
+    b         .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: mips/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(a2, 2)                           #  a2 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!0)
+    and       a2, 15                       #  a2 <- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- first arg ("this")
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- methodClassDex
+    LOAD_rSELF_method(a2)                  #  a2 <- method
+    # null obj?
+    beqz      rOBJ, common_errNullObject   #  yes, fail
+    LOAD_base_offObject_clazz(a0, rOBJ)      #  a0 <- thisPtr->clazz
+    JAL(dvmFindInterfaceMethodInCache)     #  v0 <- call(class, ref, method, dex)
+    move      a0, v0
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: mips/OP_UNUSED_73.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: mips/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: mips/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    .if (!1)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, .LOP_INVOKE_VIRTUAL_RANGE_continue     #  yes, continue on
+
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    bnez      v0, .LOP_INVOKE_VIRTUAL_RANGE_continue     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: mips/OP_INVOKE_SUPER_RANGE.S */
+/* File: mips/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    .if (!1)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this" ptr
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    # null "this"?
+    LOAD_rSELF_method(t1)                  #  t1 <- current method
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    # cmp a0, 0; already resolved?
+    LOAD_base_offMethod_clazz(rBIX, t1)    #  rBIX <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    bnez      a0, .LOP_INVOKE_SUPER_RANGE_continue     #  resolved, continue on
+
+    move      a0, rBIX                     #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_INVOKE_SUPER_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: mips/OP_INVOKE_DIRECT_RANGE.S */
+/* File: mips/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+    .if (!1)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    # already resolved?
+    bnez      a0, 1f                       #  resolved, call the function
+
+    lw        a3, offThread_method(rSELF)  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_DIRECT            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+
+1:
+    bnez      rOBJ, common_invokeMethodRange #  a0=method, rOBJ="this"
+    b         common_errNullObject         #  yes, throw exception
+
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: mips/OP_INVOKE_STATIC_RANGE.S */
+/* File: mips/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    li      rOBJ, 0                        #  null "this" in delay slot
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX<- &resolved_metherToCall
+#endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, common_invokeMethodRange #  yes, continue on
+    b         .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: mips/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: mips/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(a2, 2)                           #  a2 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!1)
+    and       a2, 15                       #  a2 <- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- first arg ("this")
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- methodClassDex
+    LOAD_rSELF_method(a2)                  #  a2 <- method
+    # null obj?
+    beqz      rOBJ, common_errNullObject   #  yes, fail
+    LOAD_base_offObject_clazz(a0, rOBJ)      #  a0 <- thisPtr->clazz
+    JAL(dvmFindInterfaceMethodInCache)     #  v0 <- call(class, ref, method, dex)
+    move      a0, v0
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         common_invokeMethodRange #  (a0=method, rOBJ="this")
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: mips/OP_UNUSED_79.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: mips/OP_UNUSED_7A.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_INT: /* 0x7b */
+/* File: mips/OP_NEG_INT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    negu a0, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NOT_INT: /* 0x7c */
+/* File: mips/OP_NOT_INT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    not a0, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: mips/OP_NEG_LONG.S */
+/* File: mips/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    negu v0, a0                              #  optional op
+    negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: mips/OP_NOT_LONG.S */
+/* File: mips/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    not a0, a0                              #  optional op
+    not a1, a1                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: mips/OP_NEG_FLOAT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    addu a0, a0, 0x80000000                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: mips/OP_NEG_DOUBLE.S */
+/* File: mips/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    addu a1, a1, 0x80000000                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: mips/OP_INT_TO_LONG.S */
+/* File: mips/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    sra a1, a0, 31                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vA/vA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: mips/OP_INT_TO_FLOAT.S */
+/* File: mips/unflop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t0 <- A+
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+                                  #  optional op
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef SOFT_FLOAT
+    JAL(__floatsisf)                                 #  a0 <- op, a0-a3 changed
+
+.LOP_INT_TO_FLOAT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vAA <- result0
+#else
+    cvt.s.w fv0, fa0
+
+.LOP_INT_TO_FLOAT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)
+#endif
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: mips/OP_INT_TO_DOUBLE.S */
+/* File: mips/unflopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__floatsidf)                                 #  result <- op, a0-a3 changed
+
+.LOP_INT_TO_DOUBLE_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    cvt.d.w fv0, fa0
+
+.LOP_INT_TO_DOUBLE_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: mips/OP_LONG_TO_INT.S */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef HAVE_BIG_ENDIAN
+    addu      a1, a1, 1
+#endif
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: mips/OP_LONG_TO_FLOAT.S */
+/* File: mips/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    LOAD64(rARG0, rARG1, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__floatdisf)                                 #  a0 <- op, a0-a3 changed
+
+.LOP_LONG_TO_FLOAT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    JAL(__floatdisf)
+
+.LOP_LONG_TO_FLOAT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: mips/OP_LONG_TO_DOUBLE.S */
+/* File: mips/unflopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be a MIPS instruction or a function call.
+     *
+     * long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  t1 <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vAA
+#else
+    LOAD64(rARG0, rARG1, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    JAL(__floatdidf)                                 #  a0/a1 <- op, a2-a3 changed
+
+.LOP_LONG_TO_DOUBLE_set_vreg:
+#ifdef SOFT_FLOAT
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vAA <- a0/a1
+#else
+    STORE64_F(fv0, fv0f, rOBJ)                             #  vAA <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: mips/OP_FLOAT_TO_INT.S */
+/* File: mips/unflop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t0 <- A+
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+                                  #  optional op
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef SOFT_FLOAT
+    b f2i_doconv                                 #  a0 <- op, a0-a3 changed
+
+.LOP_FLOAT_TO_INT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vAA <- result0
+#else
+    b f2i_doconv
+
+.LOP_FLOAT_TO_INT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)
+#endif
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: mips/OP_FLOAT_TO_LONG.S */
+/* File: mips/unflopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    b f2l_doconv                                 #  result <- op, a0-a3 changed
+
+.LOP_FLOAT_TO_LONG_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    b f2l_doconv
+
+.LOP_FLOAT_TO_LONG_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: mips/OP_FLOAT_TO_DOUBLE.S */
+/* File: mips/unflopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__extendsfdf2)                                 #  result <- op, a0-a3 changed
+
+.LOP_FLOAT_TO_DOUBLE_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    cvt.d.s fv0, fa0
+
+.LOP_FLOAT_TO_DOUBLE_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: mips/OP_DOUBLE_TO_INT.S */
+/* File: mips/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    LOAD64_F(fa0, fa0f, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    b d2i_doconv                                 #  a0 <- op, a0-a3 changed
+
+.LOP_DOUBLE_TO_INT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    b d2i_doconv
+
+.LOP_DOUBLE_TO_INT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rTEMP as global to hold arguments (they are not bound to a global var)
+ */
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: mips/OP_DOUBLE_TO_LONG.S */
+/* File: mips/unflopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be a MIPS instruction or a function call.
+     *
+     * long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  t1 <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vAA
+#else
+    LOAD64_F(fa0, fa0f, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    b d2l_doconv                                 #  a0/a1 <- op, a2-a3 changed
+
+.LOP_DOUBLE_TO_LONG_set_vreg:
+#ifdef SOFT_FLOAT
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vAA <- a0/a1
+#else
+    STORE64(rRESULT0, rRESULT1, rOBJ)                             #  vAA <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: mips/OP_DOUBLE_TO_FLOAT.S */
+/* File: mips/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    LOAD64_F(fa0, fa0f, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__truncdfsf2)                                 #  a0 <- op, a0-a3 changed
+
+.LOP_DOUBLE_TO_FLOAT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    cvt.s.d fv0, fa0
+
+.LOP_DOUBLE_TO_FLOAT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: mips/OP_INT_TO_BYTE.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sll a0, a0, 24                              #  optional op
+    sra a0, a0, 24                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: mips/OP_INT_TO_CHAR.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    and a0, 0xffff                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: mips/OP_INT_TO_SHORT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sll a0, 16                              #  optional op
+    sra a0, 16                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT: /* 0x90 */
+/* File: mips/OP_ADD_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_INT: /* 0x91 */
+/* File: mips/OP_SUB_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    subu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT: /* 0x92 */
+/* File: mips/OP_MUL_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT: /* 0x93 */
+/* File: mips/OP_DIV_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT: /* 0x94 */
+/* File: mips/OP_REM_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT: /* 0x95 */
+/* File: mips/OP_AND_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT: /* 0x96 */
+/* File: mips/OP_OR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT: /* 0x97 */
+/* File: mips/OP_XOR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_INT: /* 0x98 */
+/* File: mips/OP_SHL_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    and a1, a1, 31                              #  optional op
+    sll a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_INT: /* 0x99 */
+/* File: mips/OP_SHR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    and a1, a1, 31                              #  optional op
+    sra a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_INT: /* 0x9a */
+/* File: mips/OP_USHR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    and a1, a1, 31                              #  optional op
+    srl a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: mips/OP_ADD_LONG.S */
+/*
+ *  The compiler generates the following sequence for
+ *  [v1 v0] =  [a1 a0] + [a3 a2];
+ *    addu v0,a2,a0
+ *    addu a1,a3,a1
+ *    sltu v1,v0,a2
+ *    addu v1,v1,a1
+ */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    addu v0, a2, a0                              #  optional op
+    addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: mips/OP_SUB_LONG.S */
+/*
+ * For little endian the code sequence looks as follows:
+ *    subu    v0,a0,a2
+ *    subu    v1,a1,a3
+ *    sltu    a0,a0,v0
+ *    subu    v1,v1,a0
+ */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    subu v0, a0, a2                              #  optional op
+    subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: mips/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       t0, a0, 255                  #  a2 <- BB
+    srl       t1, a0, 8                    #  a3 <- CC
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[BB]
+    LOAD64(a0, a1, t0)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t1, rFP, t1)                      #  t0 <- &fp[CC]
+    LOAD64(a2, a3, t1)                     #  a2/a3 <- vCC/vCC+1
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t0, a2, a1                   #  t0= a2a1
+    addu      v1, v1, t1                   #  v1+= hi(a2a0)
+    addu      v1, v1, t0                   #  v1= a3a0 + a2a1;
+
+    GET_OPA(a0)                            #  a0 <- AA
+    EAS2(a0, rFP, a0)                      #  a0 <- &fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    b         .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: mips/OP_DIV_LONG.S */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#else
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a1, a0, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a3, a2, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_LONG: /* 0x9f */
+/* File: mips/OP_REM_LONG.S */
+/* ldivmod returns quotient in a0/a1 and remainder in a2/a3 */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#else
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a1, a0, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a3, a2, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: mips/OP_AND_LONG.S */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a0, a0, a2                              #  optional op
+    and a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: mips/OP_OR_LONG.S */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    or a0, a0, a2                              #  optional op
+    or a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: mips/OP_XOR_LONG.S */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    xor a0, a0, a2                              #  optional op
+    xor a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: mips/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t2)                            #  t2 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: mips/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t3)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    STORE64(v0, v1, t3)                    #  vAA/VAA+1 <- v0/v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: mips/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- v0/v1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: mips/OP_ADD_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__addsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    add.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: mips/OP_SUB_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    sub.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: mips/OP_MUL_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__mulsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    mul.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: mips/OP_DIV_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    div.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: mips/OP_REM_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmodf)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    JAL(fmodf)                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: mips/OP_ADD_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__adddf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    add.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: mips/OP_SUB_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    sub.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: mips/OP_MUL_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__muldf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    mul.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: mips/OP_DIV_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    div.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: mips/OP_REM_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmod)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    JAL(fmod)
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: mips/OP_ADD_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: mips/OP_SUB_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    subu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: mips/OP_MUL_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: mips/OP_DIV_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: mips/OP_REM_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: mips/OP_AND_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: mips/OP_OR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: mips/OP_XOR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: mips/OP_SHL_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sll a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: mips/OP_SHR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sra a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: mips/OP_USHR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    srl a0, a0, a1                                  #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: mips/OP_ADD_LONG_2ADDR.S */
+/*
+ *See OP_ADD_LONG.S for details
+ */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    addu v0, a2, a0                              #  optional op
+    addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: mips/OP_SUB_LONG_2ADDR.S */
+/*
+ * See comments in OP_SUB_LONG.S
+ */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    subu v0, a0, a2                              #  optional op
+    subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: mips/OP_MUL_LONG_2ADDR.S */
+    /*
+     * See comments in OP_MUL_LONG.S
+     */
+    /* mul-long/2addr vA, vB */
+    GET_OPA4(t0)                           #  t0 <- A+
+
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[A]
+    LOAD64(a0, a1, t0)                     #  vAA.low / high
+
+    GET_OPB(t1)                            #  t1 <- B
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[B]
+    LOAD64(a2, a3, t1)                     #  vBB.low / high
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t2, a2, a1                   #  t2= a2a1
+    addu      v1, v1, t1                   #  v1= a3a0 + hi(a2a0)
+    addu      v1, v1, t2                   #  v1= v1 + a2a1;
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    # vAA <- v0 (low)
+    STORE64(v0, v1, t0)                    #  vAA+1 <- v1 (high)
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: mips/OP_DIV_LONG_2ADDR.S */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#else
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a3, a2, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a1, a0, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: mips/OP_REM_LONG_2ADDR.S */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#else
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a3, a2, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a1, a0, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: mips/OP_AND_LONG_2ADDR.S */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a0, a0, a2                              #  optional op
+    and a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: mips/OP_OR_LONG_2ADDR.S */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    or a0, a0, a2                              #  optional op
+    or a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: mips/OP_XOR_LONG_2ADDR.S */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    xor a0, a0, a2                              #  optional op
+    xor a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: mips/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(rOBJ, rFP, t2)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: mips/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[A]
+    LOAD64(a0, a1, t2)                     #  a0/a1 <- vAA/vAA+1
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: mips/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    GET_OPA4(t3)                           #  t3 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[A]
+    LOAD64(a0, a1, t3)                     #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t3)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: mips/OP_ADD_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__addsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    add.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: mips/OP_SUB_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    sub.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: mips/OP_MUL_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__mulsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    mul.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: mips/OP_DIV_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    div.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: mips/OP_REM_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmodf)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    JAL(fmodf)
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: mips/OP_ADD_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__adddf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    add.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: mips/OP_SUB_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    sub.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: mips/OP_MUL_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__muldf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    mul.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: mips/OP_DIV_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    div.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: mips/OP_REM_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmod)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    JAL(fmod)
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: mips/OP_ADD_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: mips/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    subu a0, a1, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: mips/OP_MUL_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: mips/OP_DIV_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 1
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: mips/OP_REM_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 1
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: mips/OP_AND_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: mips/OP_OR_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: mips/OP_XOR_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: mips/OP_ADD_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: mips/OP_RSUB_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    subu a0, a1, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: mips/OP_MUL_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: mips/OP_DIV_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: mips/OP_REM_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: mips/OP_AND_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: mips/OP_OR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: mips/OP_XOR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: mips/OP_SHL_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sll a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: mips/OP_SHR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sra a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: mips/OP_USHR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    srl a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: mips/OP_IGET_VOLATILE.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_VOLATILE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: mips/OP_IPUT_VOLATILE.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_VOLATILE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: mips/OP_SGET_VOLATILE.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_VOLATILE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: mips/OP_SPUT_VOLATILE.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_VOLATILE_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: mips/OP_IGET_OBJECT_VOLATILE.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_OBJECT_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_OBJECT_VOLATILE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: mips/OP_IGET_WIDE_VOLATILE.S */
+/* File: mips/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    # iget-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_WIDE_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test return code
+    move      a0, v0
+    bnez      v0, .LOP_IGET_WIDE_VOLATILE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: mips/OP_IPUT_WIDE_VOLATILE.S */
+/* File: mips/OP_IPUT_WIDE.S */
+    # iput-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_WIDE_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_WIDE_VOLATILE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: mips/OP_SGET_WIDE_VOLATILE.S */
+/* File: mips/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    # sget-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_SGET_WIDE_VOLATILE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in v0.
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+
+    b        .LOP_SGET_WIDE_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: mips/OP_SPUT_WIDE_VOLATILE.S */
+/* File: mips/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    # sput-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    GET_OPA(t0)                            #  t0 <- AA
+    LOAD_eas2(a2, rBIX, a1)                #  a2 <- resolved StaticField ptr
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ<- &fp[AA]
+    # is resolved entry null?
+    beqz      a2, .LOP_SPUT_WIDE_VOLATILE_resolve      #  yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish:                        #  field ptr in a2, AA in rOBJ
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    .if 1
+    addu    a2, offStaticField_value       #  a2<- pointer to data
+    JAL(dvmQuasiAtomicSwap64Sync)          #  stores a0/a1 into addr a2
+    .else
+    STORE64_off(a0, a1, a2, offStaticField_value) #  field <- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_BREAKPOINT: /* 0xec */
+    /* (stub) */
+    SAVE_PC_TO_SELF()            # only need to export PC and FP
+    SAVE_FP_TO_SELF()
+    move        a0, rSELF        # self is first arg to function
+    JAL(dvmMterp_OP_BREAKPOINT)      # call
+    LOAD_PC_FROM_SELF()          # retrieve updated values
+    LOAD_FP_FROM_SELF()
+    FETCH_INST()                 # load next instruction from rPC
+    GET_INST_OPCODE(t0)          # ...trim down to just the opcode
+    GOTO_OPCODE(t0)              # ...and jump to the handler
+/* ------------------------------ */
+    .balign 128
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: mips/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    EXPORT_PC()                            #  export the PC
+    GET_OPA(a1)                            #  a1 <- AA
+    JAL(dvmThrowVerificationError)         #  always throws
+    b         common_exceptionThrown       #  handle exception
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: mips/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                         #  rBIX <- BBBB
+    EXPORT_PC()                            #  can throw
+    and       a2, kSubModeDebugProfile     #  Any going on?
+    bnez      a2, .LOP_EXECUTE_INLINE_debugmode    #  yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    addu      a1, rSELF, offThread_retval  #  a1 <- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.LOP_EXECUTE_INLINE_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    # test boolean result of inline
+    beqz      v0, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: mips/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                       # rBIX<- BBBB
+    EXPORT_PC()                          # can throw
+    and       a2, kSubModeDebugProfile   # Any going on?
+    bnez      a2, .LOP_EXECUTE_INLINE_RANGE_debugmode  # yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    addu      a1, rSELF, offThread_retval # a1<- &self->retval
+    GET_OPA(a0)
+    sw        a1, STACK_OFFSET_ARG04(sp)  # push &self->retval
+    BAL(.LOP_EXECUTE_INLINE_RANGE_continue)             # make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)     #  restore gp
+    beqz      v0, common_exceptionThrown  # returned false, handle exception
+    FETCH_ADVANCE_INST(3)                 # advance rPC, load rINST
+    GET_INST_OPCODE(t0)                   # extract opcode from rINST
+    GOTO_OPCODE(t0)                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: mips/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(a1, 2)                  # a1<- CCCC
+    GET_VREG(a0, a1)                    # a0<- "this" ptr
+    # check for NULL
+    beqz    a0, common_errNullObject    # export PC and throw NPE
+    LOAD_base_offObject_clazz(a1, a0)   # a1<- obj->clazz
+    LOAD_base_offClassObject_accessFlags(a2, a1) # a2<- clazz->accessFlags
+    and     a2, CLASS_ISFINALIZABLE     # is this class finalizable?
+    beqz    a2, .LOP_INVOKE_OBJECT_INIT_RANGE_finish      # no, go
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         # can throw
+    JAL(dvmSetFinalizable)              # call dvmSetFinalizable(obj)
+    LOAD_offThread_exception(a0, rSELF)	# a0<- self->exception
+    # exception pending?
+    bnez    a0, common_exceptionThrown  # yes, handle it
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    lhu     a1, offThread_subMode(rSELF)
+    and     a1, kSubModeDebuggerActive  # debugger active?
+    bnez    a1, .LOP_INVOKE_OBJECT_INIT_RANGE_debugger    # Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       # advance to next instr, load rINST
+    GET_INST_OPCODE(t0)                 # t0<- opcode from rINST
+    GOTO_OPCODE(t0)                     # execute it
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: mips/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB
+    b         common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: mips/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1 #
+    lw        a0, 0(t0)                    #  a0 <- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: mips/OP_IGET_WIDE_QUICK.S */
+    # iget-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1                   #  t0 <- a3 + a1
+    LOAD64(a0, a1, t0)                     #  a0 <- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: mips/OP_IGET_OBJECT_QUICK.S */
+/* File: mips/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1 #
+    lw        a0, 0(t0)                    #  a0 <- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: mips/OP_IPUT_QUICK.S */
+    /* For: iput-quick, iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: mips/OP_IPUT_WIDE_QUICK.S */
+    # iput-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPA4(a0)                           #  a0 <- A(+)
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a2, a1)                       #  a2 <- fp[B], the object pointer
+    EAS2(a3, rFP, a0)                      #  a3 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[A]
+    # check object for null
+    beqz      a2, common_errNullObject     #  object was null
+    FETCH(a3, 1)                           #  a3 <- field byte offset
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      a2, a2, a3                   #  obj.field (64 bits, aligned) <- a0/a1
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: mips/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    beqz      a0, 1f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t1, a3, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, 0(t2)
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: mips/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(a3, 2)                           #  a3 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!0)
+    and       a3, a3, 15                   #  a3 <- C (or stays CCCC)
+    .endif
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- vC ("this" ptr)
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a2, rOBJ)    #  a2 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- thisPtr->clazz->vtable
+    EXPORT_PC()                            #  invoke must export
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- vtable[BBBB]
+    b         common_invokeMethodNoRange #  (a0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: mips/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(a3, 2)                           #  a3 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!1)
+    and       a3, a3, 15                   #  a3 <- C (or stays CCCC)
+    .endif
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- vC ("this" ptr)
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a2, rOBJ)    #  a2 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- thisPtr->clazz->vtable
+    EXPORT_PC()                            #  invoke must export
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- vtable[BBBB]
+    b         common_invokeMethodRange #  (a0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: mips/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    .if (!0)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offMethod_clazz(a2, a2)      #  a2 <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    LOAD_base_offClassObject_super(a2, a2) #  a2 <- method->clazz->super
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this"
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- ...clazz->super->vtable
+    # is "this" null ?
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- super->vtable[BBBB]
+    beqz      rOBJ, common_errNullObject   #  "this" is null, throw exception
+    b         common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: mips/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: mips/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    .if (!1)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offMethod_clazz(a2, a2)      #  a2 <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    LOAD_base_offClassObject_super(a2, a2) #  a2 <- method->clazz->super
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this"
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- ...clazz->super->vtable
+    # is "this" null ?
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- super->vtable[BBBB]
+    beqz      rOBJ, common_errNullObject   #  "this" is null, throw exception
+    b         common_invokeMethodRange #  (a0=method, rOBJ="this")
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: mips/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: mips/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_OBJECT_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_OBJECT_VOLATILE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: mips/OP_SGET_OBJECT_VOLATILE.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_OBJECT_VOLATILE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_OBJECT_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: mips/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: mips/OP_SPUT_OBJECT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_OBJECT_VOLATILE_finish       #  is resolved entry null?
+
+    /* Continuation if the field has not yet been resolved.
+     * a1:  BBBB field ref
+     * rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish             # resume
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: mips/OP_UNUSED_FF.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+    .balign 128
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CHECK_CAST */
+
+.LOP_CHECK_CAST_castfailure:
+    # A cast has failed. We need to throw a ClassCastException with the
+    # class of the object that failed to be cast.
+    EXPORT_PC()                            #  about to throw
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    move      a1,rBIX                      #  r1<- desired class
+    JAL(dvmThrowClassCastException)
+    b         common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a2   holds BBBB
+     *  rOBJ holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      a1, a2                       #  a1 <- BBBB
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    b         .LOP_CHECK_CAST_resolved         #  pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0   holds obj->clazz
+     *  a1   holds class resolved from BBBB
+     *  rOBJ holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    move      a0, v0                       #  fall through to OP_INSTANCE_OF_store
+    b         .LOP_INSTANCE_OF_store
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a3   holds BBBB
+     *  rOBJ holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    move      a1, a3                       #  a1 <- BBBB
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    b         .LOP_INSTANCE_OF_resolved         #  pick up where we left off
+
+
+/* continuation for OP_NEW_INSTANCE */
+
+.LOP_NEW_INSTANCE_continue:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(v0, a3)                       #  vAA <- v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * v0: new object
+     * a3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    lw        a1, 0(rBIX)                  #  reload resolved class
+    # okay?
+    bnez      a1, .LOP_NEW_INSTANCE_continue     #  yes, finish
+    move      rOBJ, v0                     #  preserve new object
+    move      rBIX, a3                     #  preserve vAA
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(rOBJ, rBIX)                   #  vAA <- new object
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  a0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    JAL(dvmInitClass)                      #  initialize class
+    move      a0, rOBJ                     #  restore a0
+    # check boolean result
+    bnez      v0, .LOP_NEW_INSTANCE_initialized  #  success, continue
+    b         common_exceptionThrown       #  failed, deal with init exception
+
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    move      a0, v0
+    # got null?
+    bnez      v0, .LOP_NEW_INSTANCE_resolved     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  a1 holds array length
+     *  a2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      rOBJ, a1                     #  rOBJ <- length (save)
+    move      a1, a2                       #  a1 <- CCCC
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a1, rOBJ                     #  a1 <- length (restore)
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a0, v0
+    b         .LOP_NEW_ARRAY_finish           #  continue with OP_NEW_ARRAY_finish
+
+
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  a0 holds array class
+     *  rOBJ holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    LOAD_base_offClassObject_descriptor(a3, a0) #  a3 <- arrayClass->descriptor
+    li        a2, ALLOC_DONT_TRACK         #  a2 <- alloc flags
+    lbu       rINST, 1(a3)                 #  rINST <- descriptor[1]
+    .if 0
+    move      a1, rOBJ                     #  a1 <- AA (length)
+    .else
+    srl       a1, rOBJ, 4                  #  rOBJ <- B (length)
+    .endif
+    seq       t0, rINST, 'I'               #  array of ints?
+    seq       t1, rINST, 'L'               #  array of objects?
+    or        t0, t1
+    seq       t1, rINST, '['               #  array of arrays?
+    or        t0, t1
+    move      rBIX, a1                     #  save length in rBIX
+    beqz      t0, .LOP_FILLED_NEW_ARRAY_notimpl      #  no, not handled yet
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(arClass, length, flags)
+    # null return?
+    beqz      v0, common_exceptionThrown   #  alloc failed, handle exception
+
+    FETCH(a1, 2)                           #  a1 <- FEDC or CCCC
+    sw        v0, offThread_retval(rSELF)  #  retval.l <- new array
+    sw        rINST, (offThread_retval+4)(rSELF) #  retval.h <- type
+    addu      a0, v0, offArrayObject_contents #  a0 <- newArray->contents
+    subu      rBIX, rBIX, 1                #  length--, check for neg
+    FETCH_ADVANCE_INST(3)                  #  advance to next instr, load rINST
+    bltz      rBIX, 2f                     #  was zero, bail
+
+    # copy values from registers into the array
+    # a0=array, a1=CCCC/FEDC, t0=length (from AA or B), rOBJ=AA/BA
+    move      t0, rBIX
+    .if 0
+    EAS2(a2, rFP, a1)                      #  a2 <- &fp[CCCC]
+1:
+    lw        a3, 0(a2)                    #  a3 <- *a2++
+    addu      a2, 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, (a0)                     #  *contents++ = vX
+    addu      a0, 4
+    bgez      t0, 1b
+
+    # continue at 2
+    .else
+    slt       t1, t0, 4                    #  length was initially 5?
+    and       a2, rOBJ, 15                 #  a2 <- A
+    bnez      t1, 1f                       #  <= 4 args, branch
+    GET_VREG(a3, a2)                       #  a3 <- vA
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 16(a0)                   #  contents[4] = vA
+1:
+    and       a2, a1, 15                   #  a2 <- F/E/D/C
+    GET_VREG(a3, a2)                       #  a3 <- vF/vE/vD/vC
+    srl       a1, a1, 4                    #  a1 <- next reg in low 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 0(a0)                    #  *contents++ = vX
+    addu      a0, a0, 4
+    bgez      t0, 1b
+    # continue at 2
+    .endif
+
+2:
+    lw        a0, offThread_retval(rSELF)  #  a0 <- object
+    lw        a1, (offThread_retval+4)(rSELF) #  a1 <- type
+    seq       t1, a1, 'I'                  #  Is int array?
+    bnez      t1, 3f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t3, a0, GC_CARD_SHIFT
+    addu      t2, a2, t3
+    sb        a2, (t2)
+3:
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    GOTO_OPCODE(t0)                        #  execute it
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    la        a0, .LstrFilledNewArrayNotImpl
+    JAL(dvmThrowInternalError)
+    b         common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  a0 holds array class
+     *  rOBJ holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    LOAD_base_offClassObject_descriptor(a3, a0) #  a3 <- arrayClass->descriptor
+    li        a2, ALLOC_DONT_TRACK         #  a2 <- alloc flags
+    lbu       rINST, 1(a3)                 #  rINST <- descriptor[1]
+    .if 1
+    move      a1, rOBJ                     #  a1 <- AA (length)
+    .else
+    srl       a1, rOBJ, 4                  #  rOBJ <- B (length)
+    .endif
+    seq       t0, rINST, 'I'               #  array of ints?
+    seq       t1, rINST, 'L'               #  array of objects?
+    or        t0, t1
+    seq       t1, rINST, '['               #  array of arrays?
+    or        t0, t1
+    move      rBIX, a1                     #  save length in rBIX
+    beqz      t0, .LOP_FILLED_NEW_ARRAY_RANGE_notimpl      #  no, not handled yet
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(arClass, length, flags)
+    # null return?
+    beqz      v0, common_exceptionThrown   #  alloc failed, handle exception
+
+    FETCH(a1, 2)                           #  a1 <- FEDC or CCCC
+    sw        v0, offThread_retval(rSELF)  #  retval.l <- new array
+    sw        rINST, (offThread_retval+4)(rSELF) #  retval.h <- type
+    addu      a0, v0, offArrayObject_contents #  a0 <- newArray->contents
+    subu      rBIX, rBIX, 1                #  length--, check for neg
+    FETCH_ADVANCE_INST(3)                  #  advance to next instr, load rINST
+    bltz      rBIX, 2f                     #  was zero, bail
+
+    # copy values from registers into the array
+    # a0=array, a1=CCCC/FEDC, t0=length (from AA or B), rOBJ=AA/BA
+    move      t0, rBIX
+    .if 1
+    EAS2(a2, rFP, a1)                      #  a2 <- &fp[CCCC]
+1:
+    lw        a3, 0(a2)                    #  a3 <- *a2++
+    addu      a2, 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, (a0)                     #  *contents++ = vX
+    addu      a0, 4
+    bgez      t0, 1b
+
+    # continue at 2
+    .else
+    slt       t1, t0, 4                    #  length was initially 5?
+    and       a2, rOBJ, 15                 #  a2 <- A
+    bnez      t1, 1f                       #  <= 4 args, branch
+    GET_VREG(a3, a2)                       #  a3 <- vA
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 16(a0)                   #  contents[4] = vA
+1:
+    and       a2, a1, 15                   #  a2 <- F/E/D/C
+    GET_VREG(a3, a2)                       #  a3 <- vF/vE/vD/vC
+    srl       a1, a1, 4                    #  a1 <- next reg in low 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 0(a0)                    #  *contents++ = vX
+    addu      a0, a0, 4
+    bgez      t0, 1b
+    # continue at 2
+    .endif
+
+2:
+    lw        a0, offThread_retval(rSELF)  #  a0 <- object
+    lw        a1, (offThread_retval+4)(rSELF) #  a1 <- type
+    seq       t1, a1, 'I'                  #  Is int array?
+    bnez      t1, 3f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t3, a0, GC_CARD_SHIFT
+    addu      t2, a2, t3
+    sb        a2, (t2)
+3:
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    GOTO_OPCODE(t0)                        #  execute it
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    la        a0, .LstrFilledNewArrayNotImpl
+    JAL(dvmThrowInternalError)
+    b         common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+/* continuation for OP_CMPL_FLOAT */
+
+OP_CMPL_FLOAT_nan:
+    li rTEMP, -1
+    b         OP_CMPL_FLOAT_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPL_FLOAT_continue:
+    JAL(__gtsf2)                           #  v0 <- (vBB > vCC)
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    bgtz      v0, OP_CMPL_FLOAT_finish
+    b         OP_CMPL_FLOAT_nan
+#endif
+
+OP_CMPL_FLOAT_finish:
+    GET_OPA(t0)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rTEMP, t0)                    #  vAA <- rTEMP
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)
+
+/* continuation for OP_CMPG_FLOAT */
+
+OP_CMPG_FLOAT_nan:
+    li rTEMP, 1
+    b         OP_CMPG_FLOAT_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPG_FLOAT_continue:
+    JAL(__gtsf2)                           #  v0 <- (vBB > vCC)
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    bgtz      v0, OP_CMPG_FLOAT_finish
+    b         OP_CMPG_FLOAT_nan
+#endif
+
+OP_CMPG_FLOAT_finish:
+    GET_OPA(t0)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rTEMP, t0)                    #  vAA <- rTEMP
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)
+
+/* continuation for OP_CMPL_DOUBLE */
+
+OP_CMPL_DOUBLE_nan:
+    li rTEMP, -1
+    b         OP_CMPL_DOUBLE_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPL_DOUBLE_continue:
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__gtdf2)                           #  fallthru
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    blez      v0, OP_CMPL_DOUBLE_nan            #  fall thru for finish
+#endif
+
+OP_CMPL_DOUBLE_finish:
+    GET_OPA(rOBJ)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(rTEMP, rOBJ, t0)         #  vAA <- rTEMP
+
+/* continuation for OP_CMPG_DOUBLE */
+
+OP_CMPG_DOUBLE_nan:
+    li rTEMP, 1
+    b         OP_CMPG_DOUBLE_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPG_DOUBLE_continue:
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__gtdf2)                           #  fallthru
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    blez      v0, OP_CMPG_DOUBLE_nan            #  fall thru for finish
+#endif
+
+OP_CMPG_DOUBLE_finish:
+    GET_OPA(rOBJ)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(rTEMP, rOBJ, t0)         #  vAA <- rTEMP
+
+/* continuation for OP_APUT_OBJECT */
+.LOP_APUT_OBJECT_checks:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    JAL(dvmCanPutArrayElement)             #  test object type vs. array type
+    beqz      v0, .LOP_APUT_OBJECT_throw        #  okay ?
+    lw        a2, offThread_cardTable(rSELF)
+    srl       t1, rINST, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)
+    b         .LOP_APUT_OBJECT_finish           #  yes, skip type checks
+.LOP_APUT_OBJECT_throw:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    EXPORT_PC()
+    JAL(dvmThrowArrayStoreExceptionIncompatibleElement)
+    b         common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IGET_WIDE_finish:
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    beqz      rOBJ, common_errNullObject   #  object was null
+    GET_OPA4(a2)                           #  a2 <- A+
+    addu      rOBJ, rOBJ, a3               #  form address
+    .if 0
+    vLOAD64(a0, a1, rOBJ)                  #  a0/a1 <- obj.field (64-bit align ok)
+    .else
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- obj.field (64-bit align ok)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)                      #  a3 <- &fp[A]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_OBJECT_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_BOOLEAN_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_BYTE_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_CHAR_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_SHORT_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    GET_OPA4(a2)                           #  a2 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- fp[A]
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    addu      a2, rOBJ, a3                 #  form address
+    .if 0
+    JAL(dvmQuasiAtomicSwap64Sync)          # stores r0/r1 into addr r2
+#    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .else
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      t2, rOBJ, a3                 #  form address
+        #  noop                            #  releasing store
+    sw a0, (t2)                        #  obj.field (32 bits) <- a0
+        #  noop
+    beqz      a0, 1f                       #  stored a null reference?
+    srl       t1, rOBJ, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)                     #  mark card if not
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_SGET */
+
+.LOP_SGET_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_WIDE */
+
+.LOP_SGET_WIDE_finish:
+    GET_OPA(a1)                            #  a1 <- AA
+    .if 0
+    vLOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .else
+    LOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[AA]
+    STORE64(a2, a3, a1)                    #  vAA/vAA+1 <- a2/a3
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* continuation for OP_SGET_OBJECT */
+
+.LOP_SGET_OBJECT_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_BOOLEAN */
+
+.LOP_SGET_BOOLEAN_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_BYTE */
+
+.LOP_SGET_BYTE_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_CHAR */
+
+.LOP_SGET_CHAR_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_SHORT */
+
+.LOP_SGET_SHORT_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SPUT */
+
+.LOP_SPUT_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rOBJ:  &fp[AA]
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in a2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    # success ?
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    move      a2, v0
+    b         .LOP_SPUT_WIDE_finish           # resume
+
+/* continuation for OP_SPUT_OBJECT */
+.LOP_SPUT_OBJECT_finish:                        #  field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    lw        t1, offField_clazz(a0)       #  t1 <- field->clazz
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    beqz      a1, 1f
+    srl       t2, t1, GC_CARD_SHIFT
+    addu      t3, a2, t2
+    sb        a2, (t3)
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+.LOP_SPUT_BOOLEAN_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_BYTE */
+
+.LOP_SPUT_BYTE_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_CHAR */
+
+.LOP_SPUT_CHAR_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_SHORT */
+
+.LOP_SPUT_SHORT_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX= C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a3, rOBJ)    #  a3 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a3, a3) #  a3 <- thisPtr->clazz->vtable
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    LOAD_base_offClassObject_super(a1, rBIX) #  a1 <- method->clazz->super
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    LOAD_base_offClassObject_vtableCount(a3, a1) #  a3 <- super->vtableCount
+    EXPORT_PC()                            #  must export for invoke
+    # compare (methodIndex, vtableCount)
+    bgeu      a2, a3, .LOP_INVOKE_SUPER_nsm      #  method not present in superclass
+    LOAD_base_offClassObject_vtable(a1, a1) #  a1 <- ...clazz->super->vtable
+    LOAD_eas2(a0, a1, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodNoRange #  continue on
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  a0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    LOAD_base_offMethod_name(a1, a0)       #  a1 <- method name
+    b         common_errNoSuchMethod
+
+
+/* continuation for OP_INVOKE_STATIC */
+
+.LOP_INVOKE_STATIC_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_STATIC            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * rBIX: &resolved_methodToCall
+     */
+    lhu       a2, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  null, handle exception
+    and       a2, kSubModeJitTraceBuild    #  trace under construction?
+    beqz      a2, common_invokeMethodNoRange #  no, (a0=method, rOBJ="this")
+    lw        a1, 0(rBIX)                  #  reload resolved method
+    # finished resloving?
+    bnez      a1, common_invokeMethodNoRange #  yes, (a0=method, rOBJ="this")
+    move      rBIX, a0                     #  preserve method
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    move      a0, rBIX
+    b         common_invokeMethodNoRange #  whew, finally!
+#else
+    # got null?
+    bnez      v0, common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+    b         common_exceptionThrown       #  yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX= C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a3, rOBJ)    #  a3 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a3, a3) #  a3 <- thisPtr->clazz->vtable
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodRange #  (a0=method, rOBJ="this")
+
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    LOAD_base_offClassObject_super(a1, rBIX) #  a1 <- method->clazz->super
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    LOAD_base_offClassObject_vtableCount(a3, a1) #  a3 <- super->vtableCount
+    EXPORT_PC()                            #  must export for invoke
+    # compare (methodIndex, vtableCount)
+    bgeu      a2, a3, .LOP_INVOKE_SUPER_RANGE_nsm      #  method not present in superclass
+    LOAD_base_offClassObject_vtable(a1, a1) #  a1 <- ...clazz->super->vtable
+    LOAD_eas2(a0, a1, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodRange #  continue on
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  a0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    LOAD_base_offMethod_name(a1, a0)       #  a1 <- method name
+    b         common_errNoSuchMethod
+
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_STATIC            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * rBIX: &resolved_methodToCall
+     */
+    lhu       a2, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  null, handle exception
+    and       a2, kSubModeJitTraceBuild    #  trace under construction?
+    beqz      a2, common_invokeMethodRange #  no, (a0=method, rOBJ="this")
+    lw        a1, 0(rBIX)                  #  reload resolved method
+    # finished resloving?
+    bnez      a1, common_invokeMethodRange #  yes, (a0=method, rOBJ="this")
+    move      rBIX, a0                     #  preserve method
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    move      a0, rBIX
+    b         common_invokeMethodRange #  whew, finally!
+#else
+    # got null?
+    bnez      v0, common_invokeMethodRange #  (a0=method, rOBJ="this")
+    b         common_exceptionThrown       #  yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_INT */
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x4f000000               #  (float)maxint
+    move      rBIX, a0
+    JAL(__gesf2)                           #  is arg >= maxint?
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .LOP_FLOAT_TO_INT_set_vreg
+
+    move      a0, rBIX                     #  recover arg
+    li        a1, 0xcf000000               #  (float)minint
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .LOP_FLOAT_TO_INT_set_vreg
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        v0, 0                        #  return zero for NaN
+    bnez      t0, .LOP_FLOAT_TO_INT_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfsi)
+    b         .LOP_FLOAT_TO_INT_set_vreg
+#else
+    l.s       fa1, .LFLOAT_TO_INT_max
+    c.ole.s   fcc0, fa1, fa0
+    l.s       fv0, .LFLOAT_TO_INT_ret_max
+    bc1t      .LOP_FLOAT_TO_INT_set_vreg_f
+
+    l.s       fa1, .LFLOAT_TO_INT_min
+    c.ole.s   fcc0, fa0, fa1
+    l.s       fv0, .LFLOAT_TO_INT_ret_min
+    bc1t      .LOP_FLOAT_TO_INT_set_vreg_f
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .LOP_FLOAT_TO_INT_set_vreg_f
+
+    trunc.w.s  fv0, fa0
+    b         .LOP_FLOAT_TO_INT_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+    .word 0x4f000000
+.LFLOAT_TO_INT_min:
+    .word 0xcf000000
+.LFLOAT_TO_INT_ret_max:
+    .word 0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+    .word 0x80000000
+
+
+/* continuation for OP_FLOAT_TO_LONG */
+
+f2l_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x5f000000
+    move      rBIX, a0
+    JAL(__gesf2)
+
+    move      t0, v0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bgez      t0, .LOP_FLOAT_TO_LONG_set_vreg
+
+    move      a0, rBIX
+    li        a1, 0xdf000000
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    blez      t0, .LOP_FLOAT_TO_LONG_set_vreg
+
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .LOP_FLOAT_TO_LONG_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfdi)
+
+#else
+    l.s       fa1, .LLONG_TO_max
+    c.ole.s   fcc0, fa1, fa0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bc1t      .LOP_FLOAT_TO_LONG_set_vreg
+
+    l.s       fa1, .LLONG_TO_min
+    c.ole.s   fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    bc1t      .LOP_FLOAT_TO_LONG_set_vreg
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .LOP_FLOAT_TO_LONG_set_vreg
+
+    JAL(__fixsfdi)
+#endif
+
+    b         .LOP_FLOAT_TO_LONG_set_vreg
+
+.LLONG_TO_max:
+    .word 0x5f000000
+
+.LLONG_TO_min:
+    .word 0xdf000000
+
+/* continuation for OP_DOUBLE_TO_INT */
+
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)                           #  is arg >= maxint?
+
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .LOP_DOUBLE_TO_INT_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                           #  is arg <= minint?
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .LOP_DOUBLE_TO_INT_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    move      rARG2, rBIX                  #  compare against self
+    move      rARG3, rTEMP
+    JAL(__nedf2)                           #  is arg == self?
+
+    move      t0, v0                       #  zero == no
+    li        v0, 0
+    bnez      t0, .LOP_DOUBLE_TO_INT_set_vreg     #  return zero for NaN
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    JAL(__fixdfsi)                         #  convert double to int
+    b         .LOP_DOUBLE_TO_INT_set_vreg
+#else
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    l.s       fv0, .LDOUBLE_TO_INT_maxret
+    bc1t      .LOP_DOUBLE_TO_INT_set_vreg_f
+
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    l.s       fv0, .LDOUBLE_TO_INT_minret
+    bc1t      .LOP_DOUBLE_TO_INT_set_vreg_f
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .LOP_DOUBLE_TO_INT_set_vreg_f
+
+    trunc.w.d  fv0, fa0
+    b         .LOP_DOUBLE_TO_INT_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword 0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword 0xc1e0000000000000              #  minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word 0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word 0x80000000
+
+/* continuation for OP_DOUBLE_TO_LONG */
+
+d2l_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bgez      t1, .LOP_DOUBLE_TO_LONG_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    blez      t1, .LOP_DOUBLE_TO_LONG_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    move      rARG2, rBIX
+    move      rARG3, rTEMP
+    JAL(__nedf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .LOP_DOUBLE_TO_LONG_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    JAL(__fixdfdi)
+
+#else
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .LOP_DOUBLE_TO_LONG_set_vreg
+
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .LOP_DOUBLE_TO_LONG_set_vreg
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .LOP_DOUBLE_TO_LONG_set_vreg
+    JAL(__fixdfdi)
+#endif
+    b         .LOP_DOUBLE_TO_LONG_set_vreg
+
+
+.LDOUBLE_TO_LONG_max:
+    .dword 0x43e0000000000000              #  maxlong, as a double (high word)
+.LDOUBLE_TO_LONG_min:
+    .dword 0xc3e0000000000000              #  minlong, as a double (high word)
+.LDOUBLE_TO_LONG_ret_max:
+    .dword 0x7fffffffffffffff
+.LDOUBLE_TO_LONG_ret_min:
+    .dword 0x8000000000000000
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, a0)                    #  vAA::vAA+1 <- v0(low) :: v1(high)
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_VOLATILE_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+    SMP_DMB                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+    SMP_DMB_ST                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+    SMP_DMB
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_SGET_VOLATILE */
+
+.LOP_SGET_VOLATILE_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+    SMP_DMB                               #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SPUT_VOLATILE */
+
+.LOP_SPUT_VOLATILE_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SMP_DMB_ST                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    SMP_DMB
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+    SMP_DMB                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    beqz      rOBJ, common_errNullObject   #  object was null
+    GET_OPA4(a2)                           #  a2 <- A+
+    addu      rOBJ, rOBJ, a3               #  form address
+    .if 1
+    vLOAD64(a0, a1, rOBJ)                  #  a0/a1 <- obj.field (64-bit align ok)
+    .else
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- obj.field (64-bit align ok)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)                      #  a3 <- &fp[A]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    GET_OPA4(a2)                           #  a2 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- fp[A]
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    addu      a2, rOBJ, a3                 #  form address
+    .if 1
+    JAL(dvmQuasiAtomicSwap64Sync)          # stores r0/r1 into addr r2
+#    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .else
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+.LOP_SGET_WIDE_VOLATILE_finish:
+    GET_OPA(a1)                            #  a1 <- AA
+    .if 1
+    vLOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .else
+    LOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[AA]
+    STORE64(a2, a3, a1)                    #  vAA/vAA+1 <- a2/a3
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rOBJ:  &fp[AA]
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in a2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    # success ?
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    move      a2, v0
+    b         .LOP_SPUT_WIDE_VOLATILE_finish           # resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LW pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    FETCH(rINST, 2)                        #  rINST <- FEDC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    and       t0, rINST, 0xf000            #  isolate F
+    ESRN(t1, rFP, t0, 10)
+    lw        a3, 0(t1)                    #  a3 <- vF (shift right 12, left 2)
+3:
+    and       t0, rINST, 0x0f00            #  isolate E
+    ESRN(t1, rFP, t0, 6)
+    lw        a2, 0(t1)                    #  a2 <- vE
+2:
+    and       t0, rINST, 0x00f0            #  isolate D
+    ESRN(t1, rFP, t0, 2)
+    lw        a1, 0(t1)                    #  a1 <- vD
+1:
+    and       t0, rINST, 0x000f            #  isolate C
+    EASN(t1, rFP, t0, 2)
+    lw        a0, 0(t1)                    #  a0 <- vC
+0:
+    la        rINST, gDvmInlineOpsTable    #  table of InlineOperation
+    EAS4(t1, rINST, rBIX)                  #  t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                           #  sizeof=16, "func" is first entry
+    # (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .LOP_EXECUTE_INLINE_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.LOP_EXECUTE_INLINE_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rINST, v0                    #  save result of inline
+    move      a0, rOBJ                     #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rINST, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *  ra = return addr, above  [DO NOT JAL out of here w/o preserving ra]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    FETCH(rOBJ, 2)                       # rOBJ <- CCCC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    add       t0, rOBJ, 3
+    GET_VREG(a3, t0)
+3:
+    add       t0, rOBJ, 2
+    GET_VREG(a2, t0)
+2:
+    add       t0, rOBJ, 1
+    GET_VREG(a1, t0)
+1:
+    GET_VREG(a0, rOBJ)
+0:
+    la        rOBJ, gDvmInlineOpsTable      # table of InlineOperation
+    EAS4(t1, rOBJ, rBIX)                    # t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                            # sizeof=16, "func" is first entry
+    # not reached
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .LOP_EXECUTE_INLINE_RANGE_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPA(a0)                            #  a0 <- A
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    move      rINST, rOBJ                  #  rINST<- method
+    BAL(.LOP_EXECUTE_INLINE_RANGE_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rOBJ, v0                     #  save result of inline
+    move      a0, rINST                    #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rOBJ, common_exceptionThrown #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    lw      a1, offThread_mainHandlerTable(rSELF)
+    li      t0, OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(a1, t0)            # execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    addu      t2, rOBJ, a3                 #  form address
+    SMP_DMB_ST                            #  releasing store
+    sw a0, (t2)                        #  obj.field (32 bits) <- a0
+    SMP_DMB
+    beqz      a0, 1f                       #  stored a null reference?
+    srl       t1, rOBJ, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)                     #  mark card if not
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+.LOP_SGET_OBJECT_VOLATILE_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+    SMP_DMB                               #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.LOP_SPUT_OBJECT_VOLATILE_finish:                        #  field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    lw        t1, offField_clazz(a0)       #  t1 <- field->clazz
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SMP_DMB_ST                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    SMP_DMB
+    beqz      a1, 1f
+    srl       t2, t1, GC_CARD_SHIFT
+    addu      t3, a2, t2
+    sb        a2, (t3)
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: mips/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      rBIX, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    b       jitSVShadowRunStart                 # resume as if cache hit
+                                                # expects resume addr in rBIX
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    li        a2, kSVSPunt                 #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    move      rPC, a0                      # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a2, kSVSSingleStep           #  a2 <- interpreter entry point
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoProfile            #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSBackwardBranch       #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSNormal               #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoChain              #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+#else                                   /*  WITH_SELF_VERIFICATION */
+
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      a0, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    jr      a0                                  # resume translation
+
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0
+#if defined(WITH_JIT_TUNING)
+    move      a0, ra
+    JAL(dvmBumpPunt)
+#endif
+    EXPORT_PC()
+    sw        zero, offThread_inJitCodeCache(rSELF) # Back to the interp land
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    rPC <= Dalvik PC of instrucion to interpret
+ *    a1 <= Dalvik PC of resume instruction
+ *    ra <= resume point in translation
+ */
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0                       # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        sp, offThread_jitResumeNSP(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a1, 1
+    sw        a1, offThread_singleStepCount(rSELF) # just step once
+    move      a0, rSELF
+    li        a1, kSubModeCountedStep
+    JAL(dvmEnableSubMode)                   # (self, subMode)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    move      a1, rPC                      # arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 2f                       # 0 means translation does not exist
+    jr        a0
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # @ (pc, self)
+    sw        v0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    beqz      v0, 2f
+    move      a0, v0
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/* No translation, so request one if profiling isn't disabled */
+2:
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()
+    li        t0, kJitTSelectRequestHot
+    movn      a2, t0, a0                   #  ask for trace selection
+    bnez      a0, common_selectTrace
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNormal)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)           # @ (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    beqz      a0, toInterpreter            #  go if not, otherwise do chain
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, footer235
+
+    jr        a0                           #  continue native execution if so
+footer235:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 1f
+    jr        a0                           #  continue native execution if so
+1:
+#endif                                  /*  WITH_SELF_VERIFICATION */
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+
+toInterpreter:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    lw        a0, offThread_pJitProfTable(rSELF)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    # NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+
+common_testUpdateProfile:
+
+    beqz      a0, 4f
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    srl       a3, rPC, 12                  #  cheap, but fast hash function
+    xor       a3, a3, rPC
+    andi      a3, a3, JIT_PROF_SIZE-1      #  eliminate excess bits
+    addu      t1, a0, a3
+    lbu       a1, (t1)                     #  get counter
+    GET_INST_OPCODE(t0)
+    subu      a1, a1, 1                    #  decrement counter
+    sb        a1, (t1)                     #  and store it
+    beqz      a1, 1f
+    GOTO_OPCODE(t0)                        #  if not threshold, fallthrough otherwise
+1:
+    /* Looks good, reset the counter */
+    lw        a1, offThread_jitThreshold(rSELF)
+    sb        a1, (t1)
+    EXPORT_PC()
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        v0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+
+#if !defined(WITH_SELF_VERIFICATION)
+    li        t0, kJitTSelectRequest       #  ask for trace selection
+    movz      a2, t0, a0
+    beqz      a0, common_selectTrace
+    jr        a0                           #  jump to the translation
+#else
+
+    bne       a0, zero, skip_ask_for_trace_selection
+    li        a2, kJitTSelectRequest       #  ask for trace selection
+    j         common_selectTrace
+
+skip_ask_for_trace_selection:
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    move      rBIX, a0                     #  save target
+    jal       dvmCompilerGetInterpretTemplate
+    # special case?
+    bne       v0, rBIX, jitSVShadowRunStart  #  set up self verification shadow space
+    # Need to clear the inJitCodeCache flag
+    sw        zero, offThread_inJitCodeCache(rSELF) #  back to the interp land
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+
+common_selectTrace:
+    lhu        a0, offThread_subMode(rSELF)
+    andi       a0, (kSubModeJitTraceBuild | kSubModeJitSV)
+    bnez       a0, 3f                      # already doing JIT work, continue
+    sw         a2, offThread_jitState(rSELF)
+    move       a0, rSELF
+
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+
+    EXPORT_PC()
+    SAVE_PC_TO_SELF()
+    SAVE_FP_TO_SELF()
+    JAL(dvmJitCheckTraceRequest)
+3:
+    FETCH_INST()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+4:
+    GET_INST_OPCODE(t0)                    # extract opcode from rINST
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    move      a0, rPC                      #  r0 <- program counter
+    move      a1, rFP                      #  r1 <- frame pointer
+    move      a2, rSELF                    #  r2 <- InterpState pointer
+    move      a3, rBIX                     #  r3 <- target translation
+    jal       dvmSelfVerificationSaveState #  save registers to shadow space
+    lw        rFP, offShadowSpace_shadowFP(v0) #  rFP <- fp in shadow space
+    jr        rBIX                         #  jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    move      a1, rFP                      #  pass ending fp
+    move      a3, rSELF                    #  pass self ptr for convenience
+    jal       dvmSelfVerificationRestoreState #  restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()                 #  restore pc, fp
+    lw        a1, offShadowSpace_svState(a0) #  get self verification state
+    beq       a1, zero, 1f                 #  check for punt condition
+
+    # Setup SV single-stepping
+    move      a0, rSELF
+    li        a1, kSubModeJitSV
+    JAL(dvmEnableSubMode)                  # (self, subMode)
+    li        a2, kJitSelfVerification     #  ask for self verification
+    sw        a2, offThread_jitState(rSELF)
+    # Intentional fallthrough
+
+1:
+    # exit to interpreter without check
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+    .ent common_gotoBail
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                   # export state to "thread"
+    move      a0, rSELF                    # a0 <- self ptr
+    b         dvmMterpStdBail              # call(self, changeInterp)
+    .end common_gotoBail
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    beqz    rOBJ, 1f
+    lw      rOBJ, offObject_clazz(rOBJ)
+1:
+    sw      a0, offThread_methodToCall(rSELF)
+    sw      rOBJ, offThread_callsiteClass(rSELF)
+    jr      ra
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPA(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)              #  rBIX <- stack save area
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)                           #  a1 <- CCCC
+.LinvokeRangeArgs:
+    # a0=methodToCall, a1=CCCC, a2=count, rBIX=outs
+    # (very few methods have > 10 args; could unroll for common cases)
+    EAS2(a3, rFP, a1)
+    sll       t0, a2, 2
+    subu      rBIX, rBIX, t0
+
+1:
+    lw        a1, 0(a3)
+    addu      a3, a3, 4
+    subu      a2, a2, 1
+    sw        a1, 0(rBIX)
+    addu      rBIX, 4
+    bnez      a2, 1b
+    b         .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", "rOBJ is this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPB(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)
+
+    # a0=methodToCall, a1=GFED, a2=count,
+.LinvokeNonRange:
+    beq       a2, 0, 0f
+    beq       a2, 1, 1f
+    beq       a2, 2, 2f
+    beq       a2, 3, 3f
+    beq       a2, 4, 4f
+    beq       a2, 5, 5f
+
+5:
+    and       t0, rINST, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+4:
+    and       t0, a1, 0xf000
+    ESRN(t2, rFP, t0, 10)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+3:
+    and       t0, a1, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+2:
+    and       t0, a1, 0x00f0
+    ESRN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+1:
+    and       t0, a1, 0x000f
+    EASN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+0:
+    #fall through .LinvokeArgsDone
+
+
+.LinvokeArgsDone:                          #  a0=methodToCall
+    lhu       rOBJ, offMethod_registersSize(a0)
+    lhu       a3, offMethod_outsSize(a0)
+    lw        a2, offMethod_insns(a0)
+    lw        rINST, offMethod_clazz(a0)
+    # find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(a1, rFP)              # a1 <- stack save area
+    sll       t0, rOBJ, 2                    #  a1 <- newFp (old savearea - regsSize)
+    subu      a1, a1, t0
+    SAVEAREA_FROM_FP(rBIX, a1)
+    lw        rOBJ, offThread_interpStackEnd(rSELF) #  t3 <- interpStackEnd
+    sll       t2, a3, 2
+    subu      t0, rBIX, t2
+    lhu       ra, offThread_subMode(rSELF)
+    lw        a3, offMethod_accessFlags(a0) #  a3 <- methodToCall->accessFlags
+    bltu      t0, rOBJ, .LstackOverflow      #  yes, this frame will overflow stack
+
+
+    # set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(t0, rFP)
+    sw        t0, offStackSaveArea_prevSave(rBIX)
+#endif
+    sw        rFP, (offStackSaveArea_prevFrame)(rBIX)
+    sw        rPC, (offStackSaveArea_savedPc)(rBIX)
+#if defined(WITH_JIT)
+    sw        zero, (offStackSaveArea_returnAddr)(rBIX)
+#endif
+    sw        a0, (offStackSaveArea_method)(rBIX)
+    # Profiling?
+    bnez       ra, 2f
+1:
+    and       t2, a3, ACC_NATIVE
+    bnez      t2, .LinvokeNative
+    lhu       rOBJ, (a2)           # rOBJ -< load Inst from New PC
+    lw        a3, offClassObject_pDvmDex(rINST)
+    move      rPC, a2              # Publish new rPC
+    # Update state values for the new method
+    # a0=methodToCall, a1=newFp, a3=newMethodClass, rOBJ=newINST
+    sw        a0, offThread_method(rSELF)
+    sw        a3, offThread_methodClassDex(rSELF)
+    li        a2, 1
+    sw        a2, offThread_debugIsMethodEntry(rSELF)
+
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    move      rFP, a1                    # fp = newFp
+    GET_PREFETCHED_OPCODE(t0, rOBJ)      # extract prefetched opcode from rOBJ
+    move      rINST, rOBJ                # publish new rINST
+    sw        a1, offThread_curFrame(rSELF)
+    bnez      a0, common_updateProfile
+    GOTO_OPCODE(t0)
+#else
+    move      rFP, a1
+    GET_PREFETCHED_OPCODE(t0, rOBJ)
+    move      rINST, rOBJ
+    sw        a1, offThread_curFrame(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+2:
+    # Profiling - record method entry.  a0: methodToCall
+    STACK_STORE(a0, 0)
+    STACK_STORE(a1, 4)
+    STACK_STORE(a2, 8)
+    STACK_STORE(a3, 12)
+    sw       rPC, offThread_pc(rSELF)          # update interpSave.pc
+    move     a1, a0
+    move     a0, rSELF
+    JAL(dvmReportInvoke)
+    STACK_LOAD(a3, 12)                         # restore a0-a3
+    STACK_LOAD(a2, 8)
+    STACK_LOAD(a1, 4)
+    STACK_LOAD(a0, 0)
+    b        1b
+.LinvokeNative:
+    # Prep for the native call
+    # a0=methodToCall, a1=newFp, rBIX=newSaveArea
+    lhu       ra, offThread_subMode(rSELF)
+    lw        t3, offThread_jniLocal_topCookie(rSELF)
+    sw        a1, offThread_curFrame(rSELF)
+    sw        t3, offStackSaveArea_localRefCookie(rBIX) # newFp->localRefCookie=top
+    move      a2, a0
+    move      a0, a1
+    addu      a1, rSELF, offThread_retval
+    move      a3, rSELF
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b         .Lskip
+    .ent dalvik_mterp
+dalvik_mterp:
+    STACK_STORE_FULL()
+.Lskip:
+#endif
+    bnez      ra, 11f                          # Any special SubModes active?
+    lw        t9, offMethod_nativeFunc(a2)
+    jalr      t9
+    lw        gp, STACK_OFFSET_GP(sp)
+7:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw        a0, offStackSaveArea_localRefCookie(rBIX)
+    lw        a1, offThread_exception(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    sw        a0, offThread_jniLocal_topCookie(rSELF)    # new top <- old top
+    bnez      a1, common_exceptionThrown
+
+    FETCH_ADVANCE_INST(3)
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+11:
+    # a0=newFp, a1=&retval, a2=methodToCall, a3=self, ra=subModes
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    move      a0, a2                    # a0 <- methodToCall
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPreNativeInvoke)       # (methodToCall, self, fp)
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Call the native method
+    lw       t9, offMethod_nativeFunc(a2)      # t9<-methodToCall->nativeFunc
+    jalr     t9
+    lw       gp, STACK_OFFSET_GP(sp)
+
+    # Restore the pre-call arguments
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Finish up any post-invoke subMode requirements
+    move      a0, a2
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPostNativeInvoke)      # (methodToCall, self, fp)
+    b         7b
+
+
+.LstackOverflow:       # a0=methodToCall
+    move      a1, a0                    #  a1 <- methodToCall
+    move      a0, rSELF                 # a0 <- self
+    JAL(dvmHandleStackOverflow)         #  dvmHandleStackOverflow(self, methodToCall)
+    b         common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .end dalvik_mterp
+#endif
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    lhu       t0, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(a0, rFP)
+    lw        rOBJ, offStackSaveArea_savedPc(a0) # rOBJ = saveArea->savedPc
+    bnez      t0, 19f
+14:
+    lw        rFP, offStackSaveArea_prevFrame(a0) # fp = saveArea->prevFrame
+    lw        a2, (offStackSaveArea_method - sizeofStackSaveArea)(rFP)
+                                               # a2<- method we're returning to
+    # is this a break frame?
+    beqz      a2, common_gotoBail              # break frame, bail out completely
+
+    lw        rBIX, offMethod_clazz(a2)        # rBIX<- method->clazz
+    lw        rIBASE, offThread_curHandlerTable(rSELF) # refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, rOBJ, 3)      # advance rOBJ, update new rINST
+    sw        a2, offThread_method(rSELF)      # self->method = newSave->method
+    lw        a1, offClassObject_pDvmDex(rBIX) # r1<- method->clazz->pDvmDex
+    sw        rFP, offThread_curFrame(rSELF)   # curFrame = fp
+#if defined(WITH_JIT)
+    lw         rBIX, offStackSaveArea_returnAddr(a0)
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    sw         rBIX, offThread_inJitCodeCache(rSELF) # may return to JIT'ed land
+    beqz       rBIX, 15f                       # caller is compiled code
+    move       t9, rBIX
+    jalr       t9
+    lw         gp, STACK_OFFSET_GP(sp)
+15:
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    GOTO_OPCODE(t0)                            # jump to next instruction
+#else
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+19:
+    # Handle special actions
+    # On entry, a0: StackSaveArea
+    lw         a1, offStackSaveArea_prevFrame(a0) # a1<- prevFP
+    sw         rPC, offThread_pc(rSELF)        # update interpSave.pc
+    sw         a1, offThread_curFrame(rSELF)   # update interpSave.curFrame
+    move       a0, rSELF
+    JAL(dvmReportReturn)
+    SAVEAREA_FROM_FP(a0, rFP)                  # restore StackSaveArea
+    b          14b
+
+    .if 0
+    /*
+     * Return handling, calls through "glue code".
+     */
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                       # export state
+    move       a0, rSELF                       # arg to function
+    JAL(dvmMterp_returnFromMethod)
+    b          common_resumeAfterGlueCall
+    .endif
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+    .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+    move     a0, rSELF
+    JAL(dvmCheckSuspendPending)
+    lw       rOBJ, offThread_exception(rSELF)
+    move     a1, rSELF
+    move     a0, rOBJ
+    JAL(dvmAddTrackedAlloc)
+    lhu      a2, offThread_subMode(rSELF)
+    sw       zero, offThread_exception(rSELF)
+
+    # Special subMode?
+    bnez     a2, 7f                     # any special subMode handling needed?
+8:
+    /* set up args and a local for "&fp" */
+    sw       rFP, 20(sp)                 #  store rFP => tmp
+    addu     t0, sp, 20                  #  compute &tmp
+    sw       t0, STACK_OFFSET_ARG04(sp)  #  save it in arg4 as per ABI
+    li       a3, 0                       #  a3 <- false
+    lw       a1, offThread_method(rSELF)
+    move     a0, rSELF
+    lw       a1, offMethod_insns(a1)
+    move     a2, rOBJ
+    subu     a1, rPC, a1
+    sra      a1, a1, 1
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    JAL(dvmFindCatchBlock)           # call(self, relPc, exc, scan?, &fp)
+    lw        rFP, 20(sp)            # retrieve the updated rFP
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    move      a0, v0
+    bltz      v0, .LnotCaughtLocally
+
+    /* fix earlier stack overflow if necessary; Preserve a0 */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 1f
+    move      rBIX, a0
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)
+    move      a0, rBIX
+
+1:
+
+/* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(a1, rFP)           # a1<- new save area
+    lw        a1, offStackSaveArea_method(a1)
+    sw        a1, offThread_method(rSELF)
+    lw        a2, offMethod_clazz(a1)
+    lw        a3, offMethod_insns(a1)
+    lw        a2, offClassObject_pDvmDex(a2)
+    EAS1(rPC, a3, a0)
+    sw        a2, offThread_methodClassDex(rSELF)
+
+    /* release the tracked alloc on the exception */
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+
+    /* restore the exception if the handler wants it */
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    bne       t0, OP_MOVE_EXCEPTION, 2f
+    sw        rOBJ, offThread_exception(rSELF)
+2:
+    GOTO_OPCODE(t0)
+
+    # Manage debugger bookkeeping
+7:
+    sw        rPC, offThread_pc(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmReportExceptionThrow)
+    b         8b
+
+.LnotCaughtLocally:                     #  rOBJ = exception
+    /* fix stack overflow if necessary */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 3f
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)           #  dvmCleanupStackOverflow(self, exception)
+
+3:
+    # may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    lw        a0, offThread_method(rSELF)
+    lw        a1, offMethod_insns(a0)
+    subu      a1, rPC, a1
+    sra       a1, a1, 1
+    JAL(dvmLineNumFromPC)
+    sw        v0, 20(sp)
+    # dvmGetMethodSourceFile(method)
+    lw        a0, offThread_method(rSELF)
+    JAL(dvmGetMethodSourceFile)
+    sw        v0, 16(sp)
+    # exception->clazz->descriptor
+    lw        a3, offObject_clazz(rOBJ)
+    lw        a3, offClassObject_descriptor(a3)
+    la        a2, .LstrExceptionNotCaughtLocally
+    la        a1, .LstrLogTag
+    li        a0, 3
+    JAL(__android_log_print)
+#endif
+    sw        rOBJ, offThread_exception(rSELF)
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+    b         common_gotoBail
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_TO_SELF()                # export state
+    SAVE_FP_TO_SELF()
+    move     a0, rSELF               # arg to function
+    JAL(dvmMterp_exceptionThrown)
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     rBIX: &dvmDex->pResFields[field]
+     *     a0:  field pointer (must preserve)
+     */
+common_verifyField:
+     lhu     a3, offThread_subMode(rSELF)
+     andi    a3, kSubModeJitTraceBuild
+     bnez    a3, 1f                 # Not building trace, continue
+     jr      ra
+1:
+     lw      a1, (rBIX)
+     beqz    a1, 2f                 # resolution complete ?
+     jr      ra
+2:
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(ra, 16)
+    move    a0, rSELF
+    move    a1, rPC
+    JAL(dvmJitEndTraceSelect)        #(self,pc) end trace before this inst)
+    SCRATCH_LOAD(a0, 0)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(ra, 16)
+    jr      ra                       # return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()           #  pull rPC and rFP out of thread
+    lw      rIBASE, offThread_curHandlerTable(rSELF) # refresh
+    FETCH_INST()                     #  load rINST from rPC
+    GET_INST_OPCODE(t0)              #  extract opcode from rINST
+    GOTO_OPCODE(t0)                  #  jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use a1
+ * and a3 because those just happen to be the registers all our callers are
+ * using. We move a3 before calling the C function, but a1 happens to match.
+ * a1: index
+ * a3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    move      a0, a3
+    JAL(dvmThrowArrayIndexOutOfBoundsException)
+    b         common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    la     a0, .LstrDivideByZero
+    JAL(dvmThrowArithmeticException)
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in a1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    move    a0, a1                                # arg0 <- len
+    JAL(dvmThrowNegativeArraySizeException)    # (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in a1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    move     a0, a1
+    JAL(dvmThrowNoSuchMethodError)
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    li      a0, 0
+    JAL(dvmThrowNullPointerException)
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will be in ra. Use a jal to jump here.
+ */
+common_abort:
+    lw      zero,-4(zero)            #  generate SIGSEGV
+
+/*
+ * Spit out a "we were here", preserving all registers.
+ */
+    .macro SQUEAK num
+common_squeak\num:
+    STACK_STORE_RA();
+    la        a0, .LstrSqueak
+    LOAD_IMM(a1, \num);
+    JAL(printf);
+    STACK_LOAD_RA();
+    RETURN;
+    .endm
+
+    SQUEAK 0
+    SQUEAK 1
+    SQUEAK 2
+    SQUEAK 3
+    SQUEAK 4
+    SQUEAK 5
+
+/*
+ * Spit out the number in a0, preserving registers.
+ */
+common_printNum:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrSqueak
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    STACK_STORE_RA()
+    la        a0, .LstrNewline
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+    /*
+     * Print the 32-bit quantity in a0 as a hex value, preserving registers.
+     */
+common_printHex:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrPrintHex
+    JAL(printf)
+    STACK_LOAD_RA()
+RETURN;
+
+/*
+ * Print the 64-bit quantity in a0-a1, preserving registers.
+ */
+common_printLong:
+    STACK_STORE_RA()
+    MOVE_REG(a3, a1)
+    MOVE_REG(a2, a0)
+    la        a0, .LstrPrintLong
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN;
+
+/*
+ * Print full method info.  Pass the Method* in a0.  Preserves regs.
+ */
+common_printMethod:
+    STACK_STORE_RA()
+    JAL(dvmMterpPrintMethod)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if 0
+common_dumpRegs:
+    STACK_STORE_RA()
+    JAL(dvmMterpDumpMipsRegs)
+    STACK_LOAD_RA()
+    RETURN
+    .endif
+
+/*
+ * Zero-terminated ASCII string data.
+ */
+    .data
+
+.LstrBadEntryPoint:
+    .asciiz "Bad entry point %d\n"
+.LstrDivideByZero:
+    .asciiz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciiz "filled-new-array only implemented for 'int'"
+.LstrLogTag:
+    .asciiz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciiz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciiz "\n"
+.LstrSqueak:
+    .asciiz "<%d>"
+.LstrPrintHex:
+    .asciiz "<0x%x>"
+.LstrPrintLong:
+    .asciiz "<%lld>"
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (0 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (1 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (2 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (3 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (4 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (5 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (6 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (7 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (8 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (9 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (10 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (11 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (12 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (13 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (14 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (15 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (16 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (17 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (18 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (19 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (20 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (21 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (22 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (23 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (24 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (25 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (26 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (27 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (28 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (29 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (30 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (31 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (32 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (33 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (34 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (35 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (36 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (37 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (38 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (39 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (40 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (41 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (42 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (43 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (44 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (45 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (46 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (47 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (48 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (49 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (50 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (51 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (52 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (53 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (54 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (55 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (56 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (57 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (58 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (59 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (60 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (61 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (62 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (63 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (64 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (65 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (66 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (67 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (68 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (69 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (70 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (71 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (72 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (73 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (74 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (75 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (76 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (77 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (78 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (79 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (80 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (81 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (82 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (83 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (84 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (85 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (86 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (87 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (88 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (89 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (90 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (91 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (92 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (93 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (94 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (95 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (96 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (97 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (98 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (99 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (100 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (101 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (102 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (103 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (104 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (105 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (106 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (107 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (108 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (109 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (110 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (111 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (112 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (113 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (114 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (115 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (116 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (117 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (118 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (119 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (120 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (121 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (122 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (123 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (124 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (125 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (126 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (127 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (128 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (129 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (130 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (131 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (132 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (133 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (134 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (135 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (136 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (137 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (138 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (139 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (140 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (141 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (142 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (143 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (144 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (145 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (146 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (147 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (148 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (149 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (150 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (151 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (152 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (153 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (154 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (155 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (156 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (157 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (158 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (159 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (160 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (161 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (162 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (163 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (164 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (165 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (166 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (167 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (168 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (169 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (170 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (171 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (172 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (173 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (174 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (175 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (176 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (177 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (178 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (179 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (180 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (181 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (182 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (183 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (184 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (185 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (186 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (187 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (188 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (189 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (190 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (191 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (192 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (193 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (194 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (195 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (196 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (197 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (198 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (199 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (200 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (201 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (202 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (203 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (204 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (205 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (206 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (207 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (208 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (209 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (210 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (211 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (212 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (213 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (214 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (215 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (216 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (217 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (218 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (219 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (220 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (221 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (222 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (223 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (224 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (225 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (226 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (227 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (228 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (229 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (230 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (231 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (232 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (233 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (234 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (235 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (236 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (237 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (238 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (239 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (240 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (241 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (242 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (243 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (244 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (245 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (246 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (247 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (248 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (249 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (250 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (251 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (252 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (253 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (254 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (255 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+    .balign 128
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S
new file mode 100644
index 0000000..760e674
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-x86.S
@@ -0,0 +1,16689 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: x86/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * 32-bit x86 definitions and declarations.
+ */
+
+/*
+386 ABI general notes:
+
+Caller save set:
+   eax, edx, ecx, st(0)-st(7)
+Callee save set:
+   ebx, esi, edi, ebp
+Return regs:
+   32-bit in eax
+   64-bit in edx:eax (low-order 32 in eax)
+   fp on top of fp stack st(0)
+
+Parameters passed on stack, pushed right-to-left.  On entry to target, first
+parm is at 4(%esp).  Traditional entry code is:
+
+functEntry:
+    push    %ebp             # save old frame pointer
+    mov     %ebp,%esp        # establish new frame pointer
+    sub     FrameSize,%esp   # Allocate storage for spill, locals & outs
+
+Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp)
+
+Stack must be 16-byte aligned to support SSE in native code.
+
+If we're not doing variable stack allocation (alloca), the frame pointer can be
+eliminated and all arg references adjusted to be esp relative.
+
+Mterp notes:
+
+Some key interpreter variables will be assigned to registers.  Note that each
+will also have an associated spill location (mostly useful for those assigned
+to callee save registers).
+
+  nick     reg   purpose
+  rPC      esi   interpreted program counter, used for fetching instructions
+  rFP      edi   interpreted frame pointer, used for accessing locals and args
+  rINSTw   bx    first 16-bit code of current instruction
+  rINSTbl  bl    opcode portion of instruction word
+  rINSTbh  bh    high byte of inst word, usually contains src/tgt reg names
+  rIBASE   edx   base of instruction handler table
+
+Notes:
+   o High order 16 bits of ebx must be zero on entry to handler
+   o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit
+   o eax and ecx are scratch, rINSTw/ebx sometimes scratch
+
+*/
+
+#define rSELF    8(%ebp)
+#define rPC      %esi
+#define rFP      %edi
+#define rINST    %ebx
+#define rINSTw   %bx
+#define rINSTbh  %bh
+#define rINSTbl  %bl
+#define rIBASE   %edx
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0        (  8)
+#define CALLER_RP      (  4)
+#define PREV_FP        (  0)
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL      ( -4)
+#define ESI_SPILL      ( -8)
+#define EBX_SPILL      (-12)
+#define rPC_SPILL      (-16)
+#define rFP_SPILL      (-20)
+#define rINST_SPILL    (-24)
+#define rIBASE_SPILL   (-28)
+#define TMP_SPILL1     (-32)
+#define TMP_SPILL2     (-36)
+#define TMP_SPILL3     (-20)
+#define LOCAL0_OFFSET  (-44)
+#define LOCAL1_OFFSET  (-48)
+#define LOCAL2_OFFSET  (-52)
+/* Out Arg offsets, relative to %esp */
+#define OUT_ARG4       ( 16)
+#define OUT_ARG3       ( 12)
+#define OUT_ARG2       (  8)
+#define OUT_ARG1       (  4)
+#define OUT_ARG0       (  0)  /* <- dvmMterpStdRun esp */
+#if defined(WITH_JIT)
+/* for spill region: increase size by 48 (to keep 16-byte alignment) */
+/* 76 + 48 = 124 */
+#define JIT_SPILL      (-56)
+#define FRAME_SIZE     124
+#else
+#define FRAME_SIZE     76
+#endif
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP1(reg) movl reg,TMP_SPILL1(%ebp)
+#define UNSPILL_TMP1(reg) movl TMP_SPILL1(%ebp),reg
+#define SPILL_TMP2(reg) movl reg,TMP_SPILL2(%ebp)
+#define UNSPILL_TMP2(reg) movl TMP_SPILL2(%ebp),reg
+#define SPILL_TMP3(reg) movl reg,TMP_SPILL3(%ebp)
+#define UNSPILL_TMP3(reg) movl TMP_SPILL3(%ebp),reg
+
+#if defined(WITH_JIT)
+.macro GET_JIT_PROF_TABLE _self _reg
+    movl    offThread_pJitProfTable(\_self),\_reg
+.endm
+.macro GET_JIT_THRESHOLD _self _reg
+    movl    offThread_jitThreshold(\_self),\_reg
+.endm
+#endif
+
+/* save/restore the PC and/or FP from the self struct */
+.macro SAVE_PC_FP_TO_SELF _reg
+    movl     rSELF,\_reg
+    movl     rPC,offThread_pc(\_reg)
+    movl     rFP,offThread_curFrame(\_reg)
+.endm
+
+.macro LOAD_PC_FP_FROM_SELF
+    movl    rSELF,rFP
+    movl    offThread_pc(rFP),rPC
+    movl    offThread_curFrame(rFP),rFP
+.endm
+
+/* The interpreter assumes a properly aligned stack on entry, and
+ * will preserve 16-byte alignment.
+ */
+
+/*
+ * "export" the PC to the interpreted stack frame, f/b/o future exception
+ * objects.  Must be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+.macro EXPORT_PC
+    movl     rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+.endm
+
+.macro GET_PC
+    movl     (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP), rPC
+.endm
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+.macro SAVEAREA_FROM_FP _reg
+    leal    -sizeofStackSaveArea(rFP), \_reg
+.endm
+
+/*
+ * Fetch the next instruction from rPC into rINSTw.  Does not advance rPC.
+ */
+.macro FETCH_INST
+    movzwl    (rPC),rINST
+.endm
+
+/*
+ * Fetch the opcode byte and zero-extend it into _reg.  Must be used
+ * in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_R _reg
+    movzbl    (rPC),\_reg
+.endm
+
+/*
+ * Fetch the opcode byte at _count words offset from rPC and zero-extend
+ * it into _reg.  Must be used in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_OPCODE _count _reg
+    movzbl  \_count*2(rPC),\_reg
+.endm
+
+/*
+ * Fetch the nth instruction word from rPC into rINSTw.  Does not advance
+ * rPC, and _count is in words
+ */
+.macro FETCH_INST_WORD _count
+    movzwl  \_count*2(rPC),rINST
+.endm
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+.macro FETCH_INST_INDEXED _reg
+    movzwl  (rPC,\_reg,2),rINST
+.endm
+
+/*
+ * Advance rPC by instruction count
+ */
+.macro ADVANCE_PC _count
+    leal  2*\_count(rPC),rPC
+.endm
+
+/*
+ * Advance rPC by branch offset in register
+ */
+.macro ADVANCE_PC_INDEXED _reg
+    leal (rPC,\_reg,2),rPC
+.endm
+
+.macro GOTO_NEXT
+     movzx   rINSTbl,%eax
+     movzbl  rINSTbh,rINST
+     jmp     *(rIBASE,%eax,4)
+.endm
+
+   /*
+    * Version of GOTO_NEXT that assumes _reg preloaded with opcode.
+    * Should be paired with FETCH_INST_R
+    */
+.macro GOTO_NEXT_R _reg
+     movzbl  1(rPC),rINST
+     jmp     *(rIBASE,\_reg,4)
+.endm
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+.macro GET_VREG_R _reg _vreg
+    movl     (rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG _reg _vreg
+    movl     \_reg,(rFP,\_vreg,4)
+.endm
+
+.macro GET_VREG_WORD _reg _vreg _offset
+    movl     4*(\_offset)(rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG_WORD _reg _vreg _offset
+    movl     \_reg,4*(\_offset)(rFP,\_vreg,4)
+.endm
+
+#define sReg0 LOCAL0_OFFSET(%ebp)
+#define sReg1 LOCAL1_OFFSET(%ebp)
+#define sReg2 LOCAL2_OFFSET(%ebp)
+
+   /*
+    * x86 JIT Helpers
+    */
+
+    .macro dumpSwitch _regData _regScratch1 _regScratch2
+    .endm
+
+   /*
+    * Hard coded helper values.
+    */
+
+.balign 16
+
+.LdoubNeg:
+    .quad       0x8000000000000000
+
+.L64bits:
+    .quad       0xFFFFFFFFFFFFFFFF
+
+.LshiftMask2:
+    .quad       0x0000000000000000
+.LshiftMask:
+    .quad       0x000000000000003F
+
+.Lvalue64:
+    .quad       0x0000000000000040
+
+.LvaluePosInfLong:
+    .quad       0x7FFFFFFFFFFFFFFF
+
+.LvalueNegInfLong:
+    .quad       0x8000000000000000
+
+.LvalueNanLong:
+    .quad       0x0000000000000000
+
+.LintMin:
+.long   0x80000000
+
+.LintMax:
+.long   0x7FFFFFFF
+
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+
+    .global dvmAsmInstructionStartCode
+    .type   dvmAsmInstructionStartCode, %function
+dvmAsmInstructionStartCode = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+.L_OP_NOP: /* 0x00 */
+/* File: x86/OP_NOP.S */
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE: /* 0x01 */
+/* File: x86/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    movzx    rINSTbl,%eax              # eax <= AA
+    movw     2(rPC),rINSTw             # rINSTw <= BBBB
+    GET_VREG_R rINST rINST             # rINST- fp[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG rINST %eax                # fp[AA]<- ecx]
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_16: /* 0x03 */
+/* File: x86/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    movzwl    4(rPC),%ecx              # ecx<- BBBB
+    movzwl    2(rPC),%eax              # eax<- AAAA
+    GET_VREG_R  rINST %ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG  rINST %eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: x86/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzbl    rINSTbl,%ecx                # ecx <- BA
+    sarl      $4,rINST                   # rINST<- B
+    GET_VREG_WORD %eax rINST 0            # eax<- v[B+0]
+    GET_VREG_WORD rINST rINST 1           # rINST<- v[B+1]
+    andb      $0xf,%cl                   # ecx <- A
+    SET_VREG_WORD rINST %ecx 1            # v[A+1]<- rINST
+    SET_VREG_WORD %eax %ecx 0             # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: x86/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    2(rPC),%ecx              # ecx<- BBBB
+    movzbl    rINSTbl,%eax             # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0         # rINST<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1          # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0         # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1          # v[AAAA+1]<- eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: x86/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    4(rPC),%ecx            # ecx<- BBBB
+    movzwl    2(rPC),%eax            # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0       # rINSTw_WORD<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1        # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0       # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1        # v[AAAA+1]<- ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: x86/OP_MOVE_OBJECT.S */
+/* File: x86/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: x86/OP_MOVE_OBJECT_FROM16.S */
+/* File: x86/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    movzx    rINSTbl,%eax              # eax <= AA
+    movw     2(rPC),rINSTw             # rINSTw <= BBBB
+    GET_VREG_R rINST rINST             # rINST- fp[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG rINST %eax                # fp[AA]<- ecx]
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: x86/OP_MOVE_OBJECT_16.S */
+/* File: x86/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    movzwl    4(rPC),%ecx              # ecx<- BBBB
+    movzwl    2(rPC),%eax              # eax<- AAAA
+    GET_VREG_R  rINST %ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG  rINST %eax
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    movl     rSELF,%eax                    # eax<- rSELF
+    movl     offThread_retval(%eax),%eax   # eax<- self->retval.l
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG  %eax rINST                   # fp[AA]<- retval.l
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    movl    rSELF,%ecx
+    movl    offThread_retval(%ecx),%eax
+    movl    4+offThread_retval(%ecx),%ecx
+    SET_VREG_WORD %eax rINST 0     # v[AA+0] <- eax
+    SET_VREG_WORD %ecx rINST 1     # v[AA+1] <- ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: x86/OP_MOVE_RESULT_OBJECT.S */
+/* File: x86/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    movl     rSELF,%eax                    # eax<- rSELF
+    movl     offThread_retval(%eax),%eax   # eax<- self->retval.l
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG  %eax rINST                   # fp[AA]<- retval.l
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    movl    rSELF,%ecx
+    movl    offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+    SET_VREG %eax rINST                # fp[AA]<- exception object
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    movl    $0,offThread_exception(%ecx) # dvmClearException bypass
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: x86/OP_RETURN_VOID.S */
+    jmp       common_returnFromMethod
+
+/* ------------------------------ */
+.L_OP_RETURN: /* 0x0f */
+/* File: x86/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    movl    %eax,offThread_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
+
+/* ------------------------------ */
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    movl    rSELF,%ecx
+    GET_VREG_WORD %eax rINST 0       # eax<- v[AA+0]
+    GET_VREG_WORD rINST rINST 1      # rINST<- v[AA+1]
+    movl    %eax,offThread_retval(%ecx)
+    movl    rINST,4+offThread_retval(%ecx)
+    jmp     common_returnFromMethod
+
+/* ------------------------------ */
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: x86/OP_RETURN_OBJECT.S */
+/* File: x86/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    movl    %eax,offThread_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
+
+
+/* ------------------------------ */
+.L_OP_CONST_4: /* 0x12 */
+/* File: x86/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    movsx   rINSTbl,%eax              # eax<-ssssssBx
+    movl    $0xf,rINST
+    andl    %eax,rINST                # rINST<- A
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    sarl    $4,%eax
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_16: /* 0x13 */
+/* File: x86/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    movswl  2(rPC),%ecx                # ecx<- ssssBBBB
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    SET_VREG %ecx rINST                # vAA<- ssssBBBB
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_CONST: /* 0x14 */
+/* File: x86/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    movl      2(rPC),%eax             # grab all 32 bits at once
+    movl      rINST,rINST             # rINST<- AA
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG %eax rINST               # vAA<- eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $16,%eax                  # eax<- BBBB0000
+    SET_VREG %eax rINST                   # vAA<- eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: x86/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    movswl    2(rPC),%eax               # eax<- ssssBBBB
+    SPILL(rIBASE)                       # preserve rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST 1        # store msw
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: x86/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    movl     2(rPC),%eax                # eax<- BBBBbbbb
+    SPILL(rIBASE)                       # save rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST,1        # store msw
+    FETCH_INST_OPCODE 3 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: x86/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    movl      2(rPC),%eax         # eax<- lsw
+    movzbl    rINSTbl,%ecx        # ecx<- AA
+    movl      6(rPC),rINST        # rINST<- msw
+    leal      (rFP,%ecx,4),%ecx   # dst addr
+    movl      rINST,4(%ecx)
+    movl      %eax,(%ecx)
+    FETCH_INST_OPCODE 5 %ecx
+    ADVANCE_PC 5
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $16,%eax                  # eax<- BBBB0000
+    SET_VREG_WORD %eax rINST 1            # v[AA+1]<- eax
+    xorl       %eax,%eax
+    SET_VREG_WORD %eax rINST 0            # v[AA+0]<- eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: x86/OP_CONST_STRING.S */
+
+    /* const/string vAA, String@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_STRING_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.LOP_CONST_STRING_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86/OP_CONST_STRING_JUMBO.S */
+
+    /* const/string vAA, String@BBBBBBBB */
+    movl      rSELF,%ecx
+    movl      2(rPC),%eax              # eax<- BBBBBBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 3 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_STRING_JUMBO_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.LOP_CONST_STRING_JUMBO_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     2(rPC),%ecx               # ecx<- BBBBBBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 3 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: x86/OP_CONST_CLASS.S */
+
+    /* const/class vAA, Class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+    movl      (%ecx,%eax,4),%eax       # eax<- rResClasses[BBBB]
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_CLASS_resolve
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST               # vAA<- rResClasses[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.LOP_CONST_CLASS_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     $1,OUT_ARG2(%esp)        # true
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveClass           # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    FETCH_INST_WORD 1
+    testl   %eax,%eax                   # null object?
+    EXPORT_PC                           # need for precise GC
+    je     common_errNullObject
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmLockObject               # dvmLockObject(self,object)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: x86/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_VREG_R %eax rINST
+    movl    rSELF,%ecx
+    EXPORT_PC
+    testl   %eax,%eax                   # null object?
+    je      .LOP_MONITOR_EXIT_errNullObject   # go if so
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmUnlockObject             # unlock(self,obj)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    testl   %eax,%eax                   # success?
+    ADVANCE_PC 1
+    je      common_exceptionThrown      # no, exception pending
+    GOTO_NEXT_R %ecx
+.LOP_MONITOR_EXIT_errNullObject:
+    ADVANCE_PC 1                        # advance before throw
+    jmp     common_errNullObject
+
+/* ------------------------------ */
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: x86/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    movl      rSELF,%ecx
+    GET_VREG_R  rINST,rINST             # rINST<- vAA (object)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    testl     rINST,rINST               # is oject null?
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    je        .LOP_CHECK_CAST_okay          # null obj, cast always succeeds
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved class
+    movl      offObject_clazz(rINST),%ecx # ecx<- obj->clazz
+    testl     %eax,%eax                 # have we resolved this before?
+    je        .LOP_CHECK_CAST_resolve       # no, go do it now
+.LOP_CHECK_CAST_resolved:
+    cmpl      %eax,%ecx                 # same class (trivial success)?
+    jne       .LOP_CHECK_CAST_fullcheck     # no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  ecx holds obj->clazz
+     *  eax holds class resolved from BBBB
+     *  rINST holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    movl    %eax,sReg0                 # we'll need the desired class on failure
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmInstanceofNonTrivial    # eax<- boolean result
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # failed?
+    jne     .LOP_CHECK_CAST_okay           # no, success
+
+    # A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC
+    movl    offObject_clazz(rINST),%eax
+    movl    %eax,OUT_ARG0(%esp)                 # arg0<- obj->clazz
+    movl    sReg0,%ecx
+    movl    %ecx,OUT_ARG1(%esp)                 # arg1<- desired class
+    call    dvmThrowClassCastException
+    jmp     common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path, and we're
+     * going to have to recreate some data.
+     *
+     *  rINST holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movzwl  2(rPC),%eax                # eax<- BBBB
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)        # arg1<- BBBB
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- metho->clazz
+    movl    $0,OUT_ARG2(%esp)         # arg2<- false
+    movl    %ecx,OUT_ARG0(%esp)        # arg0<- method->clazz
+    SPILL(rIBASE)
+    call    dvmResolveClass            # eax<- resolved ClassObject ptr
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # got null?
+    je      common_exceptionThrown     # yes, handle exception
+    movl    offObject_clazz(rINST),%ecx  # ecx<- obj->clazz
+    jmp     .LOP_CHECK_CAST_resolved       # pick up where we left off
+
+/* ------------------------------ */
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: x86/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                   # object null?
+    movl    offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)                       # preserve rIBASE
+    je      .LOP_INSTANCE_OF_store           # null obj, not instance, store it
+    movzwl  2(rPC),rIBASE               # rIBASE<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,rIBASE,4),%ecx        # ecx<- resolved class
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    testl   %ecx,%ecx                   # have we resolved this before?
+    je      .LOP_INSTANCE_OF_resolve         # not resolved, do it now
+.LOP_INSTANCE_OF_resolved:  # eax<- obj->clazz, ecx<- resolved class
+    cmpl    %eax,%ecx                   # same class (trivial success)?
+    je      .LOP_INSTANCE_OF_trivial         # yes, trivial finish
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  eax holds obj->clazz
+     *  ecx holds class resolved from BBBB
+     *  rINST has BA
+     */
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmInstanceofNonTrivial     # eax<- boolean result
+    # fall through to OP_INSTANCE_OF_store
+
+    /*
+     * eax holds boolean result
+     * rINST holds BA
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    SET_VREG %eax rINST                 # vA<- eax
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    movl    $1,%eax
+    SET_VREG %eax rINST                 # vA<- true
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  rIBASE holds BBBB
+     *  rINST holds BA
+     */
+.LOP_INSTANCE_OF_resolve:
+    movl    rIBASE,OUT_ARG1(%esp)         # arg1<- BBBB
+    movl    rSELF,%ecx
+    movl    offThread_method(%ecx),%ecx
+    movl    $1,OUT_ARG2(%esp)          # arg2<- true
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    EXPORT_PC
+    movl    %ecx,OUT_ARG0(%esp)         # arg0<- method->clazz
+    call    dvmResolveClass             # eax<- resolved ClassObject ptr
+    testl   %eax,%eax                   # success?
+    je      common_exceptionThrown      # no, handle exception
+/* Now, we need to sync up with fast path.  We need eax to
+ * hold the obj->clazz, and ecx to hold the resolved class
+ */
+    movl    %eax,%ecx                   # ecx<- resolved class
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    jmp     .LOP_INSTANCE_OF_resolved
+
+/* ------------------------------ */
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+   mov      rINST,%eax                # eax<- BA
+   sarl     $4,rINST                 # rINST<- B
+   GET_VREG_R %ecx rINST              # ecx<- vB (object ref)
+   andb     $0xf,%al                 # eax<- A
+   testl    %ecx,%ecx                 # is null?
+   je       common_errNullObject
+   movl     offArrayObject_length(%ecx),rINST
+   FETCH_INST_OPCODE 1 %ecx
+   ADVANCE_PC 1
+   SET_VREG rINST %eax
+   GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)
+    SPILL_TMP2(%ebx)
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    EXPORT_PC
+#if defined(WITH_JIT)
+    lea       (%ecx,%eax,4),%ebx        # ebx <- &resolved class
+#endif
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved class
+    testl     %ecx,%ecx                 # resolved?
+    je        .LOP_NEW_INSTANCE_resolve       # no, go do it
+.LOP_NEW_INSTANCE_resolved:  # on entry, ecx<- class
+    cmpb      $CLASS_INITIALIZED,offClassObject_status(%ecx)
+    jne       .LOP_NEW_INSTANCE_needinit
+.LOP_NEW_INSTANCE_initialized:  # on entry, ecx<- class
+    movl      $ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmAllocObject             # eax<- new object
+    testl    %eax,%eax                  # success?
+    je       common_exceptionThrown     # no, bail out
+#if defined(WITH_JIT)
+        /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    movl    rSELF, %ecx
+    movl    offThread_subMode(%ecx), %ecx
+    andl    $kSubModeJitTraceBuild, %ecx # under construction?
+    jne     .LOP_NEW_INSTANCE_jitCheck
+#endif
+.LOP_NEW_INSTANCE_end:
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * eax: new object
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    cmp     $0, (%ebx)                   # okay?
+    jne     .LOP_NEW_INSTANCE_end        # yes, finish
+    SPILL_TMP1(%eax)                     # preserve new object
+    movl    rSELF, %ecx
+    movl    %ecx, OUT_ARG0(%esp)
+    movl    rPC, OUT_ARG1(%esp)
+    call    dvmJitEndTraceSelect         # (self, pc)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST                  # vAA <- new object
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  ecx holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    SPILL_TMP1(%ecx)                    # save object
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmInitClass                # initialize class
+    UNSPILL_TMP1(%ecx)                  # restore object
+    testl   %eax,%eax                   # success?
+    jne     .LOP_NEW_INSTANCE_initialized     # success, continue
+    jmp     common_exceptionThrown      # go deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     */
+.LOP_NEW_INSTANCE_resolve:
+    movl    rSELF,%ecx
+    movzwl  2(rPC),%eax
+    movl    offThread_method(%ecx),%ecx   # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    movl    $0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass             # call(clazz,off,flags)
+    movl    %eax,%ecx                   # ecx<- resolved ClassObject ptr
+    testl   %ecx,%ecx                   # success?
+    jne     .LOP_NEW_INSTANCE_resolved        # good to go
+    jmp     common_exceptionThrown      # no, handle exception
+
+/* ------------------------------ */
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: x86/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movl    offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    movzwl  2(rPC),%eax                       # eax<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx  # ecx<- pDvmDex->pResClasses
+    SPILL(rIBASE)
+    movl    (%ecx,%eax,4),%ecx                # ecx<- resolved class
+    movzbl  rINSTbl,%eax
+    sarl    $4,%eax                          # eax<- B
+    GET_VREG_R %eax %eax                      # eax<- vB (array length)
+    andb    $0xf,rINSTbl                     # rINST<- A
+    testl   %eax,%eax
+    js      common_errNegativeArraySize       # bail, passing len in eax
+    testl   %ecx,%ecx                         # already resolved?
+    jne     .LOP_NEW_ARRAY_finish                # yes, fast path
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *  ecx holds class (null here)
+     *  eax holds array length (vB)
+     */
+    movl    rSELF,%ecx
+    SPILL_TMP1(%eax)                   # save array length
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movzwl  2(rPC),%eax                # eax<- CCCC
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass            # eax<- call(clazz,ref,flag)
+    movl    %eax,%ecx
+    UNSPILL_TMP1(%eax)
+    testl   %ecx,%ecx                  # successful resolution?
+    je      common_exceptionThrown     # no, bail.
+# fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation
+     *
+     * ecx holds class
+     * eax holds array length (vB)
+     */
+.LOP_NEW_ARRAY_finish:
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $ALLOC_DONT_TRACK,OUT_ARG2(%esp)
+    call    dvmAllocArrayByClass    # eax<- call(clazz,length,flags)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    testl   %eax,%eax               # failed?
+    je      common_exceptionThrown  # yup - go handle
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: x86/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    movl    rSELF,%eax
+    movl    offThread_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rIBASE)                             # preserve rIBASE
+    movl    (%eax,%ecx,4),%eax                # eax<- resolved class
+    EXPORT_PC
+    testl   %eax,%eax                         # already resolved?
+    jne     .LOP_FILLED_NEW_ARRAY_continue              # yes, continue
+    # less frequent path, so we'll redo some work
+    movl    rSELF,%eax
+    movl    $0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offThread_method(%eax),%eax         # eax<- self->method
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    testl   %eax,%eax                         # null?
+    je      common_exceptionThrown            # yes, handle it
+
+       # note: fall through to .LOP_FILLED_NEW_ARRAY_continue
+
+    /*
+     * On entry:
+     *    eax holds array class [r0]
+     *    rINST holds AA or BB [r10]
+     *    ecx is scratch
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    movl    offClassObject_descriptor(%eax),%ecx  # ecx<- arrayClass->descriptor
+    movl    $ALLOC_DONT_TRACK,OUT_ARG2(%esp)     # arg2<- flags
+    movzbl  1(%ecx),%ecx                          # ecx<- descriptor[1]
+    movl    %eax,OUT_ARG0(%esp)                   # arg0<- arrayClass
+    movl    rSELF,%eax
+    cmpb    $'I',%cl                             # supported?
+    je      1f
+    cmpb    $'L',%cl
+    je      1f
+    cmpb    $'[',%cl
+    jne      .LOP_FILLED_NEW_ARRAY_notimpl                  # no, not handled yet
+1:
+    movl    %ecx,offThread_retval+4(%eax)           # save type
+    .if      (!0)
+    SPILL_TMP1(rINST)                              # save copy, need "B" later
+    sarl    $4,rINST
+    .endif
+    movl    rINST,OUT_ARG1(%esp)                  # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass     # eax<- call(arrayClass, length, flags)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offThread_retval(%ecx)             # retval.l<- new array
+    movzwl  4(rPC),%ecx                           # ecx<- FEDC or CCCC
+    leal    offArrayObject_contents(%eax),%eax    # eax<- newArray->contents
+
+/* at this point:
+ *     eax is pointer to tgt
+ *     rINST is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL1 is BA
+ *  We now need to copy values from registers into the array
+ */
+
+    .if 0
+    # set up src pointer
+    SPILL_TMP2(%esi)
+    SPILL_TMP3(%edi)
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    %eax,%edi         # set up dst ptr
+    movl    rINST,%ecx        # load count register
+    rep
+    movsd
+    UNSPILL_TMP2(%esi)
+    UNSPILL_TMP3(%edi)
+    movl    rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .else
+    testl  rINST,rINST
+    je     4f
+    UNSPILL_TMP1(rIBASE)      # restore "BA"
+    andl   $0x0f,rIBASE      # rIBASE<- 0000000A
+    sall   $16,rIBASE        # rIBASE<- 000A0000
+    orl    %ecx,rIBASE        # rIBASE<- 000AFEDC
+3:
+    movl   $0xf,%ecx
+    andl   rIBASE,%ecx        # ecx<- next reg to load
+    GET_VREG_R %ecx %ecx
+    shrl   $4,rIBASE
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $1,rINST
+    jne    3b
+4:
+    movl   rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .endif
+
+    cmpb    $'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offThread_retval(%ecx),%eax        # eax<- object head
+    movl    offThread_cardTable(%ecx),%ecx     # card table base
+    shrl    $GC_CARD_SHIFT,%eax             # convert to card num
+    movb    %cl,(%ecx,%eax)                  # mark card based on object head
+5:
+    UNSPILL(rIBASE)                          # restore rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    movl    $.LstrFilledNewArrayNotImplA,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowInternalError
+    jmp     common_exceptionThrown
+
+/* ------------------------------ */
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: x86/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: x86/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    movl    rSELF,%eax
+    movl    offThread_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rIBASE)                             # preserve rIBASE
+    movl    (%eax,%ecx,4),%eax                # eax<- resolved class
+    EXPORT_PC
+    testl   %eax,%eax                         # already resolved?
+    jne     .LOP_FILLED_NEW_ARRAY_RANGE_continue              # yes, continue
+    # less frequent path, so we'll redo some work
+    movl    rSELF,%eax
+    movl    $0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offThread_method(%eax),%eax         # eax<- self->method
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    testl   %eax,%eax                         # null?
+    je      common_exceptionThrown            # yes, handle it
+
+       # note: fall through to .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+    /*
+     * On entry:
+     *    eax holds array class [r0]
+     *    rINST holds AA or BB [r10]
+     *    ecx is scratch
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    movl    offClassObject_descriptor(%eax),%ecx  # ecx<- arrayClass->descriptor
+    movl    $ALLOC_DONT_TRACK,OUT_ARG2(%esp)     # arg2<- flags
+    movzbl  1(%ecx),%ecx                          # ecx<- descriptor[1]
+    movl    %eax,OUT_ARG0(%esp)                   # arg0<- arrayClass
+    movl    rSELF,%eax
+    cmpb    $'I',%cl                             # supported?
+    je      1f
+    cmpb    $'L',%cl
+    je      1f
+    cmpb    $'[',%cl
+    jne      .LOP_FILLED_NEW_ARRAY_RANGE_notimpl                  # no, not handled yet
+1:
+    movl    %ecx,offThread_retval+4(%eax)           # save type
+    .if      (!1)
+    SPILL_TMP1(rINST)                              # save copy, need "B" later
+    sarl    $4,rINST
+    .endif
+    movl    rINST,OUT_ARG1(%esp)                  # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass     # eax<- call(arrayClass, length, flags)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offThread_retval(%ecx)             # retval.l<- new array
+    movzwl  4(rPC),%ecx                           # ecx<- FEDC or CCCC
+    leal    offArrayObject_contents(%eax),%eax    # eax<- newArray->contents
+
+/* at this point:
+ *     eax is pointer to tgt
+ *     rINST is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL1 is BA
+ *  We now need to copy values from registers into the array
+ */
+
+    .if 1
+    # set up src pointer
+    SPILL_TMP2(%esi)
+    SPILL_TMP3(%edi)
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    %eax,%edi         # set up dst ptr
+    movl    rINST,%ecx        # load count register
+    rep
+    movsd
+    UNSPILL_TMP2(%esi)
+    UNSPILL_TMP3(%edi)
+    movl    rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .else
+    testl  rINST,rINST
+    je     4f
+    UNSPILL_TMP1(rIBASE)      # restore "BA"
+    andl   $0x0f,rIBASE      # rIBASE<- 0000000A
+    sall   $16,rIBASE        # rIBASE<- 000A0000
+    orl    %ecx,rIBASE        # rIBASE<- 000AFEDC
+3:
+    movl   $0xf,%ecx
+    andl   rIBASE,%ecx        # ecx<- next reg to load
+    GET_VREG_R %ecx %ecx
+    shrl   $4,rIBASE
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $1,rINST
+    jne    3b
+4:
+    movl   rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .endif
+
+    cmpb    $'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offThread_retval(%ecx),%eax        # eax<- object head
+    movl    offThread_cardTable(%ecx),%ecx     # card table base
+    shrl    $GC_CARD_SHIFT,%eax             # convert to card num
+    movb    %cl,(%ecx,%eax)                  # mark card based on object head
+5:
+    UNSPILL(rIBASE)                          # restore rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    movl    $.LstrFilledNewArrayNotImplA,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowInternalError
+    jmp     common_exceptionThrown
+
+
+/* ------------------------------ */
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    movl    2(rPC),%ecx                # ecx<- BBBBbbbb
+    leal    (rPC,%ecx,2),%ecx          # ecx<- PC + BBBBbbbb*2
+    GET_VREG_R %eax rINST
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmInterpHandleFillArrayData
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 3 %ecx
+    testl   %eax,%eax                   # exception thrown?
+    je      common_exceptionThrown
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_THROW: /* 0x27 */
+/* File: x86/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    EXPORT_PC
+    GET_VREG_R %eax rINST              # eax<- exception object
+    movl     rSELF,%ecx                # ecx<- self
+    testl    %eax,%eax                 # null object?
+    je       common_errNullObject
+    movl     %eax,offThread_exception(%ecx) # thread->exception<- obj
+    jmp      common_exceptionThrown
+
+/* ------------------------------ */
+.L_OP_GOTO: /* 0x28 */
+/* File: x86/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    movl    rSELF,%ecx
+    movsbl  rINSTbl,%eax          # eax<- ssssssAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_GOTO_16: /* 0x29 */
+/* File: x86/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset
+     */
+    /* goto/16 +AAAA */
+    movl    rSELF,%ecx
+    movswl  2(rPC),%eax            # eax<- ssssAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_GOTO_32: /* 0x2a */
+/* File: x86/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset.
+     */
+    /* goto/32 AAAAAAAA */
+    movl    rSELF,%ecx
+    movl    2(rPC),%eax            # eax<- AAAAAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: x86/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    movl    2(rPC),%ecx           # ecx<- BBBBbbbb
+    GET_VREG_R %eax rINST         # eax<- vAA
+    leal    (rPC,%ecx,2),%ecx     # ecx<- PC + BBBBbbbb*2
+    movl    %eax,OUT_ARG1(%esp)   # ARG1<- vAA
+    movl    %ecx,OUT_ARG0(%esp)   # ARG0<- switchData
+    call    dvmInterpHandlePackedSwitch
+    movl    rSELF,%ecx
+    ADVANCE_PC_INDEXED %eax
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: x86/OP_SPARSE_SWITCH.S */
+/* File: x86/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    movl    2(rPC),%ecx           # ecx<- BBBBbbbb
+    GET_VREG_R %eax rINST         # eax<- vAA
+    leal    (rPC,%ecx,2),%ecx     # ecx<- PC + BBBBbbbb*2
+    movl    %eax,OUT_ARG1(%esp)   # ARG1<- vAA
+    movl    %ecx,OUT_ARG0(%esp)   # ARG0<- switchData
+    call    dvmInterpHandleSparseSwitch
+    movl    rSELF,%ecx
+    ADVANCE_PC_INDEXED %eax
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: x86/OP_CMPL_FLOAT.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 0
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPL_FLOAT_isNaN
+    je       .LOP_CMPL_FLOAT_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPL_FLOAT_finish
+    incl     %ecx
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPL_FLOAT_isNaN:
+    movl      $-1,%ecx
+    jmp       .LOP_CMPL_FLOAT_finish
+
+
+/* ------------------------------ */
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: x86/OP_CMPG_FLOAT.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 0
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPG_FLOAT_isNaN
+    je       .LOP_CMPG_FLOAT_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPG_FLOAT_finish
+    incl     %ecx
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPG_FLOAT_isNaN:
+    movl      $1,%ecx
+    jmp       .LOP_CMPG_FLOAT_finish
+
+
+/* ------------------------------ */
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: x86/OP_CMPL_DOUBLE.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 1
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPL_DOUBLE_isNaN
+    je       .LOP_CMPL_DOUBLE_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPL_DOUBLE_finish
+    incl     %ecx
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPL_DOUBLE_isNaN:
+    movl      $-1,%ecx
+    jmp       .LOP_CMPL_DOUBLE_finish
+
+
+/* ------------------------------ */
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 1
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPG_DOUBLE_isNaN
+    je       .LOP_CMPG_DOUBLE_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPG_DOUBLE_finish
+    incl     %ecx
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPG_DOUBLE_isNaN:
+    movl      $1,%ecx
+    jmp       .LOP_CMPG_DOUBLE_finish
+
+/* ------------------------------ */
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: x86/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     */
+    // TUNING: rework to avoid rIBASE spill
+    /* cmp-long vAA, vBB, vCC */
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)
+    movzbl    3(rPC),rIBASE            # rIBASE- CC
+    GET_VREG_WORD %eax %ecx,1          # eax<- v[BB+1]
+    GET_VREG_WORD %ecx %ecx 0          # ecx<- v[BB+0]
+    cmpl      4(rFP,rIBASE,4),%eax
+    jl        .LOP_CMP_LONG_smaller
+    jg        .LOP_CMP_LONG_bigger
+    sub       (rFP,rIBASE,4),%ecx
+    ja        .LOP_CMP_LONG_bigger
+    jb        .LOP_CMP_LONG_smaller
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_CMP_LONG_bigger:
+    movl      $1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_CMP_LONG_smaller:
+    movl      $-1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IF_EQ: /* 0x32 */
+/* File: x86/OP_IF_EQ.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jne   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_NE: /* 0x33 */
+/* File: x86/OP_IF_NE.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    je   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LT: /* 0x34 */
+/* File: x86/OP_IF_LT.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jge   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GE: /* 0x35 */
+/* File: x86/OP_IF_GE.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jl   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GT: /* 0x36 */
+/* File: x86/OP_IF_GT.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jle   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LE: /* 0x37 */
+/* File: x86/OP_IF_LE.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jg   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: x86/OP_IF_EQZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jne   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: x86/OP_IF_NEZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    je   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: x86/OP_IF_LTZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jge   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: x86/OP_IF_GEZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jl   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: x86/OP_IF_GTZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jle   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: x86/OP_IF_LEZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jg   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: x86/OP_UNUSED_3E.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: x86/OP_UNUSED_3F.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: x86/OP_UNUSED_40.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: x86/OP_UNUSED_41.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: x86/OP_UNUSED_42.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: x86/OP_UNUSED_43.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_AGET: /* 0x44 */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movl     offArrayObject_contents(%eax,%ecx,4),%eax
+.LOP_AGET_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: x86/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    movl      (%eax),%ecx
+    movl      4(%eax),%eax
+    SET_VREG_WORD %ecx rINST 0
+    SET_VREG_WORD %eax rINST 1
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: x86/OP_AGET_OBJECT.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movl     offArrayObject_contents(%eax,%ecx,4),%eax
+.LOP_AGET_OBJECT_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: x86/OP_AGET_BOOLEAN.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movzbl     offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_AGET_BOOLEAN_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: x86/OP_AGET_BYTE.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movsbl     offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_AGET_BYTE_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: x86/OP_AGET_CHAR.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movzwl     offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_AGET_CHAR_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: x86/OP_AGET_SHORT.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movswl     offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_AGET_SHORT_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT: /* 0x4b */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,4),%eax
+.LOP_APUT_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movl     rINST,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: x86/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC]<-vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    GET_VREG_WORD %ecx rINST 0
+    GET_VREG_WORD rINST rINST 1
+    movl      %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: x86/OP_APUT_OBJECT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    GET_VREG_R  rINST rINST             # rINST<- vAA
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    /* On entry:
+     *   eax<- array object
+     *   ecx<- index
+     *   rINST<- vAA
+     */
+    leal      offArrayObject_contents(%eax,%ecx,4),%ecx
+    testl     rINST,rINST                    # storing null reference?
+    je        .LOP_APUT_OBJECT_skip_check
+    SPILL_TMP1(%ecx)                         # save target address
+    SPILL_TMP2(%eax)                         # save object head
+    movl      offObject_clazz(%eax),%eax     # eax<- arrayObj->clazz
+    movl      offObject_clazz(rINST),%ecx    # ecx<- obj->clazz
+    movl      %eax,OUT_ARG1(%esp)
+    movl      %ecx,OUT_ARG0(%esp)
+    movl      %ecx,sReg0                     # store the two classes for later
+    movl      %eax,sReg1
+    SPILL(rIBASE)
+    call      dvmCanPutArrayElement          # test object type vs. array type
+    UNSPILL(rIBASE)
+    UNSPILL_TMP1(%ecx)                       # recover target address
+    testl     %eax,%eax
+    movl      rSELF,%eax
+    jne       .LOP_APUT_OBJECT_types_okay
+
+    # The types don't match.  We need to throw an ArrayStoreException.
+    EXPORT_PC
+    movl      sReg0,%eax                     # restore the two classes...
+    movl      %eax,OUT_ARG0(%esp)
+    movl      sReg1,%ecx
+    movl      %ecx,OUT_ARG1(%esp)
+    call      dvmThrowArrayStoreExceptionIncompatibleElement # ...and throw
+    jmp       common_exceptionThrown
+
+.LOP_APUT_OBJECT_types_okay:
+    movl      offThread_cardTable(%eax),%eax   # get card table base
+    movl      rINST,(%ecx)                   # store into array
+    UNSPILL_TMP2(rINST)                      # recover object head
+    FETCH_INST_OPCODE 2 %ecx
+    shrl      $GC_CARD_SHIFT,rINST          # object head to card number
+    movb      %al,(%eax,rINST)               # mark card using object head
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_APUT_OBJECT_skip_check:
+    movl      rINST,(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: x86/OP_APUT_BOOLEAN.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_APUT_BOOLEAN_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movb     rINSTbl,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: x86/OP_APUT_BYTE.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_APUT_BYTE_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movb     rINSTbl,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: x86/OP_APUT_CHAR.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_APUT_CHAR_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movw     rINSTw,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: x86/OP_APUT_SHORT.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_APUT_SHORT_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movw     rINSTw,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IGET: /* 0x52 */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: x86/OP_IGET_WIDE.S */
+    /*
+     * 64-bit instance field get.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_WIDE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # for dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save objpointer across call
+    movl    rPC,OUT_ARG0(%esp)                  # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IGET_WIDE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_WIDE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    movl    (%eax),%ecx                         # ecx<- lsw
+    movl    4(%eax),%eax                        # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                             # restore rIBASE
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: x86/OP_IGET_OBJECT.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_OBJECT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_OBJECT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_OBJECT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: x86/OP_IGET_BOOLEAN.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_BOOLEAN_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_BOOLEAN_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_BOOLEAN_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movzbl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: x86/OP_IGET_BYTE.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_BYTE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_BYTE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_BYTE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movsbl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: x86/OP_IGET_CHAR.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_CHAR_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_CHAR_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_CHAR_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movzwl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: x86/OP_IGET_SHORT.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_SHORT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_SHORT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_SHORT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movswl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IPUT: /* 0x59 */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   rINST,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86/OP_IPUT_WIDE.S */
+    /*
+     * 64-bit instance field put.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_WIDE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_WIDE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_WIDE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    GET_VREG_WORD %ecx rINST 0                  # ecx<- lsw
+    GET_VREG_WORD rINST rINST 1                 # rINST<- msw
+    movl    rINST,4(%eax)
+    movl    %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86/OP_IPUT_OBJECT.S */
+    /*
+     * Object field put.
+     *
+     * for: iput-object
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                  # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_OBJECT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_OBJECT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_OBJECT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                      # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl    rINST,(%ecx,%eax)      # obj.field <- v[A](8/16/32 bits)
+    movl    rSELF,%eax
+    testl   rINST,rINST                         # stored a NULL?
+    movl    offThread_cardTable(%eax),%eax      # get card table base
+    je      1f                                  # skip card mark if null store
+    shrl    $GC_CARD_SHIFT,%ecx                # object head to card number
+    movb    %al,(%eax,%ecx)                     # mark card using object head
+1:
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: x86/OP_IPUT_BOOLEAN.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_BOOLEAN_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_BOOLEAN_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_BOOLEAN_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movb   rINSTbl,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: x86/OP_IPUT_BYTE.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_BYTE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_BYTE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_BYTE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movb   rINSTbl,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: x86/OP_IPUT_CHAR.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_CHAR_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_CHAR_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_CHAR_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movw   rINSTw,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: x86/OP_IPUT_SHORT.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_SHORT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_SHORT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_SHORT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movw   rINSTw,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SGET: /* 0x60 */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_resolve                # if not, make it so
+.LOP_SGET_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: x86/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     *
+     */
+    /* sget-wide vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_WIDE_resolve                # if not, make it so
+.LOP_SGET_WIDE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%ecx    # ecx<- lsw
+    movl      4+offStaticField_value(%eax),%eax  # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_WIDE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_WIDE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: x86/OP_SGET_OBJECT.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_OBJECT_resolve                # if not, make it so
+.LOP_SGET_OBJECT_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_OBJECT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_OBJECT_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: x86/OP_SGET_BOOLEAN.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_BOOLEAN_resolve                # if not, make it so
+.LOP_SGET_BOOLEAN_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_BOOLEAN_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: x86/OP_SGET_BYTE.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_BYTE_resolve                # if not, make it so
+.LOP_SGET_BYTE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_BYTE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_BYTE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: x86/OP_SGET_CHAR.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_CHAR_resolve                # if not, make it so
+.LOP_SGET_CHAR_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_CHAR_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_CHAR_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: x86/OP_SGET_SHORT.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_SHORT_resolve                # if not, make it so
+.LOP_SGET_SHORT_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_SHORT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_SHORT_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SPUT: /* 0x67 */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_resolve                # if not, make it so
+.LOP_SPUT_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_finish                 # success, continue
+/* ------------------------------ */
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: x86/OP_SPUT_WIDE.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_WIDE_resolve                # if not, make it so
+.LOP_SPUT_WIDE_finish:     # field ptr in eax
+    GET_VREG_WORD %ecx rINST 0                  # rINST<- lsw
+    GET_VREG_WORD rINST rINST 1                 # ecx<- msw
+    movl      %ecx,offStaticField_value(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4+offStaticField_value(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_WIDE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_WIDE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86/OP_SPUT_OBJECT.S */
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_OBJECT_resolve                # if not, make it so
+.LOP_SPUT_OBJECT_finish:                              # field ptr in eax
+    movzbl    rINSTbl,%ecx                       # ecx<- AA
+    GET_VREG_R  %ecx %ecx
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    je        1f                                 # skip card mark if null
+    movl      rSELF,%ecx
+    movl      offField_clazz(%eax),%eax          # eax<- method->clazz
+    movl      offThread_cardTable(%ecx),%ecx       # get card table base
+    shrl      $GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_SPUT_OBJECT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_OBJECT_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: x86/OP_SPUT_BOOLEAN.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_BOOLEAN_resolve                # if not, make it so
+.LOP_SPUT_BOOLEAN_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_BOOLEAN_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: x86/OP_SPUT_BYTE.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_BYTE_resolve                # if not, make it so
+.LOP_SPUT_BYTE_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_BYTE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_BYTE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: x86/OP_SPUT_CHAR.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_CHAR_resolve                # if not, make it so
+.LOP_SPUT_CHAR_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_CHAR_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_CHAR_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: x86/OP_SPUT_SHORT.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_SHORT_resolve                # if not, make it so
+.LOP_SPUT_SHORT_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_SHORT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_SHORT_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: x86/OP_INVOKE_VIRTUAL.S */
+
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%eax
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offThread_methodClassDex(%eax),%eax  # eax<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%eax),%eax   # eax<- pDvmDex->pResMethods
+    movl      (%eax,%ecx,4),%eax          # eax<- resolved baseMethod
+    testl     %eax,%eax                   # already resolved?
+    jne       .LOP_INVOKE_VIRTUAL_continue        # yes, continue
+    movl      rSELF,%eax
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offThread_method(%eax),%eax   # eax<- self->method
+    movl      offMethod_clazz(%eax),%eax  # ecx<- method->clazz
+    movl      %eax,OUT_ARG0(%esp)         # arg0<- clazz
+    movl      $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+    call      dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl     %eax,%eax                   # got null?
+    jne       .LOP_INVOKE_VIRTUAL_continue        # no, continue
+    jmp       common_exceptionThrown      # yes, handle exception
+
+    /* At this point:
+     *   eax = resolved base method
+     *   ecx = scratch
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    movzwl    4(rPC),%ecx               # ecx<- GFED or CCCC
+    .if       (!0)
+    andl      $0xf,%ecx                # ecx<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- "this"
+    movzwl    offMethod_methodIndex(%eax),%eax  # eax<- baseMethod->methodIndex
+    testl     %ecx,%ecx                 # null this?
+    je        common_errNullObject      # go if so
+    movl      offObject_clazz(%ecx),%edx  # edx<- thisPtr->clazz
+    movl      offClassObject_vtable(%edx),%edx # edx<- thisPtr->clazz->vtable
+    movl      (%edx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: x86/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,rINST
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(rINST),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offThread_method(rINST),%eax # eax<- method
+    movzwl    4(rPC),rINST              # rINST<- GFED or CCCC
+    .if       (!0)
+    andl      $0xf,rINST               # rINST<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %edx rINST             # %edx<- "this" ptr
+    testl     %edx,%edx                # null "this"?
+    SPILL_TMP1(%edx)
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    je       .LOP_INVOKE_SUPER_resolve
+    /*
+     * At this point:
+     *  ecx = resolved base method [r0]
+     *  eax = method->clazz [r9]
+     */
+.LOP_INVOKE_SUPER_continue:
+    movl    offClassObject_super(%eax),%eax   # eax<- method->clazz->super
+    movzwl  offMethod_methodIndex(%ecx),%edx  # edx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%edx # compare(methodIndex,vtableCount)
+    jae     .LOP_INVOKE_SUPER_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%edx,4),%eax        # eax<- vtable[methodIndex]
+    UNSPILL_TMP1(%edx)
+    movl    %edx, %ecx
+    jmp     common_invokeMethodNoRange
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.LOP_INVOKE_SUPER_resolve:
+    SPILL_TMP2(%eax)                    # method->clazz
+    movl    %eax,OUT_ARG0(%esp)         # arg0<- method->clazz
+    movzwl  2(rPC),%ecx                 # ecx<- BBBB
+    movl    $METHOD_VIRTUAL,OUT_ARG2(%esp)  # arg2<- resolver method type
+    movl    %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP2(%eax)                  # restore method->clazz
+    jne     .LOP_INVOKE_SUPER_continue        # good to go - continue
+    jmp     common_exceptionThrown      # handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  ecx = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    movl    offMethod_name(%ecx),%eax
+    jmp     common_errNoSuchMethod
+
+/* ------------------------------ */
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: x86/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rIBASE            # rIBASE<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!0)
+    andl      $0xf,rIBASE             # rIBASE<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG_R  %ecx rIBASE            # ecx<- "this" ptr
+    je        .LOP_INVOKE_DIRECT_resolve      # not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethodNoRange  # no, continue on
+    jmp       common_errNullObject
+
+    /*
+     * On entry:
+     *   TMP_SPILL  <- "this" register
+     * Things a bit ugly on this path, but it's the less
+     * frequent one.  We'll have to do some reloading.
+     */
+.LOP_INVOKE_DIRECT_resolve:
+     SPILL_TMP1(%ecx)
+     movl     rSELF,%ecx
+     movl     offThread_method(%ecx),%ecx  # ecx<- self->method
+     movzwl   2(rPC),%eax      # reference (BBBB or CCCC)
+     movl     offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+     movl     $METHOD_DIRECT,OUT_ARG2(%esp)
+     movl     %eax,OUT_ARG1(%esp)
+     movl     %ecx,OUT_ARG0(%esp)
+     call     dvmResolveMethod # eax<- call(clazz, ref, flags)
+     UNSPILL_TMP1(%ecx)
+     testl    %eax,%eax
+     jne      .LOP_INVOKE_DIRECT_finish
+     jmp      common_exceptionThrown
+
+/* ------------------------------ */
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: x86/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+#if defined(WITH_JIT)
+    movl     %edx, TMP_SPILL1(%ebp)
+    lea      (%ecx,%eax,4), %edx
+    movl     %edx, TMP_SPILL2(%ebp)
+    movl     TMP_SPILL1(%ebp), %edx
+#endif
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    movl      $0, %ecx                 # make "this" null
+    testl     %eax,%eax
+    jne       common_invokeMethodNoRange
+
+    movl      rSELF,%ecx
+    movl      offThread_method(%ecx),%ecx # ecx<- self->method
+    movzwl    2(rPC),%eax
+    movl      offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+    movl      %eax,OUT_ARG1(%esp)       # arg1<- BBBB
+    movl      %ecx,OUT_ARG0(%esp)       # arg0<- clazz
+    movl      $METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rIBASE)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rIBASE)
+    testl     %eax,%eax                 # got null?
+#if defined(WITH_JIT)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      rSELF,%ecx
+    movzwl    offThread_subMode(%ecx), %ecx
+    je        common_exceptionThrown    # null, handle exception
+    andl      $kSubModeJitTraceBuild, %ecx # is trace under construction?
+    movl      $0, %ecx                 # make "this" null
+    je        common_invokeMethodNoRange # no (%eax=method, %ecx="this")
+    movl      TMP_SPILL2(%ebp), %edx
+    cmpl      $0, (%edx)                  # finished resolving
+    movl      TMP_SPILL1(%ebp), %edx
+    jne        common_invokeMethodNoRange # yes (%eax=method, %ecx="this")
+    movl      rSELF, %edx
+    movl      %edx, OUT_ARG0(%esp)
+    movl      rPC, OUT_ARG1(%esp)
+    movl      %eax, TMP_SPILL2(%ebp)
+    movl      %ecx, TMP_SPILL3(%ebp)
+    SPILL(rIBASE)
+    call      dvmJitEndTraceSelect
+    UNSPILL(rIBASE)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      TMP_SPILL2(%ebp), %eax
+    movl      TMP_SPILL3(%ebp), %ecx
+    jmp       common_invokeMethodNoRange
+#else
+    movl      $0, %ecx                 # make "this" null
+    jne       common_invokeMethodNoRange
+    jmp       common_exceptionThrown
+#endif
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: x86/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl     4(rPC),%eax              # eax<- FEDC or CCCC
+    movl       rSELF,%ecx
+    .if        (!0)
+    andl       $0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R   %eax %eax              # eax<- "this"
+    EXPORT_PC
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       %eax, TMP_SPILL1(%ebp)
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offThread_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offThread_method(%ecx),%ecx           # ecx<- method
+    movl       %eax,OUT_ARG3(%esp)                 # arg3<- dex
+    movzwl     2(rPC),%eax                         # eax<- BBBB
+    movl       %ecx,OUT_ARG2(%esp)                 # arg2<- method
+    movl       %eax,OUT_ARG1(%esp)                 # arg1<- BBBB
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    movl       TMP_SPILL1(%ebp), %ecx
+    jmp        common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: x86/OP_UNUSED_73.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: x86/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: x86/OP_INVOKE_VIRTUAL.S */
+
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%eax
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offThread_methodClassDex(%eax),%eax  # eax<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%eax),%eax   # eax<- pDvmDex->pResMethods
+    movl      (%eax,%ecx,4),%eax          # eax<- resolved baseMethod
+    testl     %eax,%eax                   # already resolved?
+    jne       .LOP_INVOKE_VIRTUAL_RANGE_continue        # yes, continue
+    movl      rSELF,%eax
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offThread_method(%eax),%eax   # eax<- self->method
+    movl      offMethod_clazz(%eax),%eax  # ecx<- method->clazz
+    movl      %eax,OUT_ARG0(%esp)         # arg0<- clazz
+    movl      $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+    call      dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl     %eax,%eax                   # got null?
+    jne       .LOP_INVOKE_VIRTUAL_RANGE_continue        # no, continue
+    jmp       common_exceptionThrown      # yes, handle exception
+
+    /* At this point:
+     *   eax = resolved base method
+     *   ecx = scratch
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    movzwl    4(rPC),%ecx               # ecx<- GFED or CCCC
+    .if       (!1)
+    andl      $0xf,%ecx                # ecx<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- "this"
+    movzwl    offMethod_methodIndex(%eax),%eax  # eax<- baseMethod->methodIndex
+    testl     %ecx,%ecx                 # null this?
+    je        common_errNullObject      # go if so
+    movl      offObject_clazz(%ecx),%edx  # edx<- thisPtr->clazz
+    movl      offClassObject_vtable(%edx),%edx # edx<- thisPtr->clazz->vtable
+    movl      (%edx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: x86/OP_INVOKE_SUPER_RANGE.S */
+/* File: x86/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,rINST
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(rINST),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offThread_method(rINST),%eax # eax<- method
+    movzwl    4(rPC),rINST              # rINST<- GFED or CCCC
+    .if       (!1)
+    andl      $0xf,rINST               # rINST<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %edx rINST             # %edx<- "this" ptr
+    testl     %edx,%edx                # null "this"?
+    SPILL_TMP1(%edx)
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    je       .LOP_INVOKE_SUPER_RANGE_resolve
+    /*
+     * At this point:
+     *  ecx = resolved base method [r0]
+     *  eax = method->clazz [r9]
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    movl    offClassObject_super(%eax),%eax   # eax<- method->clazz->super
+    movzwl  offMethod_methodIndex(%ecx),%edx  # edx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%edx # compare(methodIndex,vtableCount)
+    jae     .LOP_INVOKE_SUPER_RANGE_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%edx,4),%eax        # eax<- vtable[methodIndex]
+    UNSPILL_TMP1(%edx)
+    movl    %edx, %ecx
+    jmp     common_invokeMethodRange
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    SPILL_TMP2(%eax)                    # method->clazz
+    movl    %eax,OUT_ARG0(%esp)         # arg0<- method->clazz
+    movzwl  2(rPC),%ecx                 # ecx<- BBBB
+    movl    $METHOD_VIRTUAL,OUT_ARG2(%esp)  # arg2<- resolver method type
+    movl    %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP2(%eax)                  # restore method->clazz
+    jne     .LOP_INVOKE_SUPER_RANGE_continue        # good to go - continue
+    jmp     common_exceptionThrown      # handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  ecx = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    movl    offMethod_name(%ecx),%eax
+    jmp     common_errNoSuchMethod
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: x86/OP_INVOKE_DIRECT_RANGE.S */
+/* File: x86/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rIBASE            # rIBASE<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!1)
+    andl      $0xf,rIBASE             # rIBASE<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG_R  %ecx rIBASE            # ecx<- "this" ptr
+    je        .LOP_INVOKE_DIRECT_RANGE_resolve      # not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethodRange  # no, continue on
+    jmp       common_errNullObject
+
+    /*
+     * On entry:
+     *   TMP_SPILL  <- "this" register
+     * Things a bit ugly on this path, but it's the less
+     * frequent one.  We'll have to do some reloading.
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+     SPILL_TMP1(%ecx)
+     movl     rSELF,%ecx
+     movl     offThread_method(%ecx),%ecx  # ecx<- self->method
+     movzwl   2(rPC),%eax      # reference (BBBB or CCCC)
+     movl     offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+     movl     $METHOD_DIRECT,OUT_ARG2(%esp)
+     movl     %eax,OUT_ARG1(%esp)
+     movl     %ecx,OUT_ARG0(%esp)
+     call     dvmResolveMethod # eax<- call(clazz, ref, flags)
+     UNSPILL_TMP1(%ecx)
+     testl    %eax,%eax
+     jne      .LOP_INVOKE_DIRECT_RANGE_finish
+     jmp      common_exceptionThrown
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: x86/OP_INVOKE_STATIC_RANGE.S */
+/* File: x86/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+#if defined(WITH_JIT)
+    movl     %edx, TMP_SPILL1(%ebp)
+    lea      (%ecx,%eax,4), %edx
+    movl     %edx, TMP_SPILL2(%ebp)
+    movl     TMP_SPILL1(%ebp), %edx
+#endif
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    movl      $0, %ecx                 # make "this" null
+    testl     %eax,%eax
+    jne       common_invokeMethodRange
+
+    movl      rSELF,%ecx
+    movl      offThread_method(%ecx),%ecx # ecx<- self->method
+    movzwl    2(rPC),%eax
+    movl      offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+    movl      %eax,OUT_ARG1(%esp)       # arg1<- BBBB
+    movl      %ecx,OUT_ARG0(%esp)       # arg0<- clazz
+    movl      $METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rIBASE)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rIBASE)
+    testl     %eax,%eax                 # got null?
+#if defined(WITH_JIT)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      rSELF,%ecx
+    movzwl    offThread_subMode(%ecx), %ecx
+    je        common_exceptionThrown    # null, handle exception
+    andl      $kSubModeJitTraceBuild, %ecx # is trace under construction?
+    movl      $0, %ecx                 # make "this" null
+    je        common_invokeMethodRange # no (%eax=method, %ecx="this")
+    movl      TMP_SPILL2(%ebp), %edx
+    cmpl      $0, (%edx)                  # finished resolving
+    movl      TMP_SPILL1(%ebp), %edx
+    jne        common_invokeMethodRange # yes (%eax=method, %ecx="this")
+    movl      rSELF, %edx
+    movl      %edx, OUT_ARG0(%esp)
+    movl      rPC, OUT_ARG1(%esp)
+    movl      %eax, TMP_SPILL2(%ebp)
+    movl      %ecx, TMP_SPILL3(%ebp)
+    SPILL(rIBASE)
+    call      dvmJitEndTraceSelect
+    UNSPILL(rIBASE)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      TMP_SPILL2(%ebp), %eax
+    movl      TMP_SPILL3(%ebp), %ecx
+    jmp       common_invokeMethodRange
+#else
+    movl      $0, %ecx                 # make "this" null
+    jne       common_invokeMethodRange
+    jmp       common_exceptionThrown
+#endif
+
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: x86/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: x86/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl     4(rPC),%eax              # eax<- FEDC or CCCC
+    movl       rSELF,%ecx
+    .if        (!1)
+    andl       $0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R   %eax %eax              # eax<- "this"
+    EXPORT_PC
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       %eax, TMP_SPILL1(%ebp)
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offThread_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offThread_method(%ecx),%ecx           # ecx<- method
+    movl       %eax,OUT_ARG3(%esp)                 # arg3<- dex
+    movzwl     2(rPC),%eax                         # eax<- BBBB
+    movl       %ecx,OUT_ARG2(%esp)                 # arg2<- method
+    movl       %eax,OUT_ARG1(%esp)                 # arg1<- BBBB
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    movl       TMP_SPILL1(%ebp), %ecx
+    jmp        common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: x86/OP_UNUSED_79.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: x86/OP_UNUSED_7A.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_NEG_INT: /* 0x7b */
+/* File: x86/OP_NEG_INT.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    negl %eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_NOT_INT: /* 0x7c */
+/* File: x86/OP_NOT_INT.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    notl %eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: x86/OP_NEG_LONG.S */
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx        # ecx<- BA
+    sarl      $4,%ecx            # ecx<- B
+    andb      $0xf,rINSTbl       # rINST<- A
+    GET_VREG_WORD %eax %ecx 0     # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1     # ecx<- v[B+1]
+    negl      %eax
+    adcl      $0,%ecx
+    negl      %ecx
+    SET_VREG_WORD %eax rINST 0    # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1    # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: x86/OP_NOT_LONG.S */
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- BA
+    sarl      $4,%ecx           # ecx<- B
+    andb      $0xf,rINSTbl      # rINST<- A
+    GET_VREG_WORD %eax %ecx 0    # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1    # ecx<- v[B+1]
+    notl      %eax
+    notl      %ecx
+    SET_VREG_WORD %eax rINST 0   # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1   # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: x86/OP_NEG_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    flds    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    fchs
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: x86/OP_NEG_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fldl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    fchs
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86/OP_INT_TO_LONG.S */
+    /* int to long vA, vB */
+    movzbl  rINSTbl,%eax                # eax<- +A
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    andb    $0xf,rINSTbl               # rINST<- A
+    SPILL(rIBASE)                       # cltd trashes rIBASE/edx
+    cltd                                # rINST:eax<- sssssssBBBBBBBB
+    SET_VREG_WORD rIBASE rINST 1        # v[A+1]<- rIBASE/rPC
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[A+0]<- %eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: x86/OP_INT_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: x86/OP_INT_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: x86/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: x86/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: x86/OP_LONG_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildll    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: x86/OP_LONG_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildll    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: x86/OP_FLOAT_TO_INT.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 0
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 0
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 0
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_FLOAT_TO_INT_special_case # fix up result
+
+.LOP_FLOAT_TO_INT_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_FLOAT_TO_INT_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_FLOAT_TO_INT_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 0
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_FLOAT_TO_INT_finish
+.LOP_FLOAT_TO_INT_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 0
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_FLOAT_TO_INT_finish
+
+
+/* ------------------------------ */
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: x86/OP_FLOAT_TO_LONG.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 0
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 1
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 1
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_FLOAT_TO_LONG_special_case # fix up result
+
+.LOP_FLOAT_TO_LONG_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_FLOAT_TO_LONG_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_FLOAT_TO_LONG_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 1
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_FLOAT_TO_LONG_finish
+.LOP_FLOAT_TO_LONG_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 1
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_FLOAT_TO_LONG_finish
+
+
+/* ------------------------------ */
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: x86/OP_FLOAT_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    flds    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: x86/OP_DOUBLE_TO_INT.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 1
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 0
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 0
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_DOUBLE_TO_INT_special_case # fix up result
+
+.LOP_DOUBLE_TO_INT_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DOUBLE_TO_INT_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_DOUBLE_TO_INT_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 0
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_DOUBLE_TO_INT_finish
+.LOP_DOUBLE_TO_INT_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 0
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_DOUBLE_TO_INT_finish
+
+
+/* ------------------------------ */
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: x86/OP_DOUBLE_TO_LONG.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 1
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 1
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 1
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_DOUBLE_TO_LONG_special_case # fix up result
+
+.LOP_DOUBLE_TO_LONG_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DOUBLE_TO_LONG_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_DOUBLE_TO_LONG_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 1
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_DOUBLE_TO_LONG_finish
+.LOP_DOUBLE_TO_LONG_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 1
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_DOUBLE_TO_LONG_finish
+
+
+/* ------------------------------ */
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: x86/OP_DOUBLE_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fldl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: x86/OP_INT_TO_BYTE.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    movsbl %al,%eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: x86/OP_INT_TO_CHAR.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    movzwl %ax,%eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: x86/OP_INT_TO_SHORT.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    movswl %ax,%eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_INT: /* 0x90 */
+/* File: x86/OP_ADD_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    addl (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_INT: /* 0x91 */
+/* File: x86/OP_SUB_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    subl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT: /* 0x92 */
+/* File: x86/OP_MUL_INT.S */
+    /*
+     * 32-bit binary multiplication.
+     */
+    /* mul vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    SPILL(rIBASE)
+    imull    (rFP,%ecx,4),%eax      # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT: /* 0x93 */
+/* File: x86/OP_DIV_INT.S */
+/* File: x86/bindiv.S */
+
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    SPILL(rIBASE)
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_continue_div
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT: /* 0x94 */
+/* File: x86/OP_REM_INT.S */
+/* File: x86/bindiv.S */
+
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    SPILL(rIBASE)
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_continue_div
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT: /* 0x95 */
+/* File: x86/OP_AND_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    andl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT: /* 0x96 */
+/* File: x86/OP_OR_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    orl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT: /* 0x97 */
+/* File: x86/OP_XOR_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    xorl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_INT: /* 0x98 */
+/* File: x86/OP_SHL_INT.S */
+/* File: x86/binop1.S */
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    sall    %cl,%eax                          # ex: addl    %ecx,%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHR_INT: /* 0x99 */
+/* File: x86/OP_SHR_INT.S */
+/* File: x86/binop1.S */
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    sarl    %cl,%eax                          # ex: addl    %ecx,%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_USHR_INT: /* 0x9a */
+/* File: x86/OP_USHR_INT.S */
+/* File: x86/binop1.S */
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    shrl    %cl,%eax                          # ex: addl    %ecx,%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: x86/OP_ADD_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    addl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    adcl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: x86/OP_SUB_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    subl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    sbbl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: x86/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * We could definately use more free registers for
+     * this code.   We spill rINSTw (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill edi (rFP)
+     * for use as the vB pointer and esi (rPC) for use
+     * as the vC pointer.  Yuck.
+     */
+    /* mul-long vAA, vBB, vCC */
+    movzbl    2(rPC),%eax              # eax<- B
+    movzbl    3(rPC),%ecx              # ecx<- C
+    SPILL_TMP2(%esi)                   # save Dalvik PC
+    SPILL(rFP)
+    SPILL(rINST)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # esi<- &v[B]
+    leal      (rFP,%ecx,4),rFP         # rFP<- &v[C]
+    movl      4(%esi),%ecx             # ecx<- Bmsw
+    imull     (rFP),%ecx               # ecx<- (Bmsw*Clsw)
+    movl      4(rFP),%eax              # eax<- Cmsw
+    imull     (%esi),%eax              # eax<- (Cmsw*Blsw)
+    addl      %eax,%ecx                # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+    movl      (rFP),%eax               # eax<- Clsw
+    mull      (%esi)                   # eax<- (Clsw*Alsw)
+    UNSPILL(rINST)
+    UNSPILL(rFP)
+    leal      (%ecx,rIBASE),rIBASE # full result now in rIBASE:%eax
+    UNSPILL_TMP2(%esi)             # Restore Dalvik PC
+    FETCH_INST_OPCODE 2 %ecx       # Fetch next instruction
+    movl      rIBASE,4(rFP,rINST,4)# v[B+1]<- rIBASE
+    UNSPILL(rIBASE)
+    movl      %eax,(rFP,rINST,4)   # v[B]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: x86/OP_DIV_LONG.S */
+    /* div vAA, vBB, vCC */
+    movzbl    3(rPC),%eax              # eax<- CC
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)                      # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_DIV_LONG_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_DIV_LONG_check_neg1
+.LOP_DIV_LONG_notSpecial:
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+.LOP_DIV_LONG_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __divdi3
+.LOP_DIV_LONG_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                 # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_LONG_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_DIV_LONG_notSpecial
+    jmp     common_errDivideByZero
+.LOP_DIV_LONG_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_DIV_LONG_notSpecial
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_DIV_LONG_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_DIV_LONG_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0x80000000,rIBASE
+    jmp      .LOP_DIV_LONG_finish
+
+/* ------------------------------ */
+.L_OP_REM_LONG: /* 0x9f */
+/* File: x86/OP_REM_LONG.S */
+/* File: x86/OP_DIV_LONG.S */
+    /* div vAA, vBB, vCC */
+    movzbl    3(rPC),%eax              # eax<- CC
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)                      # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_REM_LONG_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_REM_LONG_check_neg1
+.LOP_REM_LONG_notSpecial:
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+.LOP_REM_LONG_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __moddi3
+.LOP_REM_LONG_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                 # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_LONG_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_REM_LONG_notSpecial
+    jmp     common_errDivideByZero
+.LOP_REM_LONG_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_REM_LONG_notSpecial
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_REM_LONG_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_REM_LONG_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0,rIBASE
+    jmp      .LOP_REM_LONG_finish
+
+
+/* ------------------------------ */
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: x86/OP_AND_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    andl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    andl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: x86/OP_OR_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    orl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    orl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: x86/OP_XOR_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    xorl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    xorl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: x86/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shl-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rINST */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # ecx<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: x86/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: x86/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R  %ecx %ecx               # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1          # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[BB+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: x86/OP_ADD_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fadds   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: x86/OP_SUB_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fsubs   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: x86/OP_MUL_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fmuls   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: x86/OP_DIV_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fdivs   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: x86/OP_REM_FLOAT.S */
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    flds     (rFP,%ecx,4)           # vCC to fp stack
+    flds     (rFP,%eax,4)           # vCC to fp stack
+    movzbl   rINSTbl,%ecx           # ecx<- AA
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    fstps    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86/OP_ADD_DOUBLE.S */
+   /*
+    * File: OP_ADD_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    movq     (rFP, %eax, 4), %xmm0      # %xmm0<- vBB
+    movq     (rFP, %ecx, 4), %xmm1      # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    addsd    %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq     %xmm0, (rFP, rINST, 4)     # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86/OP_SUB_DOUBLE.S */
+   /*
+    * File: OP_SUB_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    subsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB - vCC
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86/OP_MUL_DOUBLE.S */
+   /*
+    * File: OP_MUL_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    mulsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: x86/OP_DIV_DOUBLE.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    fldl    (rFP,%eax,4)         # vCC to fp stack
+    fdivl   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstpl   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: x86/OP_REM_DOUBLE.S */
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    fldl     (rFP,%ecx,4)           # vCC to fp stack
+    fldl     (rFP,%eax,4)           # vCC to fp stack
+    FETCH_INST_OPCODE 2 %ecx
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC 2
+    fstpl    (rFP,rINST,4)           # %st to vAA
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: x86/OP_ADD_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    addl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: x86/OP_SUB_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    subl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86/OP_MUL_INT_2ADDR.S */
+    /* mul vA, vB */
+    movzx   rINSTbl,%ecx              # ecx<- A+
+    sarl    $4,rINST                 # rINST<- B
+    GET_VREG_R %eax rINST             # eax<- vB
+    andb    $0xf,%cl                 # ecx<- A
+    SPILL(rIBASE)
+    imull   (rFP,%ecx,4),%eax         # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: x86/OP_DIV_INT_2ADDR.S */
+/* File: x86/bindiv2addr.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/2addr vA, vB */
+    movzx    rINSTbl,%ecx          # eax<- BA
+    SPILL(rIBASE)
+    sarl     $4,%ecx              # ecx<- B
+    GET_VREG_R %ecx %ecx           # eax<- vBB
+    andb     $0xf,rINSTbl         # rINST<- A
+    GET_VREG_R %eax rINST          # eax<- vBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_2ADDR_continue_div2addr
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_2ADDR_continue_div2addr
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_2ADDR_continue_div2addr:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: x86/OP_REM_INT_2ADDR.S */
+/* File: x86/bindiv2addr.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/2addr vA, vB */
+    movzx    rINSTbl,%ecx          # eax<- BA
+    SPILL(rIBASE)
+    sarl     $4,%ecx              # ecx<- B
+    GET_VREG_R %ecx %ecx           # eax<- vBB
+    andb     $0xf,rINSTbl         # rINST<- A
+    GET_VREG_R %eax rINST          # eax<- vBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_2ADDR_continue_div2addr
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_2ADDR_continue_div2addr
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_2ADDR_continue_div2addr:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: x86/OP_AND_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    andl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: x86/OP_OR_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    orl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: x86/OP_XOR_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    xorl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: x86/OP_SHL_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    sall    %cl,%eax                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: x86/OP_SHR_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    sarl    %cl,%eax                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: x86/OP_USHR_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    shrl    %cl,%eax                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: x86/OP_ADD_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    addl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    adcl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: x86/OP_SUB_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    subl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    sbbl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: x86/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, 2-addr version
+     *
+     * We could definately use more free registers for
+     * this code.  We must spill %edx (rIBASE) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and rIBASE as computational
+     * temps.  On top of that, we'll spill %esi (edi)
+     * for use as the vA pointer and rFP (esi) for use
+     * as the vB pointer.  Yuck.
+     */
+    /* mul-long/2addr vA, vB */
+    movzbl    rINSTbl,%eax             # eax<- BA
+    andb      $0xf,%al                # eax<- A
+    sarl      $4,rINST                # rINST<- B
+    SPILL_TMP2(%esi)
+    SPILL(rFP)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # %esi<- &v[A]
+    leal      (rFP,rINST,4),rFP        # rFP<- &v[B]
+    movl      4(%esi),%ecx             # ecx<- Amsw
+    imull     (rFP),%ecx               # ecx<- (Amsw*Blsw)
+    movl      4(rFP),%eax              # eax<- Bmsw
+    imull     (%esi),%eax              # eax<- (Bmsw*Alsw)
+    addl      %eax,%ecx                # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+    movl      (rFP),%eax               # eax<- Blsw
+    mull      (%esi)                   # eax<- (Blsw*Alsw)
+    leal      (%ecx,rIBASE),rIBASE     # full result now in %edx:%eax
+    movl      rIBASE,4(%esi)           # v[A+1]<- rIBASE
+    movl      %eax,(%esi)              # v[A]<- %eax
+    UNSPILL_TMP2(%esi)
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    UNSPILL(rFP)
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86/OP_DIV_LONG_2ADDR.S */
+    /* div/2addr vA, vB */
+    movzbl    rINSTbl,%eax
+    shrl      $4,%eax                  # eax<- B
+    andb      $0xf,rINSTbl             # rINST<- A
+    SPILL(rIBASE)                       # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_DIV_LONG_2ADDR_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_DIV_LONG_2ADDR_check_neg1
+.LOP_DIV_LONG_2ADDR_notSpecial:
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+.LOP_DIV_LONG_2ADDR_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __divdi3
+.LOP_DIV_LONG_2ADDR_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                    # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_LONG_2ADDR_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_DIV_LONG_2ADDR_notSpecial
+    jmp     common_errDivideByZero
+.LOP_DIV_LONG_2ADDR_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_DIV_LONG_2ADDR_notSpecial
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_DIV_LONG_2ADDR_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_DIV_LONG_2ADDR_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0x80000000,rIBASE
+    jmp      .LOP_DIV_LONG_2ADDR_finish
+
+/* ------------------------------ */
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: x86/OP_REM_LONG_2ADDR.S */
+/* File: x86/OP_DIV_LONG_2ADDR.S */
+    /* div/2addr vA, vB */
+    movzbl    rINSTbl,%eax
+    shrl      $4,%eax                  # eax<- B
+    andb      $0xf,rINSTbl             # rINST<- A
+    SPILL(rIBASE)                       # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_REM_LONG_2ADDR_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_REM_LONG_2ADDR_check_neg1
+.LOP_REM_LONG_2ADDR_notSpecial:
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+.LOP_REM_LONG_2ADDR_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __moddi3
+.LOP_REM_LONG_2ADDR_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                    # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_LONG_2ADDR_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_REM_LONG_2ADDR_notSpecial
+    jmp     common_errDivideByZero
+.LOP_REM_LONG_2ADDR_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_REM_LONG_2ADDR_notSpecial
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_REM_LONG_2ADDR_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_REM_LONG_2ADDR_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0,rIBASE
+    jmp      .LOP_REM_LONG_2ADDR_finish
+
+
+/* ------------------------------ */
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: x86/OP_AND_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    andl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    andl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: x86/OP_OR_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    orl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    orl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: x86/OP_XOR_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    xorl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    xorl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: x86/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R  %ecx %ecx              # ecx<- vBB
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: x86/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx         # ecx<- BA
+    andb      $0xf,rINSTbl        # rINST<- A
+    GET_VREG_WORD %eax rINST 0     # eax<- v[AA+0]
+    sarl      $4,%ecx             # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1   # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx           # ecx<- vBB
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1   # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0    # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: x86/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx               # ecx<- vBB
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: x86/OP_ADD_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fadds   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: x86/OP_SUB_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fsubs   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: x86/OP_MUL_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fmuls   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: x86/OP_DIV_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fdivs   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86/OP_REM_FLOAT_2ADDR.S */
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    flds     (rFP,rINST,4)              # vBB to fp stack
+    andb    $0xf,%cl                   # ecx<- A
+    flds     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86/OP_ADD_DOUBLE_2ADDR.S */
+   /*
+    * File: OP_ADD_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $0xf,%cl               # ecx<- A
+    sarl        $4,rINST               # rINST<- B
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    addsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86/OP_SUB_DOUBLE_2ADDR.S */
+   /*
+    * File: OP_SUB_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $0xf,%cl               # ecx<- A
+    sarl        $4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    subsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86/OP_MUL_DOUBLE_2ADDR.S */
+   /*
+    * File: OP_MUL_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $0xf,%cl               # ecx<- A
+    sarl        $4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    mulsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: x86/OP_DIV_DOUBLE_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    fldl    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fdivl   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstpl    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86/OP_REM_DOUBLE_2ADDR.S */
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    fldl     (rFP,rINST,4)              # vBB to fp stack
+    andb    $0xf,%cl                   # ecx<- A
+    fldl     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstpl    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: x86/OP_ADD_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    addl %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: x86/OP_RSUB_INT.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    subl %eax,%ecx                              # for example: addl %ecx, %eax
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86/OP_MUL_INT_LIT16.S */
+    /* mul/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    SPILL(rIBASE)
+    imull     %ecx,%eax                 # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: x86/OP_DIV_INT_LIT16.S */
+/* File: x86/bindivLit16.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax         # eax<- 000000BA
+    SPILL(rIBASE)
+    sarl     $4,%eax             # eax<- B
+    GET_VREG_R %eax %eax          # eax<- vB
+    movswl   2(rPC),%ecx          # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl        # rINST<- A
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_LIT16_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_LIT16_continue_div
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_LIT16_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: x86/OP_REM_INT_LIT16.S */
+/* File: x86/bindivLit16.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax         # eax<- 000000BA
+    SPILL(rIBASE)
+    sarl     $4,%eax             # eax<- B
+    GET_VREG_R %eax %eax          # eax<- vB
+    movswl   2(rPC),%ecx          # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl        # rINST<- A
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_LIT16_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_LIT16_continue_div
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_LIT16_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: x86/OP_AND_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    andl %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: x86/OP_OR_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    orl     %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: x86/OP_XOR_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    xor    %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: x86/OP_ADD_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    addl %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: x86/OP_RSUB_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    subl  %eax,%ecx                             # ex: addl %ecx,%eax
+    SET_VREG   %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: x86/OP_MUL_INT_LIT8.S */
+    /* mul/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    SPILL(rIBASE)
+    imull     %ecx,%eax                # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG  %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: x86/OP_DIV_INT_LIT8.S */
+/* File: x86/bindivLit8.S */
+    /*
+     * 32-bit div/rem "lit8" binary operation.  Handles special case of
+     * op0=minint & op1=-1
+     */
+    /* div/rem/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax        # eax<- BB
+    movsbl    3(rPC),%ecx        # ecx<- ssssssCC
+    SPILL(rIBASE)
+    GET_VREG_R  %eax %eax        # eax<- rBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_LIT8_continue_div
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_LIT8_continue_div
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_LIT8_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: x86/OP_REM_INT_LIT8.S */
+/* File: x86/bindivLit8.S */
+    /*
+     * 32-bit div/rem "lit8" binary operation.  Handles special case of
+     * op0=minint & op1=-1
+     */
+    /* div/rem/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax        # eax<- BB
+    movsbl    3(rPC),%ecx        # ecx<- ssssssCC
+    SPILL(rIBASE)
+    GET_VREG_R  %eax %eax        # eax<- rBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_LIT8_continue_div
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_LIT8_continue_div
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_LIT8_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: x86/OP_AND_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    andl %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: x86/OP_OR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    orl     %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: x86/OP_XOR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    xor    %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: x86/OP_SHL_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    sall  %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: x86/OP_SHR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    sarl    %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: x86/OP_USHR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    shrl     %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: x86/OP_IGET_VOLATILE.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: x86/OP_IPUT_VOLATILE.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   rINST,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: x86/OP_SGET_VOLATILE.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_VOLATILE_resolve                # if not, make it so
+.LOP_SGET_VOLATILE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_VOLATILE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: x86/OP_SPUT_VOLATILE.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_VOLATILE_resolve                # if not, make it so
+.LOP_SPUT_VOLATILE_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_VOLATILE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: x86/OP_IGET_OBJECT_VOLATILE.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_OBJECT_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_OBJECT_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_IGET_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_IPUT_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_SGET_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_SPUT_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: x86/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.  We also assume that all other special "checkBefore"
+     * actions have been handled, so we'll transition directly
+     * to the real handler
+     */
+    SPILL(rIBASE)
+    movl    rPC,OUT_ARG0(%esp)
+    call    dvmGetOriginalOpcode
+    UNSPILL(rIBASE)
+    movl    rSELF,%ecx
+    movzbl  1(rPC),rINST
+    movl    offThread_mainHandlerTable(%ecx),%ecx
+    jmp     *(%ecx,%eax,4)
+
+
+/* ------------------------------ */
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: x86/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                     # eax<- BBBB
+    movl     offThread_method(%ecx),%ecx       # ecx<- self->method
+    EXPORT_PC
+    movl     %eax,OUT_ARG2(%esp)             # arg2<- BBBB
+    movl     rINST,OUT_ARG1(%esp)            # arg1<- AA
+    movl     %ecx,OUT_ARG0(%esp)             # arg0<- method
+    call     dvmThrowVerificationError       # call(method, kind, ref)
+    jmp      common_exceptionThrown          # handle exception
+
+/* ------------------------------ */
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: x86/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We will be calling through a function table:
+     *
+     * (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult)
+     *
+     * Ignores argument count - always loads 4.
+     *
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    movl      rSELF,%ecx
+    EXPORT_PC
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    SPILL(rIBASE)                       # preserve rIBASE
+    movl      offThread_subMode(%ecx), %edx # edx<- submode flags
+    andl      $kSubModeDebugProfile, %edx # debug or profile mode active?
+    jnz       .LOP_EXECUTE_INLINE_debugprofile   # yes, take slow path
+.LOP_EXECUTE_INLINE_resume:
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)
+    call      .LOP_EXECUTE_INLINE_continue      # make call; will return after
+    UNSPILL(rIBASE)                     # restore rIBASE
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+.LOP_EXECUTE_INLINE_continue:
+    /*
+     * Extract args, call function.
+     *  ecx = #of args (0-4)
+     *  eax = call index
+     *  @esp = return addr
+     *  esp is -4 from normal
+     *
+     *  Go ahead and load all 4 args, even if not used.
+     */
+    movzwl    4(rPC),rIBASE
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG0(%esp)
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG1(%esp)
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG2(%esp)
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG3(%esp)
+
+    sall      $4,%eax      # index *= sizeof(table entry)
+    jmp       *gDvmInlineOpsTable(%eax)
+    # will return to caller of .LOP_EXECUTE_INLINE_continue
+
+    /*
+     * We're debugging or profiling.
+     * eax: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugprofile:
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- BBBB
+    SPILL_TMP1(%eax)                    # save opIndex
+    call      dvmResolveInlineNative    # dvmResolveInlineNative(opIndex)
+    movl      rSELF,%ecx                # restore self
+    testl     %eax,%eax                 # method resolved?
+    movl      %eax,%edx                 # save possibly resolved method in edx
+    UNSPILL_TMP1(%eax)                  # in case not resolved, restore opIndex
+    jz        .LOP_EXECUTE_INLINE_resume        # not resolved, just move on
+    SPILL_TMP2(%edx)                    # save method
+    movl      %edx,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastMethodTraceEnter   # dvmFastMethodTraceEnter(method,self)
+    movl      rSELF,%ecx                # restore self
+    UNSPILL_TMP1(%eax)                  # restore opIndex
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)       # needed for pResult of inline operation handler
+    call      .LOP_EXECUTE_INLINE_continue      # make call; will return after
+    SPILL_TMP1(%eax)                    # save result of inline
+    UNSPILL_TMP2(%eax)                  # restore method
+    movl      rSELF,%ecx                # restore self
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastNativeMethodTraceExit # dvmFastNativeMethodTraceExit(method,self)
+    UNSPILL(rIBASE)                     # restore rIBASE
+    UNSPILL_TMP1(%eax)                  # restore result of inline
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_EXECUTE_INLINE_RANGE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_INVOKE_OBJECT_INIT_RANGE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_RETURN_VOID_BARRIER     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $0,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      (%ecx,%eax,1),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    andb      $0xf,rINSTbl             # rINST<- A
+    SET_VREG  %eax rINST                # fp[A]<- result
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86/OP_IGET_WIDE_QUICK.S */
+    /* For: iget-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $0,%ecx                  # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%eax        # eax<- address of 64-bit source
+    movl      (%eax),%ecx               # ecx<- lsw
+    movl      4(%eax),%eax              # eax<- msw
+    andb      $0xf,rINSTbl             # rINST<- A
+    SET_VREG_WORD %ecx rINST 0          # v[A+0]<- lsw
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1          # v[A+1]<- msw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: x86/OP_IGET_OBJECT_QUICK.S */
+/* File: x86/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $0,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      (%ecx,%eax,1),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    andb      $0xf,rINSTbl             # rINST<- A
+    SET_VREG  %eax rINST                # fp[A]<- result
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86/OP_IPUT_QUICK.S */
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST,rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86/OP_IPUT_WIDE_QUICK.S */
+    /* For: iput-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl      %ecx,%ecx                # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%ecx        # ecx<- Address of 64-bit target
+    andb      $0xf,rINSTbl             # rINST<- A
+    GET_VREG_WORD %eax rINST 0          # eax<- lsw
+    GET_VREG_WORD rINST rINST 1         # rINST<- msw
+    movl      %eax,(%ecx)
+    movl      rINST,4(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    movl      rSELF,%eax
+    testl     rINST,rINST               # did we store null?
+    movl      offThread_cardTable(%eax),%eax  # get card table base
+    je        1f                            # skip card mark if null store
+    shrl      $GC_CARD_SHIFT,%ecx          # object head to card number
+    movb      %al,(%eax,%ecx)               # mark card based on object head
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl    4(rPC),%ecx               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%edx               # ecx<- BBBB
+    .if     (!0)
+    andl      $0xf,%ecx                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- vC ("this" ptr)
+    testl     %ecx,%ecx                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%ecx),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC                           # might throw later - get ready
+    movl      (%eax,%edx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl    4(rPC),%ecx               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%edx               # ecx<- BBBB
+    .if     (!1)
+    andl      $0xf,%ecx                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- vC ("this" ptr)
+    testl     %ecx,%ecx                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%ecx),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC                           # might throw later - get ready
+    movl      (%eax,%edx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: x86/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offThread_method(%ecx),%ecx # ecx<- current method
+    .if       (!0)
+    andl      $0xf,%eax                # eax<- D (or stays CCCC)
+    .endif
+    movl      offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    GET_VREG_R  %eax %eax               # eax<- "this"
+    movl      offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+    testl     %eax,%eax                 # null "this"?
+    je        common_errNullObject      # "this" is null, throw exception
+    movl       %eax, TMP_SPILL1(%ebp)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    movl      TMP_SPILL1(%ebp), %ecx
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: x86/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: x86/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offThread_method(%ecx),%ecx # ecx<- current method
+    .if       (!1)
+    andl      $0xf,%eax                # eax<- D (or stays CCCC)
+    .endif
+    movl      offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    GET_VREG_R  %eax %eax               # eax<- "this"
+    movl      offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+    testl     %eax,%eax                 # null "this"?
+    je        common_errNullObject      # "this" is null, throw exception
+    movl       %eax, TMP_SPILL1(%ebp)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    movl      TMP_SPILL1(%ebp), %ecx
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: x86/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: x86/OP_IPUT_OBJECT.S */
+    /*
+     * Object field put.
+     *
+     * for: iput-object
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                  # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_OBJECT_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_OBJECT_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                      # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl    rINST,(%ecx,%eax)      # obj.field <- v[A](8/16/32 bits)
+    movl    rSELF,%eax
+    testl   rINST,rINST                         # stored a NULL?
+    movl    offThread_cardTable(%eax),%eax      # get card table base
+    je      1f                                  # skip card mark if null store
+    shrl    $GC_CARD_SHIFT,%ecx                # object head to card number
+    movb    %al,(%eax,%ecx)                     # mark card using object head
+1:
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: x86/OP_SGET_OBJECT_VOLATILE.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_OBJECT_VOLATILE_resolve                # if not, make it so
+.LOP_SGET_OBJECT_VOLATILE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_OBJECT_VOLATILE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: x86/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: x86/OP_SPUT_OBJECT.S */
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_OBJECT_VOLATILE_resolve                # if not, make it so
+.LOP_SPUT_OBJECT_VOLATILE_finish:                              # field ptr in eax
+    movzbl    rINSTbl,%ecx                       # ecx<- AA
+    GET_VREG_R  %ecx %ecx
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    je        1f                                 # skip card mark if null
+    movl      rSELF,%ecx
+    movl      offField_clazz(%eax),%eax          # eax<- method->clazz
+    movl      offThread_cardTable(%ecx),%ecx       # get card table base
+    shrl      $GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_OBJECT_VOLATILE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: x86/OP_UNUSED_FF.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+    .size   dvmAsmInstructionStartCode, .-dvmAsmInstructionStartCode
+    .global dvmAsmInstructionEndCode
+dvmAsmInstructionEndCode:
+
+    .global dvmAsmAltInstructionStartCode
+    .type   dvmAsmAltInstructionStartCode, %function
+    .text
+
+dvmAsmAltInstructionStartCode = .L_ALT_OP_NOP
+/* ------------------------------ */
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(0*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(1*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(2*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(3*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(4*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(5*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(6*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(7*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(8*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(9*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(10*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(11*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(12*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(13*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(14*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(15*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(16*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(17*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(18*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(19*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(20*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(21*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(22*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(23*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(24*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(25*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(26*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(27*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(28*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(29*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(30*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(31*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(32*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(33*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(34*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(35*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(36*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(37*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(38*4)
+
+/* ------------------------------ */
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(39*4)
+
+/* ------------------------------ */
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(40*4)
+
+/* ------------------------------ */
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(41*4)
+
+/* ------------------------------ */
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(42*4)
+
+/* ------------------------------ */
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(43*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(44*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(45*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(46*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(47*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(48*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(49*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(50*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(51*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(52*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(53*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(54*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(55*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(56*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(57*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(58*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(59*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(60*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(61*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(62*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(63*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(64*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(65*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(66*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(67*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(68*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(69*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(70*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(71*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(72*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(73*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(74*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(75*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(76*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(77*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(78*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(79*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(80*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(81*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(82*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(83*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(84*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(85*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(86*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(87*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(88*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(89*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(90*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(91*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(92*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(93*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(94*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(95*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(96*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(97*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(98*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(99*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(100*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(101*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(102*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(103*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(104*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(105*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(106*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(107*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(108*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(109*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(110*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(111*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(112*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(113*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(114*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(115*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(116*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(117*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(118*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(119*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(120*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(121*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(122*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(123*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(124*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(125*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(126*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(127*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(128*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(129*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(130*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(131*4)
+
+/* ------------------------------ */
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(132*4)
+
+/* ------------------------------ */
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(133*4)
+
+/* ------------------------------ */
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(134*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(135*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(136*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(137*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(138*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(139*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(140*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(141*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(142*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(143*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(144*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(145*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(146*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(147*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(148*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(149*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(150*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(151*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(152*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(153*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(154*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(155*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(156*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(157*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(158*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(159*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(160*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(161*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(162*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(163*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(164*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(165*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(166*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(167*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(168*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(169*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(170*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(171*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(172*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(173*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(174*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(175*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(176*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(177*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(178*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(179*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(180*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(181*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(182*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(183*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(184*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(185*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(186*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(187*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(188*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(189*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(190*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(191*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(192*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(193*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(194*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(195*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(196*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(197*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(198*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(199*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(200*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(201*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(202*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(203*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(204*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(205*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(206*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(207*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(208*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(209*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(210*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(211*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(212*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(213*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(214*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(215*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(216*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(217*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(218*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(219*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(220*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(221*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(222*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(223*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(224*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(225*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(226*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(227*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(228*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(229*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(230*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(231*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(232*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(233*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(234*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(235*4)
+
+/* ------------------------------ */
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(236*4)
+
+/* ------------------------------ */
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(237*4)
+
+/* ------------------------------ */
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(238*4)
+
+/* ------------------------------ */
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(239*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(240*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(241*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(242*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(243*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(244*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(245*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(246*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(247*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(248*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(249*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(250*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(251*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(252*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(253*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(254*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(255*4)
+
+    .size   dvmAsmAltInstructionStartCode, .-dvmAsmAltInstructionStartCode
+    .global dvmAsmAltInstructionEndCode
+dvmAsmAltInstructionEndCode:
+
+    .global dvmAsmInstructionStart
+    .text
+dvmAsmInstructionStart:
+    .long .L_OP_NOP /* 0x00 */
+    .long .L_OP_MOVE /* 0x01 */
+    .long .L_OP_MOVE_FROM16 /* 0x02 */
+    .long .L_OP_MOVE_16 /* 0x03 */
+    .long .L_OP_MOVE_WIDE /* 0x04 */
+    .long .L_OP_MOVE_WIDE_FROM16 /* 0x05 */
+    .long .L_OP_MOVE_WIDE_16 /* 0x06 */
+    .long .L_OP_MOVE_OBJECT /* 0x07 */
+    .long .L_OP_MOVE_OBJECT_FROM16 /* 0x08 */
+    .long .L_OP_MOVE_OBJECT_16 /* 0x09 */
+    .long .L_OP_MOVE_RESULT /* 0x0a */
+    .long .L_OP_MOVE_RESULT_WIDE /* 0x0b */
+    .long .L_OP_MOVE_RESULT_OBJECT /* 0x0c */
+    .long .L_OP_MOVE_EXCEPTION /* 0x0d */
+    .long .L_OP_RETURN_VOID /* 0x0e */
+    .long .L_OP_RETURN /* 0x0f */
+    .long .L_OP_RETURN_WIDE /* 0x10 */
+    .long .L_OP_RETURN_OBJECT /* 0x11 */
+    .long .L_OP_CONST_4 /* 0x12 */
+    .long .L_OP_CONST_16 /* 0x13 */
+    .long .L_OP_CONST /* 0x14 */
+    .long .L_OP_CONST_HIGH16 /* 0x15 */
+    .long .L_OP_CONST_WIDE_16 /* 0x16 */
+    .long .L_OP_CONST_WIDE_32 /* 0x17 */
+    .long .L_OP_CONST_WIDE /* 0x18 */
+    .long .L_OP_CONST_WIDE_HIGH16 /* 0x19 */
+    .long .L_OP_CONST_STRING /* 0x1a */
+    .long .L_OP_CONST_STRING_JUMBO /* 0x1b */
+    .long .L_OP_CONST_CLASS /* 0x1c */
+    .long .L_OP_MONITOR_ENTER /* 0x1d */
+    .long .L_OP_MONITOR_EXIT /* 0x1e */
+    .long .L_OP_CHECK_CAST /* 0x1f */
+    .long .L_OP_INSTANCE_OF /* 0x20 */
+    .long .L_OP_ARRAY_LENGTH /* 0x21 */
+    .long .L_OP_NEW_INSTANCE /* 0x22 */
+    .long .L_OP_NEW_ARRAY /* 0x23 */
+    .long .L_OP_FILLED_NEW_ARRAY /* 0x24 */
+    .long .L_OP_FILLED_NEW_ARRAY_RANGE /* 0x25 */
+    .long .L_OP_FILL_ARRAY_DATA /* 0x26 */
+    .long .L_OP_THROW /* 0x27 */
+    .long .L_OP_GOTO /* 0x28 */
+    .long .L_OP_GOTO_16 /* 0x29 */
+    .long .L_OP_GOTO_32 /* 0x2a */
+    .long .L_OP_PACKED_SWITCH /* 0x2b */
+    .long .L_OP_SPARSE_SWITCH /* 0x2c */
+    .long .L_OP_CMPL_FLOAT /* 0x2d */
+    .long .L_OP_CMPG_FLOAT /* 0x2e */
+    .long .L_OP_CMPL_DOUBLE /* 0x2f */
+    .long .L_OP_CMPG_DOUBLE /* 0x30 */
+    .long .L_OP_CMP_LONG /* 0x31 */
+    .long .L_OP_IF_EQ /* 0x32 */
+    .long .L_OP_IF_NE /* 0x33 */
+    .long .L_OP_IF_LT /* 0x34 */
+    .long .L_OP_IF_GE /* 0x35 */
+    .long .L_OP_IF_GT /* 0x36 */
+    .long .L_OP_IF_LE /* 0x37 */
+    .long .L_OP_IF_EQZ /* 0x38 */
+    .long .L_OP_IF_NEZ /* 0x39 */
+    .long .L_OP_IF_LTZ /* 0x3a */
+    .long .L_OP_IF_GEZ /* 0x3b */
+    .long .L_OP_IF_GTZ /* 0x3c */
+    .long .L_OP_IF_LEZ /* 0x3d */
+    .long .L_OP_UNUSED_3E /* 0x3e */
+    .long .L_OP_UNUSED_3F /* 0x3f */
+    .long .L_OP_UNUSED_40 /* 0x40 */
+    .long .L_OP_UNUSED_41 /* 0x41 */
+    .long .L_OP_UNUSED_42 /* 0x42 */
+    .long .L_OP_UNUSED_43 /* 0x43 */
+    .long .L_OP_AGET /* 0x44 */
+    .long .L_OP_AGET_WIDE /* 0x45 */
+    .long .L_OP_AGET_OBJECT /* 0x46 */
+    .long .L_OP_AGET_BOOLEAN /* 0x47 */
+    .long .L_OP_AGET_BYTE /* 0x48 */
+    .long .L_OP_AGET_CHAR /* 0x49 */
+    .long .L_OP_AGET_SHORT /* 0x4a */
+    .long .L_OP_APUT /* 0x4b */
+    .long .L_OP_APUT_WIDE /* 0x4c */
+    .long .L_OP_APUT_OBJECT /* 0x4d */
+    .long .L_OP_APUT_BOOLEAN /* 0x4e */
+    .long .L_OP_APUT_BYTE /* 0x4f */
+    .long .L_OP_APUT_CHAR /* 0x50 */
+    .long .L_OP_APUT_SHORT /* 0x51 */
+    .long .L_OP_IGET /* 0x52 */
+    .long .L_OP_IGET_WIDE /* 0x53 */
+    .long .L_OP_IGET_OBJECT /* 0x54 */
+    .long .L_OP_IGET_BOOLEAN /* 0x55 */
+    .long .L_OP_IGET_BYTE /* 0x56 */
+    .long .L_OP_IGET_CHAR /* 0x57 */
+    .long .L_OP_IGET_SHORT /* 0x58 */
+    .long .L_OP_IPUT /* 0x59 */
+    .long .L_OP_IPUT_WIDE /* 0x5a */
+    .long .L_OP_IPUT_OBJECT /* 0x5b */
+    .long .L_OP_IPUT_BOOLEAN /* 0x5c */
+    .long .L_OP_IPUT_BYTE /* 0x5d */
+    .long .L_OP_IPUT_CHAR /* 0x5e */
+    .long .L_OP_IPUT_SHORT /* 0x5f */
+    .long .L_OP_SGET /* 0x60 */
+    .long .L_OP_SGET_WIDE /* 0x61 */
+    .long .L_OP_SGET_OBJECT /* 0x62 */
+    .long .L_OP_SGET_BOOLEAN /* 0x63 */
+    .long .L_OP_SGET_BYTE /* 0x64 */
+    .long .L_OP_SGET_CHAR /* 0x65 */
+    .long .L_OP_SGET_SHORT /* 0x66 */
+    .long .L_OP_SPUT /* 0x67 */
+    .long .L_OP_SPUT_WIDE /* 0x68 */
+    .long .L_OP_SPUT_OBJECT /* 0x69 */
+    .long .L_OP_SPUT_BOOLEAN /* 0x6a */
+    .long .L_OP_SPUT_BYTE /* 0x6b */
+    .long .L_OP_SPUT_CHAR /* 0x6c */
+    .long .L_OP_SPUT_SHORT /* 0x6d */
+    .long .L_OP_INVOKE_VIRTUAL /* 0x6e */
+    .long .L_OP_INVOKE_SUPER /* 0x6f */
+    .long .L_OP_INVOKE_DIRECT /* 0x70 */
+    .long .L_OP_INVOKE_STATIC /* 0x71 */
+    .long .L_OP_INVOKE_INTERFACE /* 0x72 */
+    .long .L_OP_UNUSED_73 /* 0x73 */
+    .long .L_OP_INVOKE_VIRTUAL_RANGE /* 0x74 */
+    .long .L_OP_INVOKE_SUPER_RANGE /* 0x75 */
+    .long .L_OP_INVOKE_DIRECT_RANGE /* 0x76 */
+    .long .L_OP_INVOKE_STATIC_RANGE /* 0x77 */
+    .long .L_OP_INVOKE_INTERFACE_RANGE /* 0x78 */
+    .long .L_OP_UNUSED_79 /* 0x79 */
+    .long .L_OP_UNUSED_7A /* 0x7a */
+    .long .L_OP_NEG_INT /* 0x7b */
+    .long .L_OP_NOT_INT /* 0x7c */
+    .long .L_OP_NEG_LONG /* 0x7d */
+    .long .L_OP_NOT_LONG /* 0x7e */
+    .long .L_OP_NEG_FLOAT /* 0x7f */
+    .long .L_OP_NEG_DOUBLE /* 0x80 */
+    .long .L_OP_INT_TO_LONG /* 0x81 */
+    .long .L_OP_INT_TO_FLOAT /* 0x82 */
+    .long .L_OP_INT_TO_DOUBLE /* 0x83 */
+    .long .L_OP_LONG_TO_INT /* 0x84 */
+    .long .L_OP_LONG_TO_FLOAT /* 0x85 */
+    .long .L_OP_LONG_TO_DOUBLE /* 0x86 */
+    .long .L_OP_FLOAT_TO_INT /* 0x87 */
+    .long .L_OP_FLOAT_TO_LONG /* 0x88 */
+    .long .L_OP_FLOAT_TO_DOUBLE /* 0x89 */
+    .long .L_OP_DOUBLE_TO_INT /* 0x8a */
+    .long .L_OP_DOUBLE_TO_LONG /* 0x8b */
+    .long .L_OP_DOUBLE_TO_FLOAT /* 0x8c */
+    .long .L_OP_INT_TO_BYTE /* 0x8d */
+    .long .L_OP_INT_TO_CHAR /* 0x8e */
+    .long .L_OP_INT_TO_SHORT /* 0x8f */
+    .long .L_OP_ADD_INT /* 0x90 */
+    .long .L_OP_SUB_INT /* 0x91 */
+    .long .L_OP_MUL_INT /* 0x92 */
+    .long .L_OP_DIV_INT /* 0x93 */
+    .long .L_OP_REM_INT /* 0x94 */
+    .long .L_OP_AND_INT /* 0x95 */
+    .long .L_OP_OR_INT /* 0x96 */
+    .long .L_OP_XOR_INT /* 0x97 */
+    .long .L_OP_SHL_INT /* 0x98 */
+    .long .L_OP_SHR_INT /* 0x99 */
+    .long .L_OP_USHR_INT /* 0x9a */
+    .long .L_OP_ADD_LONG /* 0x9b */
+    .long .L_OP_SUB_LONG /* 0x9c */
+    .long .L_OP_MUL_LONG /* 0x9d */
+    .long .L_OP_DIV_LONG /* 0x9e */
+    .long .L_OP_REM_LONG /* 0x9f */
+    .long .L_OP_AND_LONG /* 0xa0 */
+    .long .L_OP_OR_LONG /* 0xa1 */
+    .long .L_OP_XOR_LONG /* 0xa2 */
+    .long .L_OP_SHL_LONG /* 0xa3 */
+    .long .L_OP_SHR_LONG /* 0xa4 */
+    .long .L_OP_USHR_LONG /* 0xa5 */
+    .long .L_OP_ADD_FLOAT /* 0xa6 */
+    .long .L_OP_SUB_FLOAT /* 0xa7 */
+    .long .L_OP_MUL_FLOAT /* 0xa8 */
+    .long .L_OP_DIV_FLOAT /* 0xa9 */
+    .long .L_OP_REM_FLOAT /* 0xaa */
+    .long .L_OP_ADD_DOUBLE /* 0xab */
+    .long .L_OP_SUB_DOUBLE /* 0xac */
+    .long .L_OP_MUL_DOUBLE /* 0xad */
+    .long .L_OP_DIV_DOUBLE /* 0xae */
+    .long .L_OP_REM_DOUBLE /* 0xaf */
+    .long .L_OP_ADD_INT_2ADDR /* 0xb0 */
+    .long .L_OP_SUB_INT_2ADDR /* 0xb1 */
+    .long .L_OP_MUL_INT_2ADDR /* 0xb2 */
+    .long .L_OP_DIV_INT_2ADDR /* 0xb3 */
+    .long .L_OP_REM_INT_2ADDR /* 0xb4 */
+    .long .L_OP_AND_INT_2ADDR /* 0xb5 */
+    .long .L_OP_OR_INT_2ADDR /* 0xb6 */
+    .long .L_OP_XOR_INT_2ADDR /* 0xb7 */
+    .long .L_OP_SHL_INT_2ADDR /* 0xb8 */
+    .long .L_OP_SHR_INT_2ADDR /* 0xb9 */
+    .long .L_OP_USHR_INT_2ADDR /* 0xba */
+    .long .L_OP_ADD_LONG_2ADDR /* 0xbb */
+    .long .L_OP_SUB_LONG_2ADDR /* 0xbc */
+    .long .L_OP_MUL_LONG_2ADDR /* 0xbd */
+    .long .L_OP_DIV_LONG_2ADDR /* 0xbe */
+    .long .L_OP_REM_LONG_2ADDR /* 0xbf */
+    .long .L_OP_AND_LONG_2ADDR /* 0xc0 */
+    .long .L_OP_OR_LONG_2ADDR /* 0xc1 */
+    .long .L_OP_XOR_LONG_2ADDR /* 0xc2 */
+    .long .L_OP_SHL_LONG_2ADDR /* 0xc3 */
+    .long .L_OP_SHR_LONG_2ADDR /* 0xc4 */
+    .long .L_OP_USHR_LONG_2ADDR /* 0xc5 */
+    .long .L_OP_ADD_FLOAT_2ADDR /* 0xc6 */
+    .long .L_OP_SUB_FLOAT_2ADDR /* 0xc7 */
+    .long .L_OP_MUL_FLOAT_2ADDR /* 0xc8 */
+    .long .L_OP_DIV_FLOAT_2ADDR /* 0xc9 */
+    .long .L_OP_REM_FLOAT_2ADDR /* 0xca */
+    .long .L_OP_ADD_DOUBLE_2ADDR /* 0xcb */
+    .long .L_OP_SUB_DOUBLE_2ADDR /* 0xcc */
+    .long .L_OP_MUL_DOUBLE_2ADDR /* 0xcd */
+    .long .L_OP_DIV_DOUBLE_2ADDR /* 0xce */
+    .long .L_OP_REM_DOUBLE_2ADDR /* 0xcf */
+    .long .L_OP_ADD_INT_LIT16 /* 0xd0 */
+    .long .L_OP_RSUB_INT /* 0xd1 */
+    .long .L_OP_MUL_INT_LIT16 /* 0xd2 */
+    .long .L_OP_DIV_INT_LIT16 /* 0xd3 */
+    .long .L_OP_REM_INT_LIT16 /* 0xd4 */
+    .long .L_OP_AND_INT_LIT16 /* 0xd5 */
+    .long .L_OP_OR_INT_LIT16 /* 0xd6 */
+    .long .L_OP_XOR_INT_LIT16 /* 0xd7 */
+    .long .L_OP_ADD_INT_LIT8 /* 0xd8 */
+    .long .L_OP_RSUB_INT_LIT8 /* 0xd9 */
+    .long .L_OP_MUL_INT_LIT8 /* 0xda */
+    .long .L_OP_DIV_INT_LIT8 /* 0xdb */
+    .long .L_OP_REM_INT_LIT8 /* 0xdc */
+    .long .L_OP_AND_INT_LIT8 /* 0xdd */
+    .long .L_OP_OR_INT_LIT8 /* 0xde */
+    .long .L_OP_XOR_INT_LIT8 /* 0xdf */
+    .long .L_OP_SHL_INT_LIT8 /* 0xe0 */
+    .long .L_OP_SHR_INT_LIT8 /* 0xe1 */
+    .long .L_OP_USHR_INT_LIT8 /* 0xe2 */
+    .long .L_OP_IGET_VOLATILE /* 0xe3 */
+    .long .L_OP_IPUT_VOLATILE /* 0xe4 */
+    .long .L_OP_SGET_VOLATILE /* 0xe5 */
+    .long .L_OP_SPUT_VOLATILE /* 0xe6 */
+    .long .L_OP_IGET_OBJECT_VOLATILE /* 0xe7 */
+    .long .L_OP_IGET_WIDE_VOLATILE /* 0xe8 */
+    .long .L_OP_IPUT_WIDE_VOLATILE /* 0xe9 */
+    .long .L_OP_SGET_WIDE_VOLATILE /* 0xea */
+    .long .L_OP_SPUT_WIDE_VOLATILE /* 0xeb */
+    .long .L_OP_BREAKPOINT /* 0xec */
+    .long .L_OP_THROW_VERIFICATION_ERROR /* 0xed */
+    .long .L_OP_EXECUTE_INLINE /* 0xee */
+    .long .L_OP_EXECUTE_INLINE_RANGE /* 0xef */
+    .long .L_OP_INVOKE_OBJECT_INIT_RANGE /* 0xf0 */
+    .long .L_OP_RETURN_VOID_BARRIER /* 0xf1 */
+    .long .L_OP_IGET_QUICK /* 0xf2 */
+    .long .L_OP_IGET_WIDE_QUICK /* 0xf3 */
+    .long .L_OP_IGET_OBJECT_QUICK /* 0xf4 */
+    .long .L_OP_IPUT_QUICK /* 0xf5 */
+    .long .L_OP_IPUT_WIDE_QUICK /* 0xf6 */
+    .long .L_OP_IPUT_OBJECT_QUICK /* 0xf7 */
+    .long .L_OP_INVOKE_VIRTUAL_QUICK /* 0xf8 */
+    .long .L_OP_INVOKE_VIRTUAL_QUICK_RANGE /* 0xf9 */
+    .long .L_OP_INVOKE_SUPER_QUICK /* 0xfa */
+    .long .L_OP_INVOKE_SUPER_QUICK_RANGE /* 0xfb */
+    .long .L_OP_IPUT_OBJECT_VOLATILE /* 0xfc */
+    .long .L_OP_SGET_OBJECT_VOLATILE /* 0xfd */
+    .long .L_OP_SPUT_OBJECT_VOLATILE /* 0xfe */
+    .long .L_OP_UNUSED_FF /* 0xff */
+
+    .global dvmAsmAltInstructionStart
+    .text
+dvmAsmAltInstructionStart:
+    .long .L_ALT_OP_NOP /* 0x00 */
+    .long .L_ALT_OP_MOVE /* 0x01 */
+    .long .L_ALT_OP_MOVE_FROM16 /* 0x02 */
+    .long .L_ALT_OP_MOVE_16 /* 0x03 */
+    .long .L_ALT_OP_MOVE_WIDE /* 0x04 */
+    .long .L_ALT_OP_MOVE_WIDE_FROM16 /* 0x05 */
+    .long .L_ALT_OP_MOVE_WIDE_16 /* 0x06 */
+    .long .L_ALT_OP_MOVE_OBJECT /* 0x07 */
+    .long .L_ALT_OP_MOVE_OBJECT_FROM16 /* 0x08 */
+    .long .L_ALT_OP_MOVE_OBJECT_16 /* 0x09 */
+    .long .L_ALT_OP_MOVE_RESULT /* 0x0a */
+    .long .L_ALT_OP_MOVE_RESULT_WIDE /* 0x0b */
+    .long .L_ALT_OP_MOVE_RESULT_OBJECT /* 0x0c */
+    .long .L_ALT_OP_MOVE_EXCEPTION /* 0x0d */
+    .long .L_ALT_OP_RETURN_VOID /* 0x0e */
+    .long .L_ALT_OP_RETURN /* 0x0f */
+    .long .L_ALT_OP_RETURN_WIDE /* 0x10 */
+    .long .L_ALT_OP_RETURN_OBJECT /* 0x11 */
+    .long .L_ALT_OP_CONST_4 /* 0x12 */
+    .long .L_ALT_OP_CONST_16 /* 0x13 */
+    .long .L_ALT_OP_CONST /* 0x14 */
+    .long .L_ALT_OP_CONST_HIGH16 /* 0x15 */
+    .long .L_ALT_OP_CONST_WIDE_16 /* 0x16 */
+    .long .L_ALT_OP_CONST_WIDE_32 /* 0x17 */
+    .long .L_ALT_OP_CONST_WIDE /* 0x18 */
+    .long .L_ALT_OP_CONST_WIDE_HIGH16 /* 0x19 */
+    .long .L_ALT_OP_CONST_STRING /* 0x1a */
+    .long .L_ALT_OP_CONST_STRING_JUMBO /* 0x1b */
+    .long .L_ALT_OP_CONST_CLASS /* 0x1c */
+    .long .L_ALT_OP_MONITOR_ENTER /* 0x1d */
+    .long .L_ALT_OP_MONITOR_EXIT /* 0x1e */
+    .long .L_ALT_OP_CHECK_CAST /* 0x1f */
+    .long .L_ALT_OP_INSTANCE_OF /* 0x20 */
+    .long .L_ALT_OP_ARRAY_LENGTH /* 0x21 */
+    .long .L_ALT_OP_NEW_INSTANCE /* 0x22 */
+    .long .L_ALT_OP_NEW_ARRAY /* 0x23 */
+    .long .L_ALT_OP_FILLED_NEW_ARRAY /* 0x24 */
+    .long .L_ALT_OP_FILLED_NEW_ARRAY_RANGE /* 0x25 */
+    .long .L_ALT_OP_FILL_ARRAY_DATA /* 0x26 */
+    .long .L_ALT_OP_THROW /* 0x27 */
+    .long .L_ALT_OP_GOTO /* 0x28 */
+    .long .L_ALT_OP_GOTO_16 /* 0x29 */
+    .long .L_ALT_OP_GOTO_32 /* 0x2a */
+    .long .L_ALT_OP_PACKED_SWITCH /* 0x2b */
+    .long .L_ALT_OP_SPARSE_SWITCH /* 0x2c */
+    .long .L_ALT_OP_CMPL_FLOAT /* 0x2d */
+    .long .L_ALT_OP_CMPG_FLOAT /* 0x2e */
+    .long .L_ALT_OP_CMPL_DOUBLE /* 0x2f */
+    .long .L_ALT_OP_CMPG_DOUBLE /* 0x30 */
+    .long .L_ALT_OP_CMP_LONG /* 0x31 */
+    .long .L_ALT_OP_IF_EQ /* 0x32 */
+    .long .L_ALT_OP_IF_NE /* 0x33 */
+    .long .L_ALT_OP_IF_LT /* 0x34 */
+    .long .L_ALT_OP_IF_GE /* 0x35 */
+    .long .L_ALT_OP_IF_GT /* 0x36 */
+    .long .L_ALT_OP_IF_LE /* 0x37 */
+    .long .L_ALT_OP_IF_EQZ /* 0x38 */
+    .long .L_ALT_OP_IF_NEZ /* 0x39 */
+    .long .L_ALT_OP_IF_LTZ /* 0x3a */
+    .long .L_ALT_OP_IF_GEZ /* 0x3b */
+    .long .L_ALT_OP_IF_GTZ /* 0x3c */
+    .long .L_ALT_OP_IF_LEZ /* 0x3d */
+    .long .L_ALT_OP_UNUSED_3E /* 0x3e */
+    .long .L_ALT_OP_UNUSED_3F /* 0x3f */
+    .long .L_ALT_OP_UNUSED_40 /* 0x40 */
+    .long .L_ALT_OP_UNUSED_41 /* 0x41 */
+    .long .L_ALT_OP_UNUSED_42 /* 0x42 */
+    .long .L_ALT_OP_UNUSED_43 /* 0x43 */
+    .long .L_ALT_OP_AGET /* 0x44 */
+    .long .L_ALT_OP_AGET_WIDE /* 0x45 */
+    .long .L_ALT_OP_AGET_OBJECT /* 0x46 */
+    .long .L_ALT_OP_AGET_BOOLEAN /* 0x47 */
+    .long .L_ALT_OP_AGET_BYTE /* 0x48 */
+    .long .L_ALT_OP_AGET_CHAR /* 0x49 */
+    .long .L_ALT_OP_AGET_SHORT /* 0x4a */
+    .long .L_ALT_OP_APUT /* 0x4b */
+    .long .L_ALT_OP_APUT_WIDE /* 0x4c */
+    .long .L_ALT_OP_APUT_OBJECT /* 0x4d */
+    .long .L_ALT_OP_APUT_BOOLEAN /* 0x4e */
+    .long .L_ALT_OP_APUT_BYTE /* 0x4f */
+    .long .L_ALT_OP_APUT_CHAR /* 0x50 */
+    .long .L_ALT_OP_APUT_SHORT /* 0x51 */
+    .long .L_ALT_OP_IGET /* 0x52 */
+    .long .L_ALT_OP_IGET_WIDE /* 0x53 */
+    .long .L_ALT_OP_IGET_OBJECT /* 0x54 */
+    .long .L_ALT_OP_IGET_BOOLEAN /* 0x55 */
+    .long .L_ALT_OP_IGET_BYTE /* 0x56 */
+    .long .L_ALT_OP_IGET_CHAR /* 0x57 */
+    .long .L_ALT_OP_IGET_SHORT /* 0x58 */
+    .long .L_ALT_OP_IPUT /* 0x59 */
+    .long .L_ALT_OP_IPUT_WIDE /* 0x5a */
+    .long .L_ALT_OP_IPUT_OBJECT /* 0x5b */
+    .long .L_ALT_OP_IPUT_BOOLEAN /* 0x5c */
+    .long .L_ALT_OP_IPUT_BYTE /* 0x5d */
+    .long .L_ALT_OP_IPUT_CHAR /* 0x5e */
+    .long .L_ALT_OP_IPUT_SHORT /* 0x5f */
+    .long .L_ALT_OP_SGET /* 0x60 */
+    .long .L_ALT_OP_SGET_WIDE /* 0x61 */
+    .long .L_ALT_OP_SGET_OBJECT /* 0x62 */
+    .long .L_ALT_OP_SGET_BOOLEAN /* 0x63 */
+    .long .L_ALT_OP_SGET_BYTE /* 0x64 */
+    .long .L_ALT_OP_SGET_CHAR /* 0x65 */
+    .long .L_ALT_OP_SGET_SHORT /* 0x66 */
+    .long .L_ALT_OP_SPUT /* 0x67 */
+    .long .L_ALT_OP_SPUT_WIDE /* 0x68 */
+    .long .L_ALT_OP_SPUT_OBJECT /* 0x69 */
+    .long .L_ALT_OP_SPUT_BOOLEAN /* 0x6a */
+    .long .L_ALT_OP_SPUT_BYTE /* 0x6b */
+    .long .L_ALT_OP_SPUT_CHAR /* 0x6c */
+    .long .L_ALT_OP_SPUT_SHORT /* 0x6d */
+    .long .L_ALT_OP_INVOKE_VIRTUAL /* 0x6e */
+    .long .L_ALT_OP_INVOKE_SUPER /* 0x6f */
+    .long .L_ALT_OP_INVOKE_DIRECT /* 0x70 */
+    .long .L_ALT_OP_INVOKE_STATIC /* 0x71 */
+    .long .L_ALT_OP_INVOKE_INTERFACE /* 0x72 */
+    .long .L_ALT_OP_UNUSED_73 /* 0x73 */
+    .long .L_ALT_OP_INVOKE_VIRTUAL_RANGE /* 0x74 */
+    .long .L_ALT_OP_INVOKE_SUPER_RANGE /* 0x75 */
+    .long .L_ALT_OP_INVOKE_DIRECT_RANGE /* 0x76 */
+    .long .L_ALT_OP_INVOKE_STATIC_RANGE /* 0x77 */
+    .long .L_ALT_OP_INVOKE_INTERFACE_RANGE /* 0x78 */
+    .long .L_ALT_OP_UNUSED_79 /* 0x79 */
+    .long .L_ALT_OP_UNUSED_7A /* 0x7a */
+    .long .L_ALT_OP_NEG_INT /* 0x7b */
+    .long .L_ALT_OP_NOT_INT /* 0x7c */
+    .long .L_ALT_OP_NEG_LONG /* 0x7d */
+    .long .L_ALT_OP_NOT_LONG /* 0x7e */
+    .long .L_ALT_OP_NEG_FLOAT /* 0x7f */
+    .long .L_ALT_OP_NEG_DOUBLE /* 0x80 */
+    .long .L_ALT_OP_INT_TO_LONG /* 0x81 */
+    .long .L_ALT_OP_INT_TO_FLOAT /* 0x82 */
+    .long .L_ALT_OP_INT_TO_DOUBLE /* 0x83 */
+    .long .L_ALT_OP_LONG_TO_INT /* 0x84 */
+    .long .L_ALT_OP_LONG_TO_FLOAT /* 0x85 */
+    .long .L_ALT_OP_LONG_TO_DOUBLE /* 0x86 */
+    .long .L_ALT_OP_FLOAT_TO_INT /* 0x87 */
+    .long .L_ALT_OP_FLOAT_TO_LONG /* 0x88 */
+    .long .L_ALT_OP_FLOAT_TO_DOUBLE /* 0x89 */
+    .long .L_ALT_OP_DOUBLE_TO_INT /* 0x8a */
+    .long .L_ALT_OP_DOUBLE_TO_LONG /* 0x8b */
+    .long .L_ALT_OP_DOUBLE_TO_FLOAT /* 0x8c */
+    .long .L_ALT_OP_INT_TO_BYTE /* 0x8d */
+    .long .L_ALT_OP_INT_TO_CHAR /* 0x8e */
+    .long .L_ALT_OP_INT_TO_SHORT /* 0x8f */
+    .long .L_ALT_OP_ADD_INT /* 0x90 */
+    .long .L_ALT_OP_SUB_INT /* 0x91 */
+    .long .L_ALT_OP_MUL_INT /* 0x92 */
+    .long .L_ALT_OP_DIV_INT /* 0x93 */
+    .long .L_ALT_OP_REM_INT /* 0x94 */
+    .long .L_ALT_OP_AND_INT /* 0x95 */
+    .long .L_ALT_OP_OR_INT /* 0x96 */
+    .long .L_ALT_OP_XOR_INT /* 0x97 */
+    .long .L_ALT_OP_SHL_INT /* 0x98 */
+    .long .L_ALT_OP_SHR_INT /* 0x99 */
+    .long .L_ALT_OP_USHR_INT /* 0x9a */
+    .long .L_ALT_OP_ADD_LONG /* 0x9b */
+    .long .L_ALT_OP_SUB_LONG /* 0x9c */
+    .long .L_ALT_OP_MUL_LONG /* 0x9d */
+    .long .L_ALT_OP_DIV_LONG /* 0x9e */
+    .long .L_ALT_OP_REM_LONG /* 0x9f */
+    .long .L_ALT_OP_AND_LONG /* 0xa0 */
+    .long .L_ALT_OP_OR_LONG /* 0xa1 */
+    .long .L_ALT_OP_XOR_LONG /* 0xa2 */
+    .long .L_ALT_OP_SHL_LONG /* 0xa3 */
+    .long .L_ALT_OP_SHR_LONG /* 0xa4 */
+    .long .L_ALT_OP_USHR_LONG /* 0xa5 */
+    .long .L_ALT_OP_ADD_FLOAT /* 0xa6 */
+    .long .L_ALT_OP_SUB_FLOAT /* 0xa7 */
+    .long .L_ALT_OP_MUL_FLOAT /* 0xa8 */
+    .long .L_ALT_OP_DIV_FLOAT /* 0xa9 */
+    .long .L_ALT_OP_REM_FLOAT /* 0xaa */
+    .long .L_ALT_OP_ADD_DOUBLE /* 0xab */
+    .long .L_ALT_OP_SUB_DOUBLE /* 0xac */
+    .long .L_ALT_OP_MUL_DOUBLE /* 0xad */
+    .long .L_ALT_OP_DIV_DOUBLE /* 0xae */
+    .long .L_ALT_OP_REM_DOUBLE /* 0xaf */
+    .long .L_ALT_OP_ADD_INT_2ADDR /* 0xb0 */
+    .long .L_ALT_OP_SUB_INT_2ADDR /* 0xb1 */
+    .long .L_ALT_OP_MUL_INT_2ADDR /* 0xb2 */
+    .long .L_ALT_OP_DIV_INT_2ADDR /* 0xb3 */
+    .long .L_ALT_OP_REM_INT_2ADDR /* 0xb4 */
+    .long .L_ALT_OP_AND_INT_2ADDR /* 0xb5 */
+    .long .L_ALT_OP_OR_INT_2ADDR /* 0xb6 */
+    .long .L_ALT_OP_XOR_INT_2ADDR /* 0xb7 */
+    .long .L_ALT_OP_SHL_INT_2ADDR /* 0xb8 */
+    .long .L_ALT_OP_SHR_INT_2ADDR /* 0xb9 */
+    .long .L_ALT_OP_USHR_INT_2ADDR /* 0xba */
+    .long .L_ALT_OP_ADD_LONG_2ADDR /* 0xbb */
+    .long .L_ALT_OP_SUB_LONG_2ADDR /* 0xbc */
+    .long .L_ALT_OP_MUL_LONG_2ADDR /* 0xbd */
+    .long .L_ALT_OP_DIV_LONG_2ADDR /* 0xbe */
+    .long .L_ALT_OP_REM_LONG_2ADDR /* 0xbf */
+    .long .L_ALT_OP_AND_LONG_2ADDR /* 0xc0 */
+    .long .L_ALT_OP_OR_LONG_2ADDR /* 0xc1 */
+    .long .L_ALT_OP_XOR_LONG_2ADDR /* 0xc2 */
+    .long .L_ALT_OP_SHL_LONG_2ADDR /* 0xc3 */
+    .long .L_ALT_OP_SHR_LONG_2ADDR /* 0xc4 */
+    .long .L_ALT_OP_USHR_LONG_2ADDR /* 0xc5 */
+    .long .L_ALT_OP_ADD_FLOAT_2ADDR /* 0xc6 */
+    .long .L_ALT_OP_SUB_FLOAT_2ADDR /* 0xc7 */
+    .long .L_ALT_OP_MUL_FLOAT_2ADDR /* 0xc8 */
+    .long .L_ALT_OP_DIV_FLOAT_2ADDR /* 0xc9 */
+    .long .L_ALT_OP_REM_FLOAT_2ADDR /* 0xca */
+    .long .L_ALT_OP_ADD_DOUBLE_2ADDR /* 0xcb */
+    .long .L_ALT_OP_SUB_DOUBLE_2ADDR /* 0xcc */
+    .long .L_ALT_OP_MUL_DOUBLE_2ADDR /* 0xcd */
+    .long .L_ALT_OP_DIV_DOUBLE_2ADDR /* 0xce */
+    .long .L_ALT_OP_REM_DOUBLE_2ADDR /* 0xcf */
+    .long .L_ALT_OP_ADD_INT_LIT16 /* 0xd0 */
+    .long .L_ALT_OP_RSUB_INT /* 0xd1 */
+    .long .L_ALT_OP_MUL_INT_LIT16 /* 0xd2 */
+    .long .L_ALT_OP_DIV_INT_LIT16 /* 0xd3 */
+    .long .L_ALT_OP_REM_INT_LIT16 /* 0xd4 */
+    .long .L_ALT_OP_AND_INT_LIT16 /* 0xd5 */
+    .long .L_ALT_OP_OR_INT_LIT16 /* 0xd6 */
+    .long .L_ALT_OP_XOR_INT_LIT16 /* 0xd7 */
+    .long .L_ALT_OP_ADD_INT_LIT8 /* 0xd8 */
+    .long .L_ALT_OP_RSUB_INT_LIT8 /* 0xd9 */
+    .long .L_ALT_OP_MUL_INT_LIT8 /* 0xda */
+    .long .L_ALT_OP_DIV_INT_LIT8 /* 0xdb */
+    .long .L_ALT_OP_REM_INT_LIT8 /* 0xdc */
+    .long .L_ALT_OP_AND_INT_LIT8 /* 0xdd */
+    .long .L_ALT_OP_OR_INT_LIT8 /* 0xde */
+    .long .L_ALT_OP_XOR_INT_LIT8 /* 0xdf */
+    .long .L_ALT_OP_SHL_INT_LIT8 /* 0xe0 */
+    .long .L_ALT_OP_SHR_INT_LIT8 /* 0xe1 */
+    .long .L_ALT_OP_USHR_INT_LIT8 /* 0xe2 */
+    .long .L_ALT_OP_IGET_VOLATILE /* 0xe3 */
+    .long .L_ALT_OP_IPUT_VOLATILE /* 0xe4 */
+    .long .L_ALT_OP_SGET_VOLATILE /* 0xe5 */
+    .long .L_ALT_OP_SPUT_VOLATILE /* 0xe6 */
+    .long .L_ALT_OP_IGET_OBJECT_VOLATILE /* 0xe7 */
+    .long .L_ALT_OP_IGET_WIDE_VOLATILE /* 0xe8 */
+    .long .L_ALT_OP_IPUT_WIDE_VOLATILE /* 0xe9 */
+    .long .L_ALT_OP_SGET_WIDE_VOLATILE /* 0xea */
+    .long .L_ALT_OP_SPUT_WIDE_VOLATILE /* 0xeb */
+    .long .L_ALT_OP_BREAKPOINT /* 0xec */
+    .long .L_ALT_OP_THROW_VERIFICATION_ERROR /* 0xed */
+    .long .L_ALT_OP_EXECUTE_INLINE /* 0xee */
+    .long .L_ALT_OP_EXECUTE_INLINE_RANGE /* 0xef */
+    .long .L_ALT_OP_INVOKE_OBJECT_INIT_RANGE /* 0xf0 */
+    .long .L_ALT_OP_RETURN_VOID_BARRIER /* 0xf1 */
+    .long .L_ALT_OP_IGET_QUICK /* 0xf2 */
+    .long .L_ALT_OP_IGET_WIDE_QUICK /* 0xf3 */
+    .long .L_ALT_OP_IGET_OBJECT_QUICK /* 0xf4 */
+    .long .L_ALT_OP_IPUT_QUICK /* 0xf5 */
+    .long .L_ALT_OP_IPUT_WIDE_QUICK /* 0xf6 */
+    .long .L_ALT_OP_IPUT_OBJECT_QUICK /* 0xf7 */
+    .long .L_ALT_OP_INVOKE_VIRTUAL_QUICK /* 0xf8 */
+    .long .L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE /* 0xf9 */
+    .long .L_ALT_OP_INVOKE_SUPER_QUICK /* 0xfa */
+    .long .L_ALT_OP_INVOKE_SUPER_QUICK_RANGE /* 0xfb */
+    .long .L_ALT_OP_IPUT_OBJECT_VOLATILE /* 0xfc */
+    .long .L_ALT_OP_SGET_OBJECT_VOLATILE /* 0xfd */
+    .long .L_ALT_OP_SPUT_OBJECT_VOLATILE /* 0xfe */
+    .long .L_ALT_OP_UNUSED_FF /* 0xff */
+/* File: x86/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+    .text
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+/*
+ * bool dvmMterpStdRun(Thread* self)
+ *
+ * Interpreter entry point.  Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+    push    %ebp                 # save caller base pointer
+    movl    %esp, %ebp           # set our %ebp
+    movl    rSELF, %ecx          # get incoming rSELF
+/*
+ * At this point we've allocated one slot on the stack
+ * via push and stack is 8-byte aligned.  Allocate space
+ * for 9 spill slots, 4 local slots, 5 arg slots to bring
+ * us to 16-byte alignment
+ */
+    subl    $(FRAME_SIZE-4), %esp
+
+/* Spill callee save regs */
+    movl    %edi,EDI_SPILL(%ebp)
+    movl    %esi,ESI_SPILL(%ebp)
+    movl    %ebx,EBX_SPILL(%ebp)
+
+/* Set up "named" registers */
+    movl    offThread_pc(%ecx),rPC
+    movl    offThread_curFrame(%ecx),rFP
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+
+    /* Remember %esp for future "longjmp" */
+    movl    %esp,offThread_bailPtr(%ecx)
+
+    /* Fetch next instruction before potential jump */
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    movl        $0, offThread_inJitCodeCache(%ecx)
+    cmpl        $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+
+   /* Normal case: start executing the instruction at rPC */
+    GOTO_NEXT
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(Thread* self, bool changeInterp)
+ *
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We're not going to build a standard frame here, so the arg accesses will
+ * look a little strange.
+ *
+ * On entry:
+ *  esp+4 (arg0)  Thread* self
+ *  esp+8 (arg1)  bool changeInterp
+ */
+dvmMterpStdBail:
+    movl    4(%esp),%ecx                 # grab self
+    movl    8(%esp),%eax                 # changeInterp to return reg
+    movl    offThread_bailPtr(%ecx),%esp # Restore "setjmp" esp
+    movl    %esp,%ebp
+    addl    $(FRAME_SIZE-4), %ebp       # Restore %ebp at point of setjmp
+    movl    EDI_SPILL(%ebp),%edi
+    movl    ESI_SPILL(%ebp),%esi
+    movl    EBX_SPILL(%ebp),%ebx
+    movl    %ebp, %esp                   # strip frame
+    pop     %ebp                         # restore caller's ebp
+    ret                                  # return to dvmMterpStdRun's caller
+
+
+#ifdef WITH_JIT
+    .global     dvmNcgInvokeInterpreter
+    .type       dvmNcgInvokeInterpreter, %function
+/* input: start of method in %eax */
+dvmNcgInvokeInterpreter:
+    movl        %eax, rPC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx                    # %edx<- opcode
+    GOTO_NEXT_R %ecx                    # start executing the instruction at rPC
+#endif
+
+/*
+ * Strings
+ */
+    .section    .rodata
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+
+
+/* File: x86/footer.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Common subroutines and data.
+ */
+
+#if defined(WITH_JIT)
+/*
+ * JIT-related re-entries into the interpreter.  In general, if the
+ * exit from a translation can at some point be chained, the entry
+ * here requires that control arrived via a call, and that the "rp"
+ * on TOS is actually a pointer to a 32-bit cell containing the Dalvik PC
+ * of the next insn to handle.  If no chaining will happen, the entry
+ * should be reached via a direct jump and rPC set beforehand.
+ */
+
+    .global dvmJitToInterpPunt
+/*
+ * The compiler will generate a jump to this entry point when it is
+ * having difficulty translating a Dalvik instruction.  We must skip
+ * the code cache lookup & prevent chaining to avoid bouncing between
+ * the interpreter and code cache. rPC must be set on entry.
+ */
+dvmJitToInterpPunt:
+    GET_PC
+#if defined(WITH_JIT_TUNING)
+    movl   rPC, OUT_ARG0(%esp)
+    call   dvmBumpPunt
+#endif
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    movl        $0, offThread_inJitCodeCache(%ecx)
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+    .global dvmJitToInterpSingleStep
+/*
+ * Return to the interpreter to handle a single instruction.
+ * Should be reached via a call.
+ * On entry:
+ *   0(%esp)          <= native return address within trace
+ *   rPC              <= Dalvik PC of this instruction
+ *   OUT_ARG0+4(%esp) <= Dalvik PC of next instruction
+ */
+dvmJitToInterpSingleStep:
+/* TODO */
+    call     dvmAbort
+#if 0
+    pop    %eax
+    movl   rSELF, %ecx
+    movl   OUT_ARG0(%esp), %edx
+    movl   %eax,offThread_jitResumeNPC(%ecx)
+    movl   %edx,offThread_jitResumeDPC(%ecx)
+    movl   $kInterpEntryInstr,offThread_entryPoint(%ecx)
+    movl   $1,rINST     # changeInterp <= true
+    jmp    common_gotoBail
+#endif
+
+    .global dvmJitToInterpNoChainNoProfile
+/*
+ * Return from the translation cache to the interpreter to do method
+ * invocation.  Check if the translation exists for the callee, but don't
+ * chain to it. rPC must be set on entry.
+ */
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    call   dvmBumpNoChain
+#endif
+    movl   %eax, rPC
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx                # ecx <- self
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmpl   $0, %eax
+    jz     1f
+    jmp    *%eax                     # exec translation if we've got one
+    # won't return
+1:
+    EXPORT_PC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation from the exit target, but don't attempt to chain.
+ * rPC set on entry.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    call   dvmBumpNoChain
+#endif
+    movl   %ebx, rPC
+    lea    4(%esp), %esp #to recover the esp update due to function call
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx
+    cmpl   $0,%eax
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1f
+    jmp    *%eax              # jump to tranlation
+    # won't return
+
+/* No Translation - request one */
+1:
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmpl   $0, %eax          # JIT enabled?
+    jnz    2f                 # Request one if so
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx         # Continue interpreting if not
+    GOTO_NEXT_R %ecx
+2:
+    ## Looks like an EXPORT_PC is needed here. Now jmp to common_selectTrace2
+    movl   $kJitTSelectRequestHot,%eax # ask for trace select
+    jmp    common_selectTrace
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation for the exit target.  Reached via a call, and
+ * (TOS)->rPC.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    movl   0(%esp), %eax          # get return address
+    movl   %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    lea    4(%esp), %esp #to recover the esp update due to function call
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea    -17(%eax), %ebx #$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea    -4(%esp), %esp
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread # (pc, self)
+    lea    4(%esp), %esp
+    cmpl   $0,%eax
+    movl   rSELF, %ecx
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1b                 # no - ask for one
+    movl   %eax,OUT_ARG0(%esp)
+    movl   rINST,OUT_ARG1(%esp)
+    call   dvmJitChain        # Attempt dvmJitChain(codeAddr,chainAddr)
+    cmpl   $0,%eax           # Success?
+    jz     toInterpreter      # didn't chain - interpret
+    jmp    *%eax
+    # won't return
+
+/*
+ * Placeholder entries for x86 JIT
+ */
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+
+    .global     dvmJitToExceptionThrown
+dvmJitToExceptionThrown: //rPC in
+    movl   rSELF, %edx
+    GET_PC
+    movl   $0, offThread_inJitCodeCache(%edx)
+    jmp common_exceptionThrown
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+/* one input: the target rPC value */
+    movl        0(%esp), %eax          # get return address
+    movl        %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea         -17(%eax), %ebx #$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea         4(%esp), %esp
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    #lea         4(%esp), %esp
+    cmp         $0, %eax
+    je          toInterpreter
+    #lea         -8(%esp), %esp
+    movl        %ebx, OUT_ARG1(%esp)    # %ebx live thorugh dvmJitGetTraceAddrThread
+    movl        %eax, OUT_ARG0(%esp)    # first argument
+    call        dvmJitChain
+    #lea         8(%esp), %esp
+    cmp         $0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+dvmJitToInterpNoChain: #rPC in eax
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    movl        %eax, rPC
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmp         $0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+toInterpreter:
+    EXPORT_PC
+    movl        rSELF, %ecx
+    movl        offThread_curHandlerTable(%ecx), rIBASE
+    FETCH_INST
+    movl        offThread_pJitProfTable(%ecx), %eax
+    #Fallthrough
+
+/* ebx holds the pointer to the jit profile table
+   edx has the opCode */
+common_testUpdateProfile:
+    cmp         $0, %eax
+    je          4f
+/* eax holds the pointer to the jit profile table
+   edx has the opCode
+   rPC points to the next bytecode */
+
+common_updateProfile:
+    # quick & dirty hash
+    movl   rPC, %ecx
+    shrl   $12, %ecx
+    xorl   rPC, %ecx
+    andl   $((1<<JIT_PROF_SIZE_LOG_2)-1), %ecx
+    decb   (%ecx,%eax)
+    #jmp    1f # remove
+    jz     2f
+1:
+    GOTO_NEXT
+2:
+common_Profile:
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection.  First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now.
+ */
+    SPILL(rIBASE)
+    SPILL_TMP1(rINST)
+    movl        rSELF, rIBASE
+    GET_JIT_THRESHOLD rIBASE rINST  # leaves rSELF in %ecx
+    EXPORT_PC
+    movb   rINSTbl,(%ecx,%eax)   # reset counter
+    movl   rIBASE,rINST            # preserve rSELF
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   rIBASE,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    UNSPILL(rIBASE)
+    movl   %eax,offThread_inJitCodeCache(rINST)   # set the inJitCodeCache flag
+    UNSPILL_TMP1(rINST)
+    cmpl   $0,%eax
+    #jmp    1f # remove
+    jz     1f
+    jmp   *%eax        # TODO: decide call vs/ jmp!.  No return either way
+1:
+    movl   $kJitTSelectRequest,%eax
+    # On entry, eax<- jitState, rPC valid
+common_selectTrace:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movzwl      offThread_subMode(%ebx), %ecx
+    and         $(kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+    movl        %eax, offThread_jitState(%ebx)
+    movl        rSELF, %eax
+    movl       %eax, OUT_ARG0(%esp)
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   mov          EBX_SPILL(%ebp), %ebx
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+common_selectTrace2:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movl        %ebx, OUT_ARG0(%esp)
+    movl        %eax, offThread_jitState(%ebx)
+    movzwl      offThread_subMode(%ebx), %ecx
+    mov         EBX_SPILL(%ebp), %ebx
+    and         (kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+
+
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+#endif
+
+/*
+ * For the invoke codes we need to know what register holds the "this" pointer. However
+ * it seems the this pointer is assigned consistently most times it is in %ecx but other
+ * times it is in OP_INVOKE_INTERFACE, OP_INVOKE_SUPER_QUICK, or OP_INVOKE_VIRTUAL_QUICK.
+*/
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   ecx = "this"
+ *   rINSTw trashed, must reload
+ *   rIBASE trashed, must reload before resuming interpreter
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    movzbl      1(rPC),rINST       # rINST<- AA
+    movzwl      4(rPC), %ecx            # %ecx<- CCCC
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+    test        rINST, rINST
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- AA
+    jz          .LinvokeArgsDone        # no args; jump to args done
+
+
+   /*
+    * %eax=methodToCall, %ecx=CCCC, LOCAL0_OFFSET(%ebp)=count,
+    * %edx=&outs (&stackSaveArea).  (very few methods have > 10 args;
+    * could unroll for common cases)
+    */
+
+.LinvokeRangeArgs:
+    movl        %ebx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- save %ebx
+    lea         (rFP, %ecx, 4), %ecx    # %ecx<- &vCCCC
+    shll        $2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+    subl        LOCAL0_OFFSET(%ebp), %edx       # %edx<- update &outs
+    shrl        $2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+1:
+    movl        (%ecx), %ebx            # %ebx<- vCCCC
+    lea         4(%ecx), %ecx           # %ecx<- &vCCCC++
+    subl        $1, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET<- LOCAL0_OFFSET--
+    movl        %ebx, (%edx)            # *outs<- vCCCC
+    lea         4(%edx), %edx           # outs++
+    jne         1b                      # loop if count (LOCAL0_OFFSET(%ebp)) not zero
+    movl        LOCAL1_OFFSET(%ebp), %ebx       # %ebx<- restore %ebx
+    jmp         .LinvokeArgsDone        # continue
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * prepare to copy args to "outs" area of current frame
+    * rIBASE trashed, must reload before resuming interpreter
+    */
+
+common_invokeMethodNoRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+.LinvokeNewNoRange:
+    movzbl      1(rPC),rINST       # rINST<- BA
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- BA
+    shrl        $4, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- B
+    je          .LinvokeArgsDone        # no args; jump to args done
+    movzwl      4(rPC), %ecx            # %ecx<- GFED
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+
+   /*
+    * %eax=methodToCall, %ecx=GFED, LOCAL0_OFFSET(%ebp)=count, %edx=outs
+    */
+
+.LinvokeNonRange:
+    cmp         $2, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 2
+    movl        %ecx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- GFED
+    jl          1f                      # handle 1 arg
+    je          2f                      # handle 2 args
+    cmp         $4, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 4
+    jl          3f                      # handle 3 args
+    je          4f                      # handle 4 args
+5:
+    andl        $15, rINST             # rINSTw<- A
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, rINST, 4), %ecx   # %ecx<- vA
+    movl        %ecx, (%edx)            # *outs<- vA
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+4:
+    shr         $12, %ecx              # %ecx<- G
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vG
+    movl        %ecx, (%edx)            # *outs<- vG
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+3:
+    and         $0x0f00, %ecx          # %ecx<- 0F00
+    shr         $8, %ecx               # %ecx<- F
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vF
+    movl        %ecx, (%edx)            # *outs<- vF
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+2:
+    and         $0x00f0, %ecx          # %ecx<- 00E0
+    shr         $4, %ecx               # %ecx<- E
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vE
+    movl        %ecx, (%edx)            # *outs<- vE
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+1:
+    and         $0x000f, %ecx          # %ecx<- 000D
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vD
+    movl        %ecx, -4(%edx)          # *--outs<- vD
+0:
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * find space for the new stack frame, check for overflow
+    */
+
+.LinvokeArgsDone:
+    movzwl      offMethod_registersSize(%eax), %edx # %edx<- methodToCall->regsSize
+    movzwl      offMethod_outsSize(%eax), %ecx # %ecx<- methodToCall->outsSize
+    movl        %eax, LOCAL0_OFFSET(%ebp)       # LOCAL0_OFFSET<- methodToCall
+    shl         $2, %edx               # %edx<- update offset
+    SAVEAREA_FROM_FP %eax               # %eax<- &StackSaveArea
+    subl        %edx, %eax              # %eax<- newFP; (old savearea - regsSize)
+    movl        rSELF,%edx              # %edx<- pthread
+    movl        %eax, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- &outs
+    subl        $sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+    movl        offThread_interpStackEnd(%edx), %edx # %edx<- self->interpStackEnd
+    movl        %edx, TMP_SPILL1(%ebp)  # spill self->interpStackEnd
+    shl         $2, %ecx               # %ecx<- update offset for outsSize
+    movl        %eax, %edx              # %edx<- newSaveArea
+    sub         %ecx, %eax              # %eax<- bottom; (newSaveArea - outsSize)
+    cmp         TMP_SPILL1(%ebp), %eax  # compare interpStackEnd and bottom
+    movl        LOCAL0_OFFSET(%ebp), %eax       # %eax<- restore methodToCall
+    jl          .LstackOverflow         # handle frame overflow
+
+   /*
+    * set up newSaveArea
+    */
+
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP %ecx               # %ecx<- &StackSaveArea
+    movl        %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rSELF,%ecx              # %ecx<- pthread
+    movl        rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+    movl        rPC, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+#if defined(WITH_JIT)
+    movl        $0, offStackSaveArea_returnAddr(%edx)
+#endif
+
+    /* Any special actions to take? */
+    cmpw        $0, offThread_subMode(%ecx)
+    jne         2f                     # Yes - handle them
+1:
+    testl       $ACC_NATIVE, offMethod_accessFlags(%eax) # check for native call
+    movl        %eax, offStackSaveArea_method(%edx) # newSaveArea->method<- method to call
+    jne         .LinvokeNative          # handle native call
+
+   /*
+    * Update "self" values for the new method
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+    */
+    movl        offMethod_clazz(%eax), %edx # %edx<- method->clazz
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %eax, offThread_method(%ecx) # self->method<- methodToCall
+    movl        %edx, offThread_methodClassDex(%ecx) # self->methodClassDex<- method->clazz->pDvmDex
+    movl        offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+    movl        $1, offThread_debugIsMethodEntry(%ecx)
+    movl        LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+    movl        rFP, offThread_curFrame(%ecx) # curFrame<-newFP
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    /* rPC is already updated */
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT                           # jump to methodToCall->insns
+
+2:
+    /*
+     * On entry, preserve all:
+     *  %eax: method
+     *  %ecx: self
+     *  %edx: new save area
+     */
+    SPILL_TMP1(%eax)                   # preserve methodToCall
+    SPILL_TMP2(%edx)                   # preserve newSaveArea
+    movl        rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl        %ecx, OUT_ARG0(%esp)
+    movl        %eax, OUT_ARG1(%esp)
+    call        dvmReportInvoke        # (self, method)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%edx)
+    movl        rSELF,%ecx             # restore rSELF
+    jmp         1b
+
+   /*
+    * Prep for the native call
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea, %ecx=self
+    */
+
+.LinvokeNative:
+    movl        offThread_jniLocal_topCookie(%ecx), rINST # rINST<- self->localRef->...
+    movl        rINST, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+    movl        %edx, LOCAL2_OFFSET(%ebp)  # save newSaveArea
+    movl        LOCAL1_OFFSET(%ebp), rINST # rINST<- newFP
+    movl        rINST, offThread_curFrame(%ecx)  # curFrame<- newFP
+    cmpw        $0, offThread_subMode(%ecx)  # Anything special going on?
+    jne         11f                     # yes - handle it
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)    # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+7:
+    movl        LOCAL2_OFFSET(%ebp), %ecx    # %ecx<- newSaveArea
+    movl        rSELF, %eax             # %eax<- self
+    movl        offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+    cmp         $0, offThread_exception(%eax) # check for exception
+    movl        rFP, offThread_curFrame(%eax) # curFrame<- rFP
+    movl        %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+    jne         common_exceptionThrown  # handle exception
+    movl        offThread_curHandlerTable(%eax),rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx                    # jump to next instruction
+
+11:
+    /*
+     * Handle any special subMode actions
+     * %eax=methodToCall, rINST=newFP, %ecx=self
+     */
+    SPILL_TMP1(%eax)                    # save methodTocall
+    movl        rPC, offThread_pc(%ecx)
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPreNativeInvoke # (methodToCall, self, fp)
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF,%ecx              # restore self
+
+    /* Do the native call */
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)   # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPostNativeInvoke # (methodToCall, self, fp)
+    jmp         7b                      # rejoin
+
+.LstackOverflow:    # eax=methodToCall
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    movl        rSELF,%eax              # %eax<- self
+    movl        %eax, OUT_ARG0(%esp)    # push parameter self
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method* meth)
+    jmp         common_exceptionThrown  # handle exception
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+    movl    rSELF, %ecx
+    SAVEAREA_FROM_FP %eax                       # %eax<- saveArea(old)
+    cmpw    $0, offThread_subMode(%ecx)          # special action needed?
+    jne     19f                                   # go if so
+14:
+
+    movl        offStackSaveArea_prevFrame(%eax), rFP # rFP<- saveArea->PrevFrame
+    movl        (offStackSaveArea_method - sizeofStackSaveArea)(rFP), rINST # rINST<- method we are returning to
+    cmpl        $0, rINST               # check for break frame
+    je          common_gotoBail         # bail if break frame
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    movl        offStackSaveArea_savedPc(%eax), rPC # rPC<- saveAreaOld->savedPc
+#if defined(WITH_JIT)
+    movl        offStackSaveArea_returnAddr(%eax), %ecx
+#endif
+    movl        rSELF, %eax
+    movl        rINST, offThread_method(%eax) # glue->method<- newSave->method
+    movl        offMethod_clazz(rINST), rINST # rINST<- method->clazz
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+#if defined(WITH_JIT)
+    //update self->offThread_inJitCodeCache
+    movl        %ecx, offThread_inJitCodeCache(%eax)
+#endif
+    movl        offClassObject_pDvmDex(rINST), rINST # rINST<- method->clazz->pDvmDex
+    movl        rINST, offThread_methodClassDex(%eax) # glue->pDvmDex<- method->clazz->pDvmDex
+#if defined(WITH_JIT)
+    cmp         $0, %ecx
+    je          .returnToBC
+    movl        %ecx, %eax
+    jmp         *%eax
+#endif
+
+.returnToBC:
+
+#if defined(WITH_JIT)
+    FETCH_INST_OPCODE  3, %ecx                 # %eax<- next instruction hi; fetch, advance
+    // %ecx has the opcode
+    addl         $6, rPC               # 3*2 = 6
+    SPILL_TMP1   (%ecx)
+    movl         rSELF, %ecx
+    FETCH_INST
+    UNSPILL_TMP1   (%ecx)
+    movzbl      1(rPC), rINST
+    jmp     *(rIBASE,%ecx,4)
+#else
+    FETCH_INST_WORD 3
+    ADVANCE_PC 3
+    GOTO_NEXT
+#endif
+
+19:
+    /*
+     * Handle special subMode actions
+     * On entry, rFP: prevFP, %ecx: self, %eax: saveArea
+     */
+    SPILL_TMP1(%ebx)
+    movl     offStackSaveArea_prevFrame(%eax), %ebx # %ebx<- saveArea->PrevFrame
+    movl     rPC, offThread_pc(%ecx)          # update interpSave.pc
+    movl     %ebx, offThread_curFrame(%ecx)    # update interpSave.curFrame
+    movl     %ecx, OUT_ARG0(%esp)             # parameter self
+    call     dvmReportReturn                  # (self)
+    UNSPILL_TMP1(%ebx)
+    movl     rSELF, %ecx                      # restore self
+    SAVEAREA_FROM_FP %eax                     # restore saveArea
+    jmp      14b
+
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ *    rINST holds changeInterp
+ *    ecx holds self pointer
+ *
+ * expected profile: dvmMterpStdBail(Thread *self, bool changeInterp)
+ */
+common_gotoBail:
+    movl   rPC,offThread_pc(%ecx)     # export state to self
+    movl   rFP,offThread_curFrame(%ecx)
+    movl   %ecx,OUT_ARG0(%esp)      # self in arg0
+    movl   rINST,OUT_ARG1(%esp)     # changeInterp in arg1
+    call   dvmMterpStdBail          # bail out....
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ *
+ * eax = Method* methodToCall
+ * ecx = "this"
+ * edx = rSELF
+ * ebx = free to use
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     $0, %ecx
+    je      2f
+    movl    offObject_clazz(%ecx), %ecx
+2:
+    movl    rSELF, %ebx
+    movl    %eax, offThread_methodToCall(%ebx)
+    movl    %ecx, offThread_callsiteClass(%ebx)
+    ret
+#endif
+
+#if defined(WITH_JIT)
+
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     %ecx: &dvmDex->pResFields[field]
+     *     %eax:  field pointer (must preserve)
+     */
+common_verifyField:
+    movl    %ebx, TMP_SPILL1(%ebp)
+    movl     rSELF, %ebx
+    movzwl   offThread_subMode(%ebx), %ebx
+    andl     $kSubModeJitTraceBuild, %ebx
+    movl    TMP_SPILL1(%ebp), %ebx
+    jne      1f
+    ret
+1:
+    movl    (%ecx), %ecx
+    cmp     $0, %ecx
+    je      1f
+    ret
+1:
+    SPILL_TMP1(%eax)
+    SPILL_TMP2(%edx)
+    movl     rSELF, %ecx
+    # Because we call into this helper from a bytecode, we have
+    # to be careful not to write over the return address when using
+    # the OUT_ARG macros
+    lea      -8(%esp), %esp
+    movl     %ecx, OUT_ARG0(%esp)
+    movl     rPC, OUT_ARG1(%esp)
+    call     dvmJitEndTraceSelect
+    lea      8(%esp), %esp
+    UNSPILL_TMP2(%edx)
+    UNSPILL_TMP1(%eax)
+    ret
+#endif
+
+/*
+ * After returning from a "selfd" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+     movl  rSELF, %eax
+     movl  offThread_pc(%eax),rPC
+     movl  offThread_curFrame(%eax),rFP
+     movl  offThread_curHandlerTable(%eax),rIBASE
+     FETCH_INST
+     GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+    EXPORT_PC
+    movl    $.LstrDivideByZero,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowArithmeticException
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, len in eax
+ */
+common_errNegativeArraySize:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)                  # arg0<- len
+    call    dvmThrowNegativeArraySizeException   # (len)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, method name in eax
+ */
+common_errNoSuchMethod:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNoSuchMethodError
+    jmp     common_exceptionThrown
+
+/*
+ * Hit a null object when we weren't expecting one.  Export the PC, throw a
+ * NullPointerException and goto the exception processing code.
+ */
+common_errNullObject:
+    EXPORT_PC
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNullPointerException
+    jmp     common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ * On entry:
+ *    eax <- array object
+ *    ecx <- index
+ */
+common_errArrayIndex:
+    EXPORT_PC
+    movl    offArrayObject_length(%eax), %eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmThrowArrayIndexOutOfBoundsException   # args (length, index)
+    jmp     common_exceptionThrown
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * NOTE: special subMode handling done in dvmMterp_exceptionThrown
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC
+    movl       rSELF, %ecx
+    movl       %ecx, OUT_ARG0(%esp)
+    call       dvmCheckSuspendPending
+
+    movl       rSELF, %ecx
+    movl       offThread_exception(%ecx), %edx   # %edx <- self->exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmAddTrackedAlloc      # don't let the exception be GCed
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+    movl       offThread_subMode(%ecx), %eax    # get subMode flags
+    movl       $0, offThread_exception(%ecx)
+
+    # Special subMode?
+    cmpl       $0, %eax                # any special subMode handling needed?
+    je         8f                      # go if so
+
+    # Manage debugger bookkeeping
+    movl       rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl       rFP, offThread_curFrame(%ecx) # update interpSave.curFrame
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmReportExceptionThrow # (self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+
+8:
+    /*
+    * set up args and a local for &fp
+    */
+    lea        20(%esp), %esp          # raise %esp
+    movl       rFP, (%esp)               # save fp
+    movl       %esp, %eax              # %eax = &fp
+    lea        -20(%esp), %esp         # reset %esp
+    movl       %eax, OUT_ARG4(%esp)    # Arg 4 = &fp
+    movl       $0, OUT_ARG3(%esp)      # Arg 3 = false
+    movl       %edx, OUT_ARG2(%esp)    # Arg 2 = exception
+    movl       %ecx, OUT_ARG0(%esp)    # Arg 0 = self
+
+    movl       offThread_method(%ecx), %eax # %eax = self->method
+    movl       offMethod_insns(%eax), %eax  # %eax = self->method->insn
+    movl       rPC, %ecx
+    subl       %eax, %ecx              # %ecx = pc - self->method->insn
+    sar        $1, %ecx                # adjust %ecx for code offset
+    movl       %ecx, OUT_ARG1(%esp)    # Arg 1 = %ecx
+
+    /* call, %eax gets catchRelPc (a code-unit offset) */
+    SPILL_TMP1(%edx)                   # save exception
+    call       dvmFindCatchBlock       # call(self, relPc, exc, scan?, &fp)
+    UNSPILL_TMP1(%edx)                 # restore exception
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    movl       rSELF, %ecx
+    cmpl       $0, offThread_stackOverflowed(%ecx) # did we overflow?
+    je         1f                         # no, skip ahead
+    movl       %eax, rFP                  # save relPc result in rFP
+    movl       %ecx, OUT_ARG0(%esp)       # Arg 0 = self
+    movl       %edx, OUT_ARG1(%esp)       # Arg 1 = exception
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow    # call(self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rFP, %eax                  # restore result
+    movl       rSELF, %ecx
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    movl       20(%esp), rFP              # retrieve the updated rFP
+    cmpl       $0, %eax                  # is catchRelPc < 0?
+    jl         .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP rINST             # rINST<- new save area
+    movl       offStackSaveArea_method(rINST), rINST # rINST<- new method
+    movl       rINST, offThread_method(%ecx)         # self->method = new method
+    movl       offMethod_clazz(rINST), %ecx          # %ecx = method->clazz
+    movl       offMethod_insns(rINST), rINST         # rINST = method->insn
+    movl       offClassObject_pDvmDex(%ecx), %ecx    # %ecx = method->clazz->pDvmDex
+    lea        (rINST, %eax, 2), rPC      # rPC<- method->insns + catchRelPc
+    movl       rSELF, rINST
+    movl       %ecx, offThread_methodClassDex(rINST) # self->pDvmDex = method->clazz->pDvmDex
+
+    /* release the tracked alloc on the exception */
+    movl       %edx, OUT_ARG0(%esp)       # Arg 0 = exception
+    movl       rINST, OUT_ARG1(%esp)      # Arg 1 = self
+    SPILL_TMP1(%edx)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    UNSPILL_TMP1(%edx)
+
+    /* restore the exception if the handler wants it */
+    movl       rSELF, %ecx
+    FETCH_INST
+    movzbl     rINSTbl, %eax
+    cmpl       $OP_MOVE_EXCEPTION, %eax   # is it "move-exception"?
+    jne        1f
+    movl       %edx, offThread_exception(%ecx) # restore exception
+1:
+    movl       offThread_curHandlerTable(%ecx), rIBASE # refresh rIBASE
+    GOTO_NEXT
+
+.LnotCaughtLocally: # %edx = exception
+    /* fix stack overflow if necessary */
+    movl       rSELF, %ecx
+    movl       offThread_stackOverflowed(%ecx), %eax
+    cmpl       $0, %eax                   # did we overflow earlier?
+    je         1f
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow
+    UNSPILL_TMP1(%edx)
+
+1:
+    movl       rSELF, %ecx
+    movl       %edx, offThread_exception(%ecx) #restore exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    movl       rSELF, %ecx
+    jmp        common_gotoBail            # bail out
+
+common_abort:
+    movl    $0xdeadf00d,%eax
+    call     *%eax
+
+
+/*
+ * Strings
+ */
+
+    .section     .rodata
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImplA:
+    .asciz  "filled-new-array only implemented for 'int'"
+
diff --git a/vm/mterp/out/InterpC-allstubs.cpp b/vm/mterp/out/InterpC-allstubs.cpp
new file mode 100644
index 0000000..1ef8783
--- /dev/null
+++ b/vm/mterp/out/InterpC-allstubs.cpp
@@ -0,0 +1,4069 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'allstubs'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_NOP.cpp */
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+    /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+     * "move-wide v6, v7" and "move-wide v7, v6" */
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move-wide/from16 v%d,v%d  (v%d=0x%08llx)", vdst, vsrc1,
+        vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_OBJECT.cpp */
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_FROM16.cpp */
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_16.cpp */
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+    SET_REGISTER_WIDE(vdst, retval.j);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_OBJECT.cpp */
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_OBJECT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_EXCEPTION.cpp */
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-exception v%d", vdst);
+    assert(self->exception != NULL);
+    SET_REGISTER(vdst, (u4)self->exception);
+    dvmClearException(self);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_RETURN_VOID.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_WIDE.cpp */
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return-wide v%d", vsrc1);
+    retval.j = GET_REGISTER_WIDE(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_OBJECT.cpp */
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN_OBJECT /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+
+/* File: c/OP_CONST_4.cpp */
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+    {
+        s4 tmp;
+
+        vdst = INST_A(inst);
+        tmp = (s4) (INST_B(inst) << 28) >> 28;  // sign extend 4-bit value
+        ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CONST_16.cpp */
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER(vdst, (s2) vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST.cpp */
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+    SET_REGISTER(vdst, vsrc1 << 16);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_32.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, (s4) tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_WIDE.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+    {
+        u8 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u8)FETCH(2) << 16;
+        tmp |= (u8)FETCH(3) << 32;
+        tmp |= (u8)FETCH(4) << 48;
+        ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, tmp);
+    }
+    FINISH(5);
+OP_END
+
+/* File: c/OP_CONST_WIDE_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+    SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING.cpp */
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+    {
+        StringObject* strObj;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+        strObj = dvmDexGetResolvedString(methodClassDex, ref);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, ref);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING_JUMBO.cpp */
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+    {
+        StringObject* strObj;
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+        strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, tmp);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_CLASS.cpp */
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            EXPORT_PC();
+            clazz = dvmResolveClass(curMethod->clazz, ref, true);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) clazz);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MONITOR_ENTER.cpp */
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+    {
+        Object* obj;
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-enter v%d %s(0x%08x)",
+            vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+        ILOGV("+ locking %p %s", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC */
+        dvmLockObject(self, obj);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.cpp */
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+    {
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-exit v%d %s(0x%08x)",
+            vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /*
+             * The exception needs to be processed at the *following*
+             * instruction, not the current instruction (see the Dalvik
+             * spec).  Because we're jumping to an exception handler,
+             * we're not actually at risk of skipping an instruction
+             * by doing so.
+             */
+            ADJUST_PC(1);           /* monitor-exit width is 1 */
+            GOTO_exceptionThrown();
+        }
+        ILOGV("+ unlocking %p %s", obj, obj->clazz->descriptor);
+        if (!dvmUnlockObject(self, obj)) {
+            assert(dvmCheckException(self));
+            ADJUST_PC(1);
+            GOTO_exceptionThrown();
+        }
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CHECK_CAST.cpp */
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                clazz = dvmResolveClass(curMethod->clazz, ref, false);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            if (!dvmInstanceof(obj->clazz, clazz)) {
+                dvmThrowClassCastException(obj->clazz, clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.cpp */
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);   /* object to check */
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj == NULL) {
+            SET_REGISTER(vdst, 0);
+        } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNullExportPC(obj, fp, pc))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                EXPORT_PC();
+                clazz = dvmResolveClass(curMethod->clazz, ref, true);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ARRAY_LENGTH.cpp */
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+    {
+        ArrayObject* arrayObj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        ILOGV("|array-length v%d,v%d  (%p)", vdst, vsrc1, arrayObj);
+        if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+            GOTO_exceptionThrown();
+        /* verifier guarantees this is an array reference */
+        SET_REGISTER(vdst, arrayObj->length);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_NEW_INSTANCE.cpp */
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* newObj;
+
+        EXPORT_PC();
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            clazz = dvmResolveClass(curMethod->clazz, ref, false);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+            GOTO_exceptionThrown();
+
+#if defined(WITH_JIT)
+        /*
+         * The JIT needs dvmDexGetResolvedClass() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (!dvmDexGetResolvedClass(methodClassDex, ref))) {
+            /* Class initialization is still ongoing - end the trace */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage(gDvm.exInstantiationError,
+        //        clazz->descriptor);
+        //    GOTO_exceptionThrown();
+        //}
+        newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        if (newObj == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        s4 length;
+
+        EXPORT_PC();
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);       /* length reg */
+        ref = FETCH(1);
+        ILOGV("|new-array v%d,v%d,class@0x%04x  (%d elements)",
+            vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+        length = (s4) GET_REGISTER(vsrc1);
+        if (length < 0) {
+            dvmThrowNegativeArraySizeException(length);
+            GOTO_exceptionThrown();
+        }
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newArray);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+    GOTO_invoke(filledNewArray, false);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY_RANGE.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+    GOTO_invoke(filledNewArray, true);
+OP_END
+
+/* File: c/OP_FILL_ARRAY_DATA.cpp */
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA)   /*vAA, +BBBBBBBB*/
+    {
+        const u2* arrayData;
+        s4 offset;
+        ArrayObject* arrayObj;
+
+        EXPORT_PC();
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+        arrayData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (arrayData < curMethod->insns ||
+            arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            dvmThrowInternalError("bad fill array data");
+            GOTO_exceptionThrown();
+        }
+#endif
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+            GOTO_exceptionThrown();
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_THROW.cpp */
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+    {
+        Object* obj;
+
+        /*
+         * We don't create an exception here, but the process of searching
+         * for a catch block can do class lookups and throw exceptions.
+         * We need to update the saved PC.
+         */
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|throw v%d  (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+        obj = (Object*) GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /* will throw a null pointer exception */
+            LOGVV("Bad exception");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
+
+/* File: c/OP_GOTO.cpp */
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+    vdst = INST_AA(inst);
+    if ((s1)vdst < 0)
+        ILOGV("|goto -0x%02x", -((s1)vdst));
+    else
+        ILOGV("|goto +0x%02x", ((s1)vdst));
+    ILOGV("> branch taken");
+    if ((s1)vdst < 0)
+        PERIODIC_CHECKS((s1)vdst);
+    FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.cpp */
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+    {
+        s4 offset = (s2) FETCH(1);          /* sign-extend next code unit */
+
+        if (offset < 0)
+            ILOGV("|goto/16 -0x%04x", -offset);
+        else
+            ILOGV("|goto/16 +0x%04x", offset);
+        ILOGV("> branch taken");
+        if (offset < 0)
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_GOTO_32.cpp */
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+    {
+        s4 offset = FETCH(1);               /* low-order 16 bits */
+        offset |= ((s4) FETCH(2)) << 16;    /* high-order 16 bits */
+
+        if (offset < 0)
+            ILOGV("|goto/32 -0x%08x", -offset);
+        else
+            ILOGV("|goto/32 +0x%08x", offset);
+        ILOGV("> branch taken");
+        if (offset <= 0)    /* allowed to branch to self */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.cpp */
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|packed-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.cpp */
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|sparse-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.cpp */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.cpp */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.cpp */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.cpp */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.cpp */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.cpp */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.cpp */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.cpp */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.cpp */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.cpp */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.cpp */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.cpp */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.cpp */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.cpp */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.cpp */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.cpp */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.cpp */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.cpp */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.cpp */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.cpp */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.cpp */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.cpp */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.cpp */
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+    {
+        ArrayObject* arrayObj;
+        Object* obj;
+        u2 arrayInfo;
+        EXPORT_PC();
+        vdst = INST_AA(inst);       /* AA: source value */
+        arrayInfo = FETCH(1);
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */
+        vsrc2 = arrayInfo >> 8;     /* CC: index */
+        ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!checkForNull((Object*) arrayObj))
+            GOTO_exceptionThrown();
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+            dvmThrowArrayIndexOutOfBoundsException(
+                arrayObj->length, GET_REGISTER(vsrc2));
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+                ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->clazz->descriptor, arrayObj);
+                dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+        dvmSetObjectArrayElement(arrayObj,
+                                 GET_REGISTER(vsrc2),
+                                 (Object *)GET_REGISTER(vdst));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_APUT_BOOLEAN.cpp */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.cpp */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.cpp */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.cpp */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.cpp */
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.cpp */
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.cpp */
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.cpp */
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.cpp */
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.cpp */
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.cpp */
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible.  In practice, many popular VMs don't
+ * do this because it slows down a very common operation.  It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_BOOLEAN.cpp */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.cpp */
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.cpp */
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.cpp */
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SGET.cpp */
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.cpp */
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.cpp */
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.cpp */
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.cpp */
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.cpp */
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.cpp */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.cpp */
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.cpp */
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.cpp */
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.cpp */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtual, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuper, true);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeDirect, true);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeStatic, true);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.cpp */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.cpp */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.cpp */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.cpp */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.cpp */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.cpp */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.cpp */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.cpp */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.cpp */
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.cpp */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.cpp */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.cpp */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.cpp */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.cpp */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.cpp */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.cpp */
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.cpp */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.cpp */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.cpp */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.cpp */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.cpp */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.cpp */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.cpp */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_FLOAT(vdst,
+            fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_DOUBLE(vdst,
+            fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_FLOAT(vdst,
+        fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_DOUBLE(vdst,
+        fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.cpp */
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+    {
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        vsrc2 = FETCH(1);
+        ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.cpp */
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+    {
+        u2 litInfo;
+        vdst = INST_AA(inst);
+        litInfo = FETCH(1);
+        vsrc1 = litInfo & 0xff;
+        vsrc2 = litInfo >> 8;
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.cpp */
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.cpp */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+    EXPORT_PC();
+    vsrc1 = INST_AA(inst);
+    ref = FETCH(1);             /* class/field/method ref */
+    dvmThrowVerificationError(curMethod, vsrc1, ref);
+    GOTO_exceptionThrown();
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+    {
+        /*
+         * This has the same form as other method calls, but we ignore
+         * the 5th argument (vA).  This is chiefly because the first four
+         * arguments to a function on ARM are in registers.
+         *
+         * We only set the arguments that are actually used, leaving
+         * the rest uninitialized.  We're assuming that, if the method
+         * needs them, they'll be specified in the call.
+         *
+         * However, this annoys gcc when optimizations are enabled,
+         * causing a "may be used uninitialized" warning.  Quieting
+         * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+         * on empty method).  Note that valgrind is perfectly happy
+         * either way as the uninitialiezd values are never actually
+         * used.
+         */
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_B(inst);       /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* 0-4 register indices */
+        ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+            vsrc1, ref, vdst);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst >> 12);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst & 0x0f);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_OBJECT_INIT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_RETURN_VOID_BARRIER.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_IGET_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtualQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtualQuick, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuperQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuperQuick, true);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.cpp */
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    ALOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
+
+/* File: cstubs/entry.cpp */
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) (const void*) dvmMterp_##_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlers)
+
+#undef H
+#define H(_op) #_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlerNames)
+
+#include <setjmp.h>
+
+/*
+ * C mterp entry point.  This just calls the various C fallbacks, making
+ * this a slow but portable interpeter.
+ *
+ * This is only used for the "allstubs" variant.
+ */
+void dvmMterpStdRun(Thread* self)
+{
+    jmp_buf jmpBuf;
+
+    self->interpSave.bailPtr = &jmpBuf;
+
+    /* We exit via a longjmp */
+    if (setjmp(jmpBuf)) {
+        LOGVV("mterp threadid=%d returning", dvmThreadSelf()->threadId);
+        return;
+    }
+
+    /* run until somebody longjmp()s out */
+    while (true) {
+        typedef void (*Handler)(Thread* self);
+
+        u2 inst = /*self->interpSave.*/pc[0];
+        /*
+         * In mterp, dvmCheckBefore is handled via the altHandlerTable,
+         * while in the portable interpreter it is part of the handler
+         * FINISH code.  For allstubs, we must do an explicit check
+         * in the interpretation loop.
+         */
+        if (self->interpBreak.ctl.subMode) {
+            dvmCheckBefore(pc, fp, self);
+        }
+        Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+        (void) gDvmMterpHandlerNames;   /* avoid gcc "defined but not used" */
+        LOGVV("handler %p %s",
+            handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+        (*handler)(self);
+    }
+}
+
+/*
+ * C mterp exit point.  Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(Thread* self)
+{
+    jmp_buf* pJmpBuf = (jmp_buf*) self->interpSave.bailPtr;
+    longjmp(*pJmpBuf, 1);
+}
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/out/InterpC-armv5te-vfp.cpp b/vm/mterp/out/InterpC-armv5te-vfp.cpp
new file mode 100644
index 0000000..9d6b458
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te-vfp.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv5te.cpp b/vm/mterp/out/InterpC-armv5te.cpp
new file mode 100644
index 0000000..99831b1
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv7-a-neon.cpp b/vm/mterp/out/InterpC-armv7-a-neon.cpp
new file mode 100644
index 0000000..7a3434c
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a-neon.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv7-a.cpp b/vm/mterp/out/InterpC-armv7-a.cpp
new file mode 100644
index 0000000..1dcfb68
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-mips.cpp b/vm/mterp/out/InterpC-mips.cpp
new file mode 100644
index 0000000..69260da
--- /dev/null
+++ b/vm/mterp/out/InterpC-mips.cpp
@@ -0,0 +1,2268 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'mips'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_BREAKPOINT.cpp */
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: mips/debug.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose MIPS registers, along with some other info.
+ *
+ */
+void dvmMterpDumpMipsRegs(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3)
+{
+    register uint32_t rPC       asm("s0");
+    register uint32_t rFP       asm("s1");
+    register uint32_t rSELF     asm("s2");
+    register uint32_t rIBASE    asm("s3");
+    register uint32_t rINST     asm("s4");
+    register uint32_t rOBJ      asm("s5");
+    register uint32_t rBIX      asm("s6");
+    register uint32_t rTEMP	asm("s7");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: a0=%08x a1=%08x a2=%08x a3=%08x\n", a0, a1, a2, a3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rIBASE=%08x\n",
+        rPC, rFP, rSELF, rIBASE);
+    printf("    : rINST=%08x rOBJ=%08x rBIX=%08x rTEMP=%08x \n", rINST, rOBJ, rBIX, rTEMP);
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->signature);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-portable.cpp b/vm/mterp/out/InterpC-portable.cpp
new file mode 100644
index 0000000..0328aa8
--- /dev/null
+++ b/vm/mterp/out/InterpC-portable.cpp
@@ -0,0 +1,4015 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'portable'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: portable/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)
+
+#define GOTO_TARGET(_target, ...) _target:
+
+#define GOTO_TARGET_END
+
+/* ugh */
+#define STUB_HACK(x)
+#define JIT_STUB_HACK(x)
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()                                                    \
+    self->interpSave.pc = pc;                                              \
+    self->interpSave.curFrame = fp;
+#define PC_TO_SELF() self->interpSave.pc = pc;
+
+/*
+ * Instruction framing.  For a switch-oriented implementation this is
+ * case/break, for a threaded implementation it's a goto label and an
+ * instruction fetch/computed goto.
+ *
+ * Assumes the existence of "const u2* pc" and (for threaded operation)
+ * "u2 inst".
+ */
+# define H(_op)             &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) {                                                  \
+        ADJUST_PC(_offset);                                                 \
+        inst = FETCH(0);                                                    \
+        if (self->interpBreak.ctl.subMode) {                                \
+            dvmCheckBefore(pc, fp, self);                                   \
+        }                                                                   \
+        goto *handlerTable[INST_INST(inst)];                                \
+    }
+# define FINISH_BKPT(_opcode) {                                             \
+        goto *handlerTable[_opcode];                                        \
+    }
+
+#define OP_END
+
+/*
+ * The "goto" targets just turn into goto statements.  The "arguments" are
+ * passed through local variables.
+ */
+
+#define GOTO_exceptionThrown() goto exceptionThrown;
+
+#define GOTO_returnFromMethod() goto returnFromMethod;
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        methodCallRange = _methodCallRange;                                 \
+        goto _target;                                                       \
+    } while(false)
+
+/* for this, the "args" are already in the locals */
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) goto invokeMethod;
+
+#define GOTO_bail() goto bail;
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.  If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: portable/entry.cpp */
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+void dvmInterpretPortable(Thread* self)
+{
+#if defined(EASY_GDB)
+    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+#endif
+    DvmDex* methodClassDex;     // curMethod->clazz->pDvmDex
+    JValue retval;
+
+    /* core state */
+    const Method* curMethod;    // method we're interpreting
+    const u2* pc;               // program counter
+    u4* fp;                     // frame pointer
+    u2 inst;                    // current instruction
+    /* instruction decoding */
+    u4 ref;                     // 16 or 32-bit quantity fetched directly
+    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
+    /* method call setup */
+    const Method* methodToCall;
+    bool methodCallRange;
+
+    /* static computed goto table */
+    DEFINE_GOTO_TABLE(handlerTable);
+
+    /* copy state in */
+    curMethod = self->interpSave.method;
+    pc = self->interpSave.pc;
+    fp = self->interpSave.curFrame;
+    retval = self->interpSave.retval;   /* only need for kInterpEntryReturn? */
+
+    methodClassDex = curMethod->clazz->pDvmDex;
+
+    LOGVV("threadid=%d: %s.%s pc=%#x fp=%p",
+        self->threadId, curMethod->clazz->descriptor, curMethod->name,
+        pc - curMethod->insns, fp);
+
+    /*
+     * Handle any ongoing profiling and prep for debugging.
+     */
+    if (self->interpBreak.ctl.subMode != 0) {
+        TRACE_METHOD_ENTER(self, curMethod);
+        self->debugIsMethodEntry = true;   // Always true on startup
+    }
+    /*
+     * DEBUG: scramble this to ensure we're not relying on it.
+     */
+    methodToCall = (const Method*) -1;
+
+#if 0
+    if (self->debugIsMethodEntry) {
+        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+                curMethod->name);
+        DUMP_REGS(curMethod, self->interpSave.curFrame, false);
+    }
+#endif
+
+    FINISH(0);                  /* fetch and execute first instruction */
+
+/*--- start of opcodes ---*/
+
+/* File: c/OP_NOP.cpp */
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+    /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+     * "move-wide v6, v7" and "move-wide v7, v6" */
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move-wide/from16 v%d,v%d  (v%d=0x%08llx)", vdst, vsrc1,
+        vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_OBJECT.cpp */
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_FROM16.cpp */
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_16.cpp */
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+    SET_REGISTER_WIDE(vdst, retval.j);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_OBJECT.cpp */
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_OBJECT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_EXCEPTION.cpp */
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-exception v%d", vdst);
+    assert(self->exception != NULL);
+    SET_REGISTER(vdst, (u4)self->exception);
+    dvmClearException(self);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_RETURN_VOID.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_WIDE.cpp */
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return-wide v%d", vsrc1);
+    retval.j = GET_REGISTER_WIDE(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_OBJECT.cpp */
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN_OBJECT /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+
+/* File: c/OP_CONST_4.cpp */
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+    {
+        s4 tmp;
+
+        vdst = INST_A(inst);
+        tmp = (s4) (INST_B(inst) << 28) >> 28;  // sign extend 4-bit value
+        ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CONST_16.cpp */
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER(vdst, (s2) vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST.cpp */
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+    SET_REGISTER(vdst, vsrc1 << 16);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_32.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, (s4) tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_WIDE.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+    {
+        u8 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u8)FETCH(2) << 16;
+        tmp |= (u8)FETCH(3) << 32;
+        tmp |= (u8)FETCH(4) << 48;
+        ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, tmp);
+    }
+    FINISH(5);
+OP_END
+
+/* File: c/OP_CONST_WIDE_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+    SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING.cpp */
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+    {
+        StringObject* strObj;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+        strObj = dvmDexGetResolvedString(methodClassDex, ref);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, ref);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING_JUMBO.cpp */
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+    {
+        StringObject* strObj;
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+        strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, tmp);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_CLASS.cpp */
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            EXPORT_PC();
+            clazz = dvmResolveClass(curMethod->clazz, ref, true);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) clazz);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MONITOR_ENTER.cpp */
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+    {
+        Object* obj;
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-enter v%d %s(0x%08x)",
+            vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+        ILOGV("+ locking %p %s", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC */
+        dvmLockObject(self, obj);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.cpp */
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+    {
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-exit v%d %s(0x%08x)",
+            vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /*
+             * The exception needs to be processed at the *following*
+             * instruction, not the current instruction (see the Dalvik
+             * spec).  Because we're jumping to an exception handler,
+             * we're not actually at risk of skipping an instruction
+             * by doing so.
+             */
+            ADJUST_PC(1);           /* monitor-exit width is 1 */
+            GOTO_exceptionThrown();
+        }
+        ILOGV("+ unlocking %p %s", obj, obj->clazz->descriptor);
+        if (!dvmUnlockObject(self, obj)) {
+            assert(dvmCheckException(self));
+            ADJUST_PC(1);
+            GOTO_exceptionThrown();
+        }
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CHECK_CAST.cpp */
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                clazz = dvmResolveClass(curMethod->clazz, ref, false);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            if (!dvmInstanceof(obj->clazz, clazz)) {
+                dvmThrowClassCastException(obj->clazz, clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.cpp */
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);   /* object to check */
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj == NULL) {
+            SET_REGISTER(vdst, 0);
+        } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNullExportPC(obj, fp, pc))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                EXPORT_PC();
+                clazz = dvmResolveClass(curMethod->clazz, ref, true);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ARRAY_LENGTH.cpp */
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+    {
+        ArrayObject* arrayObj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        ILOGV("|array-length v%d,v%d  (%p)", vdst, vsrc1, arrayObj);
+        if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+            GOTO_exceptionThrown();
+        /* verifier guarantees this is an array reference */
+        SET_REGISTER(vdst, arrayObj->length);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_NEW_INSTANCE.cpp */
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* newObj;
+
+        EXPORT_PC();
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            clazz = dvmResolveClass(curMethod->clazz, ref, false);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+            GOTO_exceptionThrown();
+
+#if defined(WITH_JIT)
+        /*
+         * The JIT needs dvmDexGetResolvedClass() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (!dvmDexGetResolvedClass(methodClassDex, ref))) {
+            /* Class initialization is still ongoing - end the trace */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage(gDvm.exInstantiationError,
+        //        clazz->descriptor);
+        //    GOTO_exceptionThrown();
+        //}
+        newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        if (newObj == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        s4 length;
+
+        EXPORT_PC();
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);       /* length reg */
+        ref = FETCH(1);
+        ILOGV("|new-array v%d,v%d,class@0x%04x  (%d elements)",
+            vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+        length = (s4) GET_REGISTER(vsrc1);
+        if (length < 0) {
+            dvmThrowNegativeArraySizeException(length);
+            GOTO_exceptionThrown();
+        }
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newArray);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+    GOTO_invoke(filledNewArray, false);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY_RANGE.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+    GOTO_invoke(filledNewArray, true);
+OP_END
+
+/* File: c/OP_FILL_ARRAY_DATA.cpp */
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA)   /*vAA, +BBBBBBBB*/
+    {
+        const u2* arrayData;
+        s4 offset;
+        ArrayObject* arrayObj;
+
+        EXPORT_PC();
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+        arrayData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (arrayData < curMethod->insns ||
+            arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            dvmThrowInternalError("bad fill array data");
+            GOTO_exceptionThrown();
+        }
+#endif
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+            GOTO_exceptionThrown();
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_THROW.cpp */
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+    {
+        Object* obj;
+
+        /*
+         * We don't create an exception here, but the process of searching
+         * for a catch block can do class lookups and throw exceptions.
+         * We need to update the saved PC.
+         */
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|throw v%d  (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+        obj = (Object*) GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /* will throw a null pointer exception */
+            LOGVV("Bad exception");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
+
+/* File: c/OP_GOTO.cpp */
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+    vdst = INST_AA(inst);
+    if ((s1)vdst < 0)
+        ILOGV("|goto -0x%02x", -((s1)vdst));
+    else
+        ILOGV("|goto +0x%02x", ((s1)vdst));
+    ILOGV("> branch taken");
+    if ((s1)vdst < 0)
+        PERIODIC_CHECKS((s1)vdst);
+    FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.cpp */
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+    {
+        s4 offset = (s2) FETCH(1);          /* sign-extend next code unit */
+
+        if (offset < 0)
+            ILOGV("|goto/16 -0x%04x", -offset);
+        else
+            ILOGV("|goto/16 +0x%04x", offset);
+        ILOGV("> branch taken");
+        if (offset < 0)
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_GOTO_32.cpp */
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+    {
+        s4 offset = FETCH(1);               /* low-order 16 bits */
+        offset |= ((s4) FETCH(2)) << 16;    /* high-order 16 bits */
+
+        if (offset < 0)
+            ILOGV("|goto/32 -0x%08x", -offset);
+        else
+            ILOGV("|goto/32 +0x%08x", offset);
+        ILOGV("> branch taken");
+        if (offset <= 0)    /* allowed to branch to self */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.cpp */
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|packed-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.cpp */
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|sparse-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.cpp */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.cpp */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.cpp */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.cpp */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.cpp */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.cpp */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.cpp */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.cpp */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.cpp */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.cpp */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.cpp */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.cpp */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.cpp */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.cpp */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.cpp */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.cpp */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.cpp */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.cpp */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.cpp */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.cpp */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.cpp */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.cpp */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.cpp */
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+    {
+        ArrayObject* arrayObj;
+        Object* obj;
+        u2 arrayInfo;
+        EXPORT_PC();
+        vdst = INST_AA(inst);       /* AA: source value */
+        arrayInfo = FETCH(1);
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */
+        vsrc2 = arrayInfo >> 8;     /* CC: index */
+        ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!checkForNull((Object*) arrayObj))
+            GOTO_exceptionThrown();
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+            dvmThrowArrayIndexOutOfBoundsException(
+                arrayObj->length, GET_REGISTER(vsrc2));
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+                ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->clazz->descriptor, arrayObj);
+                dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+        dvmSetObjectArrayElement(arrayObj,
+                                 GET_REGISTER(vsrc2),
+                                 (Object *)GET_REGISTER(vdst));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_APUT_BOOLEAN.cpp */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.cpp */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.cpp */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.cpp */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.cpp */
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.cpp */
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.cpp */
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.cpp */
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.cpp */
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.cpp */
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.cpp */
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible.  In practice, many popular VMs don't
+ * do this because it slows down a very common operation.  It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_BOOLEAN.cpp */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.cpp */
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.cpp */
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.cpp */
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SGET.cpp */
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.cpp */
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.cpp */
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.cpp */
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.cpp */
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.cpp */
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.cpp */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.cpp */
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.cpp */
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.cpp */
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.cpp */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtual, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuper, true);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeDirect, true);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeStatic, true);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.cpp */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.cpp */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.cpp */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.cpp */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.cpp */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.cpp */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.cpp */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.cpp */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.cpp */
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.cpp */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.cpp */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.cpp */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.cpp */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.cpp */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.cpp */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.cpp */
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.cpp */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.cpp */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.cpp */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.cpp */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.cpp */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.cpp */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.cpp */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_FLOAT(vdst,
+            fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_DOUBLE(vdst,
+            fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_FLOAT(vdst,
+        fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_DOUBLE(vdst,
+        fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.cpp */
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+    {
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        vsrc2 = FETCH(1);
+        ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.cpp */
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+    {
+        u2 litInfo;
+        vdst = INST_AA(inst);
+        litInfo = FETCH(1);
+        vsrc1 = litInfo & 0xff;
+        vsrc2 = litInfo >> 8;
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.cpp */
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.cpp */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+    EXPORT_PC();
+    vsrc1 = INST_AA(inst);
+    ref = FETCH(1);             /* class/field/method ref */
+    dvmThrowVerificationError(curMethod, vsrc1, ref);
+    GOTO_exceptionThrown();
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+    {
+        /*
+         * This has the same form as other method calls, but we ignore
+         * the 5th argument (vA).  This is chiefly because the first four
+         * arguments to a function on ARM are in registers.
+         *
+         * We only set the arguments that are actually used, leaving
+         * the rest uninitialized.  We're assuming that, if the method
+         * needs them, they'll be specified in the call.
+         *
+         * However, this annoys gcc when optimizations are enabled,
+         * causing a "may be used uninitialized" warning.  Quieting
+         * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+         * on empty method).  Note that valgrind is perfectly happy
+         * either way as the uninitialiezd values are never actually
+         * used.
+         */
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_B(inst);       /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* 0-4 register indices */
+        ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+            vsrc1, ref, vdst);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst >> 12);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst & 0x0f);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_OBJECT_INIT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_RETURN_VOID_BARRIER.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_IGET_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtualQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtualQuick, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuperQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuperQuick, true);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.cpp */
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    ALOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: portable/enddefs.cpp */
+/*--- end of opcodes ---*/
+
+bail:
+    ILOGD("|-- Leaving interpreter loop");      // note "curMethod" may be NULL
+
+    self->interpSave.retval = retval;
+}
+
diff --git a/vm/mterp/out/InterpC-x86.cpp b/vm/mterp/out/InterpC-x86.cpp
new file mode 100644
index 0000000..77dc888
--- /dev/null
+++ b/vm/mterp/out/InterpC-x86.cpp
@@ -0,0 +1,2250 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_IGET_WIDE_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_OBJECT_INIT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_RETURN_VOID_BARRIER.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/portable/enddefs.cpp b/vm/mterp/portable/enddefs.cpp
new file mode 100644
index 0000000..ef28e2f
--- /dev/null
+++ b/vm/mterp/portable/enddefs.cpp
@@ -0,0 +1,7 @@
+/*--- end of opcodes ---*/
+
+bail:
+    ILOGD("|-- Leaving interpreter loop");      // note "curMethod" may be NULL
+
+    self->interpSave.retval = retval;
+}
diff --git a/vm/mterp/portable/entry.cpp b/vm/mterp/portable/entry.cpp
new file mode 100644
index 0000000..f8c01eb
--- /dev/null
+++ b/vm/mterp/portable/entry.cpp
@@ -0,0 +1,63 @@
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+void dvmInterpretPortable(Thread* self)
+{
+#if defined(EASY_GDB)
+    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+#endif
+    DvmDex* methodClassDex;     // curMethod->clazz->pDvmDex
+    JValue retval;
+
+    /* core state */
+    const Method* curMethod;    // method we're interpreting
+    const u2* pc;               // program counter
+    u4* fp;                     // frame pointer
+    u2 inst;                    // current instruction
+    /* instruction decoding */
+    u4 ref;                     // 16 or 32-bit quantity fetched directly
+    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
+    /* method call setup */
+    const Method* methodToCall;
+    bool methodCallRange;
+
+    /* static computed goto table */
+    DEFINE_GOTO_TABLE(handlerTable);
+
+    /* copy state in */
+    curMethod = self->interpSave.method;
+    pc = self->interpSave.pc;
+    fp = self->interpSave.curFrame;
+    retval = self->interpSave.retval;   /* only need for kInterpEntryReturn? */
+
+    methodClassDex = curMethod->clazz->pDvmDex;
+
+    LOGVV("threadid=%d: %s.%s pc=%#x fp=%p",
+        self->threadId, curMethod->clazz->descriptor, curMethod->name,
+        pc - curMethod->insns, fp);
+
+    /*
+     * Handle any ongoing profiling and prep for debugging.
+     */
+    if (self->interpBreak.ctl.subMode != 0) {
+        TRACE_METHOD_ENTER(self, curMethod);
+        self->debugIsMethodEntry = true;   // Always true on startup
+    }
+    /*
+     * DEBUG: scramble this to ensure we're not relying on it.
+     */
+    methodToCall = (const Method*) -1;
+
+#if 0
+    if (self->debugIsMethodEntry) {
+        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+                curMethod->name);
+        DUMP_REGS(curMethod, self->interpSave.curFrame, false);
+    }
+#endif
+
+    FINISH(0);                  /* fetch and execute first instruction */
+
+/*--- start of opcodes ---*/
diff --git a/vm/mterp/portable/stubdefs.cpp b/vm/mterp/portable/stubdefs.cpp
new file mode 100644
index 0000000..015057e
--- /dev/null
+++ b/vm/mterp/portable/stubdefs.cpp
@@ -0,0 +1,82 @@
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)
+
+#define GOTO_TARGET(_target, ...) _target:
+
+#define GOTO_TARGET_END
+
+/* ugh */
+#define STUB_HACK(x)
+#define JIT_STUB_HACK(x)
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()                                                    \
+    self->interpSave.pc = pc;                                              \
+    self->interpSave.curFrame = fp;
+#define PC_TO_SELF() self->interpSave.pc = pc;
+
+/*
+ * Instruction framing.  For a switch-oriented implementation this is
+ * case/break, for a threaded implementation it's a goto label and an
+ * instruction fetch/computed goto.
+ *
+ * Assumes the existence of "const u2* pc" and (for threaded operation)
+ * "u2 inst".
+ */
+# define H(_op)             &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) {                                                  \
+        ADJUST_PC(_offset);                                                 \
+        inst = FETCH(0);                                                    \
+        if (self->interpBreak.ctl.subMode) {                                \
+            dvmCheckBefore(pc, fp, self);                                   \
+        }                                                                   \
+        goto *handlerTable[INST_INST(inst)];                                \
+    }
+# define FINISH_BKPT(_opcode) {                                             \
+        goto *handlerTable[_opcode];                                        \
+    }
+
+#define OP_END
+
+/*
+ * The "goto" targets just turn into goto statements.  The "arguments" are
+ * passed through local variables.
+ */
+
+#define GOTO_exceptionThrown() goto exceptionThrown;
+
+#define GOTO_returnFromMethod() goto returnFromMethod;
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        methodCallRange = _methodCallRange;                                 \
+        goto _target;                                                       \
+    } while(false)
+
+/* for this, the "args" are already in the locals */
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) goto invokeMethod;
+
+#define GOTO_bail() goto bail;
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.  If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
diff --git a/vm/mterp/rebuild.sh b/vm/mterp/rebuild.sh
new file mode 100755
index 0000000..80419a5
--- /dev/null
+++ b/vm/mterp/rebuild.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Rebuild for all known targets.  Necessary until the stuff in "out" gets
+# generated as part of the build.
+#
+set -e
+
+for arch in portable allstubs armv5te armv5te-vfp armv7-a armv7-a-neon mips x86; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+
+# These aren't actually used, so just go ahead and remove them.  The correct
+# approach is to prevent them from being generated in the first place, but
+# this will do for now.
+echo Removing unneeded assembly source for portable interpreter
+rm -f out/InterpAsm-portable.S
diff --git a/vm/mterp/x86/OP_ADD_DOUBLE.S b/vm/mterp/x86/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..27a47c1
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_ADD_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    movq     (rFP, %eax, 4), %xmm0      # %xmm0<- vBB
+    movq     (rFP, %ecx, 4), %xmm1      # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    addsd    %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq     %xmm0, (rFP, rINST, 4)     # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
+
diff --git a/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d917578
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_ADD_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $$0xf,%cl               # ecx<- A
+    sarl        $$4,rINST               # rINST<- B
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    addsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_ADD_FLOAT.S b/vm/mterp/x86/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..61a6f68
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fadds","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..fd61457
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fadds","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_ADD_INT.S b/vm/mterp/x86/OP_ADD_INT.S
new file mode 100644
index 0000000..b95a2db
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"addl (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_ADD_INT_2ADDR.S b/vm/mterp/x86/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..5d4681b
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"addl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_ADD_INT_LIT16.S b/vm/mterp/x86/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..d96821d
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"addl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_ADD_INT_LIT8.S b/vm/mterp/x86/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..5d477fb
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"addl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_ADD_LONG.S b/vm/mterp/x86/OP_ADD_LONG.S
new file mode 100644
index 0000000..bc157f6
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"addl (rFP,%ecx,4),rIBASE", "instr2":"adcl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_ADD_LONG_2ADDR.S b/vm/mterp/x86/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..1f9d97e
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"addl %eax,(rFP,rINST,4)","instr2":"adcl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_AGET.S b/vm/mterp/x86/OP_AGET.S
new file mode 100644
index 0000000..42dfa0a
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET.S
@@ -0,0 +1,24 @@
+%default { "load":"movl", "shift":"4" }
+%verify "executed"
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    $load     offArrayObject_contents(%eax,%ecx,$shift),%eax
+.L${opcode}_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_AGET_BOOLEAN.S b/vm/mterp/x86/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..27f6de0
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movzbl", "shift":"1" }
diff --git a/vm/mterp/x86/OP_AGET_BYTE.S b/vm/mterp/x86/OP_AGET_BYTE.S
new file mode 100644
index 0000000..49c83e1
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movsbl", "shift":"1" }
diff --git a/vm/mterp/x86/OP_AGET_CHAR.S b/vm/mterp/x86/OP_AGET_CHAR.S
new file mode 100644
index 0000000..b5f6bcf
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movzwl", "shift":"2" }
diff --git a/vm/mterp/x86/OP_AGET_OBJECT.S b/vm/mterp/x86/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..0c43394
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S"
diff --git a/vm/mterp/x86/OP_AGET_SHORT.S b/vm/mterp/x86/OP_AGET_SHORT.S
new file mode 100644
index 0000000..aeeffa3
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movswl", "shift":"2" }
diff --git a/vm/mterp/x86/OP_AGET_WIDE.S b/vm/mterp/x86/OP_AGET_WIDE.S
new file mode 100644
index 0000000..32266bc
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_WIDE.S
@@ -0,0 +1,24 @@
+%verify "executed"
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    movl      (%eax),%ecx
+    movl      4(%eax),%eax
+    SET_VREG_WORD %ecx rINST 0
+    SET_VREG_WORD %eax rINST 1
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_AND_INT.S b/vm/mterp/x86/OP_AND_INT.S
new file mode 100644
index 0000000..3bb8757
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"andl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_AND_INT_2ADDR.S b/vm/mterp/x86/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..2dee26b
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"andl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_AND_INT_LIT16.S b/vm/mterp/x86/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..e0fe168
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"andl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_AND_INT_LIT8.S b/vm/mterp/x86/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..62c68dc
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"andl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_AND_LONG.S b/vm/mterp/x86/OP_AND_LONG.S
new file mode 100644
index 0000000..06df873
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"andl (rFP,%ecx,4),rIBASE", "instr2":"andl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_AND_LONG_2ADDR.S b/vm/mterp/x86/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..419850e
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"andl %eax,(rFP,rINST,4)","instr2":"andl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_APUT.S b/vm/mterp/x86/OP_APUT.S
new file mode 100644
index 0000000..f51c9c7
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT.S
@@ -0,0 +1,25 @@
+%default { "reg":"rINST", "store":"movl", "shift":"4" }
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,$shift),%eax
+.L${opcode}_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    $store     $reg,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_APUT_BOOLEAN.S b/vm/mterp/x86/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..fb1e8db
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" {"reg":"rINSTbl", "store":"movb", "shift":"1" }
diff --git a/vm/mterp/x86/OP_APUT_BYTE.S b/vm/mterp/x86/OP_APUT_BYTE.S
new file mode 100644
index 0000000..366c8c5
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"rINSTbl", "store":"movb", "shift":"1" }
diff --git a/vm/mterp/x86/OP_APUT_CHAR.S b/vm/mterp/x86/OP_APUT_CHAR.S
new file mode 100644
index 0000000..9c87384
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"rINSTw", "store":"movw", "shift":"2" }
diff --git a/vm/mterp/x86/OP_APUT_OBJECT.S b/vm/mterp/x86/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..0850e1c
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_OBJECT.S
@@ -0,0 +1,66 @@
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    GET_VREG_R  rINST rINST             # rINST<- vAA
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    /* On entry:
+     *   eax<- array object
+     *   ecx<- index
+     *   rINST<- vAA
+     */
+    leal      offArrayObject_contents(%eax,%ecx,4),%ecx
+    testl     rINST,rINST                    # storing null reference?
+    je        .L${opcode}_skip_check
+    SPILL_TMP1(%ecx)                         # save target address
+    SPILL_TMP2(%eax)                         # save object head
+    movl      offObject_clazz(%eax),%eax     # eax<- arrayObj->clazz
+    movl      offObject_clazz(rINST),%ecx    # ecx<- obj->clazz
+    movl      %eax,OUT_ARG1(%esp)
+    movl      %ecx,OUT_ARG0(%esp)
+    movl      %ecx,sReg0                     # store the two classes for later
+    movl      %eax,sReg1
+    SPILL(rIBASE)
+    call      dvmCanPutArrayElement          # test object type vs. array type
+    UNSPILL(rIBASE)
+    UNSPILL_TMP1(%ecx)                       # recover target address
+    testl     %eax,%eax
+    movl      rSELF,%eax
+    jne       .L${opcode}_types_okay
+
+    # The types don't match.  We need to throw an ArrayStoreException.
+    EXPORT_PC
+    movl      sReg0,%eax                     # restore the two classes...
+    movl      %eax,OUT_ARG0(%esp)
+    movl      sReg1,%ecx
+    movl      %ecx,OUT_ARG1(%esp)
+    call      dvmThrowArrayStoreExceptionIncompatibleElement # ...and throw
+    jmp       common_exceptionThrown
+
+.L${opcode}_types_okay:
+    movl      offThread_cardTable(%eax),%eax   # get card table base
+    movl      rINST,(%ecx)                   # store into array
+    UNSPILL_TMP2(rINST)                      # recover object head
+    FETCH_INST_OPCODE 2 %ecx
+    shrl      $$GC_CARD_SHIFT,rINST          # object head to card number
+    movb      %al,(%eax,rINST)               # mark card using object head
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_skip_check:
+    movl      rINST,(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_APUT_SHORT.S b/vm/mterp/x86/OP_APUT_SHORT.S
new file mode 100644
index 0000000..9c87384
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"rINSTw", "store":"movw", "shift":"2" }
diff --git a/vm/mterp/x86/OP_APUT_WIDE.S b/vm/mterp/x86/OP_APUT_WIDE.S
new file mode 100644
index 0000000..cd1e723
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_WIDE.S
@@ -0,0 +1,24 @@
+%verify "executed"
+    /*
+     * Array put, 64 bits.  vBB[vCC]<-vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    GET_VREG_WORD %ecx rINST 0
+    GET_VREG_WORD rINST rINST 1
+    movl      %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_ARRAY_LENGTH.S b/vm/mterp/x86/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..25caca3
--- /dev/null
+++ b/vm/mterp/x86/OP_ARRAY_LENGTH.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+   mov      rINST,%eax                # eax<- BA
+   sarl     $$4,rINST                 # rINST<- B
+   GET_VREG_R %ecx rINST              # ecx<- vB (object ref)
+   andb     $$0xf,%al                 # eax<- A
+   testl    %ecx,%ecx                 # is null?
+   je       common_errNullObject
+   movl     offArrayObject_length(%ecx),rINST
+   FETCH_INST_OPCODE 1 %ecx
+   ADVANCE_PC 1
+   SET_VREG rINST %eax
+   GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_BREAKPOINT.S b/vm/mterp/x86/OP_BREAKPOINT.S
new file mode 100644
index 0000000..6da9957
--- /dev/null
+++ b/vm/mterp/x86/OP_BREAKPOINT.S
@@ -0,0 +1,19 @@
+%verify "executed"
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.  We also assume that all other special "checkBefore"
+     * actions have been handled, so we'll transition directly
+     * to the real handler
+     */
+    SPILL(rIBASE)
+    movl    rPC,OUT_ARG0(%esp)
+    call    dvmGetOriginalOpcode
+    UNSPILL(rIBASE)
+    movl    rSELF,%ecx
+    movzbl  1(rPC),rINST
+    movl    offThread_mainHandlerTable(%ecx),%ecx
+    jmp     *(%ecx,%eax,4)
+
diff --git a/vm/mterp/x86/OP_CHECK_CAST.S b/vm/mterp/x86/OP_CHECK_CAST.S
new file mode 100644
index 0000000..0543a99
--- /dev/null
+++ b/vm/mterp/x86/OP_CHECK_CAST.S
@@ -0,0 +1,77 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    movl      rSELF,%ecx
+    GET_VREG_R  rINST,rINST             # rINST<- vAA (object)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    testl     rINST,rINST               # is oject null?
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    je        .L${opcode}_okay          # null obj, cast always succeeds
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved class
+    movl      offObject_clazz(rINST),%ecx # ecx<- obj->clazz
+    testl     %eax,%eax                 # have we resolved this before?
+    je        .L${opcode}_resolve       # no, go do it now
+.L${opcode}_resolved:
+    cmpl      %eax,%ecx                 # same class (trivial success)?
+    jne       .L${opcode}_fullcheck     # no, do full check
+.L${opcode}_okay:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  ecx holds obj->clazz
+     *  eax holds class resolved from BBBB
+     *  rINST holds object
+     */
+.L${opcode}_fullcheck:
+    movl    %eax,sReg0                 # we'll need the desired class on failure
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmInstanceofNonTrivial    # eax<- boolean result
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # failed?
+    jne     .L${opcode}_okay           # no, success
+
+    # A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC
+    movl    offObject_clazz(rINST),%eax
+    movl    %eax,OUT_ARG0(%esp)                 # arg0<- obj->clazz
+    movl    sReg0,%ecx
+    movl    %ecx,OUT_ARG1(%esp)                 # arg1<- desired class
+    call    dvmThrowClassCastException
+    jmp     common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path, and we're
+     * going to have to recreate some data.
+     *
+     *  rINST holds object
+     */
+.L${opcode}_resolve:
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movzwl  2(rPC),%eax                # eax<- BBBB
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)        # arg1<- BBBB
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- metho->clazz
+    movl    $$0,OUT_ARG2(%esp)         # arg2<- false
+    movl    %ecx,OUT_ARG0(%esp)        # arg0<- method->clazz
+    SPILL(rIBASE)
+    call    dvmResolveClass            # eax<- resolved ClassObject ptr
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # got null?
+    je      common_exceptionThrown     # yes, handle exception
+    movl    offObject_clazz(rINST),%ecx  # ecx<- obj->clazz
+    jmp     .L${opcode}_resolved       # pick up where we left off
diff --git a/vm/mterp/x86/OP_CMPG_DOUBLE.S b/vm/mterp/x86/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..1388d7c
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPG_DOUBLE.S
@@ -0,0 +1,33 @@
+%default {"is_double":"1","nanval":"1"}
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if $is_double
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .L${opcode}_isNaN
+    je       .L${opcode}_finish
+    sbbl     %ecx,%ecx
+    jb       .L${opcode}_finish
+    incl     %ecx
+.L${opcode}_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.L${opcode}_isNaN:
+    movl      $$$nanval,%ecx
+    jmp       .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_CMPG_FLOAT.S b/vm/mterp/x86/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..bd6674a
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"0","nanval":"1"}
diff --git a/vm/mterp/x86/OP_CMPL_DOUBLE.S b/vm/mterp/x86/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..54ff0d8
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"1","nanval":"-1"}
diff --git a/vm/mterp/x86/OP_CMPL_FLOAT.S b/vm/mterp/x86/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..bbb674d
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"0","nanval":"-1"}
diff --git a/vm/mterp/x86/OP_CMP_LONG.S b/vm/mterp/x86/OP_CMP_LONG.S
new file mode 100644
index 0000000..5202c27
--- /dev/null
+++ b/vm/mterp/x86/OP_CMP_LONG.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     */
+    // TUNING: rework to avoid rIBASE spill
+    /* cmp-long vAA, vBB, vCC */
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)
+    movzbl    3(rPC),rIBASE            # rIBASE- CC
+    GET_VREG_WORD %eax %ecx,1          # eax<- v[BB+1]
+    GET_VREG_WORD %ecx %ecx 0          # ecx<- v[BB+0]
+    cmpl      4(rFP,rIBASE,4),%eax
+    jl        .L${opcode}_smaller
+    jg        .L${opcode}_bigger
+    sub       (rFP,rIBASE,4),%ecx
+    ja        .L${opcode}_bigger
+    jb        .L${opcode}_smaller
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_bigger:
+    movl      $$1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_smaller:
+    movl      $$-1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST.S b/vm/mterp/x86/OP_CONST.S
new file mode 100644
index 0000000..cb70824
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const vAA, #+BBBBbbbb */
+    movl      2(rPC),%eax             # grab all 32 bits at once
+    movl      rINST,rINST             # rINST<- AA
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG %eax rINST               # vAA<- eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_16.S b/vm/mterp/x86/OP_CONST_16.S
new file mode 100644
index 0000000..b956ee6
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_16.S
@@ -0,0 +1,7 @@
+%verify "executed"
+    /* const/16 vAA, #+BBBB */
+    movswl  2(rPC),%ecx                # ecx<- ssssBBBB
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    SET_VREG %ecx rINST                # vAA<- ssssBBBB
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_CONST_4.S b/vm/mterp/x86/OP_CONST_4.S
new file mode 100644
index 0000000..3db437a
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const/4 vA, #+B */
+    movsx   rINSTbl,%eax              # eax<-ssssssBx
+    movl    $$0xf,rINST
+    andl    %eax,rINST                # rINST<- A
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    sarl    $$4,%eax
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_CLASS.S b/vm/mterp/x86/OP_CONST_CLASS.S
new file mode 100644
index 0000000..8b12226
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_CLASS.S
@@ -0,0 +1,37 @@
+
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+    /* const/class vAA, Class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+    movl      (%ecx,%eax,4),%eax       # eax<- rResClasses[BBBB]
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST               # vAA<- rResClasses[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     $$1,OUT_ARG2(%esp)        # true
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveClass           # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_HIGH16.S b/vm/mterp/x86/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..9ecf7c6
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_HIGH16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const/high16 vAA, #+BBBB0000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $$16,%eax                  # eax<- BBBB0000
+    SET_VREG %eax rINST                   # vAA<- eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_STRING.S b/vm/mterp/x86/OP_CONST_STRING.S
new file mode 100644
index 0000000..538cace
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING.S
@@ -0,0 +1,36 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_STRING_JUMBO.S b/vm/mterp/x86/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..6148244
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,36 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBBBBBB */
+    movl      rSELF,%ecx
+    movl      2(rPC),%eax              # eax<- BBBBBBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 3 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     2(rPC),%ecx               # ecx<- BBBBBBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 3 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE.S b/vm/mterp/x86/OP_CONST_WIDE.S
new file mode 100644
index 0000000..253af78
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    movl      2(rPC),%eax         # eax<- lsw
+    movzbl    rINSTbl,%ecx        # ecx<- AA
+    movl      6(rPC),rINST        # rINST<- msw
+    leal      (rFP,%ecx,4),%ecx   # dst addr
+    movl      rINST,4(%ecx)
+    movl      %eax,(%ecx)
+    FETCH_INST_OPCODE 5 %ecx
+    ADVANCE_PC 5
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE_16.S b/vm/mterp/x86/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..ee8004c
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide/16 vAA, #+BBBB */
+    movswl    2(rPC),%eax               # eax<- ssssBBBB
+    SPILL(rIBASE)                       # preserve rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST 1        # store msw
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE_32.S b/vm/mterp/x86/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..12cdd01
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_32.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    movl     2(rPC),%eax                # eax<- BBBBbbbb
+    SPILL(rIBASE)                       # save rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST,1        # store msw
+    FETCH_INST_OPCODE 3 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S b/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..15a0c55
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $$16,%eax                  # eax<- BBBB0000
+    SET_VREG_WORD %eax rINST 1            # v[AA+1]<- eax
+    xorl       %eax,%eax
+    SET_VREG_WORD %eax rINST 0            # v[AA+0]<- eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_DIV_DOUBLE.S b/vm/mterp/x86/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..8ccff2e
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fdivl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..60a0072
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fdivl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_DIV_FLOAT.S b/vm/mterp/x86/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..740e26e
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fdivs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..2ed12b6
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fdivs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DIV_INT.S b/vm/mterp/x86/OP_DIV_INT.S
new file mode 100644
index 0000000..8afdcb0
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_2ADDR.S b/vm/mterp/x86/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..a1ae0f9
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv2addr.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_LIT16.S b/vm/mterp/x86/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..4817734
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit16.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_LIT8.S b/vm/mterp/x86/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..f59838c
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit8.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_LONG.S b/vm/mterp/x86/OP_DIV_LONG.S
new file mode 100644
index 0000000..0dc5546
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG.S
@@ -0,0 +1,46 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+    /* div vAA, vBB, vCC */
+    movzbl    3(rPC),%eax              # eax<- CC
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)                      # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .L${opcode}_check_zero
+    cmpl     $$-1,%eax
+    je       .L${opcode}_check_neg1
+.L${opcode}_notSpecial:
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+.L${opcode}_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     $routine
+.L${opcode}_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                 # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .L${opcode}_notSpecial
+    jmp     common_errDivideByZero
+.L${opcode}_check_neg1:
+    testl   rIBASE,%eax
+    jne     .L${opcode}_notSpecial
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+    testl    rIBASE,rIBASE
+    jne      .L${opcode}_notSpecial1
+    cmpl     $$0x80000000,%ecx
+    jne      .L${opcode}_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $special,rIBASE
+    jmp      .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_DIV_LONG_2ADDR.S b/vm/mterp/x86/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..4722098
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,47 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+    /* div/2addr vA, vB */
+    movzbl    rINSTbl,%eax
+    shrl      $$4,%eax                  # eax<- B
+    andb      $$0xf,rINSTbl             # rINST<- A
+    SPILL(rIBASE)                       # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .L${opcode}_check_zero
+    cmpl     $$-1,%eax
+    je       .L${opcode}_check_neg1
+.L${opcode}_notSpecial:
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+.L${opcode}_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     $routine
+.L${opcode}_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                    # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .L${opcode}_notSpecial
+    jmp     common_errDivideByZero
+.L${opcode}_check_neg1:
+    testl   rIBASE,%eax
+    jne     .L${opcode}_notSpecial
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+    testl    rIBASE,rIBASE
+    jne      .L${opcode}_notSpecial1
+    cmpl     $$0x80000000,%ecx
+    jne      .L${opcode}_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $special,rIBASE
+    jmp      .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..12e73a2
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fldl","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_INT.S b/vm/mterp/x86/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..7cd993a
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"0"}
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_LONG.S b/vm/mterp/x86/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..b80b020
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"1"}
diff --git a/vm/mterp/x86/OP_EXECUTE_INLINE.S b/vm/mterp/x86/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..c739fdf
--- /dev/null
+++ b/vm/mterp/x86/OP_EXECUTE_INLINE.S
@@ -0,0 +1,106 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We will be calling through a function table:
+     *
+     * (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult)
+     *
+     * Ignores argument count - always loads 4.
+     *
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    movl      rSELF,%ecx
+    EXPORT_PC
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    SPILL(rIBASE)                       # preserve rIBASE
+    movl      offThread_subMode(%ecx), %edx # edx<- submode flags
+    andl      $$kSubModeDebugProfile, %edx # debug or profile mode active?
+    jnz       .L${opcode}_debugprofile   # yes, take slow path
+.L${opcode}_resume:
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)
+    call      .L${opcode}_continue      # make call; will return after
+    UNSPILL(rIBASE)                     # restore rIBASE
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue:
+    /*
+     * Extract args, call function.
+     *  ecx = #of args (0-4)
+     *  eax = call index
+     *  @esp = return addr
+     *  esp is -4 from normal
+     *
+     *  Go ahead and load all 4 args, even if not used.
+     */
+    movzwl    4(rPC),rIBASE
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG0(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG1(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG2(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG3(%esp)
+
+    sall      $$4,%eax      # index *= sizeof(table entry)
+    jmp       *gDvmInlineOpsTable(%eax)
+    # will return to caller of .L${opcode}_continue
+
+    /*
+     * We're debugging or profiling.
+     * eax: opIndex
+     */
+.L${opcode}_debugprofile:
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- BBBB
+    SPILL_TMP1(%eax)                    # save opIndex
+    call      dvmResolveInlineNative    # dvmResolveInlineNative(opIndex)
+    movl      rSELF,%ecx                # restore self
+    testl     %eax,%eax                 # method resolved?
+    movl      %eax,%edx                 # save possibly resolved method in edx
+    UNSPILL_TMP1(%eax)                  # in case not resolved, restore opIndex
+    jz        .L${opcode}_resume        # not resolved, just move on
+    SPILL_TMP2(%edx)                    # save method
+    movl      %edx,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastMethodTraceEnter   # dvmFastMethodTraceEnter(method,self)
+    movl      rSELF,%ecx                # restore self
+    UNSPILL_TMP1(%eax)                  # restore opIndex
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)       # needed for pResult of inline operation handler
+    call      .L${opcode}_continue      # make call; will return after
+    SPILL_TMP1(%eax)                    # save result of inline
+    UNSPILL_TMP2(%eax)                  # restore method
+    movl      rSELF,%ecx                # restore self
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastNativeMethodTraceExit # dvmFastNativeMethodTraceExit(method,self)
+    UNSPILL(rIBASE)                     # restore rIBASE
+    UNSPILL_TMP1(%eax)                  # restore result of inline
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..dde53aa
--- /dev/null
+++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,129 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    movl    rSELF,%eax
+    movl    offThread_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rIBASE)                             # preserve rIBASE
+    movl    (%eax,%ecx,4),%eax                # eax<- resolved class
+    EXPORT_PC
+    testl   %eax,%eax                         # already resolved?
+    jne     .L${opcode}_continue              # yes, continue
+    # less frequent path, so we'll redo some work
+    movl    rSELF,%eax
+    movl    $$0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offThread_method(%eax),%eax         # eax<- self->method
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    testl   %eax,%eax                         # null?
+    je      common_exceptionThrown            # yes, handle it
+
+       # note: fall through to .L${opcode}_continue
+
+    /*
+     * On entry:
+     *    eax holds array class [r0]
+     *    rINST holds AA or BB [r10]
+     *    ecx is scratch
+     */
+.L${opcode}_continue:
+    movl    offClassObject_descriptor(%eax),%ecx  # ecx<- arrayClass->descriptor
+    movl    $$ALLOC_DONT_TRACK,OUT_ARG2(%esp)     # arg2<- flags
+    movzbl  1(%ecx),%ecx                          # ecx<- descriptor[1]
+    movl    %eax,OUT_ARG0(%esp)                   # arg0<- arrayClass
+    movl    rSELF,%eax
+    cmpb    $$'I',%cl                             # supported?
+    je      1f
+    cmpb    $$'L',%cl
+    je      1f
+    cmpb    $$'[',%cl
+    jne      .L${opcode}_notimpl                  # no, not handled yet
+1:
+    movl    %ecx,offThread_retval+4(%eax)           # save type
+    .if      (!$isrange)
+    SPILL_TMP1(rINST)                              # save copy, need "B" later
+    sarl    $$4,rINST
+    .endif
+    movl    rINST,OUT_ARG1(%esp)                  # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass     # eax<- call(arrayClass, length, flags)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offThread_retval(%ecx)             # retval.l<- new array
+    movzwl  4(rPC),%ecx                           # ecx<- FEDC or CCCC
+    leal    offArrayObject_contents(%eax),%eax    # eax<- newArray->contents
+
+/* at this point:
+ *     eax is pointer to tgt
+ *     rINST is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL1 is BA
+ *  We now need to copy values from registers into the array
+ */
+
+    .if $isrange
+    # set up src pointer
+    SPILL_TMP2(%esi)
+    SPILL_TMP3(%edi)
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    %eax,%edi         # set up dst ptr
+    movl    rINST,%ecx        # load count register
+    rep
+    movsd
+    UNSPILL_TMP2(%esi)
+    UNSPILL_TMP3(%edi)
+    movl    rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .else
+    testl  rINST,rINST
+    je     4f
+    UNSPILL_TMP1(rIBASE)      # restore "BA"
+    andl   $$0x0f,rIBASE      # rIBASE<- 0000000A
+    sall   $$16,rIBASE        # rIBASE<- 000A0000
+    orl    %ecx,rIBASE        # rIBASE<- 000AFEDC
+3:
+    movl   $$0xf,%ecx
+    andl   rIBASE,%ecx        # ecx<- next reg to load
+    GET_VREG_R %ecx %ecx
+    shrl   $$4,rIBASE
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $$1,rINST
+    jne    3b
+4:
+    movl   rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .endif
+
+    cmpb    $$'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offThread_retval(%ecx),%eax        # eax<- object head
+    movl    offThread_cardTable(%ecx),%ecx     # card table base
+    shrl    $$GC_CARD_SHIFT,%eax             # convert to card num
+    movb    %cl,(%ecx,%eax)                  # mark card based on object head
+5:
+    UNSPILL(rIBASE)                          # restore rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.L${opcode}_notimpl:
+    movl    $$.LstrFilledNewArrayNotImplA,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowInternalError
+    jmp     common_exceptionThrown
diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..dc6cc4d
--- /dev/null
+++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/x86/OP_FILL_ARRAY_DATA.S b/vm/mterp/x86/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..5ca17a6
--- /dev/null
+++ b/vm/mterp/x86/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* fill-array-data vAA, +BBBBBBBB */
+    movl    2(rPC),%ecx                # ecx<- BBBBbbbb
+    leal    (rPC,%ecx,2),%ecx          # ecx<- PC + BBBBbbbb*2
+    GET_VREG_R %eax rINST
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmInterpHandleFillArrayData
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 3 %ecx
+    testl   %eax,%eax                   # exception thrown?
+    je      common_exceptionThrown
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..11b2c23
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"flds","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_FLOAT_TO_INT.S b/vm/mterp/x86/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..8d55286
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"0"}
diff --git a/vm/mterp/x86/OP_FLOAT_TO_LONG.S b/vm/mterp/x86/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..2493b13
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"1"}
diff --git a/vm/mterp/x86/OP_GOTO.S b/vm/mterp/x86/OP_GOTO.S
new file mode 100644
index 0000000..a7913d1
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    movl    rSELF,%ecx
+    movsbl  rINSTbl,%eax          # eax<- ssssssAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_GOTO_16.S b/vm/mterp/x86/OP_GOTO_16.S
new file mode 100644
index 0000000..84220d0
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_16.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset
+     */
+    /* goto/16 +AAAA */
+    movl    rSELF,%ecx
+    movswl  2(rPC),%eax            # eax<- ssssAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_GOTO_32.S b/vm/mterp/x86/OP_GOTO_32.S
new file mode 100644
index 0000000..c3d1050
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_32.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "forward, backward, self"
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset.
+     */
+    /* goto/32 AAAAAAAA */
+    movl    rSELF,%ecx
+    movl    2(rPC),%eax            # eax<- AAAAAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IF_EQ.S b/vm/mterp/x86/OP_IF_EQ.S
new file mode 100644
index 0000000..a8a38b6
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86/OP_IF_EQZ.S b/vm/mterp/x86/OP_IF_EQZ.S
new file mode 100644
index 0000000..67fd5d7
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86/OP_IF_GE.S b/vm/mterp/x86/OP_IF_GE.S
new file mode 100644
index 0000000..6930328
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86/OP_IF_GEZ.S b/vm/mterp/x86/OP_IF_GEZ.S
new file mode 100644
index 0000000..71e9127
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86/OP_IF_GT.S b/vm/mterp/x86/OP_IF_GT.S
new file mode 100644
index 0000000..b987443
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86/OP_IF_GTZ.S b/vm/mterp/x86/OP_IF_GTZ.S
new file mode 100644
index 0000000..9d7b697
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86/OP_IF_LE.S b/vm/mterp/x86/OP_IF_LE.S
new file mode 100644
index 0000000..b9ac008
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86/OP_IF_LEZ.S b/vm/mterp/x86/OP_IF_LEZ.S
new file mode 100644
index 0000000..26afc85
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86/OP_IF_LT.S b/vm/mterp/x86/OP_IF_LT.S
new file mode 100644
index 0000000..79aba48
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86/OP_IF_LTZ.S b/vm/mterp/x86/OP_IF_LTZ.S
new file mode 100644
index 0000000..01f4daa
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86/OP_IF_NE.S b/vm/mterp/x86/OP_IF_NE.S
new file mode 100644
index 0000000..552b74c
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86/OP_IF_NEZ.S b/vm/mterp/x86/OP_IF_NEZ.S
new file mode 100644
index 0000000..b2fdb14
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86/OP_IGET.S b/vm/mterp/x86/OP_IGET.S
new file mode 100644
index 0000000..5740690
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET.S
@@ -0,0 +1,53 @@
+%default { "load":"movl", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    $load   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_IGET_BOOLEAN.S b/vm/mterp/x86/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..0829e2c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET.S" { "load":"movzbl", "sqnum":"1" }
diff --git a/vm/mterp/x86/OP_IGET_BYTE.S b/vm/mterp/x86/OP_IGET_BYTE.S
new file mode 100644
index 0000000..2350f8c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S" { "load":"movsbl", "sqnum":"2" }
diff --git a/vm/mterp/x86/OP_IGET_CHAR.S b/vm/mterp/x86/OP_IGET_CHAR.S
new file mode 100644
index 0000000..4218a8c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+%include "x86/OP_IGET.S" { "load":"movzwl", "sqnum":"3" }
diff --git a/vm/mterp/x86/OP_IGET_OBJECT.S b/vm/mterp/x86/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..84c0a98
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S b/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..89ebd36
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET_QUICK.S"
diff --git a/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..4576d39
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_QUICK.S b/vm/mterp/x86/OP_IGET_QUICK.S
new file mode 100644
index 0000000..86f1f66
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $$0,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      (%ecx,%eax,1),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    andb      $$0xf,rINSTbl             # rINST<- A
+    SET_VREG  %eax rINST                # fp[A]<- result
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IGET_SHORT.S b/vm/mterp/x86/OP_IGET_SHORT.S
new file mode 100644
index 0000000..71f0d34
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S" { "load":"movswl", "sqnum":"4" }
diff --git a/vm/mterp/x86/OP_IGET_VOLATILE.S b/vm/mterp/x86/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..4576d39
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_VOLATILE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_WIDE.S b/vm/mterp/x86/OP_IGET_WIDE.S
new file mode 100644
index 0000000..723c9b7
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE.S
@@ -0,0 +1,54 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit instance field get.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # for dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save objpointer across call
+    movl    rPC,OUT_ARG0(%esp)                  # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    movl    (%eax),%ecx                         # ecx<- lsw
+    movl    4(%eax),%eax                        # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                             # restore rIBASE
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IGET_WIDE_QUICK.S b/vm/mterp/x86/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..dd63c73
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $$0,%ecx                  # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%eax        # eax<- address of 64-bit source
+    movl      (%eax),%ecx               # ecx<- lsw
+    movl      4(%eax),%eax              # eax<- msw
+    andb      $$0xf,rINSTbl             # rINST<- A
+    SET_VREG_WORD %ecx rINST 0          # v[A+0]<- lsw
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1          # v[A+1]<- msw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_INSTANCE_OF.S b/vm/mterp/x86/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..c54f4f7
--- /dev/null
+++ b/vm/mterp/x86/OP_INSTANCE_OF.S
@@ -0,0 +1,93 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                   # object null?
+    movl    offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)                       # preserve rIBASE
+    je      .L${opcode}_store           # null obj, not instance, store it
+    movzwl  2(rPC),rIBASE               # rIBASE<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,rIBASE,4),%ecx        # ecx<- resolved class
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    testl   %ecx,%ecx                   # have we resolved this before?
+    je      .L${opcode}_resolve         # not resolved, do it now
+.L${opcode}_resolved:  # eax<- obj->clazz, ecx<- resolved class
+    cmpl    %eax,%ecx                   # same class (trivial success)?
+    je      .L${opcode}_trivial         # yes, trivial finish
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  eax holds obj->clazz
+     *  ecx holds class resolved from BBBB
+     *  rINST has BA
+     */
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmInstanceofNonTrivial     # eax<- boolean result
+    # fall through to ${opcode}_store
+
+    /*
+     * eax holds boolean result
+     * rINST holds BA
+     */
+.L${opcode}_store:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $$0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    SET_VREG %eax rINST                 # vA<- eax
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.L${opcode}_trivial:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $$0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    movl    $$1,%eax
+    SET_VREG %eax rINST                 # vA<- true
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  rIBASE holds BBBB
+     *  rINST holds BA
+     */
+.L${opcode}_resolve:
+    movl    rIBASE,OUT_ARG1(%esp)         # arg1<- BBBB
+    movl    rSELF,%ecx
+    movl    offThread_method(%ecx),%ecx
+    movl    $$1,OUT_ARG2(%esp)          # arg2<- true
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    EXPORT_PC
+    movl    %ecx,OUT_ARG0(%esp)         # arg0<- method->clazz
+    call    dvmResolveClass             # eax<- resolved ClassObject ptr
+    testl   %eax,%eax                   # success?
+    je      common_exceptionThrown      # no, handle exception
+/* Now, we need to sync up with fast path.  We need eax to
+ * hold the obj->clazz, and ecx to hold the resolved class
+ */
+    movl    %eax,%ecx                   # ecx<- resolved class
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    jmp     .L${opcode}_resolved
diff --git a/vm/mterp/x86/OP_INT_TO_BYTE.S b/vm/mterp/x86/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..44d032b
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movsbl %al,%eax"}
diff --git a/vm/mterp/x86/OP_INT_TO_CHAR.S b/vm/mterp/x86/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..49c8055
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movzwl %ax,%eax"}
diff --git a/vm/mterp/x86/OP_INT_TO_DOUBLE.S b/vm/mterp/x86/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..e9c5640
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_INT_TO_FLOAT.S b/vm/mterp/x86/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..06d658d
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildl","store":"fstps"}
diff --git a/vm/mterp/x86/OP_INT_TO_LONG.S b/vm/mterp/x86/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..551efaf
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_LONG.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /* int to long vA, vB */
+    movzbl  rINSTbl,%eax                # eax<- +A
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    andb    $$0xf,rINSTbl               # rINST<- A
+    SPILL(rIBASE)                       # cltd trashes rIBASE/edx
+    cltd                                # rINST:eax<- sssssssBBBBBBBB
+    SET_VREG_WORD rIBASE rINST 1        # v[A+1]<- rIBASE/rPC
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[A+0]<- %eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_INT_TO_SHORT.S b/vm/mterp/x86/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..d5dc5d8
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movswl %ax,%eax"}
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT.S b/vm/mterp/x86/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..7d27c6f
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT.S
@@ -0,0 +1,53 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rIBASE            # rIBASE<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!$isrange)
+    andl      $$0xf,rIBASE             # rIBASE<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG_R  %ecx rIBASE            # ecx<- "this" ptr
+    je        .L${opcode}_resolve      # not resolved, do it now
+.L${opcode}_finish:
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethod${routine}  # no, continue on
+    jmp       common_errNullObject
+
+    /*
+     * On entry:
+     *   TMP_SPILL  <- "this" register
+     * Things a bit ugly on this path, but it's the less
+     * frequent one.  We'll have to do some reloading.
+     */
+.L${opcode}_resolve:
+     SPILL_TMP1(%ecx)
+     movl     rSELF,%ecx
+     movl     offThread_method(%ecx),%ecx  # ecx<- self->method
+     movzwl   2(rPC),%eax      # reference (BBBB or CCCC)
+     movl     offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+     movl     $$METHOD_DIRECT,OUT_ARG2(%esp)
+     movl     %eax,OUT_ARG1(%esp)
+     movl     %ecx,OUT_ARG0(%esp)
+     call     dvmResolveMethod # eax<- call(clazz, ref, flags)
+     UNSPILL_TMP1(%ecx)
+     testl    %eax,%eax
+     jne      .L${opcode}_finish
+     jmp      common_exceptionThrown
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..140430a
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_INTERFACE.S b/vm/mterp/x86/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..26d2d5c
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,34 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl     4(rPC),%eax              # eax<- FEDC or CCCC
+    movl       rSELF,%ecx
+    .if        (!$isrange)
+    andl       $$0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R   %eax %eax              # eax<- "this"
+    EXPORT_PC
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       %eax, TMP_SPILL1(%ebp)
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offThread_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offThread_method(%ecx),%ecx           # ecx<- method
+    movl       %eax,OUT_ARG3(%esp)                 # arg3<- dex
+    movzwl     2(rPC),%eax                         # eax<- BBBB
+    movl       %ecx,OUT_ARG2(%esp)                 # arg2<- method
+    movl       %eax,OUT_ARG1(%esp)                 # arg1<- BBBB
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    movl       TMP_SPILL1(%ebp), %ecx
+    jmp        common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..cbebedb
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_OBJECT_INIT_RANGE.S b/vm/mterp/x86/OP_INVOKE_OBJECT_INIT_RANGE.S
new file mode 100644
index 0000000..f6e3f42
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_OBJECT_INIT_RANGE.S
@@ -0,0 +1,53 @@
+%default { "cccc":"2" }
+%verify "executed"
+%verify "finalizable class"
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    movzwl     4(rPC),%eax              # eax<- CCCC, offset = 2 * cccc
+    GET_VREG_R %ecx, %eax               # ecx<- "this" ptr
+    testl      %ecx,%ecx                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       offObject_clazz(%ecx), %eax # eax<- obj->clazz
+    SPILL_TMP1(rIBASE)                  # save %edx
+    movl       offClassObject_accessFlags(%eax), %edx # edx<- clazz->accessFlags
+    andl       $$CLASS_ISFINALIZABLE, %edx # is this class finalizable?
+    jnz        .L${opcode}_setFinal     # yes, go
+.L${opcode}_finish:
+    movl       rSELF, %ecx
+    movl       offThread_subMode(%ecx), %eax
+    andl       $$kSubModeDebuggerActive, %eax # debugger active?
+    jnz        .L${opcode}_debugger     # Yes - skip optimization
+    UNSPILL_TMP1(rIBASE)
+    FETCH_INST_OPCODE 3 %ecx            # 3 = cccc + 1
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+%break
+
+.L${opcode}_setFinal:
+    EXPORT_PC                           # can throw
+    movl       %ecx, OUT_ARG0(%esp)     # arg1<- obj
+    call       dvmSetFinalizable        # call dvmSetFinalizable(obj)
+    movl       rSELF, %ecx
+    movl       offThread_exception(%ecx), %eax # eax<- self->exception
+    cmpl       $$0, %eax                # exception pending?
+    jne        common_exceptionThrown   # yes, handle it
+    jmp        .L${opcode}_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.L${opcode}_debugger:
+    movl    offThread_mainHandlerTable(%ecx), %ecx # load main handler table
+    movl       $$OP_INVOKE_DIRECT_RANGE, %eax
+    /*
+     * We can't use GOTO_NEXT here since we want to jump directly to
+     * handler without touching rIBASE.
+     */
+    jmp        *(%ecx,%eax,4)
diff --git a/vm/mterp/x86/OP_INVOKE_STATIC.S b/vm/mterp/x86/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..d5004bc
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_STATIC.S
@@ -0,0 +1,68 @@
+%default { "routine":"NoRange","isrange":"0" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+#if defined(WITH_JIT)
+    movl     %edx, TMP_SPILL1(%ebp)
+    lea      (%ecx,%eax,4), %edx
+    movl     %edx, TMP_SPILL2(%ebp)
+    movl     TMP_SPILL1(%ebp), %edx
+#endif
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    movl      $$0, %ecx                 # make "this" null
+    testl     %eax,%eax
+    jne       common_invokeMethod${routine}
+
+    movl      rSELF,%ecx
+    movl      offThread_method(%ecx),%ecx # ecx<- self->method
+    movzwl    2(rPC),%eax
+    movl      offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+    movl      %eax,OUT_ARG1(%esp)       # arg1<- BBBB
+    movl      %ecx,OUT_ARG0(%esp)       # arg0<- clazz
+    movl      $$METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rIBASE)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rIBASE)
+    testl     %eax,%eax                 # got null?
+#if defined(WITH_JIT)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      rSELF,%ecx
+    movzwl    offThread_subMode(%ecx), %ecx
+    je        common_exceptionThrown    # null, handle exception
+    andl      $$kSubModeJitTraceBuild, %ecx # is trace under construction?
+    movl      $$0, %ecx                 # make "this" null
+    je        common_invokeMethod${routine} # no (%eax=method, %ecx="this")
+    movl      TMP_SPILL2(%ebp), %edx
+    cmpl      $$0, (%edx)                  # finished resolving
+    movl      TMP_SPILL1(%ebp), %edx
+    jne        common_invokeMethod${routine} # yes (%eax=method, %ecx="this")
+    movl      rSELF, %edx
+    movl      %edx, OUT_ARG0(%esp)
+    movl      rPC, OUT_ARG1(%esp)
+    movl      %eax, TMP_SPILL2(%ebp)
+    movl      %ecx, TMP_SPILL3(%ebp)
+    SPILL(rIBASE)
+    call      dvmJitEndTraceSelect
+    UNSPILL(rIBASE)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      TMP_SPILL2(%ebp), %eax
+    movl      TMP_SPILL3(%ebp), %ecx
+    jmp       common_invokeMethod${routine}
+#else
+    movl      $$0, %ecx                 # make "this" null
+    jne       common_invokeMethod${routine}
+    jmp       common_exceptionThrown
+#endif
+
diff --git a/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..da6d0bf
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_STATIC.S" { "routine":"Range","isrange":"1" }
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER.S b/vm/mterp/x86/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..d210daf
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER.S
@@ -0,0 +1,69 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,rINST
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(rINST),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offThread_method(rINST),%eax # eax<- method
+    movzwl    4(rPC),rINST              # rINST<- GFED or CCCC
+    .if       (!$isrange)
+    andl      $$0xf,rINST               # rINST<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %edx rINST             # %edx<- "this" ptr
+    testl     %edx,%edx                # null "this"?
+    SPILL_TMP1(%edx)
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    je       .L${opcode}_resolve
+    /*
+     * At this point:
+     *  ecx = resolved base method [r0]
+     *  eax = method->clazz [r9]
+     */
+.L${opcode}_continue:
+    movl    offClassObject_super(%eax),%eax   # eax<- method->clazz->super
+    movzwl  offMethod_methodIndex(%ecx),%edx  # edx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%edx # compare(methodIndex,vtableCount)
+    jae     .L${opcode}_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%edx,4),%eax        # eax<- vtable[methodIndex]
+    UNSPILL_TMP1(%edx)
+    movl    %edx, %ecx
+    jmp     common_invokeMethod${routine}
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.L${opcode}_resolve:
+    SPILL_TMP2(%eax)                    # method->clazz
+    movl    %eax,OUT_ARG0(%esp)         # arg0<- method->clazz
+    movzwl  2(rPC),%ecx                 # ecx<- BBBB
+    movl    $$METHOD_VIRTUAL,OUT_ARG2(%esp)  # arg2<- resolver method type
+    movl    %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP2(%eax)                  # restore method->clazz
+    jne     .L${opcode}_continue        # good to go - continue
+    jmp     common_exceptionThrown      # handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  ecx = resolved base method
+     */
+.L${opcode}_nsm:
+    movl    offMethod_name(%ecx),%eax
+    jmp     common_errNoSuchMethod
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..b93fce5
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,28 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offThread_method(%ecx),%ecx # ecx<- current method
+    .if       (!$isrange)
+    andl      $$0xf,%eax                # eax<- D (or stays CCCC)
+    .endif
+    movl      offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    GET_VREG_R  %eax %eax               # eax<- "this"
+    movl      offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+    testl     %eax,%eax                 # null "this"?
+    je        common_errNullObject      # "this" is null, throw exception
+    movl       %eax, TMP_SPILL1(%ebp)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    movl      TMP_SPILL1(%ebp), %ecx
+    jmp       common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..e0a27a3
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..ffbcf8c
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..23dee67
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,48 @@
+
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%eax
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offThread_methodClassDex(%eax),%eax  # eax<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%eax),%eax   # eax<- pDvmDex->pResMethods
+    movl      (%eax,%ecx,4),%eax          # eax<- resolved baseMethod
+    testl     %eax,%eax                   # already resolved?
+    jne       .L${opcode}_continue        # yes, continue
+    movl      rSELF,%eax
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offThread_method(%eax),%eax   # eax<- self->method
+    movl      offMethod_clazz(%eax),%eax  # ecx<- method->clazz
+    movl      %eax,OUT_ARG0(%esp)         # arg0<- clazz
+    movl      $$METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+    call      dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl     %eax,%eax                   # got null?
+    jne       .L${opcode}_continue        # no, continue
+    jmp       common_exceptionThrown      # yes, handle exception
+
+    /* At this point:
+     *   eax = resolved base method
+     *   ecx = scratch
+     */
+.L${opcode}_continue:
+    movzwl    4(rPC),%ecx               # ecx<- GFED or CCCC
+    .if       (!$isrange)
+    andl      $$0xf,%ecx                # ecx<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- "this"
+    movzwl    offMethod_methodIndex(%eax),%eax  # eax<- baseMethod->methodIndex
+    testl     %ecx,%ecx                 # null this?
+    je        common_errNullObject      # go if so
+    movl      offObject_clazz(%ecx),%edx  # edx<- thisPtr->clazz
+    movl      offClassObject_vtable(%edx),%edx # edx<- thisPtr->clazz->vtable
+    movl      (%edx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..58f784d
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl    4(rPC),%ecx               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%edx               # ecx<- BBBB
+    .if     (!$isrange)
+    andl      $$0xf,%ecx                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- vC ("this" ptr)
+    testl     %ecx,%ecx                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%ecx),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC                           # might throw later - get ready
+    movl      (%eax,%edx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..53d7602
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..e91a89e
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_IPUT.S b/vm/mterp/x86/OP_IPUT.S
new file mode 100644
index 0000000..2c718b2
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT.S
@@ -0,0 +1,54 @@
+
+%default { "store":"movl", "reg":"rINST", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    $store   $reg,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_BOOLEAN.S b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..1bdde9d
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb","reg":"rINSTbl", "sqnum":"1" }
diff --git a/vm/mterp/x86/OP_IPUT_BYTE.S b/vm/mterp/x86/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..3a8652d
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb", "reg":"rINSTbl", "sqnum":"2" }
diff --git a/vm/mterp/x86/OP_IPUT_CHAR.S b/vm/mterp/x86/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..ba8d38e
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINSTw", "sqnum":"3" }
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT.S b/vm/mterp/x86/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..6c9fd6e
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT.S
@@ -0,0 +1,61 @@
+%default { "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Object field put.
+     *
+     * for: iput-object
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                  # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                      # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl    rINST,(%ecx,%eax)      # obj.field <- v[A](8/16/32 bits)
+    movl    rSELF,%eax
+    testl   rINST,rINST                         # stored a NULL?
+    movl    offThread_cardTable(%eax),%eax      # get card table base
+    je      1f                                  # skip card mark if null store
+    shrl    $$GC_CARD_SHIFT,%ecx                # object head to card number
+    movb    %al,(%eax,%ecx)                     # mark card using object head
+1:
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..b628e57
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,23 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $$0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    movl      rSELF,%eax
+    testl     rINST,rINST               # did we store null?
+    movl      offThread_cardTable(%eax),%eax  # get card table base
+    je        1f                            # skip card mark if null store
+    shrl      $$GC_CARD_SHIFT,%ecx          # object head to card number
+    movb      %al,(%eax,%ecx)               # mark card based on object head
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..3959c06
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT_OBJECT.S"
diff --git a/vm/mterp/x86/OP_IPUT_QUICK.S b/vm/mterp/x86/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..cd4fe3b
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $$0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST,rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_SHORT.S b/vm/mterp/x86/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..299953a
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINSTw", "sqnum":"4" }
diff --git a/vm/mterp/x86/OP_IPUT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..caf007e
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S"
diff --git a/vm/mterp/x86/OP_IPUT_WIDE.S b/vm/mterp/x86/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..d481d02
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE.S
@@ -0,0 +1,55 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit instance field put.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  ... which returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    GET_VREG_WORD %ecx rINST 0                  # ecx<- lsw
+    GET_VREG_WORD rINST rINST 1                 # rINST<- msw
+    movl    rINST,4(%eax)
+    movl    %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S b/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..12eeed6
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl      %ecx,%ecx                # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%ecx        # ecx<- Address of 64-bit target
+    andb      $$0xf,rINSTbl             # rINST<- A
+    GET_VREG_WORD %eax rINST 0          # eax<- lsw
+    GET_VREG_WORD rINST rINST 1         # rINST<- msw
+    movl      %eax,(%ecx)
+    movl      rINST,4(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_LONG_TO_DOUBLE.S b/vm/mterp/x86/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..7235315
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildll","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_LONG_TO_FLOAT.S b/vm/mterp/x86/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..2c4359a
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildll","store":"fstps"}
diff --git a/vm/mterp/x86/OP_LONG_TO_INT.S b/vm/mterp/x86/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..bf5060f
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+%include "x86/OP_MOVE.S"
diff --git a/vm/mterp/x86/OP_MONITOR_ENTER.S b/vm/mterp/x86/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..a630db1
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_ENTER.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    FETCH_INST_WORD 1
+    testl   %eax,%eax                   # null object?
+    EXPORT_PC                           # need for precise GC
+    je     common_errNullObject
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmLockObject               # dvmLockObject(self,object)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MONITOR_EXIT.S b/vm/mterp/x86/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..98bc373
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_EXIT.S
@@ -0,0 +1,29 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_VREG_R %eax rINST
+    movl    rSELF,%ecx
+    EXPORT_PC
+    testl   %eax,%eax                   # null object?
+    je      .L${opcode}_errNullObject   # go if so
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmUnlockObject             # unlock(self,obj)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    testl   %eax,%eax                   # success?
+    ADVANCE_PC 1
+    je      common_exceptionThrown      # no, exception pending
+    GOTO_NEXT_R %ecx
+.L${opcode}_errNullObject:
+    ADVANCE_PC 1                        # advance before throw
+    jmp     common_errNullObject
diff --git a/vm/mterp/x86/OP_MOVE.S b/vm/mterp/x86/OP_MOVE.S
new file mode 100644
index 0000000..ec05288
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $$0xf,%al             # eax<- A
+    shrl   $$4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_16.S b/vm/mterp/x86/OP_MOVE_16.S
new file mode 100644
index 0000000..e25230a
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    movzwl    4(rPC),%ecx              # ecx<- BBBB
+    movzwl    2(rPC),%eax              # eax<- AAAA
+    GET_VREG_R  rINST %ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG  rINST %eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_EXCEPTION.S b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..0853866
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* move-exception vAA */
+    movl    rSELF,%ecx
+    movl    offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+    SET_VREG %eax rINST                # fp[AA]<- exception object
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    movl    $$0,offThread_exception(%ecx) # dvmClearException bypass
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_MOVE_FROM16.S b/vm/mterp/x86/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..120edb5
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    movzx    rINSTbl,%eax              # eax <= AA
+    movw     2(rPC),rINSTw             # rINSTw <= BBBB
+    GET_VREG_R rINST rINST             # rINST- fp[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG rINST %eax                # fp[AA]<- ecx]
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT.S b/vm/mterp/x86/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..f32d5a6
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE.S"
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT_16.S b/vm/mterp/x86/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..859e4db
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_16.S"
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..fef4401
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/x86/OP_MOVE_RESULT.S b/vm/mterp/x86/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..4a6917b
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    movl     rSELF,%eax                    # eax<- rSELF
+    movl     offThread_retval(%eax),%eax   # eax<- self->retval.l
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG  %eax rINST                   # fp[AA]<- retval.l
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..a3f1b1f
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S b/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..022bdb3
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* move-result-wide vAA */
+    movl    rSELF,%ecx
+    movl    offThread_retval(%ecx),%eax
+    movl    4+offThread_retval(%ecx),%ecx
+    SET_VREG_WORD %eax rINST 0     # v[AA+0] <- eax
+    SET_VREG_WORD %ecx rINST 1     # v[AA+1] <- ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_WIDE.S b/vm/mterp/x86/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..df59574
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzbl    rINSTbl,%ecx                # ecx <- BA
+    sarl      $$4,rINST                   # rINST<- B
+    GET_VREG_WORD %eax rINST 0            # eax<- v[B+0]
+    GET_VREG_WORD rINST rINST 1           # rINST<- v[B+1]
+    andb      $$0xf,%cl                   # ecx <- A
+    SET_VREG_WORD rINST %ecx 1            # v[A+1]<- rINST
+    SET_VREG_WORD %eax %ecx 0             # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_WIDE_16.S b/vm/mterp/x86/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..26ea8a4
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    4(rPC),%ecx            # ecx<- BBBB
+    movzwl    2(rPC),%eax            # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0       # rINSTw_WORD<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1        # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0       # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1        # v[AAAA+1]<- ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S b/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..3d820ad
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    2(rPC),%ecx              # ecx<- BBBB
+    movzbl    rINSTbl,%eax             # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0         # rINST<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1          # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0         # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1          # v[AAAA+1]<- eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_DOUBLE.S b/vm/mterp/x86/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..7d74f78
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_MUL_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    mulsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d277689
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,15 @@
+   /*
+    * File: OP_MUL_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $$0xf,%cl               # ecx<- A
+    sarl        $$4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    mulsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_MUL_FLOAT.S b/vm/mterp/x86/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..5515c4f
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fmuls","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..c8a3bf7
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fmuls","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_MUL_INT.S b/vm/mterp/x86/OP_MUL_INT.S
new file mode 100644
index 0000000..a958114
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * 32-bit binary multiplication.
+     */
+    /* mul vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    SPILL(rIBASE)
+    imull    (rFP,%ecx,4),%eax      # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_INT_2ADDR.S b/vm/mterp/x86/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..ebd5160
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* mul vA, vB */
+    movzx   rINSTbl,%ecx              # ecx<- A+
+    sarl    $$4,rINST                 # rINST<- B
+    GET_VREG_R %eax rINST             # eax<- vB
+    andb    $$0xf,%cl                 # ecx<- A
+    SPILL(rIBASE)
+    imull   (rFP,%ecx,4),%eax         # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_INT_LIT16.S b/vm/mterp/x86/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..a196c7e
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT16.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /* mul/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $$4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $$0xf,rINSTbl              # rINST<- A
+    SPILL(rIBASE)
+    imull     %ecx,%eax                 # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_INT_LIT8.S b/vm/mterp/x86/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..f36ffc7
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT8.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* mul/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    SPILL(rIBASE)
+    imull     %ecx,%eax                # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG  %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_LONG.S b/vm/mterp/x86/OP_MUL_LONG.S
new file mode 100644
index 0000000..7cf6ccb
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG.S
@@ -0,0 +1,37 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * We could definately use more free registers for
+     * this code.   We spill rINSTw (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill edi (rFP)
+     * for use as the vB pointer and esi (rPC) for use
+     * as the vC pointer.  Yuck.
+     */
+    /* mul-long vAA, vBB, vCC */
+    movzbl    2(rPC),%eax              # eax<- B
+    movzbl    3(rPC),%ecx              # ecx<- C
+    SPILL_TMP2(%esi)                   # save Dalvik PC
+    SPILL(rFP)
+    SPILL(rINST)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # esi<- &v[B]
+    leal      (rFP,%ecx,4),rFP         # rFP<- &v[C]
+    movl      4(%esi),%ecx             # ecx<- Bmsw
+    imull     (rFP),%ecx               # ecx<- (Bmsw*Clsw)
+    movl      4(rFP),%eax              # eax<- Cmsw
+    imull     (%esi),%eax              # eax<- (Cmsw*Blsw)
+    addl      %eax,%ecx                # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+    movl      (rFP),%eax               # eax<- Clsw
+    mull      (%esi)                   # eax<- (Clsw*Alsw)
+    UNSPILL(rINST)
+    UNSPILL(rFP)
+    leal      (%ecx,rIBASE),rIBASE # full result now in rIBASE:%eax
+    UNSPILL_TMP2(%esi)             # Restore Dalvik PC
+    FETCH_INST_OPCODE 2 %ecx       # Fetch next instruction
+    movl      rIBASE,4(rFP,rINST,4)# v[B+1]<- rIBASE
+    UNSPILL(rIBASE)
+    movl      %eax,(rFP,rINST,4)   # v[B]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_LONG_2ADDR.S b/vm/mterp/x86/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..9a0930c
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,37 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply, 2-addr version
+     *
+     * We could definately use more free registers for
+     * this code.  We must spill %edx (rIBASE) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and rIBASE as computational
+     * temps.  On top of that, we'll spill %esi (edi)
+     * for use as the vA pointer and rFP (esi) for use
+     * as the vB pointer.  Yuck.
+     */
+    /* mul-long/2addr vA, vB */
+    movzbl    rINSTbl,%eax             # eax<- BA
+    andb      $$0xf,%al                # eax<- A
+    sarl      $$4,rINST                # rINST<- B
+    SPILL_TMP2(%esi)
+    SPILL(rFP)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # %esi<- &v[A]
+    leal      (rFP,rINST,4),rFP        # rFP<- &v[B]
+    movl      4(%esi),%ecx             # ecx<- Amsw
+    imull     (rFP),%ecx               # ecx<- (Amsw*Blsw)
+    movl      4(rFP),%eax              # eax<- Bmsw
+    imull     (%esi),%eax              # eax<- (Bmsw*Alsw)
+    addl      %eax,%ecx                # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+    movl      (rFP),%eax               # eax<- Blsw
+    mull      (%esi)                   # eax<- (Blsw*Alsw)
+    leal      (%ecx,rIBASE),rIBASE     # full result now in %edx:%eax
+    movl      rIBASE,4(%esi)           # v[A+1]<- rIBASE
+    movl      %eax,(%esi)              # v[A]<- %eax
+    UNSPILL_TMP2(%esi)
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    UNSPILL(rFP)
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_NEG_DOUBLE.S b/vm/mterp/x86/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..7b24914
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"instr":"fchs","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_NEG_FLOAT.S b/vm/mterp/x86/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..e785155
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"instr":"fchs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_NEG_INT.S b/vm/mterp/x86/OP_NEG_INT.S
new file mode 100644
index 0000000..4dfd6d3
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"negl %eax"}
diff --git a/vm/mterp/x86/OP_NEG_LONG.S b/vm/mterp/x86/OP_NEG_LONG.S
new file mode 100644
index 0000000..9543351
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_LONG.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx        # ecx<- BA
+    sarl      $$4,%ecx            # ecx<- B
+    andb      $$0xf,rINSTbl       # rINST<- A
+    GET_VREG_WORD %eax %ecx 0     # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1     # ecx<- v[B+1]
+    negl      %eax
+    adcl      $$0,%ecx
+    negl      %ecx
+    SET_VREG_WORD %eax rINST 0    # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1    # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_NEW_ARRAY.S b/vm/mterp/x86/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..c9690d3
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_ARRAY.S
@@ -0,0 +1,64 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movl    offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    movzwl  2(rPC),%eax                       # eax<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx  # ecx<- pDvmDex->pResClasses
+    SPILL(rIBASE)
+    movl    (%ecx,%eax,4),%ecx                # ecx<- resolved class
+    movzbl  rINSTbl,%eax
+    sarl    $$4,%eax                          # eax<- B
+    GET_VREG_R %eax %eax                      # eax<- vB (array length)
+    andb    $$0xf,rINSTbl                     # rINST<- A
+    testl   %eax,%eax
+    js      common_errNegativeArraySize       # bail, passing len in eax
+    testl   %ecx,%ecx                         # already resolved?
+    jne     .L${opcode}_finish                # yes, fast path
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *  ecx holds class (null here)
+     *  eax holds array length (vB)
+     */
+    movl    rSELF,%ecx
+    SPILL_TMP1(%eax)                   # save array length
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movzwl  2(rPC),%eax                # eax<- CCCC
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $$0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass            # eax<- call(clazz,ref,flag)
+    movl    %eax,%ecx
+    UNSPILL_TMP1(%eax)
+    testl   %ecx,%ecx                  # successful resolution?
+    je      common_exceptionThrown     # no, bail.
+# fall through to ${opcode}_finish
+
+    /*
+     * Finish allocation
+     *
+     * ecx holds class
+     * eax holds array length (vB)
+     */
+.L${opcode}_finish:
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $$ALLOC_DONT_TRACK,OUT_ARG2(%esp)
+    call    dvmAllocArrayByClass    # eax<- call(clazz,length,flags)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    testl   %eax,%eax               # failed?
+    je      common_exceptionThrown  # yup - go handle
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_NEW_INSTANCE.S b/vm/mterp/x86/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..5638e10
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_INSTANCE.S
@@ -0,0 +1,105 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)
+    SPILL_TMP2(%ebx)
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    EXPORT_PC
+#if defined(WITH_JIT)
+    lea       (%ecx,%eax,4),%ebx        # ebx <- &resolved class
+#endif
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved class
+    testl     %ecx,%ecx                 # resolved?
+    je        .L${opcode}_resolve       # no, go do it
+.L${opcode}_resolved:  # on entry, ecx<- class
+    cmpb      $$CLASS_INITIALIZED,offClassObject_status(%ecx)
+    jne       .L${opcode}_needinit
+.L${opcode}_initialized:  # on entry, ecx<- class
+    movl      $$ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmAllocObject             # eax<- new object
+    testl    %eax,%eax                  # success?
+    je       common_exceptionThrown     # no, bail out
+#if defined(WITH_JIT)
+        /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    movl    rSELF, %ecx
+    movl    offThread_subMode(%ecx), %ecx
+    andl    $$kSubModeJitTraceBuild, %ecx # under construction?
+    jne     .L${opcode}_jitCheck
+#endif
+.L${opcode}_end:
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * eax: new object
+     */
+.L${opcode}_jitCheck:
+    cmp     $$0, (%ebx)                   # okay?
+    jne     .L${opcode}_end        # yes, finish
+    SPILL_TMP1(%eax)                     # preserve new object
+    movl    rSELF, %ecx
+    movl    %ecx, OUT_ARG0(%esp)
+    movl    rPC, OUT_ARG1(%esp)
+    call    dvmJitEndTraceSelect         # (self, pc)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST                  # vAA <- new object
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  ecx holds class object
+     */
+.L${opcode}_needinit:
+    SPILL_TMP1(%ecx)                    # save object
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmInitClass                # initialize class
+    UNSPILL_TMP1(%ecx)                  # restore object
+    testl   %eax,%eax                   # success?
+    jne     .L${opcode}_initialized     # success, continue
+    jmp     common_exceptionThrown      # go deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     */
+.L${opcode}_resolve:
+    movl    rSELF,%ecx
+    movzwl  2(rPC),%eax
+    movl    offThread_method(%ecx),%ecx   # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    movl    $$0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass             # call(clazz,off,flags)
+    movl    %eax,%ecx                   # ecx<- resolved ClassObject ptr
+    testl   %ecx,%ecx                   # success?
+    jne     .L${opcode}_resolved        # good to go
+    jmp     common_exceptionThrown      # no, handle exception
diff --git a/vm/mterp/x86/OP_NOP.S b/vm/mterp/x86/OP_NOP.S
new file mode 100644
index 0000000..99fa2b6
--- /dev/null
+++ b/vm/mterp/x86/OP_NOP.S
@@ -0,0 +1,4 @@
+%verify "executed"
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_NOT_INT.S b/vm/mterp/x86/OP_NOT_INT.S
new file mode 100644
index 0000000..c910716
--- /dev/null
+++ b/vm/mterp/x86/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"notl %eax"}
diff --git a/vm/mterp/x86/OP_NOT_LONG.S b/vm/mterp/x86/OP_NOT_LONG.S
new file mode 100644
index 0000000..54e1c44
--- /dev/null
+++ b/vm/mterp/x86/OP_NOT_LONG.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- BA
+    sarl      $$4,%ecx           # ecx<- B
+    andb      $$0xf,rINSTbl      # rINST<- A
+    GET_VREG_WORD %eax %ecx 0    # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1    # ecx<- v[B+1]
+    notl      %eax
+    notl      %ecx
+    SET_VREG_WORD %eax rINST 0   # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1   # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_OR_INT.S b/vm/mterp/x86/OP_OR_INT.S
new file mode 100644
index 0000000..9453bfd
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"orl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_OR_INT_2ADDR.S b/vm/mterp/x86/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..db69633
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"orl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_OR_INT_LIT16.S b/vm/mterp/x86/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..fa70e99
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"orl     %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_OR_INT_LIT8.S b/vm/mterp/x86/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..5761806
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"orl     %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_OR_LONG.S b/vm/mterp/x86/OP_OR_LONG.S
new file mode 100644
index 0000000..90f0e3e
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"orl (rFP,%ecx,4),rIBASE", "instr2":"orl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_OR_LONG_2ADDR.S b/vm/mterp/x86/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..52d740e
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"orl %eax,(rFP,rINST,4)","instr2":"orl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_PACKED_SWITCH.S b/vm/mterp/x86/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..af0df62
--- /dev/null
+++ b/vm/mterp/x86/OP_PACKED_SWITCH.S
@@ -0,0 +1,28 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    movl    2(rPC),%ecx           # ecx<- BBBBbbbb
+    GET_VREG_R %eax rINST         # eax<- vAA
+    leal    (rPC,%ecx,2),%ecx     # ecx<- PC + BBBBbbbb*2
+    movl    %eax,OUT_ARG1(%esp)   # ARG1<- vAA
+    movl    %ecx,OUT_ARG0(%esp)   # ARG0<- switchData
+    call    $func
+    movl    rSELF,%ecx
+    ADVANCE_PC_INDEXED %eax
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_DOUBLE.S b/vm/mterp/x86/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..d670a31
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    fldl     (rFP,%ecx,4)           # vCC to fp stack
+    fldl     (rFP,%eax,4)           # vCC to fp stack
+    FETCH_INST_OPCODE 2 %ecx
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC 2
+    fstpl    (rFP,rINST,4)           # %st to vAA
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..ee751d0
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $$4,rINST                  # rINST<- B
+    fldl     (rFP,rINST,4)              # vBB to fp stack
+    andb    $$0xf,%cl                   # ecx<- A
+    fldl     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstpl    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_REM_FLOAT.S b/vm/mterp/x86/OP_REM_FLOAT.S
new file mode 100644
index 0000000..342bf3e
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_FLOAT.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    flds     (rFP,%ecx,4)           # vCC to fp stack
+    flds     (rFP,%eax,4)           # vCC to fp stack
+    movzbl   rINSTbl,%ecx           # ecx<- AA
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    fstps    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..1f494bd
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $$4,rINST                  # rINST<- B
+    flds     (rFP,rINST,4)              # vBB to fp stack
+    andb    $$0xf,%cl                   # ecx<- A
+    flds     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_REM_INT.S b/vm/mterp/x86/OP_REM_INT.S
new file mode 100644
index 0000000..6e4fd04
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_2ADDR.S b/vm/mterp/x86/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..4a11617
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv2addr.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_LIT16.S b/vm/mterp/x86/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..5c4afd6
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit16.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_LIT8.S b/vm/mterp/x86/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..53e12ee
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit8.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_LONG.S b/vm/mterp/x86/OP_REM_LONG.S
new file mode 100644
index 0000000..ad8091c
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_DIV_LONG.S" {"routine":"__moddi3","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_LONG_2ADDR.S b/vm/mterp/x86/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..f6e6d61
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_DIV_LONG_2ADDR.S" {"routine":"__moddi3","special":"$0"}
diff --git a/vm/mterp/x86/OP_RETURN.S b/vm/mterp/x86/OP_RETURN.S
new file mode 100644
index 0000000..082b82e
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 32-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    movl    %eax,offThread_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RETURN_OBJECT.S b/vm/mterp/x86/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..1a2b83e
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_RETURN.S"
diff --git a/vm/mterp/x86/OP_RETURN_VOID.S b/vm/mterp/x86/OP_RETURN_VOID.S
new file mode 100644
index 0000000..4d4291f
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_VOID.S
@@ -0,0 +1,2 @@
+%verify "executed"
+    jmp       common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RETURN_WIDE.S b/vm/mterp/x86/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..c8e7df5
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 64-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    movl    rSELF,%ecx
+    GET_VREG_WORD %eax rINST 0       # eax<- v[AA+0]
+    GET_VREG_WORD rINST rINST 1      # rINST<- v[AA+1]
+    movl    %eax,offThread_retval(%ecx)
+    movl    rINST,4+offThread_retval(%ecx)
+    jmp     common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RSUB_INT.S b/vm/mterp/x86/OP_RSUB_INT.S
new file mode 100644
index 0000000..fa9b410
--- /dev/null
+++ b/vm/mterp/x86/OP_RSUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"subl %eax,%ecx","result":"%ecx"}
diff --git a/vm/mterp/x86/OP_RSUB_INT_LIT8.S b/vm/mterp/x86/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..81f49a7
--- /dev/null
+++ b/vm/mterp/x86/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"subl  %eax,%ecx" , "result":"%ecx"}
diff --git a/vm/mterp/x86/OP_SGET.S b/vm/mterp/x86/OP_SGET.S
new file mode 100644
index 0000000..27d63b1
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET.S
@@ -0,0 +1,53 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SGET_BOOLEAN.S b/vm/mterp/x86/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_BYTE.S b/vm/mterp/x86/OP_SGET_BYTE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_CHAR.S b/vm/mterp/x86/OP_SGET_CHAR.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_OBJECT.S b/vm/mterp/x86/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_SHORT.S b/vm/mterp/x86/OP_SGET_SHORT.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_VOLATILE.S b/vm/mterp/x86/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_WIDE.S b/vm/mterp/x86/OP_SGET_WIDE.S
new file mode 100644
index 0000000..be97017
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_WIDE.S
@@ -0,0 +1,54 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SGET handler.
+     *
+     */
+    /* sget-wide vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%ecx    # ecx<- lsw
+    movl      4+offStaticField_value(%eax),%eax  # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SHL_INT.S b/vm/mterp/x86/OP_SHL_INT.S
new file mode 100644
index 0000000..a72a272
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"sall    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_INT_2ADDR.S b/vm/mterp/x86/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..c8e1dfe
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"sall    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_INT_LIT8.S b/vm/mterp/x86/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..8c7f007
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"sall  %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_LONG.S b/vm/mterp/x86/OP_SHL_LONG.S
new file mode 100644
index 0000000..0750f80
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shl-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rINST */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # ecx<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $$32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SHL_LONG_2ADDR.S b/vm/mterp/x86/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..b8bbce9
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,29 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $$0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $$4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R  %ecx %ecx              # ecx<- vBB
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $$32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SHR_INT.S b/vm/mterp/x86/OP_SHR_INT.S
new file mode 100644
index 0000000..febc429
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"sarl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_INT_2ADDR.S b/vm/mterp/x86/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..89c6625
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"sarl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_INT_LIT8.S b/vm/mterp/x86/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..77ddd23
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"sarl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_LONG.S b/vm/mterp/x86/OP_SHR_LONG.S
new file mode 100644
index 0000000..d79d624
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $$31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SHR_LONG_2ADDR.S b/vm/mterp/x86/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..d380e12
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,29 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx         # ecx<- BA
+    andb      $$0xf,rINSTbl        # rINST<- A
+    GET_VREG_WORD %eax rINST 0     # eax<- v[AA+0]
+    sarl      $$4,%ecx             # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1   # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx           # ecx<- vBB
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $$31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1   # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0    # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SPARSE_SWITCH.S b/vm/mterp/x86/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..4cedd40
--- /dev/null
+++ b/vm/mterp/x86/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/x86/OP_SPUT.S b/vm/mterp/x86/OP_SPUT.S
new file mode 100644
index 0000000..57a1543
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT.S
@@ -0,0 +1,53 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
\ No newline at end of file
diff --git a/vm/mterp/x86/OP_SPUT_BOOLEAN.S b/vm/mterp/x86/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_BYTE.S b/vm/mterp/x86/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_CHAR.S b/vm/mterp/x86/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_OBJECT.S b/vm/mterp/x86/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..95a0a34
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_OBJECT.S
@@ -0,0 +1,57 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:                              # field ptr in eax
+    movzbl    rINSTbl,%ecx                       # ecx<- AA
+    GET_VREG_R  %ecx %ecx
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    je        1f                                 # skip card mark if null
+    movl      rSELF,%ecx
+    movl      offField_clazz(%eax),%eax          # eax<- method->clazz
+    movl      offThread_cardTable(%ecx),%ecx       # get card table base
+    shrl      $$GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..afc6668
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT_OBJECT.S"
diff --git a/vm/mterp/x86/OP_SPUT_SHORT.S b/vm/mterp/x86/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_WIDE.S b/vm/mterp/x86/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..d5825f6
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_WIDE.S
@@ -0,0 +1,55 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    GET_VREG_WORD %ecx rINST 0                  # rINST<- lsw
+    GET_VREG_WORD rINST rINST 1                 # ecx<- msw
+    movl      %ecx,offStaticField_value(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4+offStaticField_value(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SUB_DOUBLE.S b/vm/mterp/x86/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..cf84a65
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_SUB_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    subsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB - vCC
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..05dce86
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,15 @@
+   /*
+    * File: OP_SUB_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $$0xf,%cl               # ecx<- A
+    sarl        $$4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    subsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_SUB_FLOAT.S b/vm/mterp/x86/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..99d11c6
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fsubs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..013334a
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fsubs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_SUB_INT.S b/vm/mterp/x86/OP_SUB_INT.S
new file mode 100644
index 0000000..04fcf21
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"subl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_SUB_INT_2ADDR.S b/vm/mterp/x86/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..0f63b86
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"subl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_SUB_LONG.S b/vm/mterp/x86/OP_SUB_LONG.S
new file mode 100644
index 0000000..485818c
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"subl (rFP,%ecx,4),rIBASE", "instr2":"sbbl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_SUB_LONG_2ADDR.S b/vm/mterp/x86/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..f2a94ea
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"subl %eax,(rFP,rINST,4)","instr2":"sbbl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_THROW.S b/vm/mterp/x86/OP_THROW.S
new file mode 100644
index 0000000..6559a29
--- /dev/null
+++ b/vm/mterp/x86/OP_THROW.S
@@ -0,0 +1,13 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    EXPORT_PC
+    GET_VREG_R %eax rINST              # eax<- exception object
+    movl     rSELF,%ecx                # ecx<- self
+    testl    %eax,%eax                 # null object?
+    je       common_errNullObject
+    movl     %eax,offThread_exception(%ecx) # thread->exception<- obj
+    jmp      common_exceptionThrown
diff --git a/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..c934bdb
--- /dev/null
+++ b/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,16 @@
+%verify executed
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                     # eax<- BBBB
+    movl     offThread_method(%ecx),%ecx       # ecx<- self->method
+    EXPORT_PC
+    movl     %eax,OUT_ARG2(%esp)             # arg2<- BBBB
+    movl     rINST,OUT_ARG1(%esp)            # arg1<- AA
+    movl     %ecx,OUT_ARG0(%esp)             # arg0<- method
+    call     dvmThrowVerificationError       # call(method, kind, ref)
+    jmp      common_exceptionThrown          # handle exception
diff --git a/vm/mterp/x86/OP_UNUSED_3E.S b/vm/mterp/x86/OP_UNUSED_3E.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_3F.S b/vm/mterp/x86/OP_UNUSED_3F.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_40.S b/vm/mterp/x86/OP_UNUSED_40.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_41.S b/vm/mterp/x86/OP_UNUSED_41.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_42.S b/vm/mterp/x86/OP_UNUSED_42.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_43.S b/vm/mterp/x86/OP_UNUSED_43.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_73.S b/vm/mterp/x86/OP_UNUSED_73.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_79.S b/vm/mterp/x86/OP_UNUSED_79.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_7A.S b/vm/mterp/x86/OP_UNUSED_7A.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FF.S b/vm/mterp/x86/OP_UNUSED_FF.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_USHR_INT.S b/vm/mterp/x86/OP_USHR_INT.S
new file mode 100644
index 0000000..04b9210
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"shrl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_INT_2ADDR.S b/vm/mterp/x86/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..02a94ff
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"shrl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_INT_LIT8.S b/vm/mterp/x86/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..24fd087
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"shrl     %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_LONG.S b/vm/mterp/x86/OP_USHR_LONG.S
new file mode 100644
index 0000000..4f3647e
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R  %ecx %ecx               # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1          # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[BB+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_USHR_LONG_2ADDR.S b/vm/mterp/x86/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..d4396b4
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,29 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $$0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $$4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx               # ecx<- vBB
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_XOR_INT.S b/vm/mterp/x86/OP_XOR_INT.S
new file mode 100644
index 0000000..71e4013
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"xorl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_XOR_INT_2ADDR.S b/vm/mterp/x86/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..50880cf
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"xorl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_XOR_INT_LIT16.S b/vm/mterp/x86/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..e62e4df
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"xor    %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_XOR_INT_LIT8.S b/vm/mterp/x86/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..9ccca3f
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"xor    %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_XOR_LONG.S b/vm/mterp/x86/OP_XOR_LONG.S
new file mode 100644
index 0000000..3cd5ffb
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"xorl (rFP,%ecx,4),rIBASE", "instr2":"xorl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_XOR_LONG_2ADDR.S b/vm/mterp/x86/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..6d72124
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"xorl %eax,(rFP,rINST,4)","instr2":"xorl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/alt_stub.S b/vm/mterp/x86/alt_stub.S
new file mode 100644
index 0000000..9807ad1
--- /dev/null
+++ b/vm/mterp/x86/alt_stub.S
@@ -0,0 +1,22 @@
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $$0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(${opnum}*4)
diff --git a/vm/mterp/x86/bincmp.S b/vm/mterp/x86/bincmp.S
new file mode 100644
index 0000000..ae68dd3
--- /dev/null
+++ b/vm/mterp/x86/bincmp.S
@@ -0,0 +1,29 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $$0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $$4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $$2,%eax              # assume not taken
+    j${revcmp}   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/bindiv.S b/vm/mterp/x86/bindiv.S
new file mode 100644
index 0000000..cbd2f3d
--- /dev/null
+++ b/vm/mterp/x86/bindiv.S
@@ -0,0 +1,33 @@
+
+%default {"result":"","special":""}
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    SPILL(rIBASE)
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/bindiv2addr.S b/vm/mterp/x86/bindiv2addr.S
new file mode 100644
index 0000000..cc415be
--- /dev/null
+++ b/vm/mterp/x86/bindiv2addr.S
@@ -0,0 +1,33 @@
+%default {"result":"","special":""}
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/2addr vA, vB */
+    movzx    rINSTbl,%ecx          # eax<- BA
+    SPILL(rIBASE)
+    sarl     $$4,%ecx              # ecx<- B
+    GET_VREG_R %ecx %ecx           # eax<- vBB
+    andb     $$0xf,rINSTbl         # rINST<- A
+    GET_VREG_R %eax rINST          # eax<- vBB
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div2addr
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div2addr
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div2addr:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/bindivLit16.S b/vm/mterp/x86/bindivLit16.S
new file mode 100644
index 0000000..c935367
--- /dev/null
+++ b/vm/mterp/x86/bindivLit16.S
@@ -0,0 +1,34 @@
+%default {"result":"","special":""}
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax         # eax<- 000000BA
+    SPILL(rIBASE)
+    sarl     $$4,%eax             # eax<- B
+    GET_VREG_R %eax %eax          # eax<- vB
+    movswl   2(rPC),%ecx          # ecx<- ssssCCCC
+    andb     $$0xf,rINSTbl        # rINST<- A
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/bindivLit8.S b/vm/mterp/x86/bindivLit8.S
new file mode 100644
index 0000000..94adef4
--- /dev/null
+++ b/vm/mterp/x86/bindivLit8.S
@@ -0,0 +1,31 @@
+%default {"result":"","special":""}
+    /*
+     * 32-bit div/rem "lit8" binary operation.  Handles special case of
+     * op0=minint & op1=-1
+     */
+    /* div/rem/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax        # eax<- BB
+    movsbl    3(rPC),%ecx        # ecx<- ssssssCC
+    SPILL(rIBASE)
+    GET_VREG_R  %eax %eax        # eax<- rBB
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binflop.S b/vm/mterp/x86/binflop.S
new file mode 100644
index 0000000..a432956
--- /dev/null
+++ b/vm/mterp/x86/binflop.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    $load    (rFP,%eax,4)         # vCC to fp stack
+    $instr   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    $store   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binflop2addr.S b/vm/mterp/x86/binflop2addr.S
new file mode 100644
index 0000000..c616d8b
--- /dev/null
+++ b/vm/mterp/x86/binflop2addr.S
@@ -0,0 +1,16 @@
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $$0xf,%cl              # ecx<- A
+    $load    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $$4,rINST             # rINST<- B
+    $instr   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    $store    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/binop.S b/vm/mterp/x86/binop.S
new file mode 100644
index 0000000..af2e908
--- /dev/null
+++ b/vm/mterp/x86/binop.S
@@ -0,0 +1,19 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    $instr                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binop1.S b/vm/mterp/x86/binop1.S
new file mode 100644
index 0000000..38c2daf
--- /dev/null
+++ b/vm/mterp/x86/binop1.S
@@ -0,0 +1,15 @@
+%default {"result":"%eax","tmp":"%ecx"}
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    $instr                          # ex: addl    %ecx,%eax
+    SET_VREG $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binop2addr.S b/vm/mterp/x86/binop2addr.S
new file mode 100644
index 0000000..4d30880
--- /dev/null
+++ b/vm/mterp/x86/binop2addr.S
@@ -0,0 +1,20 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $$4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $$0xf,%cl                  # ecx<- A
+    $instr                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopLit16.S b/vm/mterp/x86/binopLit16.S
new file mode 100644
index 0000000..4ca6c77
--- /dev/null
+++ b/vm/mterp/x86/binopLit16.S
@@ -0,0 +1,21 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $$4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $$0xf,rINSTbl              # rINST<- A
+    $instr                              # for example: addl %ecx, %eax
+    SET_VREG $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopLit8.S b/vm/mterp/x86/binopLit8.S
new file mode 100644
index 0000000..08e2efd
--- /dev/null
+++ b/vm/mterp/x86/binopLit8.S
@@ -0,0 +1,20 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    $instr                             # ex: addl %ecx,%eax
+    SET_VREG   $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopWide.S b/vm/mterp/x86/binopWide.S
new file mode 100644
index 0000000..6114aff
--- /dev/null
+++ b/vm/mterp/x86/binopWide.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    $instr1         # ex: addl   (rFP,%ecx,4),rIBASE
+    $instr2         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopWide2addr.S b/vm/mterp/x86/binopWide2addr.S
new file mode 100644
index 0000000..fa42644
--- /dev/null
+++ b/vm/mterp/x86/binopWide2addr.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $$0xF,rINSTbl             # rINST<- A
+    $instr1         # example: addl   %eax,(rFP,rINST,4)
+    $instr2         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/cvtfp_int.S b/vm/mterp/x86/cvtfp_int.S
new file mode 100644
index 0000000..7590943
--- /dev/null
+++ b/vm/mterp/x86/cvtfp_int.S
@@ -0,0 +1,58 @@
+%default {"srcdouble":"1","tgtlong":"1"}
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $$4,rINST         # rINST<- B
+    .if $srcdouble
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $$0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $$0xf,%cl                # ecx<- A
+    .if $tgtlong
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if $tgtlong
+    movl     $$0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $$0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .L${opcode}_special_case # fix up result
+
+.L${opcode}_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .L${opcode}_isNaN
+    adcl     $$-1,(rFP,%ecx,4)
+    .if $tgtlong
+    adcl     $$-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .L${opcode}_finish
+.L${opcode}_isNaN:
+    movl      $$0,(rFP,%ecx,4)
+    .if $tgtlong
+    movl      $$0,4(rFP,%ecx,4)
+    .endif
+    jmp       .L${opcode}_finish
diff --git a/vm/mterp/x86/entry.S b/vm/mterp/x86/entry.S
new file mode 100644
index 0000000..f97d6a5
--- /dev/null
+++ b/vm/mterp/x86/entry.S
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+    .text
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+/*
+ * bool dvmMterpStdRun(Thread* self)
+ *
+ * Interpreter entry point.  Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+    push    %ebp                 # save caller base pointer
+    movl    %esp, %ebp           # set our %ebp
+    movl    rSELF, %ecx          # get incoming rSELF
+/*
+ * At this point we've allocated one slot on the stack
+ * via push and stack is 8-byte aligned.  Allocate space
+ * for 9 spill slots, 4 local slots, 5 arg slots to bring
+ * us to 16-byte alignment
+ */
+    subl    $$(FRAME_SIZE-4), %esp
+
+/* Spill callee save regs */
+    movl    %edi,EDI_SPILL(%ebp)
+    movl    %esi,ESI_SPILL(%ebp)
+    movl    %ebx,EBX_SPILL(%ebp)
+
+/* Set up "named" registers */
+    movl    offThread_pc(%ecx),rPC
+    movl    offThread_curFrame(%ecx),rFP
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+
+    /* Remember %esp for future "longjmp" */
+    movl    %esp,offThread_bailPtr(%ecx)
+
+    /* Fetch next instruction before potential jump */
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    movl        $$0, offThread_inJitCodeCache(%ecx)
+    cmpl        $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+
+   /* Normal case: start executing the instruction at rPC */
+    GOTO_NEXT
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(Thread* self, bool changeInterp)
+ *
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We're not going to build a standard frame here, so the arg accesses will
+ * look a little strange.
+ *
+ * On entry:
+ *  esp+4 (arg0)  Thread* self
+ *  esp+8 (arg1)  bool changeInterp
+ */
+dvmMterpStdBail:
+    movl    4(%esp),%ecx                 # grab self
+    movl    8(%esp),%eax                 # changeInterp to return reg
+    movl    offThread_bailPtr(%ecx),%esp # Restore "setjmp" esp
+    movl    %esp,%ebp
+    addl    $$(FRAME_SIZE-4), %ebp       # Restore %ebp at point of setjmp
+    movl    EDI_SPILL(%ebp),%edi
+    movl    ESI_SPILL(%ebp),%esi
+    movl    EBX_SPILL(%ebp),%ebx
+    movl    %ebp, %esp                   # strip frame
+    pop     %ebp                         # restore caller's ebp
+    ret                                  # return to dvmMterpStdRun's caller
+
+
+#ifdef WITH_JIT
+    .global     dvmNcgInvokeInterpreter
+    .type       dvmNcgInvokeInterpreter, %function
+/* input: start of method in %eax */
+dvmNcgInvokeInterpreter:
+    movl        %eax, rPC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx                    # %edx<- opcode
+    GOTO_NEXT_R %ecx                    # start executing the instruction at rPC
+#endif
+
+/*
+ * Strings
+ */
+    .section    .rodata
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+
diff --git a/vm/mterp/x86/footer.S b/vm/mterp/x86/footer.S
new file mode 100644
index 0000000..3b5c79e
--- /dev/null
+++ b/vm/mterp/x86/footer.S
@@ -0,0 +1,1011 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Common subroutines and data.
+ */
+
+#if defined(WITH_JIT)
+/*
+ * JIT-related re-entries into the interpreter.  In general, if the
+ * exit from a translation can at some point be chained, the entry
+ * here requires that control arrived via a call, and that the "rp"
+ * on TOS is actually a pointer to a 32-bit cell containing the Dalvik PC
+ * of the next insn to handle.  If no chaining will happen, the entry
+ * should be reached via a direct jump and rPC set beforehand.
+ */
+
+    .global dvmJitToInterpPunt
+/*
+ * The compiler will generate a jump to this entry point when it is
+ * having difficulty translating a Dalvik instruction.  We must skip
+ * the code cache lookup & prevent chaining to avoid bouncing between
+ * the interpreter and code cache. rPC must be set on entry.
+ */
+dvmJitToInterpPunt:
+    GET_PC
+#if defined(WITH_JIT_TUNING)
+    movl   rPC, OUT_ARG0(%esp)
+    call   dvmBumpPunt
+#endif
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    movl        $$0, offThread_inJitCodeCache(%ecx)
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+    .global dvmJitToInterpSingleStep
+/*
+ * Return to the interpreter to handle a single instruction.
+ * Should be reached via a call.
+ * On entry:
+ *   0(%esp)          <= native return address within trace
+ *   rPC              <= Dalvik PC of this instruction
+ *   OUT_ARG0+4(%esp) <= Dalvik PC of next instruction
+ */
+dvmJitToInterpSingleStep:
+/* TODO */
+    call     dvmAbort
+#if 0
+    pop    %eax
+    movl   rSELF, %ecx
+    movl   OUT_ARG0(%esp), %edx
+    movl   %eax,offThread_jitResumeNPC(%ecx)
+    movl   %edx,offThread_jitResumeDPC(%ecx)
+    movl   $$kInterpEntryInstr,offThread_entryPoint(%ecx)
+    movl   $$1,rINST     # changeInterp <= true
+    jmp    common_gotoBail
+#endif
+
+    .global dvmJitToInterpNoChainNoProfile
+/*
+ * Return from the translation cache to the interpreter to do method
+ * invocation.  Check if the translation exists for the callee, but don't
+ * chain to it. rPC must be set on entry.
+ */
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    call   dvmBumpNoChain
+#endif
+    movl   %eax, rPC
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx                # ecx <- self
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmpl   $$0, %eax
+    jz     1f
+    jmp    *%eax                     # exec translation if we've got one
+    # won't return
+1:
+    EXPORT_PC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation from the exit target, but don't attempt to chain.
+ * rPC set on entry.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    call   dvmBumpNoChain
+#endif
+    movl   %ebx, rPC
+    lea    4(%esp), %esp #to recover the esp update due to function call
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx
+    cmpl   $$0,%eax
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1f
+    jmp    *%eax              # jump to tranlation
+    # won't return
+
+/* No Translation - request one */
+1:
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmpl   $$0, %eax          # JIT enabled?
+    jnz    2f                 # Request one if so
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx         # Continue interpreting if not
+    GOTO_NEXT_R %ecx
+2:
+    ## Looks like an EXPORT_PC is needed here. Now jmp to common_selectTrace2
+    movl   $$kJitTSelectRequestHot,%eax # ask for trace select
+    jmp    common_selectTrace
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation for the exit target.  Reached via a call, and
+ * (TOS)->rPC.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    movl   0(%esp), %eax          # get return address
+    movl   %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    lea    4(%esp), %esp #to recover the esp update due to function call
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea    -17(%eax), %ebx #$$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea    -4(%esp), %esp
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread # (pc, self)
+    lea    4(%esp), %esp
+    cmpl   $$0,%eax
+    movl   rSELF, %ecx
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1b                 # no - ask for one
+    movl   %eax,OUT_ARG0(%esp)
+    movl   rINST,OUT_ARG1(%esp)
+    call   dvmJitChain        # Attempt dvmJitChain(codeAddr,chainAddr)
+    cmpl   $$0,%eax           # Success?
+    jz     toInterpreter      # didn't chain - interpret
+    jmp    *%eax
+    # won't return
+
+/*
+ * Placeholder entries for x86 JIT
+ */
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+
+    .global     dvmJitToExceptionThrown
+dvmJitToExceptionThrown: //rPC in
+    movl   rSELF, %edx
+    GET_PC
+    movl   $$0, offThread_inJitCodeCache(%edx)
+    jmp common_exceptionThrown
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+/* one input: the target rPC value */
+    movl        0(%esp), %eax          # get return address
+    movl        %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea         -17(%eax), %ebx #$$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea         4(%esp), %esp
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    #lea         4(%esp), %esp
+    cmp         $$0, %eax
+    je          toInterpreter
+    #lea         -8(%esp), %esp
+    movl        %ebx, OUT_ARG1(%esp)    # %ebx live thorugh dvmJitGetTraceAddrThread
+    movl        %eax, OUT_ARG0(%esp)    # first argument
+    call        dvmJitChain
+    #lea         8(%esp), %esp
+    cmp         $$0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+dvmJitToInterpNoChain: #rPC in eax
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    movl        %eax, rPC
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmp         $$0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+toInterpreter:
+    EXPORT_PC
+    movl        rSELF, %ecx
+    movl        offThread_curHandlerTable(%ecx), rIBASE
+    FETCH_INST
+    movl        offThread_pJitProfTable(%ecx), %eax
+    #Fallthrough
+
+/* ebx holds the pointer to the jit profile table
+   edx has the opCode */
+common_testUpdateProfile:
+    cmp         $$0, %eax
+    je          4f
+/* eax holds the pointer to the jit profile table
+   edx has the opCode
+   rPC points to the next bytecode */
+
+common_updateProfile:
+    # quick & dirty hash
+    movl   rPC, %ecx
+    shrl   $$12, %ecx
+    xorl   rPC, %ecx
+    andl   $$((1<<JIT_PROF_SIZE_LOG_2)-1), %ecx
+    decb   (%ecx,%eax)
+    #jmp    1f # remove
+    jz     2f
+1:
+    GOTO_NEXT
+2:
+common_Profile:
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection.  First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now.
+ */
+    SPILL(rIBASE)
+    SPILL_TMP1(rINST)
+    movl        rSELF, rIBASE
+    GET_JIT_THRESHOLD rIBASE rINST  # leaves rSELF in %ecx
+    EXPORT_PC
+    movb   rINSTbl,(%ecx,%eax)   # reset counter
+    movl   rIBASE,rINST            # preserve rSELF
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   rIBASE,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    UNSPILL(rIBASE)
+    movl   %eax,offThread_inJitCodeCache(rINST)   # set the inJitCodeCache flag
+    UNSPILL_TMP1(rINST)
+    cmpl   $$0,%eax
+    #jmp    1f # remove
+    jz     1f
+    jmp   *%eax        # TODO: decide call vs/ jmp!.  No return either way
+1:
+    movl   $$kJitTSelectRequest,%eax
+    # On entry, eax<- jitState, rPC valid
+common_selectTrace:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movzwl      offThread_subMode(%ebx), %ecx
+    and         $$(kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+    movl        %eax, offThread_jitState(%ebx)
+    movl        rSELF, %eax
+    movl       %eax, OUT_ARG0(%esp)
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   mov          EBX_SPILL(%ebp), %ebx
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+common_selectTrace2:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movl        %ebx, OUT_ARG0(%esp)
+    movl        %eax, offThread_jitState(%ebx)
+    movzwl      offThread_subMode(%ebx), %ecx
+    mov         EBX_SPILL(%ebp), %ebx
+    and         (kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+
+
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+#endif
+
+/*
+ * For the invoke codes we need to know what register holds the "this" pointer. However
+ * it seems the this pointer is assigned consistently most times it is in %ecx but other
+ * times it is in OP_INVOKE_INTERFACE, OP_INVOKE_SUPER_QUICK, or OP_INVOKE_VIRTUAL_QUICK.
+*/
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   ecx = "this"
+ *   rINSTw trashed, must reload
+ *   rIBASE trashed, must reload before resuming interpreter
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $$kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    movzbl      1(rPC),rINST       # rINST<- AA
+    movzwl      4(rPC), %ecx            # %ecx<- CCCC
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+    test        rINST, rINST
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- AA
+    jz          .LinvokeArgsDone        # no args; jump to args done
+
+
+   /*
+    * %eax=methodToCall, %ecx=CCCC, LOCAL0_OFFSET(%ebp)=count,
+    * %edx=&outs (&stackSaveArea).  (very few methods have > 10 args;
+    * could unroll for common cases)
+    */
+
+.LinvokeRangeArgs:
+    movl        %ebx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- save %ebx
+    lea         (rFP, %ecx, 4), %ecx    # %ecx<- &vCCCC
+    shll        $$2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+    subl        LOCAL0_OFFSET(%ebp), %edx       # %edx<- update &outs
+    shrl        $$2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+1:
+    movl        (%ecx), %ebx            # %ebx<- vCCCC
+    lea         4(%ecx), %ecx           # %ecx<- &vCCCC++
+    subl        $$1, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET<- LOCAL0_OFFSET--
+    movl        %ebx, (%edx)            # *outs<- vCCCC
+    lea         4(%edx), %edx           # outs++
+    jne         1b                      # loop if count (LOCAL0_OFFSET(%ebp)) not zero
+    movl        LOCAL1_OFFSET(%ebp), %ebx       # %ebx<- restore %ebx
+    jmp         .LinvokeArgsDone        # continue
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * prepare to copy args to "outs" area of current frame
+    * rIBASE trashed, must reload before resuming interpreter
+    */
+
+common_invokeMethodNoRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $$kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+.LinvokeNewNoRange:
+    movzbl      1(rPC),rINST       # rINST<- BA
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- BA
+    shrl        $$4, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- B
+    je          .LinvokeArgsDone        # no args; jump to args done
+    movzwl      4(rPC), %ecx            # %ecx<- GFED
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+
+   /*
+    * %eax=methodToCall, %ecx=GFED, LOCAL0_OFFSET(%ebp)=count, %edx=outs
+    */
+
+.LinvokeNonRange:
+    cmp         $$2, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 2
+    movl        %ecx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- GFED
+    jl          1f                      # handle 1 arg
+    je          2f                      # handle 2 args
+    cmp         $$4, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 4
+    jl          3f                      # handle 3 args
+    je          4f                      # handle 4 args
+5:
+    andl        $$15, rINST             # rINSTw<- A
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, rINST, 4), %ecx   # %ecx<- vA
+    movl        %ecx, (%edx)            # *outs<- vA
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+4:
+    shr         $$12, %ecx              # %ecx<- G
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vG
+    movl        %ecx, (%edx)            # *outs<- vG
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+3:
+    and         $$0x0f00, %ecx          # %ecx<- 0F00
+    shr         $$8, %ecx               # %ecx<- F
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vF
+    movl        %ecx, (%edx)            # *outs<- vF
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+2:
+    and         $$0x00f0, %ecx          # %ecx<- 00E0
+    shr         $$4, %ecx               # %ecx<- E
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vE
+    movl        %ecx, (%edx)            # *outs<- vE
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+1:
+    and         $$0x000f, %ecx          # %ecx<- 000D
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vD
+    movl        %ecx, -4(%edx)          # *--outs<- vD
+0:
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * find space for the new stack frame, check for overflow
+    */
+
+.LinvokeArgsDone:
+    movzwl      offMethod_registersSize(%eax), %edx # %edx<- methodToCall->regsSize
+    movzwl      offMethod_outsSize(%eax), %ecx # %ecx<- methodToCall->outsSize
+    movl        %eax, LOCAL0_OFFSET(%ebp)       # LOCAL0_OFFSET<- methodToCall
+    shl         $$2, %edx               # %edx<- update offset
+    SAVEAREA_FROM_FP %eax               # %eax<- &StackSaveArea
+    subl        %edx, %eax              # %eax<- newFP; (old savearea - regsSize)
+    movl        rSELF,%edx              # %edx<- pthread
+    movl        %eax, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- &outs
+    subl        $$sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+    movl        offThread_interpStackEnd(%edx), %edx # %edx<- self->interpStackEnd
+    movl        %edx, TMP_SPILL1(%ebp)  # spill self->interpStackEnd
+    shl         $$2, %ecx               # %ecx<- update offset for outsSize
+    movl        %eax, %edx              # %edx<- newSaveArea
+    sub         %ecx, %eax              # %eax<- bottom; (newSaveArea - outsSize)
+    cmp         TMP_SPILL1(%ebp), %eax  # compare interpStackEnd and bottom
+    movl        LOCAL0_OFFSET(%ebp), %eax       # %eax<- restore methodToCall
+    jl          .LstackOverflow         # handle frame overflow
+
+   /*
+    * set up newSaveArea
+    */
+
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP %ecx               # %ecx<- &StackSaveArea
+    movl        %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rSELF,%ecx              # %ecx<- pthread
+    movl        rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+    movl        rPC, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+#if defined(WITH_JIT)
+    movl        $$0, offStackSaveArea_returnAddr(%edx)
+#endif
+
+    /* Any special actions to take? */
+    cmpw        $$0, offThread_subMode(%ecx)
+    jne         2f                     # Yes - handle them
+1:
+    testl       $$ACC_NATIVE, offMethod_accessFlags(%eax) # check for native call
+    movl        %eax, offStackSaveArea_method(%edx) # newSaveArea->method<- method to call
+    jne         .LinvokeNative          # handle native call
+
+   /*
+    * Update "self" values for the new method
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+    */
+    movl        offMethod_clazz(%eax), %edx # %edx<- method->clazz
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %eax, offThread_method(%ecx) # self->method<- methodToCall
+    movl        %edx, offThread_methodClassDex(%ecx) # self->methodClassDex<- method->clazz->pDvmDex
+    movl        offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+    movl        $$1, offThread_debugIsMethodEntry(%ecx)
+    movl        LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+    movl        rFP, offThread_curFrame(%ecx) # curFrame<-newFP
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    /* rPC is already updated */
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT                           # jump to methodToCall->insns
+
+2:
+    /*
+     * On entry, preserve all:
+     *  %eax: method
+     *  %ecx: self
+     *  %edx: new save area
+     */
+    SPILL_TMP1(%eax)                   # preserve methodToCall
+    SPILL_TMP2(%edx)                   # preserve newSaveArea
+    movl        rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl        %ecx, OUT_ARG0(%esp)
+    movl        %eax, OUT_ARG1(%esp)
+    call        dvmReportInvoke        # (self, method)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%edx)
+    movl        rSELF,%ecx             # restore rSELF
+    jmp         1b
+
+   /*
+    * Prep for the native call
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea, %ecx=self
+    */
+
+.LinvokeNative:
+    movl        offThread_jniLocal_topCookie(%ecx), rINST # rINST<- self->localRef->...
+    movl        rINST, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+    movl        %edx, LOCAL2_OFFSET(%ebp)  # save newSaveArea
+    movl        LOCAL1_OFFSET(%ebp), rINST # rINST<- newFP
+    movl        rINST, offThread_curFrame(%ecx)  # curFrame<- newFP
+    cmpw        $$0, offThread_subMode(%ecx)  # Anything special going on?
+    jne         11f                     # yes - handle it
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)    # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+7:
+    movl        LOCAL2_OFFSET(%ebp), %ecx    # %ecx<- newSaveArea
+    movl        rSELF, %eax             # %eax<- self
+    movl        offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+    cmp         $$0, offThread_exception(%eax) # check for exception
+    movl        rFP, offThread_curFrame(%eax) # curFrame<- rFP
+    movl        %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+    jne         common_exceptionThrown  # handle exception
+    movl        offThread_curHandlerTable(%eax),rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx                    # jump to next instruction
+
+11:
+    /*
+     * Handle any special subMode actions
+     * %eax=methodToCall, rINST=newFP, %ecx=self
+     */
+    SPILL_TMP1(%eax)                    # save methodTocall
+    movl        rPC, offThread_pc(%ecx)
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPreNativeInvoke # (methodToCall, self, fp)
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF,%ecx              # restore self
+
+    /* Do the native call */
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)   # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPostNativeInvoke # (methodToCall, self, fp)
+    jmp         7b                      # rejoin
+
+.LstackOverflow:    # eax=methodToCall
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    movl        rSELF,%eax              # %eax<- self
+    movl        %eax, OUT_ARG0(%esp)    # push parameter self
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method* meth)
+    jmp         common_exceptionThrown  # handle exception
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+    movl    rSELF, %ecx
+    SAVEAREA_FROM_FP %eax                       # %eax<- saveArea(old)
+    cmpw    $$0, offThread_subMode(%ecx)          # special action needed?
+    jne     19f                                   # go if so
+14:
+
+    movl        offStackSaveArea_prevFrame(%eax), rFP # rFP<- saveArea->PrevFrame
+    movl        (offStackSaveArea_method - sizeofStackSaveArea)(rFP), rINST # rINST<- method we are returning to
+    cmpl        $$0, rINST               # check for break frame
+    je          common_gotoBail         # bail if break frame
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    movl        offStackSaveArea_savedPc(%eax), rPC # rPC<- saveAreaOld->savedPc
+#if defined(WITH_JIT)
+    movl        offStackSaveArea_returnAddr(%eax), %ecx
+#endif
+    movl        rSELF, %eax
+    movl        rINST, offThread_method(%eax) # glue->method<- newSave->method
+    movl        offMethod_clazz(rINST), rINST # rINST<- method->clazz
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+#if defined(WITH_JIT)
+    //update self->offThread_inJitCodeCache
+    movl        %ecx, offThread_inJitCodeCache(%eax)
+#endif
+    movl        offClassObject_pDvmDex(rINST), rINST # rINST<- method->clazz->pDvmDex
+    movl        rINST, offThread_methodClassDex(%eax) # glue->pDvmDex<- method->clazz->pDvmDex
+#if defined(WITH_JIT)
+    cmp         $$0, %ecx
+    je          .returnToBC
+    movl        %ecx, %eax
+    jmp         *%eax
+#endif
+
+.returnToBC:
+
+#if defined(WITH_JIT)
+    FETCH_INST_OPCODE  3, %ecx                 # %eax<- next instruction hi; fetch, advance
+    // %ecx has the opcode
+    addl         $$6, rPC               # 3*2 = 6
+    SPILL_TMP1   (%ecx)
+    movl         rSELF, %ecx
+    FETCH_INST
+    UNSPILL_TMP1   (%ecx)
+    movzbl      1(rPC), rINST
+    jmp     *(rIBASE,%ecx,4)
+#else
+    FETCH_INST_WORD 3
+    ADVANCE_PC 3
+    GOTO_NEXT
+#endif
+
+19:
+    /*
+     * Handle special subMode actions
+     * On entry, rFP: prevFP, %ecx: self, %eax: saveArea
+     */
+    SPILL_TMP1(%ebx)
+    movl     offStackSaveArea_prevFrame(%eax), %ebx # %ebx<- saveArea->PrevFrame
+    movl     rPC, offThread_pc(%ecx)          # update interpSave.pc
+    movl     %ebx, offThread_curFrame(%ecx)    # update interpSave.curFrame
+    movl     %ecx, OUT_ARG0(%esp)             # parameter self
+    call     dvmReportReturn                  # (self)
+    UNSPILL_TMP1(%ebx)
+    movl     rSELF, %ecx                      # restore self
+    SAVEAREA_FROM_FP %eax                     # restore saveArea
+    jmp      14b
+
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ *    rINST holds changeInterp
+ *    ecx holds self pointer
+ *
+ * expected profile: dvmMterpStdBail(Thread *self, bool changeInterp)
+ */
+common_gotoBail:
+    movl   rPC,offThread_pc(%ecx)     # export state to self
+    movl   rFP,offThread_curFrame(%ecx)
+    movl   %ecx,OUT_ARG0(%esp)      # self in arg0
+    movl   rINST,OUT_ARG1(%esp)     # changeInterp in arg1
+    call   dvmMterpStdBail          # bail out....
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ *
+ * eax = Method* methodToCall
+ * ecx = "this"
+ * edx = rSELF
+ * ebx = free to use
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     $$0, %ecx
+    je      2f
+    movl    offObject_clazz(%ecx), %ecx
+2:
+    movl    rSELF, %ebx
+    movl    %eax, offThread_methodToCall(%ebx)
+    movl    %ecx, offThread_callsiteClass(%ebx)
+    ret
+#endif
+
+#if defined(WITH_JIT)
+
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     %ecx: &dvmDex->pResFields[field]
+     *     %eax:  field pointer (must preserve)
+     */
+common_verifyField:
+    movl    %ebx, TMP_SPILL1(%ebp)
+    movl     rSELF, %ebx
+    movzwl   offThread_subMode(%ebx), %ebx
+    andl     $$kSubModeJitTraceBuild, %ebx
+    movl    TMP_SPILL1(%ebp), %ebx
+    jne      1f
+    ret
+1:
+    movl    (%ecx), %ecx
+    cmp     $$0, %ecx
+    je      1f
+    ret
+1:
+    SPILL_TMP1(%eax)
+    SPILL_TMP2(%edx)
+    movl     rSELF, %ecx
+    # Because we call into this helper from a bytecode, we have
+    # to be careful not to write over the return address when using
+    # the OUT_ARG macros
+    lea      -8(%esp), %esp
+    movl     %ecx, OUT_ARG0(%esp)
+    movl     rPC, OUT_ARG1(%esp)
+    call     dvmJitEndTraceSelect
+    lea      8(%esp), %esp
+    UNSPILL_TMP2(%edx)
+    UNSPILL_TMP1(%eax)
+    ret
+#endif
+
+/*
+ * After returning from a "selfd" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+     movl  rSELF, %eax
+     movl  offThread_pc(%eax),rPC
+     movl  offThread_curFrame(%eax),rFP
+     movl  offThread_curHandlerTable(%eax),rIBASE
+     FETCH_INST
+     GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+    EXPORT_PC
+    movl    $$.LstrDivideByZero,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowArithmeticException
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, len in eax
+ */
+common_errNegativeArraySize:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)                  # arg0<- len
+    call    dvmThrowNegativeArraySizeException   # (len)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, method name in eax
+ */
+common_errNoSuchMethod:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNoSuchMethodError
+    jmp     common_exceptionThrown
+
+/*
+ * Hit a null object when we weren't expecting one.  Export the PC, throw a
+ * NullPointerException and goto the exception processing code.
+ */
+common_errNullObject:
+    EXPORT_PC
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNullPointerException
+    jmp     common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ * On entry:
+ *    eax <- array object
+ *    ecx <- index
+ */
+common_errArrayIndex:
+    EXPORT_PC
+    movl    offArrayObject_length(%eax), %eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmThrowArrayIndexOutOfBoundsException   # args (length, index)
+    jmp     common_exceptionThrown
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * NOTE: special subMode handling done in dvmMterp_exceptionThrown
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC
+    movl       rSELF, %ecx
+    movl       %ecx, OUT_ARG0(%esp)
+    call       dvmCheckSuspendPending
+
+    movl       rSELF, %ecx
+    movl       offThread_exception(%ecx), %edx   # %edx <- self->exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmAddTrackedAlloc      # don't let the exception be GCed
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+    movl       offThread_subMode(%ecx), %eax    # get subMode flags
+    movl       $$0, offThread_exception(%ecx)
+
+    # Special subMode?
+    cmpl       $$0, %eax                # any special subMode handling needed?
+    je         8f                      # go if so
+
+    # Manage debugger bookkeeping
+    movl       rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl       rFP, offThread_curFrame(%ecx) # update interpSave.curFrame
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmReportExceptionThrow # (self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+
+8:
+    /*
+    * set up args and a local for &fp
+    */
+    lea        20(%esp), %esp          # raise %esp
+    movl       rFP, (%esp)               # save fp
+    movl       %esp, %eax              # %eax = &fp
+    lea        -20(%esp), %esp         # reset %esp
+    movl       %eax, OUT_ARG4(%esp)    # Arg 4 = &fp
+    movl       $$0, OUT_ARG3(%esp)      # Arg 3 = false
+    movl       %edx, OUT_ARG2(%esp)    # Arg 2 = exception
+    movl       %ecx, OUT_ARG0(%esp)    # Arg 0 = self
+
+    movl       offThread_method(%ecx), %eax # %eax = self->method
+    movl       offMethod_insns(%eax), %eax  # %eax = self->method->insn
+    movl       rPC, %ecx
+    subl       %eax, %ecx              # %ecx = pc - self->method->insn
+    sar        $$1, %ecx                # adjust %ecx for code offset
+    movl       %ecx, OUT_ARG1(%esp)    # Arg 1 = %ecx
+
+    /* call, %eax gets catchRelPc (a code-unit offset) */
+    SPILL_TMP1(%edx)                   # save exception
+    call       dvmFindCatchBlock       # call(self, relPc, exc, scan?, &fp)
+    UNSPILL_TMP1(%edx)                 # restore exception
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    movl       rSELF, %ecx
+    cmpl       $$0, offThread_stackOverflowed(%ecx) # did we overflow?
+    je         1f                         # no, skip ahead
+    movl       %eax, rFP                  # save relPc result in rFP
+    movl       %ecx, OUT_ARG0(%esp)       # Arg 0 = self
+    movl       %edx, OUT_ARG1(%esp)       # Arg 1 = exception
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow    # call(self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rFP, %eax                  # restore result
+    movl       rSELF, %ecx
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    movl       20(%esp), rFP              # retrieve the updated rFP
+    cmpl       $$0, %eax                  # is catchRelPc < 0?
+    jl         .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP rINST             # rINST<- new save area
+    movl       offStackSaveArea_method(rINST), rINST # rINST<- new method
+    movl       rINST, offThread_method(%ecx)         # self->method = new method
+    movl       offMethod_clazz(rINST), %ecx          # %ecx = method->clazz
+    movl       offMethod_insns(rINST), rINST         # rINST = method->insn
+    movl       offClassObject_pDvmDex(%ecx), %ecx    # %ecx = method->clazz->pDvmDex
+    lea        (rINST, %eax, 2), rPC      # rPC<- method->insns + catchRelPc
+    movl       rSELF, rINST
+    movl       %ecx, offThread_methodClassDex(rINST) # self->pDvmDex = method->clazz->pDvmDex
+
+    /* release the tracked alloc on the exception */
+    movl       %edx, OUT_ARG0(%esp)       # Arg 0 = exception
+    movl       rINST, OUT_ARG1(%esp)      # Arg 1 = self
+    SPILL_TMP1(%edx)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    UNSPILL_TMP1(%edx)
+
+    /* restore the exception if the handler wants it */
+    movl       rSELF, %ecx
+    FETCH_INST
+    movzbl     rINSTbl, %eax
+    cmpl       $$OP_MOVE_EXCEPTION, %eax   # is it "move-exception"?
+    jne        1f
+    movl       %edx, offThread_exception(%ecx) # restore exception
+1:
+    movl       offThread_curHandlerTable(%ecx), rIBASE # refresh rIBASE
+    GOTO_NEXT
+
+.LnotCaughtLocally: # %edx = exception
+    /* fix stack overflow if necessary */
+    movl       rSELF, %ecx
+    movl       offThread_stackOverflowed(%ecx), %eax
+    cmpl       $$0, %eax                   # did we overflow earlier?
+    je         1f
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow
+    UNSPILL_TMP1(%edx)
+
+1:
+    movl       rSELF, %ecx
+    movl       %edx, offThread_exception(%ecx) #restore exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    movl       rSELF, %ecx
+    jmp        common_gotoBail            # bail out
+
+common_abort:
+    movl    $$0xdeadf00d,%eax
+    call     *%eax
+
+
+/*
+ * Strings
+ */
+
+    .section     .rodata
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImplA:
+    .asciz  "filled-new-array only implemented for 'int'"
diff --git a/vm/mterp/x86/fpcvt.S b/vm/mterp/x86/fpcvt.S
new file mode 100644
index 0000000..983b9eb
--- /dev/null
+++ b/vm/mterp/x86/fpcvt.S
@@ -0,0 +1,14 @@
+%default {"instr":"","load":"","store":""}
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $$4,rINST         # rINST<- B
+    $load    (rFP,rINST,4)      # %st0<- vB
+    andb     $$0xf,%cl          # ecx<- A
+    $instr
+    $store  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/header.S b/vm/mterp/x86/header.S
new file mode 100644
index 0000000..16f3d98
--- /dev/null
+++ b/vm/mterp/x86/header.S
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * 32-bit x86 definitions and declarations.
+ */
+
+/*
+386 ABI general notes:
+
+Caller save set:
+   eax, edx, ecx, st(0)-st(7)
+Callee save set:
+   ebx, esi, edi, ebp
+Return regs:
+   32-bit in eax
+   64-bit in edx:eax (low-order 32 in eax)
+   fp on top of fp stack st(0)
+
+Parameters passed on stack, pushed right-to-left.  On entry to target, first
+parm is at 4(%esp).  Traditional entry code is:
+
+functEntry:
+    push    %ebp             # save old frame pointer
+    mov     %ebp,%esp        # establish new frame pointer
+    sub     FrameSize,%esp   # Allocate storage for spill, locals & outs
+
+Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp)
+
+Stack must be 16-byte aligned to support SSE in native code.
+
+If we're not doing variable stack allocation (alloca), the frame pointer can be
+eliminated and all arg references adjusted to be esp relative.
+
+Mterp notes:
+
+Some key interpreter variables will be assigned to registers.  Note that each
+will also have an associated spill location (mostly useful for those assigned
+to callee save registers).
+
+  nick     reg   purpose
+  rPC      esi   interpreted program counter, used for fetching instructions
+  rFP      edi   interpreted frame pointer, used for accessing locals and args
+  rINSTw   bx    first 16-bit code of current instruction
+  rINSTbl  bl    opcode portion of instruction word
+  rINSTbh  bh    high byte of inst word, usually contains src/tgt reg names
+  rIBASE   edx   base of instruction handler table
+
+Notes:
+   o High order 16 bits of ebx must be zero on entry to handler
+   o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit
+   o eax and ecx are scratch, rINSTw/ebx sometimes scratch
+
+*/
+
+#define rSELF    8(%ebp)
+#define rPC      %esi
+#define rFP      %edi
+#define rINST    %ebx
+#define rINSTw   %bx
+#define rINSTbh  %bh
+#define rINSTbl  %bl
+#define rIBASE   %edx
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0        (  8)
+#define CALLER_RP      (  4)
+#define PREV_FP        (  0)
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL      ( -4)
+#define ESI_SPILL      ( -8)
+#define EBX_SPILL      (-12)
+#define rPC_SPILL      (-16)
+#define rFP_SPILL      (-20)
+#define rINST_SPILL    (-24)
+#define rIBASE_SPILL   (-28)
+#define TMP_SPILL1     (-32)
+#define TMP_SPILL2     (-36)
+#define TMP_SPILL3     (-20)
+#define LOCAL0_OFFSET  (-44)
+#define LOCAL1_OFFSET  (-48)
+#define LOCAL2_OFFSET  (-52)
+/* Out Arg offsets, relative to %esp */
+#define OUT_ARG4       ( 16)
+#define OUT_ARG3       ( 12)
+#define OUT_ARG2       (  8)
+#define OUT_ARG1       (  4)
+#define OUT_ARG0       (  0)  /* <- dvmMterpStdRun esp */
+#if defined(WITH_JIT)
+/* for spill region: increase size by 48 (to keep 16-byte alignment) */
+/* 76 + 48 = 124 */
+#define JIT_SPILL      (-56)
+#define FRAME_SIZE     124
+#else
+#define FRAME_SIZE     76
+#endif
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP1(reg) movl reg,TMP_SPILL1(%ebp)
+#define UNSPILL_TMP1(reg) movl TMP_SPILL1(%ebp),reg
+#define SPILL_TMP2(reg) movl reg,TMP_SPILL2(%ebp)
+#define UNSPILL_TMP2(reg) movl TMP_SPILL2(%ebp),reg
+#define SPILL_TMP3(reg) movl reg,TMP_SPILL3(%ebp)
+#define UNSPILL_TMP3(reg) movl TMP_SPILL3(%ebp),reg
+
+#if defined(WITH_JIT)
+.macro GET_JIT_PROF_TABLE _self _reg
+    movl    offThread_pJitProfTable(\_self),\_reg
+.endm
+.macro GET_JIT_THRESHOLD _self _reg
+    movl    offThread_jitThreshold(\_self),\_reg
+.endm
+#endif
+
+/* save/restore the PC and/or FP from the self struct */
+.macro SAVE_PC_FP_TO_SELF _reg
+    movl     rSELF,\_reg
+    movl     rPC,offThread_pc(\_reg)
+    movl     rFP,offThread_curFrame(\_reg)
+.endm
+
+.macro LOAD_PC_FP_FROM_SELF
+    movl    rSELF,rFP
+    movl    offThread_pc(rFP),rPC
+    movl    offThread_curFrame(rFP),rFP
+.endm
+
+/* The interpreter assumes a properly aligned stack on entry, and
+ * will preserve 16-byte alignment.
+ */
+
+/*
+ * "export" the PC to the interpreted stack frame, f/b/o future exception
+ * objects.  Must be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+.macro EXPORT_PC
+    movl     rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+.endm
+
+.macro GET_PC
+    movl     (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP), rPC
+.endm
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+.macro SAVEAREA_FROM_FP _reg
+    leal    -sizeofStackSaveArea(rFP), \_reg
+.endm
+
+/*
+ * Fetch the next instruction from rPC into rINSTw.  Does not advance rPC.
+ */
+.macro FETCH_INST
+    movzwl    (rPC),rINST
+.endm
+
+/*
+ * Fetch the opcode byte and zero-extend it into _reg.  Must be used
+ * in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_R _reg
+    movzbl    (rPC),\_reg
+.endm
+
+/*
+ * Fetch the opcode byte at _count words offset from rPC and zero-extend
+ * it into _reg.  Must be used in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_OPCODE _count _reg
+    movzbl  \_count*2(rPC),\_reg
+.endm
+
+/*
+ * Fetch the nth instruction word from rPC into rINSTw.  Does not advance
+ * rPC, and _count is in words
+ */
+.macro FETCH_INST_WORD _count
+    movzwl  \_count*2(rPC),rINST
+.endm
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+.macro FETCH_INST_INDEXED _reg
+    movzwl  (rPC,\_reg,2),rINST
+.endm
+
+/*
+ * Advance rPC by instruction count
+ */
+.macro ADVANCE_PC _count
+    leal  2*\_count(rPC),rPC
+.endm
+
+/*
+ * Advance rPC by branch offset in register
+ */
+.macro ADVANCE_PC_INDEXED _reg
+    leal (rPC,\_reg,2),rPC
+.endm
+
+.macro GOTO_NEXT
+     movzx   rINSTbl,%eax
+     movzbl  rINSTbh,rINST
+     jmp     *(rIBASE,%eax,4)
+.endm
+
+   /*
+    * Version of GOTO_NEXT that assumes _reg preloaded with opcode.
+    * Should be paired with FETCH_INST_R
+    */
+.macro GOTO_NEXT_R _reg
+     movzbl  1(rPC),rINST
+     jmp     *(rIBASE,\_reg,4)
+.endm
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+.macro GET_VREG_R _reg _vreg
+    movl     (rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG _reg _vreg
+    movl     \_reg,(rFP,\_vreg,4)
+.endm
+
+.macro GET_VREG_WORD _reg _vreg _offset
+    movl     4*(\_offset)(rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG_WORD _reg _vreg _offset
+    movl     \_reg,4*(\_offset)(rFP,\_vreg,4)
+.endm
+
+#define sReg0 LOCAL0_OFFSET(%ebp)
+#define sReg1 LOCAL1_OFFSET(%ebp)
+#define sReg2 LOCAL2_OFFSET(%ebp)
+
+   /*
+    * x86 JIT Helpers
+    */
+
+    .macro dumpSwitch _regData _regScratch1 _regScratch2
+    .endm
+
+   /*
+    * Hard coded helper values.
+    */
+
+.balign 16
+
+.LdoubNeg:
+    .quad       0x8000000000000000
+
+.L64bits:
+    .quad       0xFFFFFFFFFFFFFFFF
+
+.LshiftMask2:
+    .quad       0x0000000000000000
+.LshiftMask:
+    .quad       0x000000000000003F
+
+.Lvalue64:
+    .quad       0x0000000000000040
+
+.LvaluePosInfLong:
+    .quad       0x7FFFFFFFFFFFFFFF
+
+.LvalueNegInfLong:
+    .quad       0x8000000000000000
+
+.LvalueNanLong:
+    .quad       0x0000000000000000
+
+.LintMin:
+.long   0x80000000
+
+.LintMax:
+.long   0x7FFFFFFF
+
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
diff --git a/vm/mterp/x86/shop2addr.S b/vm/mterp/x86/shop2addr.S
new file mode 100644
index 0000000..c891259
--- /dev/null
+++ b/vm/mterp/x86/shop2addr.S
@@ -0,0 +1,15 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $$4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $$0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    $instr                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG $result rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/stub.S b/vm/mterp/x86/stub.S
new file mode 100644
index 0000000..fb5c977
--- /dev/null
+++ b/vm/mterp/x86/stub.S
@@ -0,0 +1,9 @@
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_${opcode}     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
diff --git a/vm/mterp/x86/unop.S b/vm/mterp/x86/unop.S
new file mode 100644
index 0000000..ad9c79b
--- /dev/null
+++ b/vm/mterp/x86/unop.S
@@ -0,0 +1,17 @@
+%default {"pre0":"","pre1":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $$4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $$0xf,%cl              # ecx<- A
+    $pre0
+    $pre1
+    $instr
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/unopWide.S b/vm/mterp/x86/unopWide.S
new file mode 100644
index 0000000..41b5d10
--- /dev/null
+++ b/vm/mterp/x86/unopWide.S
@@ -0,0 +1,21 @@
+%default {"instr1":"","instr2":"","instr3":""}
+    /*
+     * Generic 64-bit unary operation.
+     * Operand in %ecx:%eax
+     *
+     * For: neg-long, not-long
+     */
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx            # ecx<- BA
+    sarl      $$4,%ecx                # ecx<- B
+    andb      $$0xf,rINSTbl           # rINST<- A
+    GET_VREG_WORD %eax %ecx 0         # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1         # ecx<- v[B+1]
+    $instr1   # ex: negl %eax
+    $instr2   # ex: adcl $$0,%ecx
+    $instr3   # ex: negl %ecx
+    SET_VREG_WORD %eax rINST 0        # v[A+0] <- eax
+    GET_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1        # v[A+1] <- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/unused.S b/vm/mterp/x86/unused.S
new file mode 100644
index 0000000..f0f117c
--- /dev/null
+++ b/vm/mterp/x86/unused.S
@@ -0,0 +1 @@
+    jmp     common_abort
diff --git a/vm/mterp/x86/zcmp.S b/vm/mterp/x86/zcmp.S
new file mode 100644
index 0000000..e9fc7d4
--- /dev/null
+++ b/vm/mterp/x86/zcmp.S
@@ -0,0 +1,25 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $$0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $$2,%eax              # assume branch not taken
+    j${revcmp}   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/native/InternalNative.cpp b/vm/native/InternalNative.cpp
new file mode 100644
index 0000000..0d06ea7
--- /dev/null
+++ b/vm/native/InternalNative.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Internal-native initialization and some common utility functions.
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * Set of classes for which we provide methods.
+ *
+ * The last field, classNameHash, is filled in at startup.
+ */
+static DalvikNativeClass gDvmNativeMethodSet[] = {
+    { "Ljava/lang/Object;",               dvm_java_lang_Object, 0 },
+    { "Ljava/lang/Class;",                dvm_java_lang_Class, 0 },
+    { "Ljava/lang/Double;",               dvm_java_lang_Double, 0 },
+    { "Ljava/lang/Float;",                dvm_java_lang_Float, 0 },
+    { "Ljava/lang/Math;",                 dvm_java_lang_Math, 0 },
+    { "Ljava/lang/Runtime;",              dvm_java_lang_Runtime, 0 },
+    { "Ljava/lang/String;",               dvm_java_lang_String, 0 },
+    { "Ljava/lang/System;",               dvm_java_lang_System, 0 },
+    { "Ljava/lang/Throwable;",            dvm_java_lang_Throwable, 0 },
+    { "Ljava/lang/VMClassLoader;",        dvm_java_lang_VMClassLoader, 0 },
+    { "Ljava/lang/VMThread;",             dvm_java_lang_VMThread, 0 },
+    { "Ljava/lang/reflect/AccessibleObject;",
+            dvm_java_lang_reflect_AccessibleObject, 0 },
+    { "Ljava/lang/reflect/Array;",        dvm_java_lang_reflect_Array, 0 },
+    { "Ljava/lang/reflect/Constructor;",
+            dvm_java_lang_reflect_Constructor, 0 },
+    { "Ljava/lang/reflect/Field;",        dvm_java_lang_reflect_Field, 0 },
+    { "Ljava/lang/reflect/Method;",       dvm_java_lang_reflect_Method, 0 },
+    { "Ljava/lang/reflect/Proxy;",        dvm_java_lang_reflect_Proxy, 0 },
+    { "Ljava/util/concurrent/atomic/AtomicLong;",
+            dvm_java_util_concurrent_atomic_AtomicLong, 0 },
+    { "Ldalvik/bytecode/OpcodeInfo;",     dvm_dalvik_bytecode_OpcodeInfo, 0 },
+    { "Ldalvik/system/VMDebug;",          dvm_dalvik_system_VMDebug, 0 },
+    { "Ldalvik/system/DexFile;",          dvm_dalvik_system_DexFile, 0 },
+    { "Ldalvik/system/VMRuntime;",        dvm_dalvik_system_VMRuntime, 0 },
+    { "Ldalvik/system/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()
+{
+    DalvikNativeClass* classPtr = gDvmNativeMethodSet;
+
+    while (classPtr->classDescriptor != NULL) {
+        classPtr->classDescriptorHash =
+            dvmComputeUtf8Hash(classPtr->classDescriptor);
+        classPtr++;
+    }
+
+    gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar);
+    if (gDvm.userDexFiles == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmInternalNativeShutdown()
+{
+    dvmHashTableFree(gDvm.userDexFiles);
+}
+
+/*
+ * Search the internal native set for a match.
+ */
+DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method)
+{
+    const char* classDescriptor = method->clazz->descriptor;
+    const DalvikNativeClass* pClass;
+    u4 hash;
+
+    hash = dvmComputeUtf8Hash(classDescriptor);
+    pClass = gDvmNativeMethodSet;
+    while (true) {
+        if (pClass->classDescriptor == NULL)
+            break;
+        if (pClass->classDescriptorHash == hash &&
+            strcmp(pClass->classDescriptor, classDescriptor) == 0)
+        {
+            const DalvikNativeMethod* pMeth = pClass->methodInfo;
+            while (true) {
+                if (pMeth->name == NULL)
+                    break;
+
+                if (dvmCompareNameDescriptorAndMethod(pMeth->name,
+                    pMeth->signature, method) == 0)
+                {
+                    /* match */
+                    //ALOGV("+++  match on %s.%s %s at %p",
+                    //    className, methodName, methodSignature, pMeth->fnPtr);
+                    return pMeth->fnPtr;
+                }
+
+                pMeth++;
+            }
+        }
+
+        pClass++;
+    }
+
+    return NULL;
+}
+
+
+/*
+ * Magic "internal native" code stub, inserted into abstract method
+ * definitions when a class is first loaded.  This throws the expected
+ * exception so we don't have to explicitly check for it in the interpreter.
+ */
+void dvmAbstractMethodStub(const u4* args, JValue* pResult)
+{
+    ALOGD("--- called into dvmAbstractMethodStub");
+    dvmThrowAbstractMethodError("abstract method not implemented");
+}
+
+
+/*
+ * Verify that "obj" is non-null and is an instance of "clazz".
+ * Used to implement reflection on fields and methods.
+ *
+ * Returns "false" and throws an exception if not.
+ */
+bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz) {
+    ClassObject* exceptionClass = NULL;
+    if (obj == NULL) {
+        exceptionClass = gDvm.exNullPointerException;
+    } else if (!dvmInstanceof(obj->clazz, clazz)) {
+        exceptionClass = gDvm.exIllegalArgumentException;
+    }
+
+    if (exceptionClass == NULL) {
+        return true;
+    }
+
+    std::string expectedClassName(dvmHumanReadableDescriptor(clazz->descriptor));
+    std::string actualClassName(dvmHumanReadableType(obj));
+    dvmThrowExceptionFmt(exceptionClass, "expected receiver of type %s, but got %s",
+            expectedClassName.c_str(), actualClassName.c_str());
+    return false;
+}
+
+/*
+ * Find a class by name, initializing it if requested.
+ */
+ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
+    bool doInit)
+{
+    ClassObject* clazz = NULL;
+    char* name = NULL;
+    char* descriptor = NULL;
+
+    if (nameObj == NULL) {
+        dvmThrowNullPointerException("name == null");
+        goto bail;
+    }
+    name = dvmCreateCstrFromString(nameObj);
+
+    /*
+     * We need to validate and convert the name (from x.y.z to x/y/z).  This
+     * is especially handy for array types, since we want to avoid
+     * auto-generating bogus array classes.
+     */
+    if (!dexIsValidClassName(name, true)) {
+        ALOGW("dvmFindClassByName rejecting '%s'", name);
+        dvmThrowClassNotFoundException(name);
+        goto bail;
+    }
+
+    descriptor = dvmDotToDescriptor(name);
+    if (descriptor == NULL) {
+        goto bail;
+    }
+
+    if (doInit)
+        clazz = dvmFindClass(descriptor, loader);
+    else
+        clazz = dvmFindClassNoInit(descriptor, loader);
+
+    if (clazz == NULL) {
+        LOGVV("FAIL: load %s (%d)", descriptor, doInit);
+        Thread* self = dvmThreadSelf();
+        Object* oldExcep = dvmGetException(self);
+        dvmAddTrackedAlloc(oldExcep, self);     /* don't let this be GCed */
+        dvmClearException(self);
+        dvmThrowChainedClassNotFoundException(name, oldExcep);
+        dvmReleaseTrackedAlloc(oldExcep, self);
+    } else {
+        LOGVV("GOOD: load %s (%d) --> %p ldr=%p",
+            descriptor, doInit, clazz, clazz->classLoader);
+    }
+
+bail:
+    free(name);
+    free(descriptor);
+    return clazz;
+}
+
+/*
+ * We insert native method stubs for abstract methods so we don't have to
+ * check the access flags at the time of the method call.  This results in
+ * "native abstract" methods, which can't exist.  If we see the "abstract"
+ * flag set, clear the "native" flag.
+ *
+ * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+ * position, because the callers of this function are trying to convey
+ * the "traditional" meaning of the flags to their callers.
+ */
+u4 dvmFixMethodFlags(u4 flags)
+{
+    if ((flags & ACC_ABSTRACT) != 0) {
+        flags &= ~ACC_NATIVE;
+    }
+
+    flags &= ~ACC_SYNCHRONIZED;
+
+    if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+        flags |= ACC_SYNCHRONIZED;
+    }
+
+    return flags & JAVA_FLAGS_MASK;
+}
diff --git a/vm/native/InternalNative.h b/vm/native/InternalNative.h
new file mode 100644
index 0000000..006fc1e
--- /dev/null
+++ b/vm/native/InternalNative.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DALVIK_NATIVE_INTERNALNATIVE_H_
+#define DALVIK_NATIVE_INTERNALNATIVE_H_
+
+/*
+ * Some setup for internal native functions.
+ */
+bool dvmInternalNativeStartup(void);
+void dvmInternalNativeShutdown(void);
+
+/* search the internal native set for a match */
+DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method);
+
+/* exception-throwing stub for abstract methods (DalvikNativeFunc) */
+extern "C" void dvmAbstractMethodStub(const u4* args, JValue* pResult);
+
+#endif  // DALVIK_NATIVE_INTERNALNATIVE_H_
diff --git a/vm/native/InternalNativePriv.h b/vm/native/InternalNativePriv.h
new file mode 100644
index 0000000..0925a61
--- /dev/null
+++ b/vm/native/InternalNativePriv.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Declarations and definitions common to internal native code.
+ */
+#ifndef DALVIK_NATIVE_INTERNALNATIVEPRIV_H_
+#define DALVIK_NATIVE_INTERNALNATIVEPRIV_H_
+
+/*
+ * Return macros.  Note we use "->i" instead of "->z" for boolean; this
+ * is because the interpreter expects everything to be a 32-bit value.
+ */
+#ifdef NDEBUG
+# define RETURN_VOID()           do { (void)(pResult); return; } while(0)
+#else
+# define RETURN_VOID()           do { pResult->i = 0xfefeabab; return; }while(0)
+#endif
+#define RETURN_BOOLEAN(_val)    do { pResult->i = (_val); return; } while(0)
+#define RETURN_INT(_val)        do { pResult->i = (_val); return; } while(0)
+#define RETURN_LONG(_val)       do { pResult->j = (_val); return; } while(0)
+#define RETURN_FLOAT(_val)      do { pResult->f = (_val); return; } while(0)
+#define RETURN_DOUBLE(_val)     do { pResult->d = (_val); return; } while(0)
+#define RETURN_PTR(_val)        do { pResult->l = (Object*)(_val); return; } while(0)
+
+/*
+ * Normally a method that has an "inline native" will be invoked using
+ * execute-inline. If the method is invoked via reflection, JNI, or by
+ * virtual dispatch (in the case of String.equals, which we may arrive
+ * at via Object.equals), we need a non-"inline native" implementation.
+ *
+ * This macro is used to implement the native methods that bridge this gap.
+ */
+#define MAKE_INTRINSIC_TRAMPOLINE(INTRINSIC_FN) \
+    extern bool INTRINSIC_FN(u4 arg0, u4 arg1, u4 arg2, u4 arg3, \
+            JValue* pResult); \
+    INTRINSIC_FN(args[0], args[1], args[2], args[3], pResult);
+
+/*
+ * Verify that "obj" is non-null and is an instance of "clazz".
+ *
+ * Returns "false" and throws an exception if not.
+ */
+bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz);
+
+/*
+ * Find a class by name, initializing it if requested.
+ */
+ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
+    bool doInit);
+
+/*
+ * We insert native method stubs for abstract methods so we don't have to
+ * check the access flags at the time of the method call.  This results in
+ * "native abstract" methods, which can't exist.  If we see the "abstract"
+ * flag set, clear the "native" flag.
+ *
+ * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+ * position, because the callers of this function are trying to convey
+ * the "traditional" meaning of the flags to their callers.
+ */
+u4 dvmFixMethodFlags(u4 flags);
+
+/*
+ * dvmHashTableFree callback for some DexFile operations.
+ */
+void dvmFreeDexOrJar(void* vptr);
+
+/*
+ * Tables of methods.
+ */
+extern const DalvikNativeMethod dvm_java_lang_Object[];
+extern const DalvikNativeMethod dvm_java_lang_Class[];
+extern const DalvikNativeMethod dvm_java_lang_Double[];
+extern const DalvikNativeMethod dvm_java_lang_Float[];
+extern const DalvikNativeMethod dvm_java_lang_Math[];
+extern const DalvikNativeMethod dvm_java_lang_Runtime[];
+extern const DalvikNativeMethod dvm_java_lang_String[];
+extern const DalvikNativeMethod dvm_java_lang_System[];
+extern const DalvikNativeMethod dvm_java_lang_Throwable[];
+extern const DalvikNativeMethod dvm_java_lang_VMClassLoader[];
+extern const DalvikNativeMethod dvm_java_lang_VMThread[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Array[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Constructor[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Field[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Method[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Proxy[];
+extern const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[];
+extern const DalvikNativeMethod dvm_dalvik_bytecode_OpcodeInfo[];
+extern const DalvikNativeMethod dvm_dalvik_system_SamplingProfiler[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMDebug[];
+extern const DalvikNativeMethod dvm_dalvik_system_DexFile[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMRuntime[];
+extern const DalvikNativeMethod dvm_dalvik_system_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_H_
diff --git a/vm/native/README.txt b/vm/native/README.txt
new file mode 100644
index 0000000..bc8912f
--- /dev/null
+++ b/vm/native/README.txt
@@ -0,0 +1,23 @@
+Internal native functions.
+
+All of the functions defined here make direct use of VM functions or data
+structures, so they can't be written with JNI and shouldn't really be in
+a separate shared library.  Do not add additional functions here unless
+they need to access VM internals directly.
+
+All functions here either complete quickly or are used to enter a wait
+state, so we don't set the thread status to THREAD_NATIVE when executing
+these methods.  This means that the GC will wait for these functions
+to finish.  DO NOT perform long operations or blocking I/O in here.
+These methods should not be declared "synchronized", because we don't
+check for that flag when issuing the call.
+
+We use "late" binding on these, rather than explicit registration,
+because it's easier to handle the core system classes that way.
+
+The functions here use the DalvikNativeFunc prototype, but we can
+also treat them as DalvikBridgeFunc, which takes two extra arguments.
+The former represents the API that we're most likely to expose should
+JNI performance be deemed insufficient.  The Bridge version is used as
+an optimization for a few high-volume Object calls, and should generally
+not be used as we may drop support for it at some point.
diff --git a/vm/native/dalvik_bytecode_OpcodeInfo.cpp b/vm/native/dalvik_bytecode_OpcodeInfo.cpp
new file mode 100644
index 0000000..3861fef
--- /dev/null
+++ b/vm/native/dalvik_bytecode_OpcodeInfo.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.bytecode.OpcodeInfo
+ *
+ * This file mostly exists in its current form so that we don't have
+ * to have duplicate definitions for things both in libcore and in
+ * libdex.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * public static native boolean isInvoke(int opcode);
+ */
+static void Dalvik_dalvik_bytecode_OpcodeInfo_isInvoke(const u4* args,
+    JValue* pResult)
+{
+    Opcode opcode = static_cast<Opcode>(args[0]);
+    int flags = dexGetFlagsFromOpcode(opcode);
+    bool result = (flags & kInstrInvoke) != 0;
+    RETURN_BOOLEAN(result);
+}
+
+const DalvikNativeMethod dvm_dalvik_bytecode_OpcodeInfo[] = {
+    { "isInvoke", "(I)Z", Dalvik_dalvik_bytecode_OpcodeInfo_isInvoke },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_DexFile.cpp b/vm/native/dalvik_system_DexFile.cpp
new file mode 100644
index 0000000..f328a19
--- /dev/null
+++ b/vm/native/dalvik_system_DexFile.cpp
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.DexFile
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * Return true if the given name ends with ".dex".
+ */
+static bool hasDexExtension(const char* name) {
+    size_t len = strlen(name);
+
+    return (len >= 5)
+        && (name[len - 5] != '/')
+        && (strcmp(&name[len - 4], ".dex") == 0);
+}
+
+/*
+ * Internal struct for managing DexFile.
+ */
+struct DexOrJar {
+    char*       fileName;
+    bool        isDex;
+    bool        okayToFree;
+    RawDexFile* pRawDexFile;
+    JarFile*    pJarFile;
+    u1*         pDexMemory; // malloc()ed memory, if any
+};
+
+/*
+ * (This is a dvmHashTableFree callback.)
+ */
+void dvmFreeDexOrJar(void* vptr)
+{
+    DexOrJar* pDexOrJar = (DexOrJar*) vptr;
+
+    ALOGV("Freeing DexOrJar '%s'", pDexOrJar->fileName);
+
+    if (pDexOrJar->isDex)
+        dvmRawDexFileFree(pDexOrJar->pRawDexFile);
+    else
+        dvmJarFileFree(pDexOrJar->pJarFile);
+    free(pDexOrJar->fileName);
+    free(pDexOrJar->pDexMemory);
+    free(pDexOrJar);
+}
+
+/*
+ * (This is a dvmHashTableLookup compare func.)
+ *
+ * Args are DexOrJar*.
+ */
+static int hashcmpDexOrJar(const void* tableVal, const void* newVal)
+{
+    return (int) newVal - (int) tableVal;
+}
+
+/*
+ * Verify that the "cookie" is a DEX file we opened.
+ *
+ * Expects that the hash table will be *unlocked* here.
+ *
+ * If the cookie is invalid, we throw an exception and return "false".
+ */
+static bool validateCookie(int cookie)
+{
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+
+    LOGVV("+++ dex verifying cookie %p", pDexOrJar);
+
+    if (pDexOrJar == NULL)
+        return false;
+
+    u4 hash = cookie;
+    dvmHashTableLock(gDvm.userDexFiles);
+    void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+                hashcmpDexOrJar, false);
+    dvmHashTableUnlock(gDvm.userDexFiles);
+    if (result == NULL) {
+        dvmThrowRuntimeException("invalid DexFile cookie");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Add given DexOrJar to the hash table of user-loaded dex files.
+ */
+static void addToDexFileTable(DexOrJar* pDexOrJar) {
+    /*
+     * Later on, we will receive this pointer as an argument and need
+     * to find it in the hash table without knowing if it's valid or
+     * not, which means we can't compute a hash value from anything
+     * inside DexOrJar. We don't share DexOrJar structs when the same
+     * file is opened multiple times, so we can just use the low 32
+     * bits of the pointer as the hash.
+     */
+    u4 hash = (u4) pDexOrJar;
+    void* result;
+
+    dvmHashTableLock(gDvm.userDexFiles);
+    result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+            hashcmpDexOrJar, true);
+    dvmHashTableUnlock(gDvm.userDexFiles);
+
+    if (result != pDexOrJar) {
+        ALOGE("Pointer has already been added?");
+        dvmAbort();
+    }
+
+    pDexOrJar->okayToFree = true;
+}
+
+/*
+ * private static int 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.
+ *
+ * TODO: should be using "long" for a pointer.
+ */
+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) {
+        dvmThrowNullPointerException("sourceName == null");
+        RETURN_VOID();
+    }
+
+    sourceName = dvmCreateCstrFromString(sourceNameObj);
+    if (outputNameObj != NULL)
+        outputName = dvmCreateCstrFromString(outputNameObj);
+    else
+        outputName = NULL;
+
+    /*
+     * We have to deal with the possibility that somebody might try to
+     * open one of our bootstrap class DEX files.  The set of dependencies
+     * will be different, and hence the results of optimization might be
+     * different, which means we'd actually need to have two versions of
+     * the optimized DEX: one that only knows about part of the boot class
+     * path, and one that knows about everything in it.  The latter might
+     * optimize field/method accesses based on a class that appeared later
+     * in the class path.
+     *
+     * We can't let the user-defined class loader open it and start using
+     * the classes, since the optimized form of the code skips some of
+     * the method and field resolution that we would ordinarily do, and
+     * we'd have the wrong semantics.
+     *
+     * We have to reject attempts to manually open a DEX file from the boot
+     * class path.  The easiest way to do this is by filename, which works
+     * out because variations in name (e.g. "/system/framework/./ext.jar")
+     * result in us hitting a different dalvik-cache entry.  It's also fine
+     * if the caller specifies their own output file.
+     */
+    if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
+        ALOGW("Refusing to reopen boot DEX '%s'", sourceName);
+        dvmThrowIOException(
+            "Re-opening BOOTCLASSPATH DEX files is not allowed");
+        free(sourceName);
+        free(outputName);
+        RETURN_VOID();
+    }
+
+    /*
+     * Try to open it directly as a DEX if the name ends with ".dex".
+     * If that fails (or isn't tried in the first place), try it as a
+     * Zip with a "classes.dex" inside.
+     */
+    if (hasDexExtension(sourceName)
+            && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
+        ALOGV("Opening DEX file '%s' (DEX)", sourceName);
+
+        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+        pDexOrJar->isDex = true;
+        pDexOrJar->pRawDexFile = pRawDexFile;
+        pDexOrJar->pDexMemory = NULL;
+    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
+        ALOGV("Opening DEX file '%s' (Jar)", sourceName);
+
+        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+        pDexOrJar->isDex = false;
+        pDexOrJar->pJarFile = pJarFile;
+        pDexOrJar->pDexMemory = NULL;
+    } else {
+        ALOGV("Unable to open DEX file '%s'", sourceName);
+        dvmThrowIOException("unable to open DEX file");
+    }
+
+    if (pDexOrJar != NULL) {
+        pDexOrJar->fileName = sourceName;
+        addToDexFileTable(pDexOrJar);
+    } else {
+        free(sourceName);
+    }
+
+    RETURN_PTR(pDexOrJar);
+}
+
+/*
+ * private static int openDexFile(byte[] fileContents) throws IOException
+ *
+ * Open a DEX file represented in a byte[], returning a pointer to our
+ * internal data structure.
+ *
+ * The system will only perform "essential" optimizations on the given file.
+ *
+ * TODO: should be using "long" for a pointer.
+ */
+static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* fileContentsObj = (ArrayObject*) args[0];
+    u4 length;
+    u1* pBytes;
+    RawDexFile* pRawDexFile;
+    DexOrJar* pDexOrJar = NULL;
+
+    if (fileContentsObj == NULL) {
+        dvmThrowNullPointerException("fileContents == null");
+        RETURN_VOID();
+    }
+
+    /* TODO: Avoid making a copy of the array. (note array *is* modified) */
+    length = fileContentsObj->length;
+    pBytes = (u1*) malloc(length);
+
+    if (pBytes == NULL) {
+        dvmThrowRuntimeException("unable to allocate DEX memory");
+        RETURN_VOID();
+    }
+
+    memcpy(pBytes, fileContentsObj->contents, length);
+
+    if (dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile) != 0) {
+        ALOGV("Unable to open in-memory DEX file");
+        free(pBytes);
+        dvmThrowRuntimeException("unable to open in-memory DEX file");
+        RETURN_VOID();
+    }
+
+    ALOGV("Opening in-memory DEX");
+    pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+    pDexOrJar->isDex = true;
+    pDexOrJar->pRawDexFile = pRawDexFile;
+    pDexOrJar->pDexMemory = pBytes;
+    pDexOrJar->fileName = strdup("<memory>"); // Needs to be free()able.
+    addToDexFileTable(pDexOrJar);
+
+    RETURN_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();
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    ALOGV("Closing DEX file %p (%s)", pDexOrJar, pDexOrJar->fileName);
+
+    /*
+     * We can't just free arbitrary DEX files because they have bits and
+     * pieces of loaded classes.  The only exception to this rule is if
+     * they were never used to load classes.
+     *
+     * If we can't free them here, dvmInternalNativeShutdown() will free
+     * them when the VM shuts down.
+     */
+    if (pDexOrJar->okayToFree) {
+        u4 hash = (u4) pDexOrJar;
+        dvmHashTableLock(gDvm.userDexFiles);
+        if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) {
+            ALOGW("WARNING: could not remove '%s' from DEX hash table",
+                pDexOrJar->fileName);
+        }
+        dvmHashTableUnlock(gDvm.userDexFiles);
+        ALOGV("+++ freeing DexFile '%s' resources", pDexOrJar->fileName);
+        dvmFreeDexOrJar(pDexOrJar);
+    } else {
+        ALOGV("+++ NOT freeing DexFile '%s' resources", pDexOrJar->fileName);
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * private static Class defineClass(String name, ClassLoader loader,
+ *      int cookie)
+ *
+ * Load a class from a DEX file.  This is roughly equivalent to defineClass()
+ * in a regular VM -- it's invoked by the class loader to cause the
+ * creation of a specific class.  The difference is that the search for and
+ * reading of the bytes is done within the VM.
+ *
+ * The class name is a "binary name", e.g. "java.lang.String".
+ *
+ * Returns a null pointer with no exception if the class was not found.
+ * Throws an exception on other failures.
+ */
+static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    Object* loader = (Object*) args[1];
+    int cookie = args[2];
+    ClassObject* clazz = NULL;
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+    DvmDex* pDvmDex;
+    char* name;
+    char* descriptor;
+
+    name = dvmCreateCstrFromString(nameObj);
+    descriptor = dvmDotToDescriptor(name);
+    ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",
+        descriptor, loader, cookie);
+    free(name);
+
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    if (pDexOrJar->isDex)
+        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
+    else
+        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
+
+    /* once we load something, we can't unmap the storage */
+    pDexOrJar->okayToFree = false;
+
+    clazz = dvmDefineClass(pDvmDex, descriptor, loader);
+    Thread* self = dvmThreadSelf();
+    if (dvmCheckException(self)) {
+        /*
+         * If we threw a "class not found" exception, stifle it, since the
+         * contract in the higher method says we simply return null if
+         * the class is not found.
+         */
+        Object* excep = dvmGetException(self);
+        if (strcmp(excep->clazz->descriptor,
+                   "Ljava/lang/ClassNotFoundException;") == 0 ||
+            strcmp(excep->clazz->descriptor,
+                   "Ljava/lang/NoClassDefFoundError;") == 0)
+        {
+            dvmClearException(self);
+        }
+        clazz = NULL;
+    }
+
+    free(descriptor);
+    RETURN_PTR(clazz);
+}
+
+/*
+ * private static String[] getClassNameList(int cookie)
+ *
+ * Returns a String array that holds the names of all classes in the
+ * specified DEX file.
+ */
+static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args,
+    JValue* pResult)
+{
+    int cookie = args[0];
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+    Thread* self = dvmThreadSelf();
+
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    DvmDex* pDvmDex;
+    if (pDexOrJar->isDex)
+        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
+    else
+        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
+    assert(pDvmDex != NULL);
+    DexFile* pDexFile = pDvmDex->pDexFile;
+
+    int count = pDexFile->pHeader->classDefsSize;
+    ClassObject* arrayClass =
+        dvmFindArrayClassForElement(gDvm.classJavaLangString);
+    ArrayObject* stringArray =
+        dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT);
+    if (stringArray == NULL) {
+        /* probably OOM */
+        ALOGD("Failed allocating array of %d strings", count);
+        assert(dvmCheckException(self));
+        RETURN_VOID();
+    }
+
+    int i;
+    for (i = 0; i < count; i++) {
+        const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i);
+        const char* descriptor =
+            dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        char* className = dvmDescriptorToDot(descriptor);
+        StringObject* str = dvmCreateStringFromCstr(className);
+        dvmSetObjectArrayElement(stringArray, i, (Object *)str);
+        dvmReleaseTrackedAlloc((Object *)str, self);
+        free(className);
+    }
+
+    dvmReleaseTrackedAlloc((Object*)stringArray, self);
+    RETURN_PTR(stringArray);
+}
+
+/*
+ * public static boolean isDexOptNeeded(String fileName)
+ *         throws FileNotFoundException, IOException
+ *
+ * Returns true if the VM believes that the apk/jar file is out of date
+ * and should be passed through "dexopt" again.
+ *
+ * @param fileName the absolute path to the apk/jar file to examine.
+ * @return true if dexopt should be called on the file, false otherwise.
+ * @throws java.io.FileNotFoundException if fileName is not readable,
+ *         not a file, or not present.
+ * @throws java.io.IOException if fileName is not a valid apk/jar file or
+ *         if problems occur while parsing it.
+ * @throws java.lang.NullPointerException if fileName is null.
+ * @throws dalvik.system.StaleDexCacheError if the optimized dex file
+ *         is stale but exists on a read-only partition.
+ */
+static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    char* name;
+    DexCacheStatus status;
+    int result;
+
+    name = dvmCreateCstrFromString(nameObj);
+    if (name == NULL) {
+        dvmThrowNullPointerException("fileName == null");
+        RETURN_VOID();
+    }
+    if (access(name, R_OK) != 0) {
+        dvmThrowFileNotFoundException(name);
+        free(name);
+        RETURN_VOID();
+    }
+    status = dvmDexCacheStatus(name);
+    ALOGV("dvmDexCacheStatus(%s) returned %d", name, status);
+
+    result = true;
+    switch (status) {
+    default: //FALLTHROUGH
+    case DEX_CACHE_BAD_ARCHIVE:
+        dvmThrowIOException(name);
+        result = -1;
+        break;
+    case DEX_CACHE_OK:
+        result = false;
+        break;
+    case DEX_CACHE_STALE:
+        result = true;
+        break;
+    case DEX_CACHE_STALE_ODEX:
+        dvmThrowStaleDexCacheError(name);
+        result = -1;
+        break;
+    }
+    free(name);
+
+    if (result >= 0) {
+        RETURN_BOOLEAN(result);
+    } else {
+        RETURN_VOID();
+    }
+}
+
+const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
+    { "openDexFile",        "(Ljava/lang/String;Ljava/lang/String;I)I",
+        Dalvik_dalvik_system_DexFile_openDexFile },
+    { "openDexFile",        "([B)I",
+        Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
+    { "closeDexFile",       "(I)V",
+        Dalvik_dalvik_system_DexFile_closeDexFile },
+    { "defineClass",        "(Ljava/lang/String;Ljava/lang/ClassLoader;I)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.cpp b/vm/native/dalvik_system_VMDebug.cpp
new file mode 100644
index 0000000..f6d91a2
--- /dev/null
+++ b/vm/native/dalvik_system_VMDebug.cpp
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "hprof/Hprof.h"
+
+#include <string.h>
+#include <unistd.h>
+
+
+/*
+ * Extracts the fd from a FileDescriptor object.
+ *
+ * If an error is encountered, or the extracted descriptor is numerically
+ * invalid, this returns -1 with an exception raised.
+ */
+static int getFileDescriptor(Object* obj)
+{
+    assert(obj != NULL);
+    assert(strcmp(obj->clazz->descriptor, "Ljava/io/FileDescriptor;") == 0);
+
+    int fd = dvmGetFieldInt(obj, gDvm.offJavaIoFileDescriptor_descriptor);
+    if (fd < 0) {
+        dvmThrowRuntimeException("Invalid file descriptor");
+        return -1;
+    }
+
+    return fd;
+}
+
+/*
+ * static String[] getVmFeatureList()
+ *
+ * Return a set of strings describing available VM features (this is chiefly
+ * of interest to DDMS).
+ */
+static void Dalvik_dalvik_system_VMDebug_getVmFeatureList(const u4* args, JValue* pResult) {
+    std::vector<std::string> features;
+    features.push_back("method-trace-profiling");
+    features.push_back("method-trace-profiling-streaming");
+    features.push_back("hprof-heap-dump");
+    features.push_back("hprof-heap-dump-streaming");
+
+    ArrayObject* result = dvmCreateStringArray(features);
+    dvmReleaseTrackedAlloc((Object*) result, dvmThreadSelf());
+    RETURN_PTR(result);
+}
+
+/* These must match the values in dalvik.system.VMDebug.
+ */
+enum {
+    KIND_ALLOCATED_OBJECTS      = 1<<0,
+    KIND_ALLOCATED_BYTES        = 1<<1,
+    KIND_FREED_OBJECTS          = 1<<2,
+    KIND_FREED_BYTES            = 1<<3,
+    KIND_GC_INVOCATIONS         = 1<<4,
+    KIND_CLASS_INIT_COUNT       = 1<<5,
+    KIND_CLASS_INIT_TIME        = 1<<6,
+
+    /* These values exist for backward compatibility. */
+    KIND_EXT_ALLOCATED_OBJECTS = 1<<12,
+    KIND_EXT_ALLOCATED_BYTES   = 1<<13,
+    KIND_EXT_FREED_OBJECTS     = 1<<14,
+    KIND_EXT_FREED_BYTES       = 1<<15,
+
+    KIND_GLOBAL_ALLOCATED_OBJECTS   = KIND_ALLOCATED_OBJECTS,
+    KIND_GLOBAL_ALLOCATED_BYTES     = KIND_ALLOCATED_BYTES,
+    KIND_GLOBAL_FREED_OBJECTS       = KIND_FREED_OBJECTS,
+    KIND_GLOBAL_FREED_BYTES         = KIND_FREED_BYTES,
+    KIND_GLOBAL_GC_INVOCATIONS      = KIND_GC_INVOCATIONS,
+    KIND_GLOBAL_CLASS_INIT_COUNT    = KIND_CLASS_INIT_COUNT,
+    KIND_GLOBAL_CLASS_INIT_TIME     = KIND_CLASS_INIT_TIME,
+
+    KIND_THREAD_ALLOCATED_OBJECTS   = KIND_ALLOCATED_OBJECTS << 16,
+    KIND_THREAD_ALLOCATED_BYTES     = KIND_ALLOCATED_BYTES << 16,
+    KIND_THREAD_FREED_OBJECTS       = KIND_FREED_OBJECTS << 16,
+    KIND_THREAD_FREED_BYTES         = KIND_FREED_BYTES << 16,
+
+    KIND_THREAD_GC_INVOCATIONS      = KIND_GC_INVOCATIONS << 16,
+
+    // TODO: failedAllocCount, failedAllocSize
+};
+
+#define KIND_ALL_COUNTS 0xffffffff
+
+/*
+ * Zero out the specified fields.
+ */
+static void clearAllocProfStateFields(AllocProfState *allocProf,
+    unsigned int kinds)
+{
+    if (kinds & KIND_ALLOCATED_OBJECTS) {
+        allocProf->allocCount = 0;
+    }
+    if (kinds & KIND_ALLOCATED_BYTES) {
+        allocProf->allocSize = 0;
+    }
+    if (kinds & KIND_FREED_OBJECTS) {
+        allocProf->freeCount = 0;
+    }
+    if (kinds & KIND_FREED_BYTES) {
+        allocProf->freeSize = 0;
+    }
+    if (kinds & KIND_GC_INVOCATIONS) {
+        allocProf->gcCount = 0;
+    }
+    if (kinds & KIND_CLASS_INIT_COUNT) {
+        allocProf->classInitCount = 0;
+    }
+    if (kinds & KIND_CLASS_INIT_TIME) {
+        allocProf->classInitTime = 0;
+    }
+}
+
+/*
+ * static void startAllocCounting()
+ *
+ * Reset the counters and enable counting.
+ *
+ * TODO: this currently only resets the per-thread counters for the current
+ * thread.  If we actually start using the per-thread counters we'll
+ * probably want to fix this.
+ */
+static void Dalvik_dalvik_system_VMDebug_startAllocCounting(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS);
+    clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS);
+    dvmStartAllocCounting();
+    RETURN_VOID();
+}
+
+/*
+ * public static void stopAllocCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmStopAllocCounting();
+    RETURN_VOID();
+}
+
+/*
+ * private static int getAllocCount(int kind)
+ */
+static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args,
+    JValue* pResult)
+{
+    AllocProfState *allocProf;
+    unsigned int kind = args[0];
+    if (kind < (1<<16)) {
+        allocProf = &gDvm.allocProf;
+    } else {
+        allocProf = &dvmThreadSelf()->allocProf;
+        kind >>= 16;
+    }
+    switch (kind) {
+    case KIND_ALLOCATED_OBJECTS:
+        pResult->i = allocProf->allocCount;
+        break;
+    case KIND_ALLOCATED_BYTES:
+        pResult->i = allocProf->allocSize;
+        break;
+    case KIND_FREED_OBJECTS:
+        pResult->i = allocProf->freeCount;
+        break;
+    case KIND_FREED_BYTES:
+        pResult->i = allocProf->freeSize;
+        break;
+    case KIND_GC_INVOCATIONS:
+        pResult->i = allocProf->gcCount;
+        break;
+    case KIND_CLASS_INIT_COUNT:
+        pResult->i = allocProf->classInitCount;
+        break;
+    case KIND_CLASS_INIT_TIME:
+        /* convert nsec to usec, reduce to 32 bits */
+        pResult->i = (int) (allocProf->classInitTime / 1000);
+        break;
+    case KIND_EXT_ALLOCATED_OBJECTS:
+    case KIND_EXT_ALLOCATED_BYTES:
+    case KIND_EXT_FREED_OBJECTS:
+    case KIND_EXT_FREED_BYTES:
+        pResult->i = 0;  /* backward compatibility */
+        break;
+    default:
+        assert(false);
+        pResult->i = -1;
+    }
+}
+
+/*
+ * public static void resetAllocCount(int kinds)
+ */
+static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args,
+    JValue* pResult)
+{
+    unsigned int kinds = args[0];
+    clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff);
+    clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16);
+    RETURN_VOID();
+}
+
+/*
+ * static void 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) {
+        dvmThrowIllegalArgumentException(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(gDvm.exRuntimeException,
+                "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 boolean isDebuggerConnected()
+ *
+ * Returns "true" if a debugger is attached.
+ */
+static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_BOOLEAN(dvmDbgIsDebuggerConnected());
+}
+
+/*
+ * static boolean isDebuggingEnabled()
+ *
+ * Returns "true" if debugging is enabled.
+ */
+static void Dalvik_dalvik_system_VMDebug_isDebuggingEnabled(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_BOOLEAN(gDvm.jdwpConfigured);
+}
+
+/*
+ * static long lastDebuggerActivity()
+ *
+ * Returns the time, in msec, since we last had an interaction with the
+ * debugger (send or receive).
+ */
+static void Dalvik_dalvik_system_VMDebug_lastDebuggerActivity(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_LONG(dvmDbgLastDebuggerActivity());
+}
+
+/*
+ * static void startInstructionCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_startInstructionCounting(const u4* args,
+    JValue* pResult)
+{
+    dvmStartInstructionCounting();
+    RETURN_VOID();
+}
+
+/*
+ * static void stopInstructionCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args,
+    JValue* pResult)
+{
+    dvmStopInstructionCounting();
+    RETURN_VOID();
+}
+
+/*
+ * static boolean getInstructionCount(int[] counts)
+ *
+ * Grab a copy of the global instruction count array.
+ *
+ * Since the instruction counts aren't synchronized, we use sched_yield
+ * to improve our chances of finishing without contention.  (Only makes
+ * sense on a uniprocessor.)
+ */
+static void Dalvik_dalvik_system_VMDebug_getInstructionCount(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* countArray = (ArrayObject*) args[0];
+
+    if (countArray != NULL) {
+        int* storage = (int*)(void*)countArray->contents;
+        u4 length = countArray->length;
+
+        /*
+         * Ensure that we copy at most kNumPackedOpcodes
+         * elements, but no more than the length of the given array.
+         */
+        if (length > kNumPackedOpcodes) {
+            length = kNumPackedOpcodes;
+        }
+
+        sched_yield();
+        memcpy(storage, gDvm.executedInstrCounts, length * sizeof(int));
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * static boolean resetInstructionCount()
+ *
+ * Reset the instruction count array.
+ */
+static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args,
+    JValue* pResult)
+{
+    sched_yield();
+    memset(gDvm.executedInstrCounts, 0, kNumPackedOpcodes * sizeof(int));
+    RETURN_VOID();
+}
+
+/*
+ * static void printLoadedClasses(int flags)
+ *
+ * Dump the list of loaded classes.
+ */
+static void Dalvik_dalvik_system_VMDebug_printLoadedClasses(const u4* args,
+    JValue* pResult)
+{
+    int flags = args[0];
+
+    dvmDumpAllClasses(flags);
+
+    RETURN_VOID();
+}
+
+/*
+ * static int getLoadedClassCount()
+ *
+ * Return the number of loaded classes
+ */
+static void Dalvik_dalvik_system_VMDebug_getLoadedClassCount(const u4* args,
+    JValue* pResult)
+{
+    int count;
+
+    UNUSED_PARAMETER(args);
+
+    count = dvmGetNumLoadedClasses();
+
+    RETURN_INT(count);
+}
+
+/*
+ * Returns the thread-specific CPU-time clock value for the current thread,
+ * or -1 if the feature isn't supported.
+ */
+static void Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos(const u4* args,
+    JValue* pResult)
+{
+    jlong result;
+
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
+    result = (jlong) (now.tv_sec*1000000000LL + now.tv_nsec);
+#else
+    result = (jlong) -1;
+#endif
+
+    RETURN_LONG(result);
+}
+
+/*
+ * static void dumpHprofData(String fileName, FileDescriptor fd)
+ *
+ * Cause "hprof" data to be dumped.  We can throw an IOException if an
+ * error occurs during file handling.
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args,
+    JValue* pResult)
+{
+    StringObject* fileNameStr = (StringObject*) args[0];
+    Object* fileDescriptor = (Object*) args[1];
+    char* fileName;
+    int result;
+
+    /*
+     * Only one of these may be NULL.
+     */
+    if (fileNameStr == NULL && fileDescriptor == NULL) {
+        dvmThrowNullPointerException("fileName == null && fd == null");
+        RETURN_VOID();
+    }
+
+    if (fileNameStr != NULL) {
+        fileName = dvmCreateCstrFromString(fileNameStr);
+        if (fileName == NULL) {
+            /* unexpected -- malloc failure? */
+            dvmThrowRuntimeException("malloc failure?");
+            RETURN_VOID();
+        }
+    } else {
+        fileName = strdup("[fd]");
+    }
+
+    int fd = -1;
+    if (fileDescriptor != NULL) {
+        fd = getFileDescriptor(fileDescriptor);
+        if (fd < 0) {
+            free(fileName);
+            RETURN_VOID();
+        }
+    }
+
+    result = hprofDumpHeap(fileName, fd, false);
+    free(fileName);
+
+    if (result != 0) {
+        /* ideally we'd throw something more specific based on actual failure */
+        dvmThrowRuntimeException(
+            "Failure during heap dump; check log output for details");
+        RETURN_VOID();
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * static void dumpHprofDataDdms()
+ *
+ * Cause "hprof" data to be computed and sent directly to DDMS.
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args,
+    JValue* pResult)
+{
+    int result;
+
+    result = hprofDumpHeap("[DDMS]", -1, true);
+
+    if (result != 0) {
+        /* ideally we'd throw something more specific based on actual failure */
+        dvmThrowRuntimeException(
+            "Failure during heap dump; check log output for details");
+        RETURN_VOID();
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * static boolean cacheRegisterMap(String classAndMethodDescr)
+ *
+ * If the specified class is loaded, and the named method exists, ensure
+ * that the method's register map is ready for use.  If the class/method
+ * cannot be found, nothing happens.
+ *
+ * This can improve the zygote's sharing of compressed register maps.  Do
+ * this after class preloading.
+ *
+ * Returns true if the register map is cached and ready, either as a result
+ * of this call or earlier activity.  Returns false if the class isn't loaded,
+ * if the method couldn't be found, or if the method has no register map.
+ *
+ * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.)
+ */
+static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args,
+    JValue* pResult)
+{
+    StringObject* classAndMethodDescStr = (StringObject*) args[0];
+    ClassObject* clazz;
+    bool result = false;
+
+    if (classAndMethodDescStr == NULL) {
+        dvmThrowNullPointerException("classAndMethodDesc == null");
+        RETURN_VOID();
+    }
+
+    char* classAndMethodDesc = NULL;
+
+    /*
+     * Pick the string apart.  We have a local copy, so just modify it
+     * in place.
+     */
+    classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr);
+
+    char* methodName = strchr(classAndMethodDesc, '.');
+    if (methodName == NULL) {
+        dvmThrowRuntimeException("method name not found in string");
+        RETURN_VOID();
+    }
+    *methodName++ = '\0';
+
+    char* methodDescr = strchr(methodName, ':');
+    if (methodDescr == NULL) {
+        dvmThrowRuntimeException("method descriptor not found in string");
+        RETURN_VOID();
+    }
+    *methodDescr++ = '\0';
+
+    //ALOGD("GOT: %s %s %s", classAndMethodDesc, methodName, methodDescr);
+
+    /*
+     * Find the class, but only if it's already loaded.
+     */
+    clazz = dvmLookupClass(classAndMethodDesc, NULL, false);
+    if (clazz == NULL) {
+        ALOGD("Class %s not found in bootstrap loader", classAndMethodDesc);
+        goto bail;
+    }
+
+    Method* method;
+
+    /*
+     * Find the method, which could be virtual or direct, defined directly
+     * or inherited.
+     */
+    if (methodName[0] == '<') {
+        /*
+         * Constructor or class initializer.  Only need to examine the
+         * "direct" list, and don't need to search up the class hierarchy.
+         */
+        method = dvmFindDirectMethodByDescriptor(clazz, methodName,
+                    methodDescr);
+    } else {
+        /*
+         * Try both lists, and scan up the tree.
+         */
+        method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName,
+                    methodDescr);
+        if (method == NULL) {
+            method = dvmFindDirectMethodHierByDescriptor(clazz, methodName,
+                        methodDescr);
+        }
+    }
+
+    if (method != NULL) {
+        /*
+         * Got it.  See if there's a register map here.
+         */
+        const RegisterMap* pMap;
+        pMap = dvmGetExpandedRegisterMap(method);
+        if (pMap == NULL) {
+            ALOGV("No map for %s.%s %s",
+                classAndMethodDesc, methodName, methodDescr);
+        } else {
+            ALOGV("Found map %s.%s %s",
+                classAndMethodDesc, methodName, methodDescr);
+            result = true;
+        }
+    } else {
+        ALOGV("Unable to find %s.%s %s",
+            classAndMethodDesc, methodName, methodDescr);
+    }
+
+bail:
+    free(classAndMethodDesc);
+    RETURN_BOOLEAN(result);
+}
+
+/*
+ * static void dumpReferenceTables()
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpReferenceTables(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    UNUSED_PARAMETER(pResult);
+
+    ALOGI("--- reference table dump ---");
+    dvmDumpJniReferenceTables();
+    // could dump thread's internalLocalRefTable, probably not useful
+    // ditto for thread's jniMonitorRefTable
+    ALOGI("---");
+    RETURN_VOID();
+}
+
+/*
+ * static void crash()
+ *
+ * Dump the current thread's interpreted stack and abort the VM.  Useful
+ * for seeing both interpreted and native stack traces.
+ *
+ * (Might want to restrict this to debuggable processes as a security
+ * measure, or check SecurityManager.checkExit().)
+ */
+static void Dalvik_dalvik_system_VMDebug_crash(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    UNUSED_PARAMETER(pResult);
+
+    ALOGW("Crashing VM on request");
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+}
+
+/*
+ * static void infopoint(int id)
+ *
+ * Provide a hook for gdb to hang to so that the VM can be stopped when
+ * user-tagged source locations are being executed.
+ */
+static void Dalvik_dalvik_system_VMDebug_infopoint(const u4* args,
+    JValue* pResult)
+{
+    gDvm.nativeDebuggerActive = true;
+
+    ALOGD("VMDebug infopoint %d hit", args[0]);
+
+    gDvm.nativeDebuggerActive = false;
+    RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMDebug_countInstancesOfClass(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*)args[0];
+    bool countAssignable = args[1];
+    if (clazz == NULL) {
+        RETURN_LONG(0);
+    }
+    if (countAssignable) {
+        size_t count = dvmCountAssignableInstancesOfClass(clazz);
+        RETURN_LONG((long long)count);
+    } else {
+        size_t count = dvmCountInstancesOfClass(clazz);
+        RETURN_LONG((long long)count);
+    }
+}
+
+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 },
+    { "startInstructionCounting",   "()V",
+        Dalvik_dalvik_system_VMDebug_startInstructionCounting },
+    { "stopInstructionCounting",    "()V",
+        Dalvik_dalvik_system_VMDebug_stopInstructionCounting },
+    { "resetInstructionCount",      "()V",
+        Dalvik_dalvik_system_VMDebug_resetInstructionCount },
+    { "getInstructionCount",        "([I)V",
+        Dalvik_dalvik_system_VMDebug_getInstructionCount },
+    { "isDebuggerConnected",        "()Z",
+        Dalvik_dalvik_system_VMDebug_isDebuggerConnected },
+    { "isDebuggingEnabled",         "()Z",
+        Dalvik_dalvik_system_VMDebug_isDebuggingEnabled },
+    { "lastDebuggerActivity",       "()J",
+        Dalvik_dalvik_system_VMDebug_lastDebuggerActivity },
+    { "printLoadedClasses",         "(I)V",
+        Dalvik_dalvik_system_VMDebug_printLoadedClasses },
+    { "getLoadedClassCount",        "()I",
+        Dalvik_dalvik_system_VMDebug_getLoadedClassCount },
+    { "threadCpuTimeNanos",         "()J",
+        Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos },
+    { "dumpHprofData",              "(Ljava/lang/String;Ljava/io/FileDescriptor;)V",
+        Dalvik_dalvik_system_VMDebug_dumpHprofData },
+    { "dumpHprofDataDdms",          "()V",
+        Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms },
+    { "cacheRegisterMap",           "(Ljava/lang/String;)Z",
+        Dalvik_dalvik_system_VMDebug_cacheRegisterMap },
+    { "dumpReferenceTables",        "()V",
+        Dalvik_dalvik_system_VMDebug_dumpReferenceTables },
+    { "crash",                      "()V",
+        Dalvik_dalvik_system_VMDebug_crash },
+    { "infopoint",                 "(I)V",
+        Dalvik_dalvik_system_VMDebug_infopoint },
+    { "countInstancesOfClass",     "(Ljava/lang/Class;Z)J",
+        Dalvik_dalvik_system_VMDebug_countInstancesOfClass },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMRuntime.cpp b/vm/native/dalvik_system_VMRuntime.cpp
new file mode 100644
index 0000000..72bad29
--- /dev/null
+++ b/vm/native/dalvik_system_VMRuntime.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.VMRuntime
+ */
+#include "Dalvik.h"
+#include "ScopedPthreadMutexLock.h"
+#include "native/InternalNativePriv.h"
+
+#include <cutils/array.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();
+}
+
+/*
+ * public native void startJitCompilation()
+ *
+ * Callback function from the framework to indicate that an app has gone
+ * through the startup phase and it is time to enable the JIT compiler.
+ */
+static void Dalvik_dalvik_system_VMRuntime_startJitCompilation(const u4* args,
+    JValue* pResult)
+{
+#if defined(WITH_JIT)
+    if (gDvm.executionMode == kExecutionModeJit && gDvmJit.disableJit == false) {
+        ScopedPthreadMutexLock lock(&gDvmJit.compilerLock);
+        gDvmJit.alreadyEnabledViaFramework = true;
+        pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+    }
+#endif
+    RETURN_VOID();
+}
+
+/*
+ * public native void disableJitCompilation()
+ *
+ * Callback function from the framework to indicate that a VM instance wants to
+ * permanently disable the JIT compiler. Currently only the system server uses
+ * this interface when it detects system-wide safe mode is enabled.
+ */
+static void Dalvik_dalvik_system_VMRuntime_disableJitCompilation(const u4* args,
+    JValue* pResult)
+{
+#if defined(WITH_JIT)
+    if (gDvm.executionMode == kExecutionModeJit) {
+        gDvmJit.disableJit = true;
+    }
+#endif
+    RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_newNonMovableArray(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* elementClass = (ClassObject*) args[1];
+    int length = args[2];
+
+    if (elementClass == NULL) {
+        dvmThrowNullPointerException("elementClass == null");
+        RETURN_VOID();
+    }
+    if (length < 0) {
+        dvmThrowNegativeArraySizeException(length);
+        RETURN_VOID();
+    }
+
+    // TODO: right now, we don't have a copying collector, so there's no need
+    // to do anything special here, but we ought to pass the non-movability
+    // through to the allocator.
+    ClassObject* arrayClass = dvmFindArrayClassForElement(elementClass);
+    ArrayObject* newArray = dvmAllocArrayByClass(arrayClass,
+                                                 length,
+                                                 ALLOC_NON_MOVING);
+    if (newArray == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        RETURN_VOID();
+    }
+    dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+
+    RETURN_PTR(newArray);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_addressOf(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* array = (ArrayObject*) args[1];
+    if (!dvmIsArray(array)) {
+        dvmThrowIllegalArgumentException(NULL);
+        RETURN_VOID();
+    }
+    // TODO: we should also check that this is a non-movable array.
+    s8 result = (uintptr_t) array->contents;
+    RETURN_LONG(result);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_clearGrowthLimit(const u4* args,
+    JValue* pResult)
+{
+    dvmClearGrowthLimit();
+    RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_isDebuggerActive(
+    const u4* args, JValue* pResult)
+{
+    RETURN_BOOLEAN(gDvm.debuggerActive || gDvm.nativeDebuggerActive);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_properties(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* result = dvmCreateStringArray(*gDvm.properties);
+    dvmReleaseTrackedAlloc((Object*) result, dvmThreadSelf());
+    RETURN_PTR(result);
+}
+
+static void returnCString(JValue* pResult, const char* s)
+{
+    Object* result = (Object*) dvmCreateStringFromCstr(s);
+    dvmReleaseTrackedAlloc(result, dvmThreadSelf());
+    RETURN_PTR(result);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_bootClassPath(const u4* args,
+    JValue* pResult)
+{
+    returnCString(pResult, gDvm.bootClassPathStr);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_classPath(const u4* args,
+    JValue* pResult)
+{
+    returnCString(pResult, gDvm.classPathStr);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_vmVersion(const u4* args,
+    JValue* pResult)
+{
+    char buf[64];
+    sprintf(buf, "%d.%d.%d",
+            DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+    returnCString(pResult, buf);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_setTargetSdkVersion(const u4* args,
+    JValue* pResult)
+{
+    // This is the target SDK version of the app we're about to run.
+    // Note that this value may be CUR_DEVELOPMENT (10000).
+    // Note that this value may be 0, meaning "current".
+    int targetSdkVersion = args[1];
+    if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) {
+        // TODO: running with CheckJNI should override this and force you to obey the strictest rules.
+        ALOGI("Turning on JNI app bug workarounds for target SDK version %i...", targetSdkVersion);
+        gDvmJni.workAroundAppJniBugs = true;
+    }
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
+    { "addressOf", "(Ljava/lang/Object;)J",
+        Dalvik_dalvik_system_VMRuntime_addressOf },
+    { "bootClassPath", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_bootClassPath },
+    { "classPath", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_classPath },
+    { "clearGrowthLimit", "()V",
+        Dalvik_dalvik_system_VMRuntime_clearGrowthLimit },
+    { "disableJitCompilation", "()V",
+        Dalvik_dalvik_system_VMRuntime_disableJitCompilation },
+    { "isDebuggerActive", "()Z",
+        Dalvik_dalvik_system_VMRuntime_isDebuggerActive },
+    { "getTargetHeapUtilization", "()F",
+        Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization },
+    { "nativeSetTargetHeapUtilization", "(F)V",
+        Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization },
+    { "newNonMovableArray", "(Ljava/lang/Class;I)Ljava/lang/Object;",
+        Dalvik_dalvik_system_VMRuntime_newNonMovableArray },
+    { "properties", "()[Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_properties },
+    { "setTargetSdkVersion", "(I)V",
+        Dalvik_dalvik_system_VMRuntime_setTargetSdkVersion },
+    { "startJitCompilation", "()V",
+        Dalvik_dalvik_system_VMRuntime_startJitCompilation },
+    { "vmVersion", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_vmVersion },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMStack.cpp b/vm/native/dalvik_system_VMStack.cpp
new file mode 100644
index 0000000..db627ef
--- /dev/null
+++ b/vm/native/dalvik_system_VMStack.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.VMStack
+ */
+#include "Dalvik.h"
+#include "UniquePtr.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * public static ClassLoader getCallingClassLoader()
+ *
+ * Return the defining class loader of the caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getCallingClassLoader(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz =
+        dvmGetCaller2Class(dvmThreadSelf()->interpSave.curFrame);
+
+    UNUSED_PARAMETER(args);
+
+    if (clazz == NULL)
+        RETURN_PTR(NULL);
+    RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public static Class<?> getStackClass2()
+ *
+ * Returns the class of the caller's caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getStackClass2(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz =
+        dvmGetCaller3Class(dvmThreadSelf()->interpSave.curFrame);
+
+    UNUSED_PARAMETER(args);
+
+    RETURN_PTR(clazz);
+}
+
+/*
+ * public static Class<?>[] getClasses(int maxDepth)
+ *
+ * Create an array of classes for the methods on the stack, skipping the
+ * first two and all reflection methods.  If "stopAtPrivileged" is set,
+ * stop shortly after we encounter a privileged class.
+ */
+static void Dalvik_dalvik_system_VMStack_getClasses(const u4* args,
+    JValue* pResult)
+{
+    /* note "maxSize" is unsigned, so -1 turns into a very large value */
+    size_t maxSize = args[0];
+    size_t size = 0;
+    const size_t kSkip = 2;
+
+    /*
+     * Get an array with the stack trace in it.
+     */
+    void *fp = dvmThreadSelf()->interpSave.curFrame;
+    size_t depth = dvmComputeExactFrameDepth(fp);
+    UniquePtr<const Method*[]> methods(new const Method*[depth]);
+    dvmFillStackTraceArray(fp, methods.get(), depth);
+
+    /*
+     * Run through the array and count up how many elements there are.
+     */
+    for (size_t i = kSkip; i < depth && size < maxSize; ++i) {
+        const Method* meth = methods[i];
+
+        if (dvmIsReflectionMethod(meth))
+            continue;
+
+        size++;
+    }
+
+    /*
+     * Create an array object to hold the classes.
+     * TODO: can use gDvm.classJavaLangClassArray here?
+     */
+    ClassObject* classArrayClass = dvmFindArrayClass("[Ljava/lang/Class;",
+                                                     NULL);
+    if (classArrayClass == NULL) {
+        ALOGW("Unable to find java.lang.Class array class");
+        return;
+    }
+    ArrayObject* classes = dvmAllocArrayByClass(classArrayClass,
+                                                size,
+                                                ALLOC_DEFAULT);
+    if (classes == NULL) {
+        ALOGW("Unable to allocate class array of %zd elements", size);
+        return;
+    }
+
+    /*
+     * Fill in the array.
+     */
+    size_t objCount = 0;
+    for (size_t i = kSkip; i < depth; ++i) {
+        if (dvmIsReflectionMethod(methods[i])) {
+            continue;
+        }
+        Object* klass = (Object *)methods[i]->clazz;
+        dvmSetObjectArrayElement(classes, objCount, klass);
+        objCount++;
+    }
+    assert(objCount == classes->length);
+
+    dvmReleaseTrackedAlloc((Object*)classes, NULL);
+    RETURN_PTR(classes);
+}
+
+/*
+ * Return a trace buffer for the specified thread or NULL if the
+ * thread is not still alive. *depth is set to the length of a
+ * non-NULL trace buffer. Caller is responsible for freeing the trace
+ * buffer.
+ */
+static int* getTraceBuf(Object* targetThreadObj, size_t* pStackDepth)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    int* traceBuf;
+
+    assert(targetThreadObj != NULL);
+
+    dvmLockThreadList(self);
+
+    /*
+     * Make sure the thread is still alive and in the list.
+     */
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadObj == targetThreadObj)
+            break;
+    }
+    if (thread == NULL) {
+        ALOGI("VMStack.getTraceBuf: threadObj %p not active",
+            targetThreadObj);
+        dvmUnlockThreadList();
+        return NULL;
+    }
+
+    /*
+     * Suspend the thread, pull out the stack trace, then resume the thread
+     * and release the thread list lock.  If we're being asked to examine
+     * our own stack trace, skip the suspend/resume.
+     */
+    if (thread != self)
+        dvmSuspendThread(thread);
+    traceBuf = dvmFillInStackTraceRaw(thread, pStackDepth);
+    if (thread != self)
+        dvmResumeThread(thread);
+    dvmUnlockThreadList();
+
+    return traceBuf;
+}
+
+/*
+ * public static StackTraceElement[] getThreadStackTrace(Thread t)
+ *
+ * Retrieve the stack trace of the specified thread and return it as an
+ * array of StackTraceElement.  Returns NULL on failure.
+ */
+static void Dalvik_dalvik_system_VMStack_getThreadStackTrace(const u4* args,
+    JValue* pResult)
+{
+    Object* targetThreadObj = (Object*) args[0];
+    size_t stackDepth;
+    int* traceBuf = getTraceBuf(targetThreadObj, &stackDepth);
+
+    if (traceBuf == NULL)
+        RETURN_PTR(NULL);
+
+    /*
+     * Convert the raw buffer into an array of StackTraceElement.
+     */
+    ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
+    free(traceBuf);
+    RETURN_PTR(trace);
+}
+
+/*
+ * public static int fillStackTraceElements(Thread t, StackTraceElement[] stackTraceElements)
+ *
+ * Retrieve a partial stack trace of the specified thread and return
+ * the number of frames filled.  Returns 0 on failure.
+ */
+static void Dalvik_dalvik_system_VMStack_fillStackTraceElements(const u4* args,
+    JValue* pResult)
+{
+    Object* targetThreadObj = (Object*) args[0];
+    ArrayObject* steArray = (ArrayObject*) args[1];
+    size_t stackDepth;
+    int* traceBuf = getTraceBuf(targetThreadObj, &stackDepth);
+
+    if (traceBuf == NULL)
+        RETURN_PTR(NULL);
+
+    /*
+     * Set the raw buffer into an array of StackTraceElement.
+     */
+    if (stackDepth > steArray->length) {
+        stackDepth = steArray->length;
+    }
+    dvmFillStackTraceElements(traceBuf, stackDepth, steArray);
+    free(traceBuf);
+    RETURN_INT(stackDepth);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMStack[] = {
+    { "getCallingClassLoader",  "()Ljava/lang/ClassLoader;",
+        Dalvik_dalvik_system_VMStack_getCallingClassLoader },
+    { "getStackClass2",         "()Ljava/lang/Class;",
+        Dalvik_dalvik_system_VMStack_getStackClass2 },
+    { "getClasses",             "(I)[Ljava/lang/Class;",
+        Dalvik_dalvik_system_VMStack_getClasses },
+    { "getThreadStackTrace",    "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;",
+        Dalvik_dalvik_system_VMStack_getThreadStackTrace },
+    { "fillStackTraceElements", "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I",
+        Dalvik_dalvik_system_VMStack_fillStackTraceElements },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_Zygote.cpp b/vm/native/dalvik_system_Zygote.cpp
new file mode 100644
index 0000000..70d4fea
--- /dev/null
+++ b/vm/native/dalvik_system_Zygote.cpp
@@ -0,0 +1,754 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <selinux/android.h>
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <grp.h>
+#include <errno.h>
+#include <paths.h>
+#include <sys/personality.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
+#include <cutils/fs.h>
+#include <cutils/sched_policy.h>
+#include <cutils/multiuser.h>
+#include <sched.h>
+#include <sys/utsname.h>
+#include <sys/capability.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,
+    DEBUG_ENABLE_JNI_LOGGING        = 1 << 4,
+};
+
+/* must match values in dalvik.system.Zygote */
+enum {
+    MOUNT_EXTERNAL_NONE = 0,
+    MOUNT_EXTERNAL_SINGLEUSER = 1,
+    MOUNT_EXTERNAL_MULTIUSER = 2,
+    MOUNT_EXTERNAL_MULTIUSER_ALL = 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 ALOG(...) from a signal handler because of possible
+           reentrancy.  However, we know a priori that the current implementation
+           of ALOG() is safe to call from a SIGCHLD handler in the zygote process.
+           If the ALOG() 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)) {
+                ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)",
+                    (int) pid, WEXITSTATUS(status));
+            } else {
+                IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) {
+                    ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+                        "Process %d exited cleanly (%d)",
+                        (int) pid, WEXITSTATUS(status));
+                }
+            }
+        } else if (WIFSIGNALED(status)) {
+            if (WTERMSIG(status) != SIGKILL) {
+                ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG,
+                    "Process %d terminated by signal (%d)",
+                    (int) pid, WTERMSIG(status));
+            } else {
+                IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) {
+                    ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+                        "Process %d terminated by signal (%d)",
+                        (int) pid, WTERMSIG(status));
+                }
+            }
+#ifdef WCOREDUMP
+            if (WCOREDUMP(status)) {
+                ALOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core",
+                    (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) {
+            ALOG(LOG_INFO, ZYGOTE_LOG_TAG,
+                "Exit zygote because system server (%d) has terminated",
+                (int) pid);
+            kill(getpid(), SIGKILL);
+        }
+    }
+
+    if (pid < 0) {
+        ALOG(LOG_WARN, ZYGOTE_LOG_TAG,
+            "Zygote SIGCHLD error in waitpid: %s",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) {
+        ALOGW("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) {
+        ALOGW("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 = (gid_t *)alloca(sizeof(gid_t) * gidArray->length);
+    contents = (s4 *)(void *)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 **)(void *)rlimits->contents;
+
+    for (i = 0; i < rlimits->length; i++) {
+        ArrayObject * rlimit_tuple = tuples[i];
+        s4* contents = (s4 *)(void *)rlimit_tuple->contents;
+        int err;
+
+        if (rlimit_tuple->length != 3) {
+            ALOGE("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;
+}
+
+/*
+ * Create a private mount namespace and bind mount appropriate emulated
+ * storage for the given user.
+ */
+static int mountEmulatedStorage(uid_t uid, u4 mountMode) {
+    // See storage config details at http://source.android.com/tech/storage/
+    userid_t userid = multiuser_get_user_id(uid);
+
+    // Create a second private mount namespace for our process
+    if (unshare(CLONE_NEWNS) == -1) {
+        ALOGE("Failed to unshare(): %s", strerror(errno));
+        return -1;
+    }
+
+    // Create bind mounts to expose external storage
+    if (mountMode == MOUNT_EXTERNAL_MULTIUSER
+            || mountMode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
+        // These paths must already be created by init.rc
+        const char* source = getenv("EMULATED_STORAGE_SOURCE");
+        const char* target = getenv("EMULATED_STORAGE_TARGET");
+        const char* legacy = getenv("EXTERNAL_STORAGE");
+        if (source == NULL || target == NULL || legacy == NULL) {
+            ALOGE("Storage environment undefined; unable to provide external storage");
+            return -1;
+        }
+
+        // Prepare source paths
+        char source_user[PATH_MAX];
+        char source_obb[PATH_MAX];
+        char target_user[PATH_MAX];
+
+        // /mnt/shell/emulated/0
+        snprintf(source_user, PATH_MAX, "%s/%d", source, userid);
+        // /mnt/shell/emulated/obb
+        snprintf(source_obb, PATH_MAX, "%s/obb", source);
+        // /storage/emulated/0
+        snprintf(target_user, PATH_MAX, "%s/%d", target, userid);
+
+        if (fs_prepare_dir(source_user, 0000, 0, 0) == -1
+                || fs_prepare_dir(source_obb, 0000, 0, 0) == -1
+                || fs_prepare_dir(target_user, 0000, 0, 0) == -1) {
+            return -1;
+        }
+
+        if (mountMode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
+            // Mount entire external storage tree for all users
+            if (mount(source, target, NULL, MS_BIND, NULL) == -1) {
+                ALOGE("Failed to mount %s to %s: %s", source, target, strerror(errno));
+                return -1;
+            }
+        } else {
+            // Only mount user-specific external storage
+            if (mount(source_user, target_user, NULL, MS_BIND, NULL) == -1) {
+                ALOGE("Failed to mount %s to %s: %s", source_user, target_user, strerror(errno));
+                return -1;
+            }
+        }
+
+        // Now that user is mounted, prepare and mount OBB storage
+        // into place for current user
+        char target_android[PATH_MAX];
+        char target_obb[PATH_MAX];
+
+        // /storage/emulated/0/Android
+        snprintf(target_android, PATH_MAX, "%s/%d/Android", target, userid);
+        // /storage/emulated/0/Android/obb
+        snprintf(target_obb, PATH_MAX, "%s/%d/Android/obb", target, userid);
+
+        if (fs_prepare_dir(target_android, 0000, 0, 0) == -1
+                || fs_prepare_dir(target_obb, 0000, 0, 0) == -1
+                || fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
+            return -1;
+        }
+        if (mount(source_obb, target_obb, NULL, MS_BIND, NULL) == -1) {
+            ALOGE("Failed to mount %s to %s: %s", source_obb, target_obb, strerror(errno));
+            return -1;
+        }
+
+        // Finally, mount user-specific path into place for legacy users
+        if (mount(target_user, legacy, NULL, MS_BIND | MS_REC, NULL) == -1) {
+            ALOGE("Failed to mount %s to %s: %s", target_user, legacy, strerror(errno));
+            return -1;
+        }
+
+    } else {
+        ALOGE("Mount mode %d unsupported", mountMode);
+        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) {
+        dvmThrowIllegalStateException(
+            "VM instance not started with -Xzygote");
+
+        RETURN_VOID();
+    }
+
+    if (!dvmGcPreZygoteFork()) {
+        ALOGE("pre-fork heap failed");
+        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 enabled.
+ * assert
+ *   If set, make sure assertions are enabled.  This gets fairly weird,
+ *   because it affects the result of a method called by class initializers,
+ *   and hence can't affect pre-loaded/initialized classes.
+ * safemode
+ *   If set, operates the VM in the safe mode. The definition of "safe mode" is
+ *   implementation dependent and currently only the JIT compiler is disabled.
+ *   This is easy to handle because the compiler thread and associated resources
+ *   are not requested until we call dvmInitAfterZygote().
+ */
+static void enableDebugFeatures(u4 debugFlags)
+{
+    ALOGV("debugFlags is 0x%02x", debugFlags);
+
+    gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0);
+
+    if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) {
+        /* turn it on if it's not already enabled */
+        dvmLateEnableCheckedJni();
+    }
+
+    if ((debugFlags & DEBUG_ENABLE_JNI_LOGGING) != 0) {
+        gDvmJni.logThirdPartyJni = true;
+    }
+
+    if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) {
+        /* turn it on if it's not already enabled */
+        dvmLateEnableAssertions();
+    }
+
+    if ((debugFlags & DEBUG_ENABLE_SAFEMODE) != 0) {
+#if defined(WITH_JIT)
+        /* turn off the jit if it is explicitly requested by the app */
+        if (gDvm.executionMode == kExecutionModeJit)
+            gDvm.executionMode = kExecutionModeInterpFast;
+#endif
+    }
+
+#ifdef HAVE_ANDROID_OS
+    if ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0) {
+        /* To let a non-privileged gdbserver attach to this
+         * process, we must set its dumpable bit flag. However
+         * we are not interested in generating a coredump in
+         * case of a crash, so also set the coredump size to 0
+         * to disable that
+         */
+        if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+            ALOGE("could not set dumpable bit flag for pid %d: %s",
+                 getpid(), strerror(errno));
+        } else {
+            struct rlimit rl;
+            rl.rlim_cur = 0;
+            rl.rlim_max = RLIM_INFINITY;
+            if (setrlimit(RLIMIT_CORE, &rl) < 0) {
+                ALOGE("could not disable core file generation for pid %d: %s",
+                    getpid(), strerror(errno));
+            }
+        }
+    }
+#endif
+}
+
+/*
+ * 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;
+
+    ALOGV("CAPSET perm=%llx eff=%llx", permitted, effective);
+    if (capset(&capheader, &capdata) != 0)
+        return errno;
+#endif /*HAVE_ANDROID_OS*/
+
+    return 0;
+}
+
+/*
+ * Set SELinux security context.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int setSELinuxContext(uid_t uid, bool isSystemServer,
+                             const char *seInfo, const char *niceName)
+{
+#ifdef HAVE_ANDROID_OS
+    return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName);
+#else
+    return 0;
+#endif
+}
+
+static bool needsNoRandomizeWorkaround() {
+#if !defined(__arm__)
+    return false;
+#else
+    int major;
+    int minor;
+    struct utsname uts;
+    if (uname(&uts) == -1) {
+        return false;
+    }
+
+    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+        return false;
+    }
+
+    // Kernels before 3.4.* need the workaround.
+    return (major < 3) || ((major == 3) && (minor < 4));
+#endif
+}
+
+/*
+ * 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];
+    u4 mountMode = MOUNT_EXTERNAL_NONE;
+    int64_t permittedCapabilities, effectiveCapabilities;
+    char *seInfo = NULL;
+    char *niceName = NULL;
+
+    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 {
+        mountMode = args[5];
+        permittedCapabilities = effectiveCapabilities = 0;
+        StringObject* seInfoObj = (StringObject*)args[6];
+        if (seInfoObj) {
+            seInfo = dvmCreateCstrFromString(seInfoObj);
+            if (!seInfo) {
+                ALOGE("seInfo dvmCreateCstrFromString failed");
+                dvmAbort();
+            }
+        }
+        StringObject* niceNameObj = (StringObject*)args[7];
+        if (niceNameObj) {
+            niceName = dvmCreateCstrFromString(niceNameObj);
+            if (!niceName) {
+                ALOGE("niceName dvmCreateCstrFromString failed");
+                dvmAbort();
+            }
+        }
+    }
+
+    if (!gDvm.zygote) {
+        dvmThrowIllegalStateException(
+            "VM instance not started with -Xzygote");
+
+        return -1;
+    }
+
+    if (!dvmGcPreZygoteFork()) {
+        ALOGE("pre-fork heap failed");
+        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) {
+                ALOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
+                dvmAbort();
+            }
+        }
+
+        for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
+            err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+            if (err < 0) {
+                if (errno == EINVAL) {
+                    ALOGW("PR_CAPBSET_DROP %d failed: %s. "
+                          "Please make sure your kernel is compiled with "
+                          "file capabilities support enabled.",
+                          i, strerror(errno));
+                } else {
+                    ALOGE("PR_CAPBSET_DROP %d failed: %s.", i, strerror(errno));
+                    dvmAbort();
+                }
+            }
+        }
+
+#endif /* HAVE_ANDROID_OS */
+
+        if (mountMode != MOUNT_EXTERNAL_NONE) {
+            err = mountEmulatedStorage(uid, mountMode);
+            if (err < 0) {
+                ALOGE("cannot mountExternalStorage(): %s", strerror(errno));
+
+                if (errno == ENOTCONN || errno == EROFS) {
+                    // When device is actively encrypting, we get ENOTCONN here
+                    // since FUSE was mounted before the framework restarted.
+                    // When encrypted device is booting, we get EROFS since
+                    // FUSE hasn't been created yet by init.
+                    // In either case, continue without external storage.
+                } else {
+                    dvmAbort();
+                }
+            }
+        }
+
+        err = setgroupsIntarray(gids);
+        if (err < 0) {
+            ALOGE("cannot setgroups(): %s", strerror(errno));
+            dvmAbort();
+        }
+
+        err = setrlimitsFromArray(rlimits);
+        if (err < 0) {
+            ALOGE("cannot setrlimit(): %s", strerror(errno));
+            dvmAbort();
+        }
+
+        err = setresgid(gid, gid, gid);
+        if (err < 0) {
+            ALOGE("cannot setresgid(%d): %s", gid, strerror(errno));
+            dvmAbort();
+        }
+
+        err = setresuid(uid, uid, uid);
+        if (err < 0) {
+            ALOGE("cannot setresuid(%d): %s", uid, strerror(errno));
+            dvmAbort();
+        }
+
+        if (needsNoRandomizeWorkaround()) {
+            int current = personality(0xffffFFFF);
+            int success = personality((ADDR_NO_RANDOMIZE | current));
+            if (success == -1) {
+                ALOGW("Personality switch failed. current=%d error=%d\n", current, errno);
+            }
+        }
+
+        err = setCapabilities(permittedCapabilities, effectiveCapabilities);
+        if (err != 0) {
+            ALOGE("cannot set capabilities (%llx,%llx): %s",
+                permittedCapabilities, effectiveCapabilities, strerror(err));
+            dvmAbort();
+        }
+
+        err = set_sched_policy(0, SP_DEFAULT);
+        if (err < 0) {
+            ALOGE("cannot set_sched_policy(0, SP_DEFAULT): %s", strerror(-err));
+            dvmAbort();
+        }
+
+        err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
+        if (err < 0) {
+            ALOGE("cannot set SELinux context: %s\n", strerror(errno));
+            dvmAbort();
+        }
+        // These free(3) calls are safe because we know we're only ever forking
+        // a single-threaded process, so we know no other thread held the heap
+        // lock when we forked.
+        free(seInfo);
+        free(niceName);
+
+        /*
+         * 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()) {
+            ALOGE("error in post-zygote initialization");
+            dvmAbort();
+        }
+    } else if (pid > 0) {
+        /* the parent process */
+        free(seInfo);
+        free(niceName);
+    }
+
+    return pid;
+}
+
+/*
+ * native public static int nativeForkAndSpecialize(int uid, int gid,
+ *     int[] gids, int debugFlags, int[][] rlimits, int mountExternal,
+ *     String seInfo, String niceName);
+ */
+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 nativeForkSystemServer(int uid, int gid,
+ *     int[] gids, int debugFlags, int[][] rlimits,
+ *     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;
+
+        ALOGI("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) {
+            ALOGE("System server process %d has died. Restarting Zygote!", pid);
+            kill(getpid(), SIGKILL);
+        }
+    }
+    RETURN_INT(pid);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
+    { "nativeFork", "()I",
+      Dalvik_dalvik_system_Zygote_fork },
+    { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;)I",
+      Dalvik_dalvik_system_Zygote_forkAndSpecialize },
+    { "nativeForkSystemServer", "(II[II[[IJJ)I",
+      Dalvik_dalvik_system_Zygote_forkSystemServer },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Class.cpp b/vm/native/java_lang_Class.cpp
new file mode 100644
index 0000000..93455e7
--- /dev/null
+++ b/vm/native/java_lang_Class.cpp
@@ -0,0 +1,828 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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)
+            {
+                ALOGV("ASRT: pkg no match: '%s'(%d) vs '%s'",
+                    className, pkgLen, pCtrl->pkgOrClass);
+            } else {
+                ALOGV("ASRT: pkg match: '%s'(%d) vs '%s' --> %d",
+                    className, pkgLen, pCtrl->pkgOrClass, pCtrl->enable);
+                enable = pCtrl->enable;
+            }
+        } else {
+            /*
+             * "pkgOrClass" holds a fully-qualified class name, converted from
+             * dot-form to slash-form.  An empty string means all classes.
+             */
+            if (pCtrl->pkgOrClass == NULL) {
+                /* -esa/-dsa; see if class is a "system" class */
+                if (strncmp(className, "java/", 5) != 0) {
+                    ALOGV("ASRT: sys no match: '%s'", className);
+                } else {
+                    ALOGV("ASRT: sys match: '%s' --> %d",
+                        className, pCtrl->enable);
+                    enable = pCtrl->enable;
+                }
+            } else if (*pCtrl->pkgOrClass == '\0') {
+                ALOGV("ASRT: class all: '%s' --> %d",
+                    className, pCtrl->enable);
+                enable = pCtrl->enable;
+            } else {
+                if (strcmp(pCtrl->pkgOrClass, className) != 0) {
+                    ALOGV("ASRT: cls no match: '%s' vs '%s'",
+                        className, pCtrl->pkgOrClass);
+                } else {
+                    ALOGV("ASRT: cls match: '%s' vs '%s' --> %d",
+                        className, pCtrl->pkgOrClass, pCtrl->enable);
+                    enable = pCtrl->enable;
+                }
+            }
+        }
+    }
+
+    free(className);
+    RETURN_INT(enable);
+}
+
+/*
+ * static public Class<?> classForName(String name, boolean initialize,
+ *     ClassLoader loader)
+ *
+ * Return the Class object associated with the class or interface with
+ * the specified name.
+ *
+ * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
+ */
+static void Dalvik_java_lang_Class_classForName(const u4* args, JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    bool initialize = (args[1] != 0);
+    Object* loader = (Object*) args[2];
+
+    RETURN_PTR(dvmFindClassByName(nameObj, loader, initialize));
+}
+
+/*
+ * static private ClassLoader getClassLoader(Class clazz)
+ *
+ * Return the class' defining class loader.
+ */
+static void Dalvik_java_lang_Class_getClassLoader(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public Class<?> getComponentType()
+ *
+ * If this is an array type, return the class of the elements; otherwise
+ * return NULL.
+ */
+static void Dalvik_java_lang_Class_getComponentType(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+
+    if (!dvmIsArrayClass(thisPtr))
+        RETURN_PTR(NULL);
+
+    /*
+     * We can't just return thisPtr->elementClass, because that gives
+     * us the base type (e.g. X[][][] returns X).  If this is a multi-
+     * dimensional array, we have to do the lookup by name.
+     */
+    if (thisPtr->descriptor[1] == '[')
+        RETURN_PTR(dvmFindArrayClass(&thisPtr->descriptor[1],
+                   thisPtr->classLoader));
+    else
+        RETURN_PTR(thisPtr->elementClass);
+}
+
+/*
+ * private static Class<?>[] getDeclaredClasses(Class<?> clazz,
+ *     boolean publicOnly)
+ *
+ * Return an array with the classes that are declared by the specified class.
+ * If "publicOnly" is set, we strip out any classes that don't have "public"
+ * access.
+ */
+static void Dalvik_java_lang_Class_getDeclaredClasses(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* classes;
+
+    classes = dvmGetDeclaredClasses(clazz);
+    if (classes == NULL) {
+        if (!dvmCheckException(dvmThreadSelf())) {
+            /* empty list, so create a zero-length array */
+            classes = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+                        0, ALLOC_DEFAULT);
+        }
+    } else if (publicOnly) {
+        u4 count, newIdx, publicCount = 0;
+        ClassObject** pSource = (ClassObject**)(void*)classes->contents;
+        u4 length = classes->length;
+
+        /* count up public classes */
+        for (count = 0; count < length; count++) {
+            if (dvmIsPublicClass(pSource[count]))
+                publicCount++;
+        }
+
+        /* create a new array to hold them */
+        ArrayObject* newClasses;
+        newClasses = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+                        publicCount, ALLOC_DEFAULT);
+
+        /* copy them over */
+        for (count = newIdx = 0; count < length; count++) {
+            if (dvmIsPublicClass(pSource[count])) {
+                dvmSetObjectArrayElement(newClasses, newIdx,
+                                         (Object *)pSource[count]);
+                newIdx++;
+            }
+        }
+        assert(newIdx == publicCount);
+        dvmReleaseTrackedAlloc((Object*) classes, NULL);
+        classes = newClasses;
+    }
+
+    dvmReleaseTrackedAlloc((Object*) classes, NULL);
+    RETURN_PTR(classes);
+}
+
+/*
+ * static Constructor[] getDeclaredConstructors(Class clazz, boolean publicOnly)
+ *     throws SecurityException
+ */
+static void Dalvik_java_lang_Class_getDeclaredConstructors(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* constructors;
+
+    constructors = dvmGetDeclaredConstructors(clazz, publicOnly);
+    dvmReleaseTrackedAlloc((Object*) constructors, NULL);
+
+    RETURN_PTR(constructors);
+}
+
+/*
+ * static Field[] getDeclaredFields(Class klass, boolean publicOnly)
+ */
+static void Dalvik_java_lang_Class_getDeclaredFields(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* fields;
+
+    fields = dvmGetDeclaredFields(clazz, publicOnly);
+    dvmReleaseTrackedAlloc((Object*) fields, NULL);
+
+    RETURN_PTR(fields);
+}
+
+/*
+ * static Field getDeclaredField(Class klass, String name)
+ */
+static void Dalvik_java_lang_Class_getDeclaredField(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    Object* fieldObj = dvmGetDeclaredField(clazz, nameObj);
+    dvmReleaseTrackedAlloc((Object*) fieldObj, NULL);
+    RETURN_PTR(fieldObj);
+}
+
+/*
+ * static Method[] getDeclaredMethods(Class clazz, boolean publicOnly)
+ *     throws SecurityException
+ */
+static void Dalvik_java_lang_Class_getDeclaredMethods(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* methods;
+
+    methods = dvmGetDeclaredMethods(clazz, publicOnly);
+    dvmReleaseTrackedAlloc((Object*) methods, NULL);
+
+    RETURN_PTR(methods);
+}
+
+/*
+ * static native Member getDeclaredConstructorOrMethod(
+ *     Class clazz, String name, Class[] args);
+ */
+static void Dalvik_java_lang_Class_getDeclaredConstructorOrMethod(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    ArrayObject* methodArgs = (ArrayObject*) args[2];
+
+    Object* methodObj;
+
+    methodObj = dvmGetDeclaredConstructorOrMethod(clazz, nameObj, methodArgs);
+    dvmReleaseTrackedAlloc(methodObj, NULL);
+
+    RETURN_PTR(methodObj);
+}
+
+/*
+ * Class[] getInterfaces()
+ */
+static void Dalvik_java_lang_Class_getInterfaces(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ArrayObject* interfaces;
+
+    interfaces = dvmGetInterfaces(clazz);
+    dvmReleaseTrackedAlloc((Object*) interfaces, NULL);
+
+    RETURN_PTR(interfaces);
+}
+
+/*
+ * private static int getModifiers(Class klass, boolean
+ *     ignoreInnerClassesAttrib)
+ *
+ * Return the class' modifier flags.  If "ignoreInnerClassesAttrib" is false,
+ * and this is an inner class, we return the access flags from the inner class
+ * attribute.
+ */
+static void Dalvik_java_lang_Class_getModifiers(const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool ignoreInner = args[1];
+    u4 accessFlags;
+
+    accessFlags = clazz->accessFlags & JAVA_FLAGS_MASK;
+
+    if (!ignoreInner) {
+        /* see if we have an InnerClass annotation with flags in it */
+        StringObject* className = NULL;
+        int innerFlags;
+
+        if (dvmGetInnerClass(clazz, &className, &innerFlags))
+            accessFlags = innerFlags & JAVA_FLAGS_MASK;
+
+        dvmReleaseTrackedAlloc((Object*) className, NULL);
+    }
+
+    RETURN_INT(accessFlags);
+}
+
+/*
+ * private native String getNameNative()
+ *
+ * Return the class' name. The exact format is bizarre, but it's the specified
+ * behavior: keywords for primitive types, regular "[I" form for primitive
+ * arrays (so "int" but "[I"), and arrays of reference types written
+ * between "L" and ";" but with dots rather than slashes (so "java.lang.String"
+ * but "[Ljava.lang.String;"). Madness.
+ */
+static void Dalvik_java_lang_Class_getNameNative(const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    const char* descriptor = clazz->descriptor;
+    StringObject* nameObj;
+
+    if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+        /*
+         * The descriptor indicates that this is the class for
+         * a primitive type; special-case the return value.
+         */
+        const char* name;
+        switch (descriptor[0]) {
+            case 'Z': name = "boolean"; break;
+            case 'B': name = "byte";    break;
+            case 'C': name = "char";    break;
+            case 'S': name = "short";   break;
+            case 'I': name = "int";     break;
+            case 'J': name = "long";    break;
+            case 'F': name = "float";   break;
+            case 'D': name = "double";  break;
+            case 'V': name = "void";    break;
+            default: {
+                ALOGE("Unknown primitive type '%c'", descriptor[0]);
+                assert(false);
+                RETURN_PTR(NULL);
+            }
+        }
+
+        nameObj = dvmCreateStringFromCstr(name);
+    } else {
+        /*
+         * Convert the UTF-8 name to a java.lang.String. The
+         * name must use '.' to separate package components.
+         *
+         * TODO: this could be more efficient. Consider a custom
+         * conversion function here that walks the string once and
+         * avoids the allocation for the common case (name less than,
+         * say, 128 bytes).
+         */
+        char* dotName = dvmDescriptorToDot(clazz->descriptor);
+        nameObj = dvmCreateStringFromCstr(dotName);
+        free(dotName);
+    }
+
+    dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+    RETURN_PTR(nameObj);
+}
+
+/*
+ * Return the superclass for instances of this class.
+ *
+ * If the class represents a java/lang/Object, an interface, a primitive
+ * type, or void (which *is* a primitive type??), return NULL.
+ *
+ * For an array, return the java/lang/Object ClassObject.
+ */
+static void Dalvik_java_lang_Class_getSuperclass(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz))
+        RETURN_PTR(NULL);
+    else
+        RETURN_PTR(clazz->super);
+}
+
+/*
+ * public boolean isAssignableFrom(Class<?> cls)
+ *
+ * Determine if this class is either the same as, or is a superclass or
+ * superinterface of, the class specified in the "cls" parameter.
+ */
+static void Dalvik_java_lang_Class_isAssignableFrom(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+    ClassObject* testClass = (ClassObject*) args[1];
+
+    if (testClass == NULL) {
+        dvmThrowNullPointerException("cls == null");
+        RETURN_INT(false);
+    }
+    RETURN_INT(dvmInstanceof(testClass, thisPtr));
+}
+
+/*
+ * public boolean isInstance(Object o)
+ *
+ * Dynamic equivalent of Java programming language "instanceof".
+ */
+static void Dalvik_java_lang_Class_isInstance(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+    Object* testObj = (Object*) args[1];
+
+    if (testObj == NULL)
+        RETURN_INT(false);
+    RETURN_INT(dvmInstanceof(testObj->clazz, thisPtr));
+}
+
+/*
+ * public boolean isInterface()
+ */
+static void Dalvik_java_lang_Class_isInterface(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+
+    RETURN_INT(dvmIsInterfaceClass(thisPtr));
+}
+
+/*
+ * public boolean isPrimitive()
+ */
+static void Dalvik_java_lang_Class_isPrimitive(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+
+    RETURN_INT(dvmIsPrimitiveClass(thisPtr));
+}
+
+/*
+ * public T newInstance() throws InstantiationException, IllegalAccessException
+ *
+ * Create a new instance of this class.
+ */
+static void Dalvik_java_lang_Class_newInstance(const u4* args, JValue* pResult)
+{
+    Thread* self = dvmThreadSelf();
+    ClassObject* clazz = (ClassObject*) args[0];
+    Method* init;
+    Object* newObj;
+
+    /* can't instantiate these */
+    if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz)
+        || dvmIsArrayClass(clazz) || dvmIsAbstractClass(clazz))
+    {
+        ALOGD("newInstance failed: p%d i%d [%d a%d",
+            dvmIsPrimitiveClass(clazz), dvmIsInterfaceClass(clazz),
+            dvmIsArrayClass(clazz), dvmIsAbstractClass(clazz));
+        dvmThrowInstantiationException(clazz, NULL);
+        RETURN_VOID();
+    }
+
+    /* initialize the class if it hasn't been already */
+    if (!dvmIsClassInitialized(clazz)) {
+        if (!dvmInitClass(clazz)) {
+            ALOGW("Class init failed in newInstance call (%s)",
+                clazz->descriptor);
+            assert(dvmCheckException(self));
+            RETURN_VOID();
+        }
+    }
+
+    /* find the "nullary" constructor */
+    init = dvmFindDirectMethodByDescriptor(clazz, "<init>", "()V");
+    if (init == NULL) {
+        /* common cause: secret "this" arg on non-static inner class ctor */
+        ALOGD("newInstance failed: no <init>()");
+        dvmThrowInstantiationException(clazz, "no empty constructor");
+        RETURN_VOID();
+    }
+
+    /*
+     * Verify access from the call site.
+     *
+     * First, make sure the method invoking Class.newInstance() has permission
+     * to access the class.
+     *
+     * Second, make sure it has permission to invoke the constructor.  The
+     * constructor must be public or, if the caller is in the same package,
+     * have package scope.
+     */
+    ClassObject* callerClass = dvmGetCaller2Class(self->interpSave.curFrame);
+
+    if (!dvmCheckClassAccess(callerClass, clazz)) {
+        ALOGD("newInstance failed: %s not accessible to %s",
+            clazz->descriptor, callerClass->descriptor);
+        dvmThrowIllegalAccessException("access to class not allowed");
+        RETURN_VOID();
+    }
+    if (!dvmCheckMethodAccess(callerClass, init)) {
+        ALOGD("newInstance failed: %s.<init>() not accessible to %s",
+            clazz->descriptor, callerClass->descriptor);
+        dvmThrowIllegalAccessException("access to constructor not allowed");
+        RETURN_VOID();
+    }
+
+    newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    JValue unused;
+
+    /* invoke constructor; unlike reflection calls, we don't wrap exceptions */
+    dvmCallMethod(self, init, newObj, &unused);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+
+    RETURN_PTR(newObj);
+}
+
+/*
+ * 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)
+{
+    dvmThrowUnsupportedOperationException("native method not implemented");
+
+    RETURN_PTR(NULL);
+}
+
+static void Dalvik_java_lang_Class_getGenericSuperclass(const u4* args,
+    JValue* pResult)
+{
+    dvmThrowUnsupportedOperationException("native method not implemented");
+
+    RETURN_PTR(NULL);
+}
+
+static void Dalvik_java_lang_Class_getTypeParameters(const u4* args,
+    JValue* pResult)
+{
+    dvmThrowUnsupportedOperationException("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);
+}
+
+/*
+ * private Annotation getDeclaredAnnotation(Class annotationClass)
+ */
+static void Dalvik_java_lang_Class_getDeclaredAnnotation(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ClassObject* annotationClazz = (ClassObject*) args[1];
+
+    RETURN_PTR(dvmGetClassAnnotation(clazz, annotationClazz));
+}
+
+/*
+ * private boolean isDeclaredAnnotationPresent(Class annotationClass);
+ */
+static void Dalvik_java_lang_Class_isDeclaredAnnotationPresent(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ClassObject* annotationClazz = (ClassObject*) args[1];
+
+    RETURN_BOOLEAN(dvmIsClassAnnotationPresent(clazz, annotationClazz));
+}
+
+/*
+ * public String getInnerClassName()
+ *
+ * Returns the simple name of a member class or local class, or null otherwise.
+ */
+static void Dalvik_java_lang_Class_getInnerClassName(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    StringObject* nameObj;
+    int flags;
+
+    if (dvmGetInnerClass(clazz, &nameObj, &flags)) {
+        dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+        RETURN_PTR(nameObj);
+    } else {
+        RETURN_PTR(NULL);
+    }
+}
+
+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 },
+    { "getDeclaredField",      "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;",
+        Dalvik_java_lang_Class_getDeclaredField },
+    { "getDeclaredConstructorOrMethod", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Member;",
+        Dalvik_java_lang_Class_getDeclaredConstructorOrMethod },
+    { "getInterfaces",          "()[Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getInterfaces },
+    { "getModifiers",           "(Ljava/lang/Class;Z)I",
+        Dalvik_java_lang_Class_getModifiers },
+    { "getNameNative",                "()Ljava/lang/String;",
+        Dalvik_java_lang_Class_getNameNative },
+    { "getSuperclass",          "()Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getSuperclass },
+    { "isAssignableFrom",       "(Ljava/lang/Class;)Z",
+        Dalvik_java_lang_Class_isAssignableFrom },
+    { "isInstance",             "(Ljava/lang/Object;)Z",
+        Dalvik_java_lang_Class_isInstance },
+    { "isInterface",            "()Z",
+        Dalvik_java_lang_Class_isInterface },
+    { "isPrimitive",            "()Z",
+        Dalvik_java_lang_Class_isPrimitive },
+    { "newInstanceImpl",        "()Ljava/lang/Object;",
+        Dalvik_java_lang_Class_newInstance },
+    { "getDeclaringClass",      "()Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getDeclaringClass },
+    { "getEnclosingClass",      "()Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getEnclosingClass },
+    { "getEnclosingConstructor", "()Ljava/lang/reflect/Constructor;",
+        Dalvik_java_lang_Class_getEnclosingConstructor },
+    { "getEnclosingMethod",     "()Ljava/lang/reflect/Method;",
+        Dalvik_java_lang_Class_getEnclosingMethod },
+#if 0
+    { "getGenericInterfaces",   "()[Ljava/lang/reflect/Type;",
+        Dalvik_java_lang_Class_getGenericInterfaces },
+    { "getGenericSuperclass",   "()Ljava/lang/reflect/Type;",
+        Dalvik_java_lang_Class_getGenericSuperclass },
+    { "getTypeParameters",      "()Ljava/lang/reflect/TypeVariable;",
+        Dalvik_java_lang_Class_getTypeParameters },
+#endif
+    { "isAnonymousClass",       "()Z",
+        Dalvik_java_lang_Class_isAnonymousClass },
+    { "getDeclaredAnnotations", "()[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_Class_getDeclaredAnnotations },
+    { "getDeclaredAnnotation", "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_Class_getDeclaredAnnotation },
+    { "isDeclaredAnnotationPresent", "(Ljava/lang/Class;)Z",
+        Dalvik_java_lang_Class_isDeclaredAnnotationPresent },
+    { "getInnerClassName",       "()Ljava/lang/String;",
+        Dalvik_java_lang_Class_getInnerClassName },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Double.cpp b/vm/native/java_lang_Double.cpp
new file mode 100644
index 0000000..b019c8c
--- /dev/null
+++ b/vm/native/java_lang_Double.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void Double_doubleToLongBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangDouble_doubleToLongBits);
+}
+
+static void Double_doubleToRawLongBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangDouble_doubleToRawLongBits);
+}
+
+static void Double_longBitsToDouble(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangDouble_longBitsToDouble);
+}
+
+const DalvikNativeMethod dvm_java_lang_Double[] = {
+    { "doubleToLongBits",    "(D)J", Double_doubleToLongBits },
+    { "doubleToRawLongBits", "(D)J", Double_doubleToRawLongBits },
+    { "longBitsToDouble",    "(J)D", Double_longBitsToDouble },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Float.cpp b/vm/native/java_lang_Float.cpp
new file mode 100644
index 0000000..e99e4aa
--- /dev/null
+++ b/vm/native/java_lang_Float.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void Float_floatToIntBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangFloat_floatToIntBits);
+}
+
+static void Float_floatToRawIntBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangFloat_floatToRawIntBits);
+}
+
+static void Float_intBitsToFloat(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangFloat_intBitsToFloat);
+}
+
+const DalvikNativeMethod dvm_java_lang_Float[] = {
+    { "floatToIntBits",    "(F)I", Float_floatToIntBits },
+    { "floatToRawIntBits", "(F)I", Float_floatToRawIntBits },
+    { "intBitsToFloat",    "(I)F", Float_intBitsToFloat },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Math.cpp b/vm/native/java_lang_Math.cpp
new file mode 100644
index 0000000..7c17242
--- /dev/null
+++ b/vm/native/java_lang_Math.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void Math_absD(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_double);
+}
+
+static void Math_absF(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_float);
+}
+
+static void Math_absI(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_int);
+}
+
+static void Math_absJ(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_long);
+}
+
+static void Math_cos(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_cos);
+}
+
+static void Math_maxI(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_max_int);
+}
+
+static void Math_minI(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_min_int);
+}
+
+static void Math_sin(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_sin);
+}
+
+static void Math_sqrt(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_sqrt);
+}
+
+const DalvikNativeMethod dvm_java_lang_Math[] = {
+    { "abs",  "(D)D",  Math_absD },
+    { "abs",  "(F)F",  Math_absF },
+    { "abs",  "(I)I",  Math_absI },
+    { "abs",  "(J)J",  Math_absJ },
+    { "cos",  "(D)D",  Math_cos },
+    { "max",  "(II)I", Math_maxI },
+    { "min",  "(II)I", Math_minI },
+    { "sin",  "(D)D",  Math_sin },
+    { "sqrt", "(D)D",  Math_sqrt },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Object.cpp b/vm/native/java_lang_Object.cpp
new file mode 100644
index 0000000..12f701f
--- /dev/null
+++ b/vm/native/java_lang_Object.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Object
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private Object internalClone()
+ *
+ * Implements most of Object.clone().
+ */
+static void Dalvik_java_lang_Object_internalClone(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Object* clone = dvmCloneObject(thisPtr, ALLOC_DONT_TRACK);
+
+    RETURN_PTR(clone);
+}
+
+/*
+ * public int hashCode()
+ */
+static void Dalvik_java_lang_Object_hashCode(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    RETURN_INT(dvmIdentityHashCode(thisPtr));
+}
+
+/*
+ * public Class getClass()
+ */
+static void Dalvik_java_lang_Object_getClass(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    RETURN_PTR(thisPtr->clazz);
+}
+
+/*
+ * public void notify()
+ *
+ * NOTE: we declare this as a full DalvikBridgeFunc, rather than a
+ * DalvikNativeFunc, because we really want to avoid the "self" lookup.
+ */
+static void Dalvik_java_lang_Object_notify(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    dvmObjectNotify(self, thisPtr);
+    RETURN_VOID();
+}
+
+/*
+ * public void notifyAll()
+ */
+static void Dalvik_java_lang_Object_notifyAll(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    dvmObjectNotifyAll(self, thisPtr);
+    RETURN_VOID();
+}
+
+/*
+ * public void wait(long ms, int ns) throws InterruptedException
+ */
+static void Dalvik_java_lang_Object_wait(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    dvmObjectWait(self, thisPtr, GET_ARG_LONG(args,1), (s4)args[3], true);
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_Object[] = {
+    { "internalClone",  "(Ljava/lang/Cloneable;)Ljava/lang/Object;",
+        Dalvik_java_lang_Object_internalClone },
+    { "hashCode",       "()I",
+        Dalvik_java_lang_Object_hashCode },
+    { "notify",         "()V",
+        (DalvikNativeFunc) Dalvik_java_lang_Object_notify },
+    { "notifyAll",      "()V",
+        (DalvikNativeFunc) Dalvik_java_lang_Object_notifyAll },
+    { "wait",           "(JI)V",
+        (DalvikNativeFunc) Dalvik_java_lang_Object_wait },
+    { "getClass",       "()Ljava/lang/Class;",
+        Dalvik_java_lang_Object_getClass },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Runtime.cpp b/vm/native/java_lang_Runtime.cpp
new file mode 100644
index 0000000..2d1c4fe
--- /dev/null
+++ b/vm/native/java_lang_Runtime.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Runtime
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <dlfcn.h>
+#include <limits.h>
+#include <unistd.h>
+
+/*
+ * public void gc()
+ *
+ * Initiate a gc.
+ */
+static void Dalvik_java_lang_Runtime_gc(const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmCollectGarbage();
+    RETURN_VOID();
+}
+
+/*
+ * private static void nativeExit(int code)
+ *
+ * Runtime.exit() calls this after doing shutdown processing.  Runtime.halt()
+ * uses this as well.
+ */
+static void Dalvik_java_lang_Runtime_nativeExit(const u4* args,
+    JValue* pResult)
+{
+    int status = args[0];
+    if (gDvm.exitHook != NULL) {
+        dvmChangeStatus(NULL, THREAD_NATIVE);
+        (*gDvm.exitHook)(status);     // not expected to return
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+        ALOGW("JNI exit hook returned");
+    }
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    dvmCompilerDumpStats();
+#endif
+    ALOGD("Calling exit(%d)", status);
+    exit(status);
+}
+
+/*
+ * static String nativeLoad(String filename, ClassLoader loader, String ldLibraryPath)
+ *
+ * Load the specified full path as a dynamic library filled with
+ * JNI-compatible methods. Returns null on success, or a failure
+ * message on failure.
+ */
+static void Dalvik_java_lang_Runtime_nativeLoad(const u4* args,
+    JValue* pResult)
+{
+    StringObject* fileNameObj = (StringObject*) args[0];
+    Object* classLoader = (Object*) args[1];
+    StringObject* ldLibraryPathObj = (StringObject*) args[2];
+
+    assert(fileNameObj != NULL);
+    char* fileName = dvmCreateCstrFromString(fileNameObj);
+
+    if (ldLibraryPathObj != NULL) {
+        char* ldLibraryPath = dvmCreateCstrFromString(ldLibraryPathObj);
+        void* sym = dlsym(RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH");
+        if (sym != NULL) {
+            typedef void (*Fn)(const char*);
+            Fn android_update_LD_LIBRARY_PATH = reinterpret_cast<Fn>(sym);
+            (*android_update_LD_LIBRARY_PATH)(ldLibraryPath);
+        } else {
+            ALOGE("android_update_LD_LIBRARY_PATH not found; .so dependencies will not work!");
+        }
+        free(ldLibraryPath);
+    }
+
+    StringObject* result = NULL;
+    char* reason = NULL;
+    bool success = dvmLoadNativeCode(fileName, classLoader, &reason);
+    if (!success) {
+        const char* msg = (reason != NULL) ? reason : "unknown failure";
+        result = dvmCreateStringFromCstr(msg);
+        dvmReleaseTrackedAlloc((Object*) result, NULL);
+    }
+
+    free(reason);
+    free(fileName);
+    RETURN_PTR(result);
+}
+
+/*
+ * public long maxMemory()
+ *
+ * Returns GC heap max memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_maxMemory(const u4* args, JValue* pResult)
+{
+    RETURN_LONG(dvmGetHeapDebugInfo(kVirtualHeapMaximumSize));
+}
+
+/*
+ * public long totalMemory()
+ *
+ * Returns GC heap total memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_totalMemory(const u4* args,
+    JValue* pResult)
+{
+    RETURN_LONG(dvmGetHeapDebugInfo(kVirtualHeapSize));
+}
+
+/*
+ * public long freeMemory()
+ *
+ * Returns GC heap free memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_freeMemory(const u4* args,
+    JValue* pResult)
+{
+    size_t size = dvmGetHeapDebugInfo(kVirtualHeapSize);
+    size_t allocated = dvmGetHeapDebugInfo(kVirtualHeapAllocated);
+    long long result = size - allocated;
+    if (result < 0) {
+        result = 0;
+    }
+    RETURN_LONG(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_Runtime[] = {
+    { "freeMemory",          "()J",
+        Dalvik_java_lang_Runtime_freeMemory },
+    { "gc",                 "()V",
+        Dalvik_java_lang_Runtime_gc },
+    { "maxMemory",          "()J",
+        Dalvik_java_lang_Runtime_maxMemory },
+    { "nativeExit",         "(I)V",
+        Dalvik_java_lang_Runtime_nativeExit },
+    { "nativeLoad",         "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;",
+        Dalvik_java_lang_Runtime_nativeLoad },
+    { "totalMemory",          "()J",
+        Dalvik_java_lang_Runtime_totalMemory },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_String.cpp b/vm/native/java_lang_String.cpp
new file mode 100644
index 0000000..38f9e31
--- /dev/null
+++ b/vm/native/java_lang_String.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.String
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void String_charAt(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_charAt);
+}
+
+static void String_compareTo(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_compareTo);
+}
+
+static void String_equals(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_equals);
+}
+
+static void String_fastIndexOf(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_fastIndexOf_II);
+}
+
+static void String_intern(const u4* args, JValue* pResult)
+{
+    StringObject* str = (StringObject*) args[0];
+    StringObject* interned = dvmLookupInternedString(str);
+    RETURN_PTR(interned);
+}
+
+static void String_isEmpty(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_isEmpty);
+}
+
+static void String_length(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_length);
+}
+
+const DalvikNativeMethod dvm_java_lang_String[] = {
+    { "charAt",      "(I)C",                  String_charAt },
+    { "compareTo",   "(Ljava/lang/String;)I", String_compareTo },
+    { "equals",      "(Ljava/lang/Object;)Z", String_equals },
+    { "fastIndexOf", "(II)I",                 String_fastIndexOf },
+    { "intern",      "()Ljava/lang/String;",  String_intern },
+    { "isEmpty",     "()Z",                   String_isEmpty },
+    { "length",      "()I",                   String_length },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_System.cpp b/vm/native/java_lang_System.cpp
new file mode 100644
index 0000000..29063cd
--- /dev/null
+++ b/vm/native/java_lang_System.cpp
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+/*
+ * The VM makes guarantees about the atomicity of accesses to primitive
+ * variables.  These guarantees also apply to elements of arrays.
+ * In particular, 8-bit, 16-bit, and 32-bit accesses must be atomic and
+ * must not cause "word tearing".  Accesses to 64-bit array elements must
+ * either be atomic or treated as two 32-bit operations.  References are
+ * always read and written atomically, regardless of the number of bits
+ * used to represent them.
+ *
+ * We can't rely on standard libc functions like memcpy() and memmove()
+ * in our implementation of System.arraycopy(), because they may copy
+ * byte-by-byte (either for the full run or for "unaligned" parts at the
+ * start or end).  We need to use functions that guarantee 16-bit or 32-bit
+ * atomicity as appropriate.
+ *
+ * System.arraycopy() is heavily used, so having an efficient implementation
+ * is important.  The bionic libc provides a platform-optimized memory move
+ * function that should be used when possible.  If it's not available,
+ * the trivial "reference implementation" versions below can be used until
+ * a proper version can be written.
+ *
+ * For these functions, The caller must guarantee that dest/src are aligned
+ * appropriately for the element type, and that n is a multiple of the
+ * element size.
+ */
+
+/*
+ * Works like memmove(), except:
+ * - if all arguments are at least 32-bit aligned, we guarantee that we
+ *   will use operations that preserve atomicity of 32-bit values
+ * - if not, we guarantee atomicity of 16-bit values
+ *
+ * If all three arguments are not at least 16-bit aligned, the behavior
+ * of this function is undefined.  (We could remove this restriction by
+ * testing for unaligned values and punting to memmove(), but that's
+ * not currently useful.)
+ *
+ * TODO: add loop for 64-bit alignment
+ * TODO: use __builtin_prefetch
+ * TODO: write an ARM-optimized version
+ */
+static void memmove_words(void* dest, const void* src, size_t n) {
+    assert((((uintptr_t) dest | (uintptr_t) src | n) & 0x01) == 0);
+
+    char* d = (char*) dest;
+    const char* s = (const char*) src;
+    size_t copyCount;
+
+    /*
+     * If the source and destination pointers are the same, this is
+     * an expensive no-op.  Testing for an empty move now allows us
+     * to skip a check later.
+     */
+    if (n == 0 || d == s)
+        return;
+
+    /*
+     * Determine if the source and destination buffers will overlap if
+     * we copy data forward (i.e. *dest++ = *src++).
+     *
+     * It's okay if the destination buffer starts before the source and
+     * there is some overlap, because the reader is always ahead of the
+     * writer.
+     */
+    if (__builtin_expect((d < s) || ((size_t)(d - s) >= n), 1)) {
+        /*
+         * Copy forward.  We prefer 32-bit loads and stores even for 16-bit
+         * data, so sort that out.
+         */
+        if ((((uintptr_t) d | (uintptr_t) s) & 0x03) != 0) {
+            /*
+             * Not 32-bit aligned.  Two possibilities:
+             * (1) Congruent, we can align to 32-bit by copying one 16-bit val
+             * (2) Non-congruent, we can do one of:
+             *   a. copy whole buffer as a series of 16-bit values
+             *   b. load/store 32 bits, using shifts to ensure alignment
+             *   c. just copy the as 32-bit values and assume the CPU
+             *      will do a reasonable job
+             *
+             * We're currently using (a), which is suboptimal.
+             */
+            if ((((uintptr_t) d ^ (uintptr_t) s) & 0x03) != 0) {
+                copyCount = n;
+            } else {
+                copyCount = 2;
+            }
+            n -= copyCount;
+            copyCount /= sizeof(uint16_t);
+
+            while (copyCount--) {
+                *(uint16_t*)d = *(uint16_t*)s;
+                d += sizeof(uint16_t);
+                s += sizeof(uint16_t);
+            }
+        }
+
+        /*
+         * Copy 32-bit aligned words.
+         */
+        copyCount = n / sizeof(uint32_t);
+        while (copyCount--) {
+            *(uint32_t*)d = *(uint32_t*)s;
+            d += sizeof(uint32_t);
+            s += sizeof(uint32_t);
+        }
+
+        /*
+         * Check for leftovers.  Either we finished exactly, or we have
+         * one remaining 16-bit chunk.
+         */
+        if ((n & 0x02) != 0) {
+            *(uint16_t*)d = *(uint16_t*)s;
+        }
+    } else {
+        /*
+         * Copy backward, starting at the end.
+         */
+        d += n;
+        s += n;
+
+        if ((((uintptr_t) d | (uintptr_t) s) & 0x03) != 0) {
+            /* try for 32-bit alignment */
+            if ((((uintptr_t) d ^ (uintptr_t) s) & 0x03) != 0) {
+                copyCount = n;
+            } else {
+                copyCount = 2;
+            }
+            n -= copyCount;
+            copyCount /= sizeof(uint16_t);
+
+            while (copyCount--) {
+                d -= sizeof(uint16_t);
+                s -= sizeof(uint16_t);
+                *(uint16_t*)d = *(uint16_t*)s;
+            }
+        }
+
+        /* copy 32-bit aligned words */
+        copyCount = n / sizeof(uint32_t);
+        while (copyCount--) {
+            d -= sizeof(uint32_t);
+            s -= sizeof(uint32_t);
+            *(uint32_t*)d = *(uint32_t*)s;
+        }
+
+        /* copy leftovers */
+        if ((n & 0x02) != 0) {
+            d -= sizeof(uint16_t);
+            s -= sizeof(uint16_t);
+            *(uint16_t*)d = *(uint16_t*)s;
+        }
+    }
+}
+
+#define move16 memmove_words
+#define move32 memmove_words
+
+/*
+ * public static void arraycopy(Object src, int srcPos, Object dest,
+ *      int destPos, int length)
+ *
+ * The description of this function is long, and describes a multitude
+ * of checks and exceptions.
+ */
+static void Dalvik_java_lang_System_arraycopy(const u4* args, JValue* pResult)
+{
+    ArrayObject* srcArray = (ArrayObject*) args[0];
+    int srcPos = args[1];
+    ArrayObject* dstArray = (ArrayObject*) args[2];
+    int dstPos = args[3];
+    int length = args[4];
+
+    /* Check for null pointers. */
+    if (srcArray == NULL) {
+        dvmThrowNullPointerException("src == null");
+        RETURN_VOID();
+    }
+    if (dstArray == NULL) {
+        dvmThrowNullPointerException("dst == null");
+        RETURN_VOID();
+    }
+
+    /* Make sure source and destination are arrays. */
+    if (!dvmIsArray(srcArray)) {
+        dvmThrowArrayStoreExceptionNotArray(((Object*)srcArray)->clazz, "source");
+        RETURN_VOID();
+    }
+    if (!dvmIsArray(dstArray)) {
+        dvmThrowArrayStoreExceptionNotArray(((Object*)dstArray)->clazz, "destination");
+        RETURN_VOID();
+    }
+
+    /* avoid int overflow */
+    if (srcPos < 0 || dstPos < 0 || length < 0 ||
+        srcPos > (int) srcArray->length - length ||
+        dstPos > (int) dstArray->length - length)
+    {
+        dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
+            "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
+            srcArray->length, srcPos, dstArray->length, dstPos, length);
+        RETURN_VOID();
+    }
+
+    ClassObject* srcClass = srcArray->clazz;
+    ClassObject* dstClass = dstArray->clazz;
+    char srcType = srcClass->descriptor[1];
+    char dstType = dstClass->descriptor[1];
+
+    /*
+     * If one of the arrays holds a primitive type, the other array must
+     * hold the same type.
+     */
+    bool srcPrim = (srcType != '[' && srcType != 'L');
+    bool dstPrim = (dstType != '[' && dstType != 'L');
+    if (srcPrim || dstPrim) {
+        if (srcPrim != dstPrim || srcType != dstType) {
+            dvmThrowArrayStoreExceptionIncompatibleArrays(srcClass, dstClass);
+            RETURN_VOID();
+        }
+
+        if (false) ALOGD("arraycopy prim[%c] dst=%p %d src=%p %d len=%d",
+            srcType, dstArray->contents, dstPos,
+            srcArray->contents, srcPos, length);
+
+        switch (srcType) {
+        case 'B':
+        case 'Z':
+            /* 1 byte per element */
+            memmove((u1*) dstArray->contents + dstPos,
+                (const u1*) srcArray->contents + srcPos,
+                length);
+            break;
+        case 'C':
+        case 'S':
+            /* 2 bytes per element */
+            move16((u1*) dstArray->contents + dstPos * 2,
+                (const u1*) srcArray->contents + srcPos * 2,
+                length * 2);
+            break;
+        case 'F':
+        case 'I':
+            /* 4 bytes per element */
+            move32((u1*) dstArray->contents + dstPos * 4,
+                (const u1*) srcArray->contents + srcPos * 4,
+                length * 4);
+            break;
+        case 'D':
+        case 'J':
+            /*
+             * 8 bytes per element.  We don't need to guarantee atomicity
+             * of the entire 64-bit word, so we can use the 32-bit copier.
+             */
+            move32((u1*) dstArray->contents + dstPos * 8,
+                (const u1*) srcArray->contents + srcPos * 8,
+                length * 8);
+            break;
+        default:        /* illegal array type */
+            ALOGE("Weird array type '%s'", srcClass->descriptor);
+            dvmAbort();
+        }
+    } else {
+        /*
+         * Neither class is primitive.  See if elements in "src" are instances
+         * of elements in "dst" (e.g. copy String to String or String to
+         * Object).
+         */
+        const int width = sizeof(Object*);
+
+        if (srcClass->arrayDim == dstClass->arrayDim &&
+            dvmInstanceof(srcClass, dstClass))
+        {
+            /*
+             * "dst" can hold "src"; copy the whole thing.
+             */
+            if (false) ALOGD("arraycopy ref dst=%p %d src=%p %d len=%d",
+                dstArray->contents, dstPos * width,
+                srcArray->contents, srcPos * width,
+                length * width);
+            move32((u1*)dstArray->contents + dstPos * width,
+                (const u1*)srcArray->contents + srcPos * width,
+                length * width);
+            dvmWriteBarrierArray(dstArray, dstPos, dstPos+length);
+        } else {
+            /*
+             * The arrays are not fundamentally compatible.  However, we
+             * may still be able to do this if the destination object is
+             * compatible (e.g. copy Object[] to String[], but the Object
+             * being copied is actually a String).  We need to copy elements
+             * one by one until something goes wrong.
+             *
+             * Because of overlapping moves, what we really want to do
+             * is compare the types and count up how many we can move,
+             * then call move32() to shift the actual data.  If we just
+             * start from the front we could do a smear rather than a move.
+             */
+            Object** srcObj;
+            int copyCount;
+            ClassObject*   clazz = NULL;
+
+            srcObj = ((Object**)(void*)srcArray->contents) + srcPos;
+
+            if (length > 0 && srcObj[0] != NULL)
+            {
+                clazz = srcObj[0]->clazz;
+                if (!dvmCanPutArrayElement(clazz, dstClass))
+                    clazz = NULL;
+            }
+
+            for (copyCount = 0; copyCount < length; copyCount++)
+            {
+                if (srcObj[copyCount] != NULL &&
+                    srcObj[copyCount]->clazz != clazz &&
+                    !dvmCanPutArrayElement(srcObj[copyCount]->clazz, dstClass))
+                {
+                    /* can't put this element into the array */
+                    break;
+                }
+            }
+
+            if (false) ALOGD("arraycopy iref dst=%p %d src=%p %d count=%d of %d",
+                dstArray->contents, dstPos * width,
+                srcArray->contents, srcPos * width,
+                copyCount, length);
+            move32((u1*)dstArray->contents + dstPos * width,
+                (const u1*)srcArray->contents + srcPos * width,
+                copyCount * width);
+            dvmWriteBarrierArray(dstArray, 0, copyCount);
+            if (copyCount != length) {
+                dvmThrowArrayStoreExceptionIncompatibleArrayElement(srcPos + copyCount,
+                        srcObj[copyCount]->clazz, dstClass);
+                RETURN_VOID();
+            }
+        }
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * 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));
+}
+
+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) {
+        dvmThrowNullPointerException("userLibName == 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 },
+    { "identityHashCode",  "(Ljava/lang/Object;)I",
+        Dalvik_java_lang_System_identityHashCode },
+    { "mapLibraryName",     "(Ljava/lang/String;)Ljava/lang/String;",
+        Dalvik_java_lang_System_mapLibraryName },
+    { "nanoTime",  "()J",
+        Dalvik_java_lang_System_nanoTime },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Throwable.cpp b/vm/native/java_lang_Throwable.cpp
new file mode 100644
index 0000000..6e3ab6c
--- /dev/null
+++ b/vm/native/java_lang_Throwable.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Throwable
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object nativeFillInStackTrace()
+ */
+static void Dalvik_java_lang_Throwable_nativeFillInStackTrace(const u4* args,
+    JValue* pResult)
+{
+    Object* stackState = NULL;
+
+    UNUSED_PARAMETER(args);
+
+    stackState = dvmFillInStackTrace(dvmThreadSelf());
+    RETURN_PTR(stackState);
+}
+
+/*
+ * private static StackTraceElement[] nativeGetStackTrace(Object stackState)
+ *
+ * The "stackState" argument must be the value returned by an earlier call to
+ * nativeFillInStackTrace().
+ */
+static void Dalvik_java_lang_Throwable_nativeGetStackTrace(const u4* args,
+    JValue* pResult)
+{
+    Object* stackState = (Object*) args[0];
+    ArrayObject* elements = NULL;
+
+    if (stackState == NULL) {
+        ALOGW("getStackTrace() called but no trace available");
+        RETURN_PTR(NULL);   /* could throw NPE; currently caller will do so */
+    }
+
+    elements = dvmGetStackTrace(stackState);
+    RETURN_PTR(elements);
+}
+
+const DalvikNativeMethod dvm_java_lang_Throwable[] = {
+    { "nativeFillInStackTrace", "()Ljava/lang/Object;",
+        Dalvik_java_lang_Throwable_nativeFillInStackTrace },
+    { "nativeGetStackTrace",    "(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;",
+        Dalvik_java_lang_Throwable_nativeGetStackTrace },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_VMClassLoader.cpp b/vm/native/java_lang_VMClassLoader.cpp
new file mode 100644
index 0000000..eeff688
--- /dev/null
+++ b/vm/native/java_lang_VMClassLoader.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.VMClassLoader
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static Class defineClass(ClassLoader cl, String name,
+ *     byte[] data, int offset, int len)
+ *     throws ClassFormatError
+ *
+ * Convert an array of bytes to a Class object.
+ */
+static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args,
+    JValue* pResult)
+{
+    Object* loader = (Object*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    const u1* data = (const u1*) args[2];
+    int offset = args[3];
+    int len = args[4];
+    char* name = NULL;
+
+    name = dvmCreateCstrFromString(nameObj);
+    ALOGE("ERROR: defineClass(%p, %s, %p, %d, %d)",
+        loader, name, data, offset, len);
+    dvmThrowUnsupportedOperationException(
+        "can't load this type of class file");
+
+    free(name);
+    RETURN_VOID();
+}
+
+/*
+ * static Class defineClass(ClassLoader cl, byte[] data, int offset,
+ *     int len)
+ *     throws ClassFormatError
+ *
+ * Convert an array of bytes to a Class object. Deprecated version of
+ * previous method, lacks name parameter.
+ */
+static void Dalvik_java_lang_VMClassLoader_defineClass2(const u4* args,
+    JValue* pResult)
+{
+    Object* loader = (Object*) args[0];
+    const u1* data = (const u1*) args[1];
+    int offset = args[2];
+    int len = args[3];
+
+    ALOGE("ERROR: defineClass(%p, %p, %d, %d)",
+        loader, data, offset, len);
+    dvmThrowUnsupportedOperationException(
+        "can't load this type of class file");
+
+    RETURN_VOID();
+}
+
+/*
+ * static Class findLoadedClass(ClassLoader cl, String name)
+ */
+static void Dalvik_java_lang_VMClassLoader_findLoadedClass(const u4* args,
+    JValue* pResult)
+{
+    Object* loader = (Object*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    ClassObject* clazz = NULL;
+    char* name = NULL;
+    char* descriptor = NULL;
+
+    if (nameObj == NULL) {
+        dvmThrowNullPointerException("name == null");
+        goto bail;
+    }
+
+    /*
+     * Get a UTF-8 copy of the string, and convert dots to slashes.
+     */
+    name = dvmCreateCstrFromString(nameObj);
+    if (name == NULL)
+        goto bail;
+
+    descriptor = dvmDotToDescriptor(name);
+    if (descriptor == NULL)
+        goto bail;
+
+    clazz = dvmLookupClass(descriptor, loader, false);
+    LOGVV("look: %s ldr=%p --> %p", descriptor, loader, clazz);
+
+bail:
+    free(name);
+    free(descriptor);
+    RETURN_PTR(clazz);
+}
+
+/*
+ * private static int getBootClassPathSize()
+ *
+ * Get the number of entries in the boot class path.
+ */
+static void Dalvik_java_lang_VMClassLoader_getBootClassPathSize(const u4* args,
+    JValue* pResult)
+{
+    int count = dvmGetBootPathSize();
+    RETURN_INT(count);
+}
+
+/*
+ * private static String getBootClassPathResource(String name, int index)
+ *
+ * Find a resource with a matching name in a boot class path entry.
+ *
+ * This mimics the previous VM interface, since we're sharing class libraries.
+ */
+static void Dalvik_java_lang_VMClassLoader_getBootClassPathResource(
+    const u4* args, JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    StringObject* result;
+    int idx = args[1];
+    char* name;
+
+    name = dvmCreateCstrFromString(nameObj);
+    if (name == NULL)
+        RETURN_PTR(NULL);
+
+    result = dvmGetBootPathResource(name, idx);
+    free(name);
+    dvmReleaseTrackedAlloc((Object*)result, NULL);
+    RETURN_PTR(result);
+}
+
+/*
+ * static final Class getPrimitiveClass(char prim_type)
+ */
+static void Dalvik_java_lang_VMClassLoader_getPrimitiveClass(const u4* args,
+    JValue* pResult)
+{
+    int primType = args[0];
+
+    pResult->l = (Object*)dvmFindPrimitiveClass(primType);
+}
+
+/*
+ * static Class loadClass(String name, boolean resolve)
+ *     throws ClassNotFoundException
+ *
+ * Load class using bootstrap class loader.
+ *
+ * Return the Class object associated with the class or interface with
+ * the specified name.
+ *
+ * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
+ */
+static void Dalvik_java_lang_VMClassLoader_loadClass(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    bool resolve = (args[1] != 0);
+    ClassObject* clazz;
+
+    clazz = dvmFindClassByName(nameObj, NULL, resolve);
+    assert(clazz == NULL || dvmIsClassLinked(clazz));
+    RETURN_PTR(clazz);
+}
+
+const DalvikNativeMethod dvm_java_lang_VMClassLoader[] = {
+    { "defineClass",        "(Ljava/lang/ClassLoader;Ljava/lang/String;[BII)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_defineClass },
+    { "defineClass",        "(Ljava/lang/ClassLoader;[BII)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_defineClass2 },
+    { "findLoadedClass",    "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_findLoadedClass },
+    { "getBootClassPathSize", "()I",
+        Dalvik_java_lang_VMClassLoader_getBootClassPathSize },
+    { "getBootClassPathResource", "(Ljava/lang/String;I)Ljava/lang/String;",
+        Dalvik_java_lang_VMClassLoader_getBootClassPathResource },
+    { "getPrimitiveClass",  "(C)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_getPrimitiveClass },
+    { "loadClass",          "(Ljava/lang/String;Z)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_loadClass },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_VMThread.cpp b/vm/native/java_lang_VMThread.cpp
new file mode 100644
index 0000000..635b726
--- /dev/null
+++ b/vm/native/java_lang_VMThread.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.VMThread
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static void create(Thread t, long stacksize)
+ *
+ * This is eventually called as a result of Thread.start().
+ *
+ * Throws an exception on failure.
+ */
+static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult)
+{
+    Object* threadObj = (Object*) args[0];
+    s8 stackSize = GET_ARG_LONG(args, 1);
+
+    /* copying collector will pin threadObj for us since it was an argument */
+    dvmCreateInterpThread(threadObj, (int) stackSize);
+    RETURN_VOID();
+}
+
+/*
+ * static Thread currentThread()
+ */
+static void Dalvik_java_lang_VMThread_currentThread(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_PTR(dvmThreadSelf()->threadObj);
+}
+
+/*
+ * void getStatus()
+ *
+ * Gets the Thread status. Result is in VM terms, has to be mapped to
+ * Thread.State by interpreted code.
+ */
+static void Dalvik_java_lang_VMThread_getStatus(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Thread* thread;
+    int result;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        result = thread->status;
+    else
+        result = THREAD_ZOMBIE;     // assume it used to exist and is now gone
+    dvmUnlockThreadList();
+
+    RETURN_INT(result);
+}
+
+/*
+ * boolean holdsLock(Object object)
+ *
+ * Returns whether the current thread has a monitor lock on the specific
+ * object.
+ */
+static void Dalvik_java_lang_VMThread_holdsLock(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Object* object = (Object*) args[1];
+    Thread* thread;
+
+    if (object == NULL) {
+        dvmThrowNullPointerException("object == null");
+        RETURN_VOID();
+    }
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    int result = dvmHoldsLock(thread, object);
+    dvmUnlockThreadList();
+
+    RETURN_BOOLEAN(result);
+}
+
+/*
+ * void interrupt()
+ *
+ * Interrupt a thread that is waiting (or is about to wait) on a monitor.
+ */
+static void Dalvik_java_lang_VMThread_interrupt(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        dvmThreadInterrupt(thread);
+    dvmUnlockThreadList();
+    RETURN_VOID();
+}
+
+/*
+ * static boolean interrupted()
+ *
+ * Determine if the current thread has been interrupted.  Clears the flag.
+ */
+static void Dalvik_java_lang_VMThread_interrupted(const u4* args,
+    JValue* pResult)
+{
+    Thread* self = dvmThreadSelf();
+    bool interrupted;
+
+    UNUSED_PARAMETER(args);
+
+    interrupted = self->interrupted;
+    self->interrupted = false;
+    RETURN_BOOLEAN(interrupted);
+}
+
+/*
+ * boolean isInterrupted()
+ *
+ * Determine if the specified thread has been interrupted.  Does not clear
+ * the flag.
+ */
+static void Dalvik_java_lang_VMThread_isInterrupted(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Thread* thread;
+    bool interrupted;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        interrupted = thread->interrupted;
+    else
+        interrupted = false;
+    dvmUnlockThreadList();
+
+    RETURN_BOOLEAN(interrupted);
+}
+
+/*
+ * void nameChanged(String newName)
+ *
+ * The name of the target thread has changed.  We may need to alert DDMS.
+ */
+static void Dalvik_java_lang_VMThread_nameChanged(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    StringObject* nameStr = (StringObject*) args[1];
+    Thread* thread;
+    int threadId = -1;
+
+    /* get the thread's ID */
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        threadId = thread->threadId;
+    dvmUnlockThreadList();
+
+    dvmDdmSendThreadNameChange(threadId, nameStr);
+    //char* str = dvmCreateCstrFromString(nameStr);
+    //ALOGI("UPDATE: threadid=%d now '%s'", threadId, str);
+    //free(str);
+
+    RETURN_VOID();
+}
+
+/*
+ * void setPriority(int newPriority)
+ *
+ * Alter the priority of the specified thread.  "newPriority" will range
+ * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
+ * threads at Thread.NORM_PRIORITY (5).
+ */
+static void Dalvik_java_lang_VMThread_setPriority(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    int newPriority = args[1];
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        dvmChangeThreadPriority(thread, newPriority);
+    //dvmDumpAllThreads(false);
+    dvmUnlockThreadList();
+
+    RETURN_VOID();
+}
+
+/*
+ * static void sleep(long msec, int nsec)
+ */
+static void Dalvik_java_lang_VMThread_sleep(const u4* args, JValue* pResult)
+{
+    dvmThreadSleep(GET_ARG_LONG(args,0), args[2]);
+    RETURN_VOID();
+}
+
+/*
+ * public void yield()
+ *
+ * Causes the thread to temporarily pause and allow other threads to execute.
+ *
+ * The exact behavior is poorly defined.  Some discussion here:
+ *   http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html
+ */
+static void Dalvik_java_lang_VMThread_yield(const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    sched_yield();
+
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_VMThread[] = {
+    { "create",         "(Ljava/lang/Thread;J)V",
+        Dalvik_java_lang_VMThread_create },
+    { "currentThread",  "()Ljava/lang/Thread;",
+        Dalvik_java_lang_VMThread_currentThread },
+    { "getStatus",      "()I",
+        Dalvik_java_lang_VMThread_getStatus },
+    { "holdsLock",      "(Ljava/lang/Object;)Z",
+        Dalvik_java_lang_VMThread_holdsLock },
+    { "interrupt",      "()V",
+        Dalvik_java_lang_VMThread_interrupt },
+    { "interrupted",    "()Z",
+        Dalvik_java_lang_VMThread_interrupted },
+    { "isInterrupted",  "()Z",
+        Dalvik_java_lang_VMThread_isInterrupted },
+    { "nameChanged",    "(Ljava/lang/String;)V",
+        Dalvik_java_lang_VMThread_nameChanged },
+    { "setPriority",    "(I)V",
+        Dalvik_java_lang_VMThread_setPriority },
+    { "sleep",          "(JI)V",
+        Dalvik_java_lang_VMThread_sleep },
+    { "yield",          "()V",
+        Dalvik_java_lang_VMThread_yield },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_AccessibleObject.cpp b/vm/native/java_lang_reflect_AccessibleObject.cpp
new file mode 100644
index 0000000..46a1357
--- /dev/null
+++ b/vm/native/java_lang_reflect_AccessibleObject.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.AccessibleObject
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object[] getClassSignatureAnnotation(Class clazz)
+ *
+ * Return the Signature annotation for the specified class.  Equivalent to
+ * Class.getSignatureAnnotation(), but available to java.lang.reflect.
+ */
+static void Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ArrayObject* arr = dvmGetClassSignatureAnnotation(clazz);
+
+    dvmReleaseTrackedAlloc((Object*) arr, NULL);
+    RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[] = {
+    { "getClassSignatureAnnotation", "(Ljava/lang/Class;)[Ljava/lang/Object;",
+      Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Array.cpp b/vm/native/java_lang_reflect_Array.cpp
new file mode 100644
index 0000000..dbe823d
--- /dev/null
+++ b/vm/native/java_lang_reflect_Array.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Array
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object createObjectArray(Class<?> componentType,
+ *     int length) throws NegativeArraySizeException;
+ *
+ * Create a one-dimensional array of Objects.
+ */
+static void Dalvik_java_lang_reflect_Array_createObjectArray(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* elementClass = (ClassObject*) args[0];
+    int length = args[1];
+
+    assert(elementClass != NULL);       // tested by caller
+    if (length < 0) {
+        dvmThrowNegativeArraySizeException(length);
+        RETURN_VOID();
+    }
+
+    ClassObject* arrayClass =
+        dvmFindArrayClassForElement(elementClass);
+    ArrayObject* newArray =
+        dvmAllocArrayByClass(arrayClass, length, ALLOC_DEFAULT);
+    if (newArray == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        RETURN_VOID();
+    }
+    dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+
+    RETURN_PTR(newArray);
+}
+
+/*
+ * private static Object createMultiArray(Class<?> componentType,
+ *     int[] dimensions) throws NegativeArraySizeException;
+ *
+ * Create a multi-dimensional array of Objects or primitive types.
+ *
+ * We have to generate the names for X[], X[][], X[][][], and so on.  The
+ * easiest way to deal with that is to create the full name once and then
+ * subtract pieces off.  Besides, we want to start with the outermost
+ * piece and work our way in.
+ */
+static void Dalvik_java_lang_reflect_Array_createMultiArray(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* elementClass = (ClassObject*) args[0];
+    ArrayObject* dimArray = (ArrayObject*) args[1];
+    ClassObject* arrayClass;
+    ArrayObject* newArray;
+    char* acDescriptor;
+    int numDim, i;
+    int* dimensions;
+
+    ALOGV("createMultiArray: '%s' [%d]",
+        elementClass->descriptor, dimArray->length);
+
+    assert(elementClass != NULL);       // verified by caller
+
+    /*
+     * Verify dimensions.
+     *
+     * The caller is responsible for verifying that "dimArray" is non-null
+     * and has a length > 0 and <= 255.
+     */
+    assert(dimArray != NULL);           // verified by caller
+    numDim = dimArray->length;
+    assert(numDim > 0 && numDim <= 255);
+
+    dimensions = (int*)(void*)dimArray->contents;
+    for (i = 0; i < numDim; i++) {
+        if (dimensions[i] < 0) {
+            dvmThrowNegativeArraySizeException(dimensions[i]);
+            RETURN_VOID();
+        }
+        LOGVV("DIM %d: %d", i, dimensions[i]);
+    }
+
+    /*
+     * Generate the full name of the array class.
+     */
+    acDescriptor =
+        (char*) malloc(strlen(elementClass->descriptor) + numDim + 1);
+    memset(acDescriptor, '[', numDim);
+
+    LOGVV("#### element name = '%s'", elementClass->descriptor);
+    if (dvmIsPrimitiveClass(elementClass)) {
+        assert(elementClass->primitiveType != PRIM_NOT);
+        acDescriptor[numDim] = dexGetPrimitiveTypeDescriptorChar(elementClass->primitiveType);
+        acDescriptor[numDim+1] = '\0';
+    } else {
+        strcpy(acDescriptor+numDim, elementClass->descriptor);
+    }
+    LOGVV("#### array name = '%s'", acDescriptor);
+
+    /*
+     * Find/generate the array class.
+     */
+    arrayClass = dvmFindArrayClass(acDescriptor, elementClass->classLoader);
+    if (arrayClass == NULL) {
+        ALOGW("Unable to find or generate array class '%s'", acDescriptor);
+        assert(dvmCheckException(dvmThreadSelf()));
+        free(acDescriptor);
+        RETURN_VOID();
+    }
+    free(acDescriptor);
+
+    /* create the array */
+    newArray = dvmAllocMultiArray(arrayClass, numDim-1, dimensions);
+    if (newArray == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        RETURN_VOID();
+    }
+
+    dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+    RETURN_PTR(newArray);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Array[] = {
+    { "createObjectArray",  "(Ljava/lang/Class;I)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Array_createObjectArray },
+    { "createMultiArray",   "(Ljava/lang/Class;[I)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Array_createMultiArray },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Constructor.cpp b/vm/native/java_lang_reflect_Constructor.cpp
new file mode 100644
index 0000000..166dbf5
--- /dev/null
+++ b/vm/native/java_lang_reflect_Constructor.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Constructor
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public int constructNative(Object[] args, Class declaringClass,
+ *     Class[] parameterTypes, int slot, boolean noAccessCheck)
+ *
+ * We get here through Constructor.newInstance().  The Constructor object
+ * would not be available if the constructor weren't public (per the
+ * definition of Class.getConstructor), so we can skip the method access
+ * check.  We can also safely assume the constructor isn't associated
+ * with an interface, array, or primitive class.
+ */
+static void Dalvik_java_lang_reflect_Constructor_constructNative(
+    const u4* args, JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ArrayObject* argList = (ArrayObject*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ArrayObject* params = (ArrayObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    Object* newObj;
+    Method* meth;
+
+    if (dvmIsAbstractClass(declaringClass)) {
+        dvmThrowInstantiationException(declaringClass, NULL);
+        RETURN_VOID();
+    }
+
+    /* initialize the class if it hasn't been already */
+    if (!dvmIsClassInitialized(declaringClass)) {
+        if (!dvmInitClass(declaringClass)) {
+            ALOGW("Class init failed in Constructor.constructNative (%s)",
+                declaringClass->descriptor);
+            assert(dvmCheckException(dvmThreadSelf()));
+            RETURN_VOID();
+        }
+    }
+
+    newObj = dvmAllocObject(declaringClass, ALLOC_DEFAULT);
+    if (newObj == NULL)
+        RETURN_PTR(NULL);
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    (void) dvmInvokeMethod(newObj, meth, argList, params, NULL, noAccessCheck);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+    RETURN_PTR(newObj);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Constructor[] = {
+    { "constructNative",    "([Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;IZ)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Constructor_constructNative },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Field.cpp b/vm/native/java_lang_reflect_Field.cpp
new file mode 100644
index 0000000..dac784b
--- /dev/null
+++ b/vm/native/java_lang_reflect_Field.cpp
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Field
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * Validate access to a field.  Returns a pointer to the Field struct.
+ *
+ * "declaringClass" is the class in which the field was declared.  For an
+ * instance field, "obj" is the object that holds the field data; for a
+ * static field its value is ignored.
+ *
+ * "If the underlying field is static, the class that declared the
+ * field is initialized if it has not already been initialized."
+ *
+ * On failure, throws an exception and returns NULL.
+ *
+ * The documentation lists exceptional conditions and the exceptions that
+ * should be thrown, but doesn't say which exception prevails when two or
+ * more exceptional conditions exist at the same time.  For example,
+ * attempting to set a protected field from an unrelated class causes an
+ * IllegalAccessException, while passing in a data type that doesn't match
+ * the field causes an IllegalArgumentException.  If code does both at the
+ * same time, we have to choose one or the other.
+ *
+ * The expected order is:
+ *  (1) Check for illegal access. Throw IllegalAccessException.
+ *  (2) Make sure the object actually has the field.  Throw
+ *      IllegalArgumentException.
+ *  (3) Make sure the field matches the expected type, e.g. if we issued
+ *      a "getInteger" call make sure the field is an integer or can be
+ *      converted to an int with a widening conversion.  Throw
+ *      IllegalArgumentException.
+ *  (4) Make sure "obj" is not null.  Throw NullPointerException.
+ *
+ * TODO: we're currently handling #3 after #4, because we don't check the
+ * widening conversion until we're actually extracting the value from the
+ * object (which won't work well if it's a null reference).
+ */
+static Field* validateFieldAccess(Object* obj, ClassObject* declaringClass,
+    int slot, bool isSetOperation, bool noAccessCheck)
+{
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    /* verify access */
+    if (!noAccessCheck) {
+        if (isSetOperation && dvmIsFinalField(field)) {
+            dvmThrowIllegalAccessException("field is marked 'final'");
+            return NULL;
+        }
+
+        ClassObject* callerClass =
+            dvmGetCaller2Class(dvmThreadSelf()->interpSave.curFrame);
+
+        /*
+         * We need to check two things:
+         *  (1) Would an instance of the calling class have access to the field?
+         *  (2) If the field is "protected", is the object an instance of the
+         *      calling class, or is the field's declaring class in the same
+         *      package as the calling class?
+         *
+         * #1 is basic access control.  #2 ensures that, just because
+         * you're a subclass of Foo, you can't mess with protected fields
+         * in arbitrary Foo objects from other packages.
+         */
+        if (!dvmCheckFieldAccess(callerClass, field)) {
+            dvmThrowIllegalAccessException("access to field not allowed");
+            return NULL;
+        }
+        if (dvmIsProtectedField(field)) {
+            bool isInstance, samePackage;
+
+            if (obj != NULL)
+                isInstance = dvmInstanceof(obj->clazz, callerClass);
+            else
+                isInstance = false;
+            samePackage = dvmInSamePackage(declaringClass, callerClass);
+
+            if (!isInstance && !samePackage) {
+                dvmThrowIllegalAccessException(
+                    "access to protected field not allowed");
+                return NULL;
+            }
+        }
+    }
+
+    if (dvmIsStaticField(field)) {
+        /* init class if necessary, then return ptr to storage in "field" */
+        if (!dvmIsClassInitialized(declaringClass)) {
+            if (!dvmInitClass(declaringClass)) {
+                assert(dvmCheckException(dvmThreadSelf()));
+                return NULL;
+            }
+        }
+
+    } else {
+        /*
+         * Verify object is of correct type (i.e. it actually has the
+         * expected field in it), then grab a pointer to obj storage.
+         * The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL.
+         */
+        if (!dvmVerifyObjectInClass(obj, declaringClass)) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+    }
+
+    return field;
+}
+
+/*
+ * Extracts the value of a static field.  Provides appropriate barriers
+ * for volatile fields.
+ *
+ * Sub-32-bit values are sign- or zero-extended to fill out 32 bits.
+ */
+static void getStaticFieldValue(const StaticField* sfield, JValue* value)
+{
+    if (!dvmIsVolatileField(sfield)) {
+        /* just copy the whole thing */
+        *value = sfield->value;
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (sfield->signature[0]) {
+        case 'Z':
+            value->i = dvmGetStaticFieldBooleanVolatile(sfield);
+            break;
+        case 'B':
+            value->i = dvmGetStaticFieldByteVolatile(sfield);
+            break;
+        case 'S':
+            value->i = dvmGetStaticFieldShortVolatile(sfield);
+            break;
+        case 'C':
+            value->i = dvmGetStaticFieldCharVolatile(sfield);
+            break;
+        case 'I':
+            value->i = dvmGetStaticFieldIntVolatile(sfield);
+            break;
+        case 'F':
+            value->f = dvmGetStaticFieldFloatVolatile(sfield);
+            break;
+        case 'J':
+            value->j = dvmGetStaticFieldLongVolatile(sfield);
+            break;
+        case 'D':
+            value->d = dvmGetStaticFieldDoubleVolatile(sfield);
+            break;
+        case 'L':
+        case '[':
+            value->l = dvmGetStaticFieldObjectVolatile(sfield);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", sfield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Extracts the value of an instance field.  Provides appropriate barriers
+ * for volatile fields.
+ *
+ * Sub-32-bit values are sign- or zero-extended to fill out 32 bits.
+ */
+static void getInstFieldValue(const InstField* ifield, Object* obj,
+    JValue* value)
+{
+    if (!dvmIsVolatileField(ifield)) {
+        /* use type-specific get; really just 32-bit vs. 64-bit */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            value->i = dvmGetFieldBoolean(obj, ifield->byteOffset);
+            break;
+        case 'B':
+            value->i = dvmGetFieldByte(obj, ifield->byteOffset);
+            break;
+        case 'S':
+            value->i = dvmGetFieldShort(obj, ifield->byteOffset);
+            break;
+        case 'C':
+            value->i = dvmGetFieldChar(obj, ifield->byteOffset);
+            break;
+        case 'I':
+            value->i = dvmGetFieldInt(obj, ifield->byteOffset);
+            break;
+        case 'F':
+            value->f = dvmGetFieldFloat(obj, ifield->byteOffset);
+            break;
+        case 'J':
+            value->j = dvmGetFieldLong(obj, ifield->byteOffset);
+            break;
+        case 'D':
+            value->d = dvmGetFieldDouble(obj, ifield->byteOffset);
+            break;
+        case 'L':
+        case '[':
+            value->l = dvmGetFieldObject(obj, ifield->byteOffset);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            value->i = dvmGetFieldBooleanVolatile(obj, ifield->byteOffset);
+            break;
+        case 'B':
+            value->i = dvmGetFieldByteVolatile(obj, ifield->byteOffset);
+            break;
+        case 'S':
+            value->i = dvmGetFieldShortVolatile(obj, ifield->byteOffset);
+            break;
+        case 'C':
+            value->i = dvmGetFieldCharVolatile(obj, ifield->byteOffset);
+            break;
+        case 'I':
+            value->i = dvmGetFieldIntVolatile(obj, ifield->byteOffset);
+            break;
+        case 'F':
+            value->f = dvmGetFieldFloatVolatile(obj, ifield->byteOffset);
+            break;
+        case 'J':
+            value->j = dvmGetFieldLongVolatile(obj, ifield->byteOffset);
+            break;
+        case 'D':
+            value->d = dvmGetFieldDoubleVolatile(obj, ifield->byteOffset);
+            break;
+        case 'L':
+        case '[':
+            value->l = dvmGetFieldObjectVolatile(obj, ifield->byteOffset);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Copies the value of the static or instance field into "*value".
+ */
+static void getFieldValue(const Field* field, Object* obj, JValue* value)
+{
+    if (dvmIsStaticField(field)) {
+        return getStaticFieldValue((const StaticField*) field, value);
+    } else {
+        return getInstFieldValue((const InstField*) field, obj, value);
+    }
+}
+
+/*
+ * Sets the value of a static field.  Provides appropriate barriers
+ * for volatile fields.
+ */
+static void setStaticFieldValue(StaticField* sfield, const JValue* value)
+{
+    if (!dvmIsVolatileField(sfield)) {
+        switch (sfield->signature[0]) {
+        case 'L':
+        case '[':
+            dvmSetStaticFieldObject(sfield, (Object*)value->l);
+            break;
+        default:
+            /* just copy the whole thing */
+            sfield->value = *value;
+            break;
+        }
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (sfield->signature[0]) {
+        case 'Z':
+            dvmSetStaticFieldBooleanVolatile(sfield, value->z);
+            break;
+        case 'B':
+            dvmSetStaticFieldByteVolatile(sfield, value->b);
+            break;
+        case 'S':
+            dvmSetStaticFieldShortVolatile(sfield, value->s);
+            break;
+        case 'C':
+            dvmSetStaticFieldCharVolatile(sfield, value->c);
+            break;
+        case 'I':
+            dvmSetStaticFieldIntVolatile(sfield, value->i);
+            break;
+        case 'F':
+            dvmSetStaticFieldFloatVolatile(sfield, value->f);
+            break;
+        case 'J':
+            dvmSetStaticFieldLongVolatile(sfield, value->j);
+            break;
+        case 'D':
+            dvmSetStaticFieldDoubleVolatile(sfield, value->d);
+            break;
+        case 'L':
+        case '[':
+            dvmSetStaticFieldObjectVolatile(sfield, (Object*)value->l);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", sfield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Sets the value of an instance field.  Provides appropriate barriers
+ * for volatile fields.
+ */
+static void setInstFieldValue(InstField* ifield, Object* obj,
+    const JValue* value)
+{
+    if (!dvmIsVolatileField(ifield)) {
+        /* use type-specific set; really just 32-bit vs. 64-bit */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            dvmSetFieldBoolean(obj, ifield->byteOffset, value->z);
+            break;
+        case 'B':
+            dvmSetFieldByte(obj, ifield->byteOffset, value->b);
+            break;
+        case 'S':
+            dvmSetFieldShort(obj, ifield->byteOffset, value->s);
+            break;
+        case 'C':
+            dvmSetFieldChar(obj, ifield->byteOffset, value->c);
+            break;
+        case 'I':
+            dvmSetFieldInt(obj, ifield->byteOffset, value->i);
+            break;
+        case 'F':
+            dvmSetFieldFloat(obj, ifield->byteOffset, value->f);
+            break;
+        case 'J':
+            dvmSetFieldLong(obj, ifield->byteOffset, value->j);
+            break;
+        case 'D':
+            dvmSetFieldDouble(obj, ifield->byteOffset, value->d);
+            break;
+        case 'L':
+        case '[':
+            dvmSetFieldObject(obj, ifield->byteOffset, (Object *)value->l);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+#if ANDROID_SMP != 0
+        /*
+         * Special handling for final fields on SMP systems.  We need a
+         * store/store barrier here (JMM requirement).
+         */
+        if (dvmIsFinalField(ifield)) {
+            ANDROID_MEMBAR_STORE();
+        }
+#endif
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            dvmSetFieldBooleanVolatile(obj, ifield->byteOffset, value->z);
+            break;
+        case 'B':
+            dvmSetFieldByteVolatile(obj, ifield->byteOffset, value->b);
+            break;
+        case 'S':
+            dvmSetFieldShortVolatile(obj, ifield->byteOffset, value->s);
+            break;
+        case 'C':
+            dvmSetFieldCharVolatile(obj, ifield->byteOffset, value->c);
+            break;
+        case 'I':
+            dvmSetFieldIntVolatile(obj, ifield->byteOffset, value->i);
+            break;
+        case 'F':
+            dvmSetFieldFloatVolatile(obj, ifield->byteOffset, value->f);
+            break;
+        case 'J':
+            dvmSetFieldLongVolatile(obj, ifield->byteOffset, value->j);
+            break;
+        case 'D':
+            dvmSetFieldDoubleVolatile(obj, ifield->byteOffset, value->d);
+            break;
+        case 'L':
+        case '[':
+            dvmSetFieldObjectVolatile(obj, ifield->byteOffset, (Object*)value->l);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Copy "*value" into the static or instance field.
+ */
+static void setFieldValue(Field* field, Object* obj, const JValue* value)
+{
+    if (dvmIsStaticField(field)) {
+        return setStaticFieldValue((StaticField*) field, value);
+    } else {
+        return setInstFieldValue((InstField*) field, obj, value);
+    }
+}
+
+
+
+/*
+ * public int getFieldModifiers(Class declaringClass, int slot)
+ */
+static void Dalvik_java_lang_reflect_Field_getFieldModifiers(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    RETURN_INT(field->accessFlags & JAVA_FLAGS_MASK);
+}
+
+/*
+ * private Object getField(Object o, Class declaringClass, Class type,
+ *     int slot, boolean noAccessCheck)
+ *
+ * Primitive types need to be boxed.
+ */
+static void Dalvik_java_lang_reflect_Field_getField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    Field* field;
+    JValue value;
+    DataObject* result;
+
+    //dvmDumpClass(obj->clazz, kDumpClassFullDetail);
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, false,noAccessCheck);
+    if (field == NULL)
+        RETURN_VOID();
+
+    getFieldValue(field, obj, &value);
+
+    /* if it's primitive, box it up */
+    result = dvmBoxPrimitive(value, fieldType);
+    dvmReleaseTrackedAlloc((Object*) result, NULL);
+    RETURN_PTR(result);
+}
+
+/*
+ * private void setField(Object o, Class declaringClass, Class type,
+ *     int slot, boolean noAccessCheck, Object value)
+ *
+ * When assigning into a primitive field we will automatically extract
+ * the value from box types.
+ */
+static void Dalvik_java_lang_reflect_Field_setField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    Object* valueObj = (Object*) args[6];
+    Field* field;
+    JValue value;
+
+    /* unbox primitive, or verify object type */
+    if (!dvmUnboxPrimitive(valueObj, fieldType, &value)) {
+        dvmThrowIllegalArgumentException("invalid value for field");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, true, noAccessCheck);
+
+    if (field != NULL) {
+        setFieldValue(field, obj, &value);
+    }
+    RETURN_VOID();
+}
+
+/*
+ * Primitive field getters, e.g.:
+ * private double getIField(Object o, Class declaringClass,
+ *     Class type, int slot, boolean noAccessCheck, char descriptor)
+ */
+static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    jchar descriptor = args[6];
+    PrimitiveType targetType = dexGetPrimitiveTypeFromDescriptorChar(descriptor);
+    const Field* field;
+    JValue value;
+
+    if (!dvmIsPrimitiveClass(fieldType)) {
+        dvmThrowIllegalArgumentException("not a primitive field");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, false,noAccessCheck);
+    if (field == NULL)
+        RETURN_VOID();
+
+    getFieldValue(field, obj, &value);
+
+    /* retrieve value, performing a widening conversion if necessary */
+    if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType,
+        &(value.i), &(pResult->i)) < 0)
+    {
+        dvmThrowIllegalArgumentException("invalid primitive conversion");
+        RETURN_VOID();
+    }
+}
+
+/*
+ * Primitive field setters, e.g.:
+ * private void setIField(Object o, Class declaringClass,
+ *     Class type, int slot, boolean noAccessCheck, char descriptor, int value)
+ */
+static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    jchar descriptor = args[6];
+    const s4* valuePtr = (s4*) &args[7];    /* 64-bit vars spill into args[8] */
+    PrimitiveType srcType = dexGetPrimitiveTypeFromDescriptorChar(descriptor);
+    Field* field;
+    JValue value;
+
+    if (!dvmIsPrimitiveClass(fieldType)) {
+        dvmThrowIllegalArgumentException("not a primitive field");
+        RETURN_VOID();
+    }
+
+    /* convert the 32/64-bit arg to a JValue matching the field type */
+    if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType,
+        valuePtr, &(value.i)) < 0)
+    {
+        dvmThrowIllegalArgumentException("invalid primitive conversion");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, true, noAccessCheck);
+
+    if (field != NULL) {
+        setFieldValue(field, obj, &value);
+    }
+    RETURN_VOID();
+}
+
+/*
+ * private static Annotation[] getDeclaredAnnotations(
+ *         Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this field.
+ */
+static void Dalvik_java_lang_reflect_Field_getDeclaredAnnotations(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    ArrayObject* annos = dvmGetFieldAnnotations(field);
+    dvmReleaseTrackedAlloc((Object*) annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * static Annotation getAnnotation(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Field_getAnnotation(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Field* field = dvmSlotToField(clazz, slot);
+    RETURN_PTR(dvmGetFieldAnnotation(clazz, field, annotationClazz));
+}
+
+/*
+ * static boolean isAnnotationPresent(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Field_isAnnotationPresent(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Field* field = dvmSlotToField(clazz, slot);
+    RETURN_BOOLEAN(dvmIsFieldAnnotationPresent(clazz, field, annotationClazz));
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    ArrayObject* arr = dvmGetFieldSignatureAnnotation(field);
+    dvmReleaseTrackedAlloc((Object*) arr, NULL);
+    RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Field[] = {
+    { "getFieldModifiers",  "(Ljava/lang/Class;I)I",
+        Dalvik_java_lang_reflect_Field_getFieldModifiers },
+    { "getField",           "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Field_getField },
+    { "getBField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)B",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)C",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)D",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)F",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)I",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)J",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)S",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)Z",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "setField",           "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V",
+        Dalvik_java_lang_reflect_Field_setField },
+    { "setBField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCB)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCC)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCD)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCF)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCI)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCJ)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCS)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCZ)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Field_getDeclaredAnnotations },
+    { "getAnnotation", "(Ljava/lang/Class;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Field_getAnnotation },
+    { "isAnnotationPresent", "(Ljava/lang/Class;ILjava/lang/Class;)Z",
+        Dalvik_java_lang_reflect_Field_isAnnotationPresent },
+    { "getSignatureAnnotation",  "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Field_getSignatureAnnotation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Method.cpp b/vm/native/java_lang_reflect_Method.cpp
new file mode 100644
index 0000000..33d98c5
--- /dev/null
+++ b/vm/native/java_lang_reflect_Method.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Method
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static int getMethodModifiers(Class decl_class, int slot)
+ *
+ * (Not sure why the access flags weren't stored in the class along with
+ * everything else.  Not sure why this isn't static.)
+ */
+static void Dalvik_java_lang_reflect_Method_getMethodModifiers(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    RETURN_INT(dvmFixMethodFlags(meth->accessFlags));
+}
+
+/*
+ * private Object invokeNative(Object obj, Object[] args, Class declaringClass,
+ *   Class[] parameterTypes, Class returnType, int slot, boolean noAccessCheck)
+ *
+ * Invoke a static or virtual method via reflection.
+ */
+static void Dalvik_java_lang_reflect_Method_invokeNative(const u4* args,
+    JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    Object* methObj = (Object*) args[1];        // null for static methods
+    ArrayObject* argList = (ArrayObject*) args[2];
+    ClassObject* declaringClass = (ClassObject*) args[3];
+    ArrayObject* params = (ArrayObject*) args[4];
+    ClassObject* returnType = (ClassObject*) args[5];
+    int slot = args[6];
+    bool noAccessCheck = (args[7] != 0);
+    const Method* meth;
+    Object* result;
+
+    /*
+     * "If the underlying method is static, the class that declared the
+     * method is initialized if it has not already been initialized."
+     */
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    if (dvmIsStaticMethod(meth)) {
+        if (!dvmIsClassInitialized(declaringClass)) {
+            if (!dvmInitClass(declaringClass))
+                goto init_failed;
+        }
+    } else {
+        /* looks like interfaces need this too? */
+        if (dvmIsInterfaceClass(declaringClass) &&
+            !dvmIsClassInitialized(declaringClass))
+        {
+            if (!dvmInitClass(declaringClass))
+                goto init_failed;
+        }
+
+        /* make sure the object is an instance of the expected class */
+        if (!dvmVerifyObjectInClass(methObj, declaringClass)) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            RETURN_VOID();
+        }
+
+        /* do the virtual table lookup for the method */
+        meth = dvmGetVirtualizedMethod(methObj->clazz, meth);
+        if (meth == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            RETURN_VOID();
+        }
+    }
+
+    /*
+     * If the method has a return value, "result" will be an object or
+     * a boxed primitive.
+     */
+    result = dvmInvokeMethod(methObj, meth, argList, params, returnType,
+                noAccessCheck);
+
+    RETURN_PTR(result);
+
+init_failed:
+    /*
+     * If initialization failed, an exception will be raised.
+     */
+    ALOGD("Method.invoke() on bad class %s failed",
+        declaringClass->descriptor);
+    assert(dvmCheckException(dvmThreadSelf()));
+    RETURN_VOID();
+}
+
+/*
+ * static Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this method.
+ */
+static void Dalvik_java_lang_reflect_Method_getDeclaredAnnotations(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* annos = dvmGetMethodAnnotations(meth);
+    dvmReleaseTrackedAlloc((Object*)annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * static Annotation getAnnotation(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Method_getAnnotation(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Method* meth = dvmSlotToMethod(clazz, slot);
+    RETURN_PTR(dvmGetMethodAnnotation(clazz, meth, annotationClazz));
+}
+
+/*
+ * static boolean isAnnotationPresent(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Method_isAnnotationPresent(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Method* meth = dvmSlotToMethod(clazz, slot);
+    RETURN_BOOLEAN(dvmIsMethodAnnotationPresent(clazz, meth, annotationClazz));
+}
+
+/*
+ * static Annotation[][] getParameterAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this method's parameters.
+ */
+static void Dalvik_java_lang_reflect_Method_getParameterAnnotations(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* annos = dvmGetParameterAnnotations(meth);
+    dvmReleaseTrackedAlloc((Object*)annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * private Object getDefaultValue(Class declaringClass, int slot)
+ *
+ * Return the default value for the annotation member represented by
+ * this Method instance.  Returns NULL if none is defined.
+ */
+static void Dalvik_java_lang_reflect_Method_getDefaultValue(const u4* args,
+    JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Method* meth;
+
+    /* make sure this is an annotation class member */
+    if (!dvmIsAnnotationClass(declaringClass))
+        RETURN_PTR(NULL);
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    Object* def = dvmGetAnnotationDefaultValue(meth);
+    dvmReleaseTrackedAlloc(def, NULL);
+    RETURN_PTR(def);
+}
+
+/*
+ * static Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Method_getSignatureAnnotation(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* arr = dvmGetMethodSignatureAnnotation(meth);
+    dvmReleaseTrackedAlloc((Object*) arr, NULL);
+    RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Method[] = {
+    { "getMethodModifiers", "(Ljava/lang/Class;I)I",
+        Dalvik_java_lang_reflect_Method_getMethodModifiers },
+    { "invokeNative",       "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Method_invokeNative },
+    { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Method_getDeclaredAnnotations },
+    { "getAnnotation", "(Ljava/lang/Class;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Method_getAnnotation },
+    { "isAnnotationPresent", "(Ljava/lang/Class;ILjava/lang/Class;)Z",
+        Dalvik_java_lang_reflect_Method_isAnnotationPresent },
+    { "getParameterAnnotations", "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Method_getParameterAnnotations },
+    { "getDefaultValue",    "(Ljava/lang/Class;I)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Method_getDefaultValue },
+    { "getSignatureAnnotation",  "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Method_getSignatureAnnotation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Proxy.cpp b/vm/native/java_lang_reflect_Proxy.cpp
new file mode 100644
index 0000000..da1232c
--- /dev/null
+++ b/vm/native/java_lang_reflect_Proxy.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Proxy
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static Class generateProxy(String name, Class[] interfaces,
+ *      ClassLoader loader)
+ *
+ * Generate a proxy class with the specified characteristics.  Throws an
+ * exception on error.
+ */
+static void Dalvik_java_lang_reflect_Proxy_generateProxy(const u4* args,
+    JValue* pResult)
+{
+    StringObject* str = (StringObject*) args[0];
+    ArrayObject* interfaces = (ArrayObject*) args[1];
+    Object* loader = (Object*) args[2];
+    ClassObject* result;
+
+    result = dvmGenerateProxyClass(str, interfaces, loader);
+    RETURN_PTR(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Proxy[] = {
+    { "generateProxy", "(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/lang/Class;",
+        Dalvik_java_lang_reflect_Proxy_generateProxy },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_util_concurrent_atomic_AtomicLong.cpp b/vm/native/java_util_concurrent_atomic_AtomicLong.cpp
new file mode 100644
index 0000000..eb1d0de
--- /dev/null
+++ b/vm/native/java_util_concurrent_atomic_AtomicLong.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.util.concurrent.atomic.AtomicLong
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static native boolean VMSupportsCS8();
+ */
+static void Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    RETURN_BOOLEAN(1);
+}
+
+const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[] = {
+    { "VMSupportsCS8", "()Z",
+        Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8 },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_NativeTestTarget.cpp b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
new file mode 100644
index 0000000..ccc9467
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * org.apache.harmony.dalvik.NativeTestTarget
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public static void emptyInternalStaticMethod()
+ *
+ * For benchmarks, a do-nothing internal method with no arguments.
+ */
+static void Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_NativeTestTarget[] =
+{
+    { "emptyInternalStaticMethod", "()V",
+        Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp
new file mode 100644
index 0000000..570d469
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * org.apache.harmony.dalvik.ddmc.DdmServer
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static void nativeSendChunk(int type, byte[] data,
+ *      int offset, int length)
+ *
+ * Send a DDM chunk to the server.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk(
+    const u4* args, JValue* pResult)
+{
+    int type = args[0];
+    ArrayObject* data = (ArrayObject*) args[1];
+    int offset = args[2];
+    int length = args[3];
+
+    assert(offset+length <= (int)data->length);
+
+    dvmDbgDdmSendChunk(type, length, (const u1*)data->contents + offset);
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmServer[] = {
+    { "nativeSendChunk",    "(I[BII)V",
+        Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp
new file mode 100644
index 0000000..558b2ff
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * org.apache.harmony.dalvik.ddmc.DdmVmInternal
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public static void threadNotify(boolean enable)
+ *
+ * Enable DDM thread notifications.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify(
+    const u4* args, JValue* pResult)
+{
+    bool enable = (args[0] != 0);
+
+    //ALOGI("ddmThreadNotification: %d", enable);
+    dvmDdmSetThreadNotification(enable);
+    RETURN_VOID();
+}
+
+/*
+ * public static byte[] getThreadStats()
+ *
+ * Get a buffer full of thread info.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    ArrayObject* result = dvmDdmGenerateThreadStats();
+    dvmReleaseTrackedAlloc((Object*) result, NULL);
+    RETURN_PTR(result);
+}
+
+/*
+ * public static int heapInfoNotify(int what)
+ *
+ * Enable DDM heap notifications.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify(
+    const u4* args, JValue* pResult)
+{
+    int when = args[0];
+    bool ret;
+
+    ret = dvmDdmHandleHpifChunk(when);
+    RETURN_BOOLEAN(ret);
+}
+
+/*
+ * public static boolean heapSegmentNotify(int when, int what, bool native)
+ *
+ * Enable DDM heap notifications.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify(
+    const u4* args, JValue* pResult)
+{
+    int  when   = args[0];        // 0=never (off), 1=during GC
+    int  what   = args[1];        // 0=merged objects, 1=distinct objects
+    bool native = (args[2] != 0); // false=virtual heap, true=native heap
+    bool ret;
+
+    ret = dvmDdmHandleHpsgNhsgChunk(when, what, native);
+    RETURN_BOOLEAN(ret);
+}
+
+/*
+ * public static StackTraceElement[] getStackTraceById(int threadId)
+ *
+ * Get a stack trace as an array of StackTraceElement objects.  Returns
+ * NULL on failure, e.g. if the threadId couldn't be found.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById(
+    const u4* args, JValue* pResult)
+{
+    u4 threadId = args[0];
+    ArrayObject* trace;
+
+    trace = dvmDdmGetStackTraceById(threadId);
+    RETURN_PTR(trace);
+}
+
+/*
+ * public static void enableRecentAllocations(boolean enable)
+ *
+ * Enable or disable recent allocation tracking.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations(
+    const u4* args, JValue* pResult)
+{
+    bool enable = (args[0] != 0);
+
+    if (enable)
+        (void) dvmEnableAllocTracker();
+    else
+        (void) dvmDisableAllocTracker();
+    RETURN_VOID();
+}
+
+/*
+ * public static boolean getRecentAllocationStatus()
+ *
+ * Returns "true" if allocation tracking is enabled.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    RETURN_BOOLEAN(gDvm.allocRecords != NULL);
+}
+
+/*
+ * public static byte[] getRecentAllocations()
+ *
+ * Fill a buffer with data on recent heap allocations.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations(
+    const u4* args, JValue* pResult)
+{
+    ArrayObject* data;
+
+    data = dvmDdmGetRecentAllocations();
+    dvmReleaseTrackedAlloc((Object*) data, NULL);
+    RETURN_PTR(data);
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal[] = {
+    { "threadNotify",       "(Z)V",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify },
+    { "getThreadStats",     "()[B",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats },
+    { "heapInfoNotify",     "(I)Z",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify },
+    { "heapSegmentNotify",  "(IIZ)Z",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify },
+    { "getStackTraceById",  "(I)[Ljava/lang/StackTraceElement;",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById },
+    { "enableRecentAllocations", "(Z)V",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations },
+    { "getRecentAllocationStatus", "()Z",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus },
+    { "getRecentAllocations", "()[B",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/sun_misc_Unsafe.cpp b/vm/native/sun_misc_Unsafe.cpp
new file mode 100644
index 0000000..db8493b
--- /dev/null
+++ b/vm/native/sun_misc_Unsafe.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * sun.misc.Unsafe
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static native long objectFieldOffset0(Field field);
+ */
+static void Dalvik_sun_misc_Unsafe_objectFieldOffset0(const u4* args,
+    JValue* pResult)
+{
+    Object* fieldObject = (Object*) args[0];
+    InstField* field = (InstField*) dvmGetFieldFromReflectObj(fieldObject);
+    s8 result = ((s8) field->byteOffset);
+
+    RETURN_LONG(result);
+}
+
+/*
+ * private static native int arrayBaseOffset0(Class clazz);
+ */
+static void Dalvik_sun_misc_Unsafe_arrayBaseOffset0(const u4* args,
+    JValue* pResult)
+{
+    // The base offset is not type-dependent in this vm.
+    UNUSED_PARAMETER(args);
+    RETURN_INT(OFFSETOF_MEMBER(ArrayObject, contents));
+}
+
+/*
+ * private static native int arrayIndexScale0(Class clazz);
+ */
+static void Dalvik_sun_misc_Unsafe_arrayIndexScale0(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    RETURN_INT(dvmArrayClassElementWidth(clazz));
+}
+
+/*
+ * public native boolean compareAndSwapInt(Object obj, long offset,
+ *         int expectedValue, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapInt(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 expectedValue = args[4];
+    s4 newValue = args[5];
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    // Note: android_atomic_release_cas() returns 0 on success, not failure.
+    int result = android_atomic_release_cas(expectedValue, newValue, address);
+
+    RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native boolean compareAndSwapLong(Object obj, long offset,
+ *         long expectedValue, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapLong(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 expectedValue = GET_ARG_LONG(args, 4);
+    s8 newValue = GET_ARG_LONG(args, 6);
+    volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+    // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
+    int result =
+        dvmQuasiAtomicCas64(expectedValue, newValue, address);
+
+    RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native boolean compareAndSwapObject(Object obj, long offset,
+ *         Object expectedValue, Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapObject(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* expectedValue = (Object*) args[4];
+    Object* newValue = (Object*) args[5];
+    int32_t* address = (int32_t*) (((u1*) obj) + offset);
+
+    // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
+    int result = android_atomic_release_cas((int32_t) expectedValue,
+            (int32_t) newValue, address);
+    dvmWriteBarrierField(obj, address);
+    RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native int getIntVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getIntVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    int32_t value = android_atomic_acquire_load(address);
+    RETURN_INT(value);
+}
+
+/*
+ * public native void putIntVolatile(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putIntVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 value = (s4) args[4];
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    android_atomic_release_store(value, address);
+    RETURN_VOID();
+}
+
+/*
+ * public native long getLongVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getLongVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+    assert((offset & 7) == 0);
+    RETURN_LONG(dvmQuasiAtomicRead64(address));
+}
+
+/*
+ * public native void putLongVolatile(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putLongVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 value = GET_ARG_LONG(args, 4);
+    volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+    assert((offset & 7) == 0);
+    dvmQuasiAtomicSwap64(value, address);
+    RETURN_VOID();
+}
+
+/*
+ * public native Object getObjectVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getObjectVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    RETURN_PTR((Object*) android_atomic_acquire_load(address));
+}
+
+/*
+ * public native void putObjectVolatile(Object obj, long offset,
+ *         Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putObjectVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* value = (Object*) args[4];
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    android_atomic_release_store((int32_t)value, address);
+    dvmWriteBarrierField(obj, (void *)address);
+    RETURN_VOID();
+}
+
+/*
+ * public native int getInt(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getInt(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4* address = (s4*) (((u1*) obj) + offset);
+
+    RETURN_INT(*address);
+}
+
+/*
+ * public native void putInt(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putInt(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 value = (s4) args[4];
+    s4* address = (s4*) (((u1*) obj) + offset);
+
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native void putOrderedInt(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putOrderedInt(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 value = (s4) args[4];
+    s4* address = (s4*) (((u1*) obj) + offset);
+
+    ANDROID_MEMBAR_STORE();
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native long getLong(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getLong(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8* address = (s8*) (((u1*) obj) + offset);
+
+    RETURN_LONG(*address);
+}
+
+/*
+ * public native void putLong(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putLong(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 value = GET_ARG_LONG(args, 4);
+    s8* address = (s8*) (((u1*) obj) + offset);
+
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native void putOrderedLong(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putOrderedLong(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 value = GET_ARG_LONG(args, 4);
+    s8* address = (s8*) (((u1*) obj) + offset);
+
+    ANDROID_MEMBAR_STORE();
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native Object getObject(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getObject(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object** address = (Object**) (((u1*) obj) + offset);
+
+    RETURN_PTR(*address);
+}
+
+/*
+ * public native void putObject(Object obj, long offset, Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putObject(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* value = (Object*) args[4];
+    Object** address = (Object**) (((u1*) obj) + offset);
+
+    *address = value;
+    dvmWriteBarrierField(obj, address);
+    RETURN_VOID();
+}
+
+/*
+ * public native void putOrderedObject(Object obj, long offset,
+ *      Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putOrderedObject(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* value = (Object*) args[4];
+    Object** address = (Object**) (((u1*) obj) + offset);
+
+    ANDROID_MEMBAR_STORE();
+    *address = value;
+    dvmWriteBarrierField(obj, address);
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_sun_misc_Unsafe[] = {
+    { "objectFieldOffset0", "(Ljava/lang/reflect/Field;)J",
+      Dalvik_sun_misc_Unsafe_objectFieldOffset0 },
+    { "arrayBaseOffset0", "(Ljava/lang/Class;)I",
+      Dalvik_sun_misc_Unsafe_arrayBaseOffset0 },
+    { "arrayIndexScale0", "(Ljava/lang/Class;)I",
+      Dalvik_sun_misc_Unsafe_arrayIndexScale0 },
+    { "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
+      Dalvik_sun_misc_Unsafe_compareAndSwapInt },
+    { "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z",
+      Dalvik_sun_misc_Unsafe_compareAndSwapLong },
+    { "compareAndSwapObject",
+      "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
+      Dalvik_sun_misc_Unsafe_compareAndSwapObject },
+    { "getIntVolatile", "(Ljava/lang/Object;J)I",
+      Dalvik_sun_misc_Unsafe_getIntVolatile },
+    { "putIntVolatile", "(Ljava/lang/Object;JI)V",
+      Dalvik_sun_misc_Unsafe_putIntVolatile },
+    { "getLongVolatile", "(Ljava/lang/Object;J)J",
+      Dalvik_sun_misc_Unsafe_getLongVolatile },
+    { "putLongVolatile", "(Ljava/lang/Object;JJ)V",
+      Dalvik_sun_misc_Unsafe_putLongVolatile },
+    { "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;",
+      Dalvik_sun_misc_Unsafe_getObjectVolatile },
+    { "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+      Dalvik_sun_misc_Unsafe_putObjectVolatile },
+    { "getInt", "(Ljava/lang/Object;J)I",
+      Dalvik_sun_misc_Unsafe_getInt },
+    { "putInt", "(Ljava/lang/Object;JI)V",
+      Dalvik_sun_misc_Unsafe_putInt },
+    { "putOrderedInt", "(Ljava/lang/Object;JI)V",
+      Dalvik_sun_misc_Unsafe_putOrderedInt },
+    { "getLong", "(Ljava/lang/Object;J)J",
+      Dalvik_sun_misc_Unsafe_getLong },
+    { "putLong", "(Ljava/lang/Object;JJ)V",
+      Dalvik_sun_misc_Unsafe_putLong },
+    { "putOrderedLong", "(Ljava/lang/Object;JJ)V",
+      Dalvik_sun_misc_Unsafe_putOrderedLong },
+    { "getObject", "(Ljava/lang/Object;J)Ljava/lang/Object;",
+      Dalvik_sun_misc_Unsafe_getObject },
+    { "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+      Dalvik_sun_misc_Unsafe_putObject },
+    { "putOrderedObject", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+      Dalvik_sun_misc_Unsafe_putOrderedObject },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/oo/AccessCheck.cpp b/vm/oo/AccessCheck.cpp
new file mode 100644
index 0000000..cef4dab
--- /dev/null
+++ b/vm/oo/AccessCheck.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Check access to fields and methods.
+ */
+#include "Dalvik.h"
+
+/*
+ * Return the #of initial characters that match.
+ */
+static int strcmpCount(const char* str1, const char* str2)
+{
+    int count = 0;
+
+    while (true) {
+        char ch = str1[count];
+        if (ch == '\0' || ch != str2[count])
+            return count;
+        count++;
+    }
+}
+
+/*
+ * Returns "true" if the two classes are in the same runtime package.
+ */
+bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)
+{
+    /* quick test for intra-class access */
+    if (class1 == class2)
+        return true;
+
+    /* class loaders must match */
+    if (class1->classLoader != class2->classLoader)
+        return false;
+
+    /*
+     * Switch array classes to their element types.  Arrays receive the
+     * class loader of the underlying element type.  The point of doing
+     * this is to get the un-decorated class name, without all the
+     * "[[L...;" stuff.
+     */
+    if (dvmIsArrayClass(class1))
+        class1 = class1->elementClass;
+    if (dvmIsArrayClass(class2))
+        class2 = class2->elementClass;
+
+    /* check again */
+    if (class1 == class2)
+        return true;
+
+    /*
+     * We have two classes with different names.  Compare them and see
+     * if they match up through the final '/'.
+     *
+     *  Ljava/lang/Object; + Ljava/lang/Class;          --> true
+     *  LFoo;              + LBar;                      --> true
+     *  Ljava/lang/Object; + Ljava/io/File;             --> false
+     *  Ljava/lang/Object; + Ljava/lang/reflect/Method; --> false
+     */
+    int commonLen;
+
+    commonLen = strcmpCount(class1->descriptor, class2->descriptor);
+    if (strchr(class1->descriptor + commonLen, '/') != NULL ||
+        strchr(class2->descriptor + commonLen, '/') != NULL)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Validate method/field access.
+ */
+static bool checkAccess(const ClassObject* accessFrom,
+    const ClassObject* accessTo, u4 accessFlags)
+{
+    /* quick accept for public access */
+    if (accessFlags & ACC_PUBLIC)
+        return true;
+
+    /* quick accept for access from same class */
+    if (accessFrom == accessTo)
+        return true;
+
+    /* quick reject for private access from another class */
+    if (accessFlags & ACC_PRIVATE)
+        return false;
+
+    /*
+     * Semi-quick test for protected access from a sub-class, which may or
+     * may not be in the same package.
+     */
+    if (accessFlags & ACC_PROTECTED)
+        if (dvmIsSubClass(accessFrom, accessTo))
+            return true;
+
+    /*
+     * Allow protected and private access from other classes in the same
+     * package.
+     */
+    return dvmInSamePackage(accessFrom, accessTo);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "clazz".
+ *
+ * It's allowed if "clazz" is public or is in the same package.  (Only
+ * inner classes can be marked "private" or "protected", so we don't need
+ * to check for it here.)
+ */
+bool dvmCheckClassAccess(const ClassObject* accessFrom,
+    const ClassObject* clazz)
+{
+    if (dvmIsPublicClass(clazz))
+        return true;
+    return dvmInSamePackage(accessFrom, clazz);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "method".
+ */
+bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method)
+{
+    return checkAccess(accessFrom, method->clazz, method->accessFlags);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "field".
+ */
+bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field)
+{
+    //ALOGI("CHECK ACCESS from '%s' to field '%s' (in %s) flags=%#x",
+    //    accessFrom->descriptor, field->name,
+    //    field->clazz->descriptor, field->accessFlags);
+    return checkAccess(accessFrom, field->clazz, field->accessFlags);
+}
diff --git a/vm/oo/AccessCheck.h b/vm/oo/AccessCheck.h
new file mode 100644
index 0000000..61d1650
--- /dev/null
+++ b/vm/oo/AccessCheck.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Check access to fields and methods.
+ */
+#ifndef DALVIK_OO_ACCESSCHECK_H_
+#define DALVIK_OO_ACCESSCHECK_H_
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "clazz".
+ */
+bool dvmCheckClassAccess(const ClassObject* accessFrom,
+    const ClassObject* clazz);
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "method".
+ */
+bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method);
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "field".
+ */
+bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field);
+
+/*
+ * Returns "true" if the two classes are in the same runtime package.
+ */
+bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2);
+
+#endif  // DALVIK_OO_ACCESSCHECK_H_
diff --git a/vm/oo/Array.cpp b/vm/oo/Array.cpp
new file mode 100644
index 0000000..00ec6d9
--- /dev/null
+++ b/vm/oo/Array.cpp
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Array objects.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <limits.h>
+
+/* width of an object reference, for arrays of objects */
+static size_t kObjectArrayRefWidth = sizeof(Object*);
+
+static ClassObject* createArrayClass(const char* descriptor, Object* loader);
+
+/*
+ * Allocate space for a new array object.  This is the lowest-level array
+ * allocation function.
+ *
+ * Pass in the array class and the width of each element.
+ *
+ * On failure, returns NULL with an exception raised.
+ */
+static ArrayObject* allocArray(ClassObject* arrayClass, size_t length,
+    size_t elemWidth, int allocFlags)
+{
+    assert(arrayClass != NULL);
+    assert(arrayClass->descriptor != NULL);
+    assert(arrayClass->descriptor[0] == '[');
+    assert(length <= 0x7fffffff);
+    assert(elemWidth > 0);
+    assert(elemWidth <= 8);
+    assert((elemWidth & (elemWidth - 1)) == 0);
+    size_t elementShift = sizeof(size_t) * CHAR_BIT - 1 - CLZ(elemWidth);
+    size_t elementSize = length << elementShift;
+    size_t headerSize = OFFSETOF_MEMBER(ArrayObject, contents);
+    size_t totalSize = elementSize + headerSize;
+    if (elementSize >> elementShift != length || totalSize < elementSize) {
+        std::string descriptor(dvmHumanReadableDescriptor(arrayClass->descriptor));
+        dvmThrowExceptionFmt(gDvm.exOutOfMemoryError,
+                "%s of length %zd exceeds the VM limit", descriptor.c_str(), length);
+        return NULL;
+    }
+    ArrayObject* newArray = (ArrayObject*)dvmMalloc(totalSize, allocFlags);
+    if (newArray != NULL) {
+        DVM_OBJECT_INIT(newArray, arrayClass);
+        newArray->length = length;
+        dvmTrackAllocation(arrayClass, totalSize);
+    }
+    return newArray;
+}
+
+/*
+ * Create a new array, given an array class.  The class may represent an
+ * array of references or primitives.
+ */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
+    size_t length, int allocFlags)
+{
+    const char* descriptor = arrayClass->descriptor;
+
+    assert(descriptor[0] == '[');       /* must be array class */
+    if (descriptor[1] != '[' && descriptor[1] != 'L') {
+        /* primitive array */
+        assert(descriptor[2] == '\0');
+        return dvmAllocPrimitiveArray(descriptor[1], length, allocFlags);
+    } else {
+        return allocArray(arrayClass, length, kObjectArrayRefWidth,
+            allocFlags);
+    }
+}
+
+/*
+ * Find the array class for "elemClassObj", which could itself be an
+ * array class.
+ */
+ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj)
+{
+    ClassObject* arrayClass;
+
+    assert(elemClassObj != NULL);
+
+    /* Simply prepend "[" to the descriptor. */
+    int nameLen = strlen(elemClassObj->descriptor);
+    char className[nameLen + 2];
+
+    className[0] = '[';
+    memcpy(className+1, elemClassObj->descriptor, nameLen+1);
+    arrayClass = dvmFindArrayClass(className, elemClassObj->classLoader);
+
+    return arrayClass;
+}
+
+/*
+ * Create a new array that holds primitive types.
+ *
+ * "type" is the primitive type letter, e.g. 'I' for int or 'J' for long.
+ */
+ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags)
+{
+    ArrayObject* newArray;
+    ClassObject* arrayClass;
+    int width;
+
+    switch (type) {
+    case 'I':
+        arrayClass = gDvm.classArrayInt;
+        width = 4;
+        break;
+    case 'C':
+        arrayClass = gDvm.classArrayChar;
+        width = 2;
+        break;
+    case 'B':
+        arrayClass = gDvm.classArrayByte;
+        width = 1;
+        break;
+    case 'Z':
+        arrayClass = gDvm.classArrayBoolean;
+        width = 1; /* special-case this? */
+        break;
+    case 'F':
+        arrayClass = gDvm.classArrayFloat;
+        width = 4;
+        break;
+    case 'D':
+        arrayClass = gDvm.classArrayDouble;
+        width = 8;
+        break;
+    case 'S':
+        arrayClass = gDvm.classArrayShort;
+        width = 2;
+        break;
+    case 'J':
+        arrayClass = gDvm.classArrayLong;
+        width = 8;
+        break;
+    default:
+        ALOGE("Unknown primitive type '%c'", type);
+        dvmAbort();
+        return NULL; // Keeps the compiler happy.
+    }
+
+    newArray = allocArray(arrayClass, length, width, allocFlags);
+
+    /* the caller must dvmReleaseTrackedAlloc if allocFlags==ALLOC_DEFAULT */
+    return newArray;
+}
+
+/*
+ * Recursively create an array with multiple dimensions.  Elements may be
+ * Objects or primitive types.
+ *
+ * The dimension we're creating is in dimensions[0], so when we recurse
+ * we advance the pointer.
+ */
+ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
+    const int* dimensions)
+{
+    ArrayObject* newArray;
+    const char* elemName = arrayClass->descriptor + 1; // Advance past one '['.
+
+    LOGVV("dvmAllocMultiArray: class='%s' curDim=%d *dimensions=%d",
+        arrayClass->descriptor, curDim, *dimensions);
+
+    if (curDim == 0) {
+        if (*elemName == 'L' || *elemName == '[') {
+            LOGVV("  end: array class (obj) is '%s'",
+                arrayClass->descriptor);
+            newArray = allocArray(arrayClass, *dimensions,
+                        kObjectArrayRefWidth, ALLOC_DEFAULT);
+        } else {
+            LOGVV("  end: array class (prim) is '%s'",
+                arrayClass->descriptor);
+            newArray = dvmAllocPrimitiveArray(
+                    dexGetPrimitiveTypeDescriptorChar(arrayClass->elementClass->primitiveType),
+                    *dimensions, ALLOC_DEFAULT);
+        }
+    } else {
+        ClassObject* subArrayClass;
+        int i;
+
+        /* if we have X[][], find X[] */
+        subArrayClass = dvmFindArrayClass(elemName, arrayClass->classLoader);
+        if (subArrayClass == NULL) {
+            /* not enough '['s on the initial class? */
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+        assert(dvmIsArrayClass(subArrayClass));
+
+        /* allocate the array that holds the sub-arrays */
+        newArray = allocArray(arrayClass, *dimensions, kObjectArrayRefWidth,
+                        ALLOC_DEFAULT);
+        if (newArray == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+
+        /*
+         * Create a new sub-array in every element of the array.
+         */
+        for (i = 0; i < *dimensions; i++) {
+          ArrayObject* newSubArray;
+          newSubArray = dvmAllocMultiArray(subArrayClass, curDim-1,
+                          dimensions+1);
+            if (newSubArray == NULL) {
+                dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+                assert(dvmCheckException(dvmThreadSelf()));
+                return NULL;
+            }
+            dvmSetObjectArrayElement(newArray, i, (Object *)newSubArray);
+            dvmReleaseTrackedAlloc((Object*) newSubArray, NULL);
+        }
+    }
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return newArray;
+}
+
+
+/*
+ * Find an array class, by name (e.g. "[I").
+ *
+ * If the array class doesn't exist, we generate it.
+ *
+ * If the element class doesn't exist, we return NULL (no exception raised).
+ */
+ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader)
+{
+    ClassObject* clazz;
+
+    assert(descriptor[0] == '[');
+    //ALOGV("dvmFindArrayClass: '%s' %p", descriptor, loader);
+
+    clazz = dvmLookupClass(descriptor, loader, false);
+    if (clazz == NULL) {
+        ALOGV("Array class '%s' %p not found; creating", descriptor, loader);
+        clazz = createArrayClass(descriptor, loader);
+        if (clazz != NULL)
+            dvmAddInitiatingLoader(clazz, loader);
+    }
+
+    return clazz;
+}
+
+/*
+ * Create an array class (i.e. the class object for the array, not the
+ * array itself).  "descriptor" looks like "[C" or "[Ljava/lang/String;".
+ *
+ * If "descriptor" refers to an array of primitives, look up the
+ * primitive type's internally-generated class object.
+ *
+ * "loader" is the class loader of the class that's referring to us.  It's
+ * used to ensure that we're looking for the element type in the right
+ * context.  It does NOT become the class loader for the array class; that
+ * always comes from the base element class.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+static ClassObject* createArrayClass(const char* descriptor, Object* loader)
+{
+    ClassObject* newClass = NULL;
+    ClassObject* elementClass = NULL;
+    int arrayDim;
+    u4 extraFlags;
+
+    assert(descriptor[0] == '[');
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(gDvm.classJavaLangObject != NULL);
+
+    /*
+     * Identify the underlying element class and the array dimension depth.
+     */
+    extraFlags = CLASS_ISARRAY;
+    if (descriptor[1] == '[') {
+        /* array of arrays; keep descriptor and grab stuff from parent */
+        ClassObject* outer;
+
+        outer = dvmFindClassNoInit(&descriptor[1], loader);
+        if (outer != NULL) {
+            /* want the base class, not "outer", in our elementClass */
+            elementClass = outer->elementClass;
+            arrayDim = outer->arrayDim + 1;
+            extraFlags |= CLASS_ISOBJECTARRAY;
+        } else {
+            assert(elementClass == NULL);     /* make sure we fail */
+        }
+    } else {
+        arrayDim = 1;
+        if (descriptor[1] == 'L') {
+            /* array of objects; strip off "[" and look up descriptor. */
+            const char* subDescriptor = &descriptor[1];
+            LOGVV("searching for element class '%s'", subDescriptor);
+            elementClass = dvmFindClassNoInit(subDescriptor, loader);
+            extraFlags |= CLASS_ISOBJECTARRAY;
+        } else {
+            /* array of a primitive type */
+            elementClass = dvmFindPrimitiveClass(descriptor[1]);
+        }
+    }
+
+    if (elementClass == NULL) {
+        /* failed */
+        assert(dvmCheckException(dvmThreadSelf()));
+        dvmFreeClassInnards(newClass);
+        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+        return NULL;
+    }
+
+    /*
+     * See if it's already loaded.  Array classes are always associated
+     * with the class loader of their underlying element type -- an array
+     * of Strings goes with the loader for java/lang/String -- so we need
+     * to look for it there.  (The caller should have checked for the
+     * existence of the class before calling here, but they did so with
+     * *their* class loader, not the element class' loader.)
+     *
+     * If we find it, the caller adds "loader" to the class' initiating
+     * loader list, which should prevent us from going through this again.
+     *
+     * This call is unnecessary if "loader" and "elementClass->classLoader"
+     * are the same, because our caller (dvmFindArrayClass) just did the
+     * lookup.  (Even if we get this wrong we still have correct behavior,
+     * because we effectively do this lookup again when we add the new
+     * class to the hash table -- necessary because of possible races with
+     * other threads.)
+     */
+    if (loader != elementClass->classLoader) {
+        LOGVV("--- checking for '%s' in %p vs. elem %p",
+            descriptor, loader, elementClass->classLoader);
+        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
+        if (newClass != NULL) {
+            ALOGV("--- we already have %s in %p, don't need in %p",
+                descriptor, elementClass->classLoader, loader);
+            return newClass;
+        }
+    }
+
+
+    /*
+     * Fill out the fields in the ClassObject.
+     *
+     * It is possible to execute some methods against arrays, because all
+     * arrays are instances of Object, so we need to set up a vtable.  We
+     * can just point at the one in Object.
+     *
+     * Array classes are simple enough that we don't need to do a full
+     * link step.
+     */
+    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_NON_MOVING);
+    if (newClass == NULL)
+        return NULL;
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptorAlloc = strdup(descriptor);
+    newClass->descriptor = newClass->descriptorAlloc;
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, super),
+                      (Object *)gDvm.classJavaLangObject);
+    newClass->vtableCount = gDvm.classJavaLangObject->vtableCount;
+    newClass->vtable = gDvm.classJavaLangObject->vtable;
+    newClass->primitiveType = PRIM_NOT;
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, elementClass),
+                      (Object *)elementClass);
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, classLoader),
+                      (Object *)elementClass->classLoader);
+    newClass->arrayDim = arrayDim;
+    newClass->status = CLASS_INITIALIZED;
+
+    /* don't need to set newClass->objectSize */
+
+    /*
+     * All arrays have java/lang/Cloneable and java/io/Serializable as
+     * interfaces.  We need to set that up here, so that stuff like
+     * "instanceof" works right.
+     *
+     * Note: The GC could run during the call to dvmFindSystemClassNoInit(),
+     * so we need to make sure the class object is GC-valid while we're in
+     * there.  Do this by clearing the interface list so the GC will just
+     * think that the entries are null.
+     *
+     * TODO?
+     * We may want to cache these two classes to avoid the lookup, though
+     * it's not vital -- we only do it when creating an array class, not
+     * every time we create an array.  Better yet, create a single, global
+     * copy of "interfaces" and "iftable" somewhere near the start and
+     * just point to those (and remember not to free them for arrays).
+     */
+    newClass->interfaceCount = 2;
+    newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader,
+                                sizeof(ClassObject*) * 2);
+    memset(newClass->interfaces, 0, sizeof(ClassObject*) * 2);
+    newClass->interfaces[0] =
+        dvmFindSystemClassNoInit("Ljava/lang/Cloneable;");
+    newClass->interfaces[1] =
+        dvmFindSystemClassNoInit("Ljava/io/Serializable;");
+    dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
+    if (newClass->interfaces[0] == NULL || newClass->interfaces[1] == NULL) {
+        ALOGE("Unable to create array class '%s': missing interfaces",
+            descriptor);
+        dvmFreeClassInnards(newClass);
+        dvmThrowInternalError("missing array ifaces");
+        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+        return NULL;
+    }
+    /*
+     * We assume that Cloneable/Serializable don't have superinterfaces --
+     * normally we'd have to crawl up and explicitly list all of the
+     * supers as well.  These interfaces don't have any methods, so we
+     * don't have to worry about the ifviPool either.
+     */
+    newClass->iftableCount = 2;
+    newClass->iftable = (InterfaceEntry*) dvmLinearAlloc(newClass->classLoader,
+                                sizeof(InterfaceEntry) * 2);
+    memset(newClass->iftable, 0, sizeof(InterfaceEntry) * 2);
+    newClass->iftable[0].clazz = newClass->interfaces[0];
+    newClass->iftable[1].clazz = newClass->interfaces[1];
+    dvmLinearReadOnly(newClass->classLoader, newClass->iftable);
+
+    /*
+     * Inherit access flags from the element.  Arrays can't be used as a
+     * superclass or interface, so we want to add "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.
+         *
+         * (Yes, this happens.)
+         */
+
+        /* Clean up the class before letting the
+         * GC get its hands on it.
+         */
+        dvmFreeClassInnards(newClass);
+
+        /* Let the GC free the class.
+         */
+        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+        /* Grab the winning class.
+         */
+        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
+        assert(newClass != NULL);
+        return newClass;
+    }
+    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+    ALOGV("Created array class '%s' %p (access=0x%04x.%04x)",
+        descriptor, newClass->classLoader,
+        newClass->accessFlags >> 16,
+        newClass->accessFlags & JAVA_FLAGS_MASK);
+
+    return newClass;
+}
+
+/*
+ * Copy the entire contents of one array of objects to another.  If the copy
+ * is impossible because of a type clash, we fail and return "false".
+ */
+bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass)
+{
+    Object** src = (Object**)(void*)srcArray->contents;
+    u4 length, count;
+
+    assert(srcArray->length == dstArray->length);
+    assert(dstArray->clazz->elementClass == dstElemClass ||
+        (dstArray->clazz->elementClass == dstElemClass->elementClass &&
+         dstArray->clazz->arrayDim == dstElemClass->arrayDim+1));
+
+    length = dstArray->length;
+    for (count = 0; count < length; count++) {
+        if (!dvmInstanceof(src[count]->clazz, dstElemClass)) {
+            ALOGW("dvmCopyObjectArray: can't store %s in %s",
+                src[count]->clazz->descriptor, dstElemClass->descriptor);
+            return false;
+        }
+        dvmSetObjectArrayElement(dstArray, count, src[count]);
+    }
+
+    return true;
+}
+
+/*
+ * Copy the entire contents of an array of boxed primitives into an
+ * array of primitives.  The boxed value must fit in the primitive (i.e.
+ * narrowing conversions are not allowed).
+ */
+bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass)
+{
+    Object** src = (Object**)(void*)srcArray->contents;
+    void* dst = (void*)dstArray->contents;
+    u4 count = dstArray->length;
+    PrimitiveType typeIndex = dstElemClass->primitiveType;
+
+    assert(typeIndex != PRIM_NOT);
+    assert(srcArray->length == dstArray->length);
+
+    while (count--) {
+        JValue result;
+
+        /*
+         * This will perform widening conversions as appropriate.  It
+         * might make sense to be more restrictive and require that the
+         * primitive type exactly matches the box class, but it's not
+         * necessary for correctness.
+         */
+        if (!dvmUnboxPrimitive(*src, dstElemClass, &result)) {
+            ALOGW("dvmCopyObjectArray: can't store %s in %s",
+                (*src)->clazz->descriptor, dstElemClass->descriptor);
+            return false;
+        }
+
+        /* would be faster with 4 loops, but speed not crucial here */
+        switch (typeIndex) {
+        case PRIM_BOOLEAN:
+        case PRIM_BYTE:
+            {
+                u1* tmp = (u1*)dst;
+                *tmp++ = result.b;
+                dst = tmp;
+            }
+            break;
+        case PRIM_CHAR:
+        case PRIM_SHORT:
+            {
+                u2* tmp = (u2*)dst;
+                *tmp++ = result.s;
+                dst = tmp;
+            }
+            break;
+        case PRIM_FLOAT:
+        case PRIM_INT:
+            {
+                u4* tmp = (u4*)dst;
+                *tmp++ = result.i;
+                dst = tmp;
+            }
+            break;
+        case PRIM_DOUBLE:
+        case PRIM_LONG:
+            {
+                u8* tmp = (u8*)dst;
+                *tmp++ = result.j;
+                dst = tmp;
+            }
+            break;
+        default:
+            /* should not be possible to get here */
+            dvmAbort();
+        }
+
+        src++;
+    }
+
+    return true;
+}
+
+/*
+ * Returns the width, in bytes, required by elements in instances of
+ * the array class.
+ */
+size_t dvmArrayClassElementWidth(const ClassObject* arrayClass)
+{
+    const char *descriptor;
+
+    assert(dvmIsArrayClass(arrayClass));
+
+    if (dvmIsObjectArrayClass(arrayClass)) {
+        return sizeof(Object *);
+    } else {
+        descriptor = arrayClass->descriptor;
+        switch (descriptor[1]) {
+        case 'B': return 1;  /* byte */
+        case 'C': return 2;  /* char */
+        case 'D': return 8;  /* double */
+        case 'F': return 4;  /* float */
+        case 'I': return 4;  /* int */
+        case 'J': return 8;  /* long */
+        case 'S': return 2;  /* short */
+        case 'Z': return 1;  /* boolean */
+        }
+    }
+    ALOGE("class %p has an unhandled descriptor '%s'", arrayClass, descriptor);
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+    return 0;  /* Quiet the compiler. */
+}
+
+size_t dvmArrayObjectSize(const ArrayObject *array)
+{
+    assert(array != NULL);
+    size_t size = OFFSETOF_MEMBER(ArrayObject, contents);
+    size += array->length * dvmArrayClassElementWidth(array->clazz);
+    return size;
+}
diff --git a/vm/oo/Array.h b/vm/oo/Array.h
new file mode 100644
index 0000000..5390b1d
--- /dev/null
+++ b/vm/oo/Array.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Array handling.
+ */
+#ifndef DALVIK_OO_ARRAY_H_
+#define DALVIK_OO_ARRAY_H_
+
+/*
+ * Find a matching array class.  If it doesn't exist, create it.
+ *
+ * "descriptor" looks like "[I".
+ *
+ * "loader" should be the defining class loader for the elements held
+ * in the array.
+ */
+ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader);
+
+/*
+ * Find the array class for the specified class.  If "elemClassObj" is the
+ * class "Foo", this returns the class object for "[Foo".
+ */
+ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj);
+
+/*
+ * Create a new array, given an array class.  The class may represent an
+ * array of references or primitives.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+extern "C" ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
+    size_t length, int allocFlags);
+
+/*
+ * Allocate an array whose members are primitives (bools, ints, etc.).
+ *
+ * "type" should be 'I', 'J', 'Z', etc.
+ *
+ * The new object will be added to the "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags);
+
+/*
+ * Allocate an array with multiple dimensions.  Elements may be Objects or
+ * primitive types.
+ *
+ * The base object will be added to the "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
+    const int* dimensions);
+
+/*
+ * Verify that the object is actually an array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsArray(const ArrayObject* arrayObj)
+{
+    return ( ((Object*)arrayObj)->clazz->descriptor[0] == '[' );
+}
+
+/*
+ * Verify that the array is an object array and not a primitive array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsObjectArrayClass(const ClassObject* clazz)
+{
+    const char* descriptor = clazz->descriptor;
+    return descriptor[0] == '[' && (descriptor[1] == 'L' ||
+                                    descriptor[1] == '[');
+}
+
+/*
+ * Verify that the array is an object array and not a primitive array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsObjectArray(const ArrayObject* arrayObj)
+{
+    return dvmIsObjectArrayClass(arrayObj->clazz);
+}
+
+/*
+ * Verify that the class is an array class.
+ *
+ * TODO: there may be some performance advantage to setting a flag in
+ * the accessFlags field instead of chasing into the name string.
+ */
+INLINE bool dvmIsArrayClass(const ClassObject* clazz)
+{
+    return (clazz->descriptor[0] == '[');
+}
+
+/*
+ * Copy the entire contents of one array of objects to another.  If the copy
+ * is impossible because of a type clash, we fail and return "false".
+ *
+ * "dstElemClass" is the type of element that "dstArray" holds.
+ */
+bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass);
+
+/*
+ * Copy the entire contents of an array of boxed primitives into an
+ * array of primitives.  The boxed value must fit in the primitive (i.e.
+ * narrowing conversions are not allowed).
+ */
+bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass);
+
+/*
+ * Returns the size of the given array object in bytes.
+ */
+size_t dvmArrayObjectSize(const ArrayObject *array);
+
+/*
+ * Returns the width, in bytes, required by elements in instances of
+ * the array class.
+ */
+size_t dvmArrayClassElementWidth(const ClassObject* clazz);
+
+#endif  // DALVIK_OO_ARRAY_H_
diff --git a/vm/oo/Class.cpp b/vm/oo/Class.cpp
new file mode 100644
index 0000000..78a2273
--- /dev/null
+++ b/vm/oo/Class.cpp
@@ -0,0 +1,4910 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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();
+
+    ALOG(LOG_INFO, "PRELOAD", "%c%d:%d:%d:%s:%d:%s:%lld", type, ppid, pid, tid,
+        get_process_name(), (int) clazz->classLoader, clazz->descriptor,
+        time);
+}
+
+/*
+ * Logs information about a class loading.
+ */
+static void logClassLoad(char type, ClassObject* clazz) {
+    logClassLoadWithTime(type, clazz, dvmGetThreadCpuTimeNsec());
+}
+#endif
+
+/*
+ * Some LinearAlloc unit tests.
+ */
+static void linearAllocTests()
+{
+    char* fiddle;
+    int test = 1;
+
+    switch (test) {
+    case 0:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-28);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 1:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-24);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 2:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-20);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 3:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-16);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 4:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-12);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    }
+    fiddle = (char*)dvmLinearAlloc(NULL, 896);
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 20);      // watch addr of this alloc
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+
+    fiddle = (char*)dvmLinearAlloc(NULL, 1);
+    fiddle[0] = 'q';
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 4096);
+    fiddle[0] = 'x';
+    fiddle[4095] = 'y';
+    dvmLinearReadOnly(NULL, fiddle);
+    dvmLinearFree(NULL, fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 0);
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = (char*)dvmLinearRealloc(NULL, fiddle, 12);
+    fiddle[11] = 'z';
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+    fiddle = (char*)dvmLinearRealloc(NULL, fiddle, 5);
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 17001);
+    fiddle[0] = 'x';
+    fiddle[17000] = 'y';
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+
+    char* str = (char*)dvmLinearStrdup(NULL, "This is a test!");
+    ALOGI("GOT: '%s'", str);
+
+    /* try to check the bounds; allocator may round allocation size up */
+    fiddle = (char*)dvmLinearAlloc(NULL, 12);
+    ALOGI("Should be 1: %d", dvmLinearAllocContains(fiddle, 12));
+    ALOGI("Should be 0: %d", dvmLinearAllocContains(fiddle, 13));
+    ALOGI("Should be 0: %d", dvmLinearAllocContains(fiddle - 128*1024, 1));
+
+    dvmLinearAllocDump(NULL);
+    dvmLinearFree(NULL, (char*)str);
+}
+
+static size_t classObjectSize(size_t sfieldCount)
+{
+    size_t offset = OFFSETOF_MEMBER(ClassObject, sfields);
+    return offset + sizeof(StaticField) * sfieldCount;
+}
+
+size_t dvmClassObjectSize(const ClassObject *clazz)
+{
+    assert(clazz != NULL);
+    return classObjectSize(clazz->sfieldCount);
+}
+
+/* (documented in header) */
+ClassObject* dvmFindPrimitiveClass(char type)
+{
+    PrimitiveType primitiveType = dexGetPrimitiveTypeFromDescriptorChar(type);
+
+    switch (primitiveType) {
+        case PRIM_VOID:    return gDvm.typeVoid;
+        case PRIM_BOOLEAN: return gDvm.typeBoolean;
+        case PRIM_BYTE:    return gDvm.typeByte;
+        case PRIM_SHORT:   return gDvm.typeShort;
+        case PRIM_CHAR:    return gDvm.typeChar;
+        case PRIM_INT:     return gDvm.typeInt;
+        case PRIM_LONG:    return gDvm.typeLong;
+        case PRIM_FLOAT:   return gDvm.typeFloat;
+        case PRIM_DOUBLE:  return gDvm.typeDouble;
+        default: {
+            ALOGW("Unknown primitive type '%c'", type);
+            return NULL;
+        }
+    }
+}
+
+/*
+ * Synthesize a primitive class.
+ *
+ * Just creates the class and returns it (does not add it to the class list).
+ */
+static bool createPrimitiveType(PrimitiveType primitiveType, ClassObject** pClass)
+{
+    /*
+     * Fill out a few fields in the ClassObject.
+     *
+     * Note that primitive classes do not sub-class the class Object.
+     * This matters for "instanceof" checks. Also, we assume that the
+     * primitive class does not override finalize().
+     */
+
+    const char* descriptor = dexGetPrimitiveTypeDescriptor(primitiveType);
+    assert(descriptor != NULL);
+
+    ClassObject* newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_NON_MOVING);
+    if (newClass == NULL) {
+        return false;
+    }
+
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    SET_CLASS_FLAG(newClass, ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
+    newClass->primitiveType = primitiveType;
+    newClass->descriptorAlloc = NULL;
+    newClass->descriptor = descriptor;
+    newClass->super = NULL;
+    newClass->status = CLASS_INITIALIZED;
+
+    /* don't need to set newClass->objectSize */
+
+    LOGVV("Constructed class for primitive type '%s'", newClass->descriptor);
+
+    *pClass = newClass;
+    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+    return true;
+}
+
+/*
+ * Create the initial class instances. These consist of the class
+ * Class and all of the classes representing primitive types.
+ */
+static bool createInitialClasses() {
+    /*
+     * Initialize the class Class. This has to be done specially, particularly
+     * because it is an instance of itself.
+     */
+    ClassObject* clazz = (ClassObject*)
+        dvmMalloc(classObjectSize(CLASS_SFIELD_SLOTS), ALLOC_NON_MOVING);
+    if (clazz == NULL) {
+        return false;
+    }
+    DVM_OBJECT_INIT(clazz, clazz);
+    SET_CLASS_FLAG(clazz, ACC_PUBLIC | ACC_FINAL | CLASS_ISCLASS);
+    clazz->descriptor = "Ljava/lang/Class;";
+    gDvm.classJavaLangClass = clazz;
+    LOGVV("Constructed the class Class.");
+
+    /*
+     * Initialize the classes representing primitive types. These are
+     * instances of the class Class, but other than that they're fairly
+     * different from regular classes.
+     */
+    bool ok = true;
+    ok &= createPrimitiveType(PRIM_VOID,    &gDvm.typeVoid);
+    ok &= createPrimitiveType(PRIM_BOOLEAN, &gDvm.typeBoolean);
+    ok &= createPrimitiveType(PRIM_BYTE,    &gDvm.typeByte);
+    ok &= createPrimitiveType(PRIM_SHORT,   &gDvm.typeShort);
+    ok &= createPrimitiveType(PRIM_CHAR,    &gDvm.typeChar);
+    ok &= createPrimitiveType(PRIM_INT,     &gDvm.typeInt);
+    ok &= createPrimitiveType(PRIM_LONG,    &gDvm.typeLong);
+    ok &= createPrimitiveType(PRIM_FLOAT,   &gDvm.typeFloat);
+    ok &= createPrimitiveType(PRIM_DOUBLE,  &gDvm.typeDouble);
+
+    return ok;
+}
+
+/*
+ * Initialize the bootstrap class loader.
+ *
+ * Call this after the bootclasspath string has been finalized.
+ */
+bool dvmClassStartup()
+{
+    /* make this a requirement -- don't currently support dirs in path */
+    if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
+        ALOGE("ERROR: must specify non-'.' bootclasspath");
+        return false;
+    }
+
+    gDvm.loadedClasses =
+        dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
+
+    gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
+    if (gDvm.pBootLoaderAlloc == NULL)
+        return false;
+
+    if (false) {
+        linearAllocTests();
+        exit(0);
+    }
+
+    /*
+     * Class serial number.  We start with a high value to make it distinct
+     * in binary dumps (e.g. hprof).
+     */
+    gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
+
+    /*
+     * Set up the table we'll use for tracking initiating loaders for
+     * early classes.
+     * If it's NULL, we just fall back to the InitiatingLoaderList in the
+     * ClassObject, so it's not fatal to fail this allocation.
+     */
+    gDvm.initiatingLoaderList = (InitiatingLoaderList*)
+        calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
+
+    /*
+     * Create the initial classes. These are the first objects constructed
+     * within the nascent VM.
+     */
+    if (!createInitialClasses()) {
+        return false;
+    }
+
+    /*
+     * Process the bootstrap class path.  This means opening the specified
+     * DEX or Jar files and possibly running them through the optimizer.
+     */
+    assert(gDvm.bootClassPath == NULL);
+    processClassPath(gDvm.bootClassPathStr, true);
+
+    if (gDvm.bootClassPath == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmClassShutdown()
+{
+    /* discard all system-loaded classes */
+    dvmHashTableFree(gDvm.loadedClasses);
+    gDvm.loadedClasses = NULL;
+
+    /* discard primitive classes created for arrays */
+    dvmFreeClassInnards(gDvm.typeVoid);
+    dvmFreeClassInnards(gDvm.typeBoolean);
+    dvmFreeClassInnards(gDvm.typeByte);
+    dvmFreeClassInnards(gDvm.typeShort);
+    dvmFreeClassInnards(gDvm.typeChar);
+    dvmFreeClassInnards(gDvm.typeInt);
+    dvmFreeClassInnards(gDvm.typeLong);
+    dvmFreeClassInnards(gDvm.typeFloat);
+    dvmFreeClassInnards(gDvm.typeDouble);
+
+    /* this closes DEX files, JAR files, etc. */
+    freeCpeArray(gDvm.bootClassPath);
+    gDvm.bootClassPath = NULL;
+
+    dvmLinearAllocDestroy(NULL);
+
+    free(gDvm.initiatingLoaderList);
+}
+
+
+/*
+ * ===========================================================================
+ *      Bootstrap class loader
+ * ===========================================================================
+ */
+
+/*
+ * Dump the contents of a ClassPathEntry array.
+ */
+static void dumpClassPath(const ClassPathEntry* cpe)
+{
+    int idx = 0;
+
+    while (cpe->kind != kCpeLastEntry) {
+        const char* kindStr;
+
+        switch (cpe->kind) {
+        case kCpeJar:       kindStr = "jar";    break;
+        case kCpeDex:       kindStr = "dex";    break;
+        default:            kindStr = "???";    break;
+        }
+
+        ALOGI("  %2d: type=%s %s %p", idx, kindStr, cpe->fileName, cpe->ptr);
+        if (CALC_CACHE_STATS && cpe->kind == kCpeJar) {
+            JarFile* pJarFile = (JarFile*) cpe->ptr;
+            DvmDex* pDvmDex = dvmGetJarFileDex(pJarFile);
+            dvmDumpAtomicCacheStats(pDvmDex->pInterfaceCache);
+        }
+
+        cpe++;
+        idx++;
+    }
+}
+
+/*
+ * Dump the contents of the bootstrap class path.
+ */
+void dvmDumpBootClassPath()
+{
+    dumpClassPath(gDvm.bootClassPath);
+}
+
+/*
+ * Returns "true" if the class path contains the specified path.
+ */
+bool dvmClassPathContains(const ClassPathEntry* cpe, const char* path)
+{
+    while (cpe->kind != kCpeLastEntry) {
+        if (strcmp(cpe->fileName, path) == 0)
+            return true;
+
+        cpe++;
+    }
+    return false;
+}
+
+/*
+ * Free an array of ClassPathEntry structs.
+ *
+ * We release the contents of each entry, then free the array itself.
+ */
+static void freeCpeArray(ClassPathEntry* cpe)
+{
+    ClassPathEntry* cpeStart = cpe;
+
+    if (cpe == NULL)
+        return;
+
+    while (cpe->kind != kCpeLastEntry) {
+        switch (cpe->kind) {
+        case kCpeJar:
+            /* free JarFile */
+            dvmJarFileFree((JarFile*) cpe->ptr);
+            break;
+        case kCpeDex:
+            /* free RawDexFile */
+            dvmRawDexFileFree((RawDexFile*) cpe->ptr);
+            break;
+        default:
+            assert(false);
+            break;
+        }
+
+        free(cpe->fileName);
+        cpe++;
+    }
+
+    free(cpeStart);
+}
+
+/*
+ * Get the filename suffix of the given file (everything after the
+ * last "." if any, or "<none>" if there's no apparent suffix). The
+ * passed-in buffer will always be '\0' terminated.
+ */
+static void getFileNameSuffix(const char* fileName, char* suffixBuf, size_t suffixBufLen)
+{
+    const char* lastDot = strrchr(fileName, '.');
+
+    strlcpy(suffixBuf, (lastDot == NULL) ? "<none>" : (lastDot + 1), suffixBufLen);
+}
+
+/*
+ * Prepare a ClassPathEntry struct, which at this point only has a valid
+ * filename.  We need to figure out what kind of file it is, and for
+ * everything other than directories we need to open it up and see
+ * what's inside.
+ */
+static bool prepareCpe(ClassPathEntry* cpe, bool isBootstrap)
+{
+    struct stat sb;
+
+    if (stat(cpe->fileName, &sb) < 0) {
+        ALOGD("Unable to stat classpath element '%s'", cpe->fileName);
+        return false;
+    }
+    if (S_ISDIR(sb.st_mode)) {
+        ALOGE("Directory classpath elements are not supported: %s", cpe->fileName);
+        return false;
+    }
+
+    char suffix[10];
+    getFileNameSuffix(cpe->fileName, suffix, sizeof(suffix));
+
+    if ((strcmp(suffix, "jar") == 0) || (strcmp(suffix, "zip") == 0) ||
+            (strcmp(suffix, "apk") == 0)) {
+        JarFile* pJarFile = NULL;
+        if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) {
+            cpe->kind = kCpeJar;
+            cpe->ptr = pJarFile;
+            return true;
+        }
+    } else if (strcmp(suffix, "dex") == 0) {
+        RawDexFile* pRawDexFile = NULL;
+        if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0) {
+            cpe->kind = kCpeDex;
+            cpe->ptr = pRawDexFile;
+            return true;
+        }
+    } else {
+        ALOGE("Unknown type suffix '%s'", suffix);
+    }
+
+    ALOGD("Unable to process classpath element '%s'", cpe->fileName);
+    return false;
+}
+
+/*
+ * Convert a colon-separated list of directories, Zip files, and DEX files
+ * into an array of ClassPathEntry structs.
+ *
+ * During normal startup we fail if there are no entries, because we won't
+ * get very far without the basic language support classes, but if we're
+ * optimizing a DEX file we allow it.
+ *
+ * If entries are added or removed from the bootstrap class path, the
+ * dependencies in the DEX files will break, and everything except the
+ * very first entry will need to be regenerated.
+ */
+static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap)
+{
+    ClassPathEntry* cpe = NULL;
+    char* mangle;
+    char* cp;
+    const char* end;
+    int idx, count;
+
+    assert(pathStr != NULL);
+
+    mangle = strdup(pathStr);
+
+    /*
+     * Run through and essentially strtok() the string.  Get a count of
+     * the #of elements while we're at it.
+     *
+     * If the path was constructed strangely (e.g. ":foo::bar:") this will
+     * over-allocate, which isn't ideal but is mostly harmless.
+     */
+    count = 1;
+    for (cp = mangle; *cp != '\0'; cp++) {
+        if (*cp == ':') {   /* separates two entries */
+            count++;
+            *cp = '\0';
+        }
+    }
+    end = cp;
+
+    /*
+     * Allocate storage.  We over-alloc by one so we can set an "end" marker.
+     */
+    cpe = (ClassPathEntry*) calloc(count+1, sizeof(ClassPathEntry));
+
+    /*
+     * Set the global pointer so the DEX file dependency stuff can find it.
+     */
+    gDvm.bootClassPath = cpe;
+
+    /*
+     * Go through a second time, pulling stuff out.
+     */
+    cp = mangle;
+    idx = 0;
+    while (cp < end) {
+        if (*cp == '\0') {
+            /* leading, trailing, or doubled ':'; ignore it */
+        } else {
+            if (isBootstrap &&
+                    dvmPathToAbsolutePortion(cp) == NULL) {
+                ALOGE("Non-absolute bootclasspath entry '%s'", cp);
+                free(cpe);
+                cpe = NULL;
+                goto bail;
+            }
+
+            ClassPathEntry tmp;
+            tmp.kind = kCpeUnknown;
+            tmp.fileName = strdup(cp);
+            tmp.ptr = NULL;
+
+            /*
+             * Drop an end marker here so DEX loader can walk unfinished
+             * list.
+             */
+            cpe[idx].kind = kCpeLastEntry;
+            cpe[idx].fileName = NULL;
+            cpe[idx].ptr = NULL;
+
+            if (!prepareCpe(&tmp, isBootstrap)) {
+                /* drop from list and continue on */
+                free(tmp.fileName);
+            } else {
+                /* copy over, pointers and all */
+                cpe[idx] = tmp;
+                idx++;
+            }
+        }
+
+        cp += strlen(cp) +1;
+    }
+    assert(idx <= count);
+    if (idx == 0 && !gDvm.optimizing) {
+        /*
+         * There's no way the vm will be doing anything if this is the
+         * case, so just bail out (reasonably) gracefully.
+         */
+        ALOGE("No valid entries found in bootclasspath '%s'", pathStr);
+        gDvm.lastMessage = pathStr;
+        dvmAbort();
+    }
+
+    LOGVV("  (filled %d of %d slots)", idx, count);
+
+    /* put end marker in over-alloc slot */
+    cpe[idx].kind = kCpeLastEntry;
+    cpe[idx].fileName = NULL;
+    cpe[idx].ptr = NULL;
+
+    //dumpClassPath(cpe);
+
+bail:
+    free(mangle);
+    gDvm.bootClassPath = cpe;
+    return cpe;
+}
+
+/*
+ * Search the DEX files we loaded from the bootstrap class path for a DEX
+ * file that has the class with the matching descriptor.
+ *
+ * Returns the matching DEX file and DexClassDef entry if found, otherwise
+ * returns NULL.
+ */
+static DvmDex* searchBootPathForClass(const char* descriptor,
+    const DexClassDef** ppClassDef)
+{
+    const ClassPathEntry* cpe = gDvm.bootClassPath;
+    const DexClassDef* pFoundDef = NULL;
+    DvmDex* pFoundFile = NULL;
+
+    LOGVV("+++ class '%s' not yet loaded, scanning bootclasspath...",
+        descriptor);
+
+    while (cpe->kind != kCpeLastEntry) {
+        //ALOGV("+++  checking '%s' (%d)", cpe->fileName, cpe->kind);
+
+        switch (cpe->kind) {
+        case kCpeJar:
+            {
+                JarFile* pJarFile = (JarFile*) cpe->ptr;
+                const DexClassDef* pClassDef;
+                DvmDex* pDvmDex;
+
+                pDvmDex = dvmGetJarFileDex(pJarFile);
+                pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+                if (pClassDef != NULL) {
+                    /* found */
+                    pFoundDef = pClassDef;
+                    pFoundFile = pDvmDex;
+                    goto found;
+                }
+            }
+            break;
+        case kCpeDex:
+            {
+                RawDexFile* pRawDexFile = (RawDexFile*) cpe->ptr;
+                const DexClassDef* pClassDef;
+                DvmDex* pDvmDex;
+
+                pDvmDex = dvmGetRawDexFileDex(pRawDexFile);
+                pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+                if (pClassDef != NULL) {
+                    /* found */
+                    pFoundDef = pClassDef;
+                    pFoundFile = pDvmDex;
+                    goto found;
+                }
+            }
+            break;
+        default:
+            ALOGE("Unknown kind %d", cpe->kind);
+            assert(false);
+            break;
+        }
+
+        cpe++;
+    }
+
+    /*
+     * Special handling during verification + optimization.
+     *
+     * The DEX optimizer needs to load classes from the DEX file it's working
+     * on.  Rather than trying to insert it into the bootstrap class path
+     * or synthesizing a class loader to manage it, we just make it available
+     * here.  It logically comes after all existing entries in the bootstrap
+     * class path.
+     */
+    if (gDvm.bootClassPathOptExtra != NULL) {
+        const DexClassDef* pClassDef;
+
+        pClassDef =
+            dexFindClass(gDvm.bootClassPathOptExtra->pDexFile, descriptor);
+        if (pClassDef != NULL) {
+            /* found */
+            pFoundDef = pClassDef;
+            pFoundFile = gDvm.bootClassPathOptExtra;
+        }
+    }
+
+found:
+    *ppClassDef = pFoundDef;
+    return pFoundFile;
+}
+
+/*
+ * Set the "extra" DEX, which becomes a de facto member of the bootstrap
+ * class set.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex)
+{
+    gDvm.bootClassPathOptExtra = pDvmDex;
+}
+
+
+/*
+ * Return the #of entries in the bootstrap class path.
+ *
+ * (Used for ClassLoader.getResources().)
+ */
+int dvmGetBootPathSize()
+{
+    const ClassPathEntry* cpe = gDvm.bootClassPath;
+
+    while (cpe->kind != kCpeLastEntry)
+        cpe++;
+
+    return cpe - gDvm.bootClassPath;
+}
+
+/*
+ * Find a resource with the specified name in entry N of the boot class path.
+ *
+ * We return a newly-allocated String of one of these forms:
+ *   file://path/name
+ *   jar:file://path!/name
+ * Where "path" is the bootstrap class path entry and "name" is the string
+ * passed into this method.  "path" needs to be an absolute path (starting
+ * with '/'); if it's not we'd need to "absolutify" it as part of forming
+ * the URL string.
+ */
+StringObject* dvmGetBootPathResource(const char* name, int idx)
+{
+    const int kUrlOverhead = 13;        // worst case for Jar URL
+    const ClassPathEntry* cpe = gDvm.bootClassPath;
+    StringObject* urlObj = NULL;
+
+    ALOGV("+++ searching for resource '%s' in %d(%s)",
+        name, idx, cpe[idx].fileName);
+
+    /* we could use direct array index, but I don't entirely trust "idx" */
+    while (idx-- && cpe->kind != kCpeLastEntry)
+        cpe++;
+    if (cpe->kind == kCpeLastEntry) {
+        assert(false);
+        return NULL;
+    }
+
+    char urlBuf[strlen(name) + strlen(cpe->fileName) + kUrlOverhead +1];
+
+    switch (cpe->kind) {
+    case kCpeJar:
+        {
+            JarFile* pJarFile = (JarFile*) cpe->ptr;
+            if (dexZipFindEntry(&pJarFile->archive, name) == NULL)
+                goto bail;
+            sprintf(urlBuf, "jar:file://%s!/%s", cpe->fileName, name);
+        }
+        break;
+    case kCpeDex:
+        ALOGV("No resources in DEX files");
+        goto bail;
+    default:
+        assert(false);
+        goto bail;
+    }
+
+    ALOGV("+++ using URL='%s'", urlBuf);
+    urlObj = dvmCreateStringFromCstr(urlBuf);
+
+bail:
+    return urlObj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Class list management
+ * ===========================================================================
+ */
+
+/* search for these criteria in the Class hash table */
+struct ClassMatchCriteria {
+    const char* descriptor;
+    Object*     loader;
+};
+
+#define kInitLoaderInc  4       /* must be power of 2 */
+
+static InitiatingLoaderList *dvmGetInitiatingLoaderList(ClassObject* clazz)
+{
+    assert(clazz->serialNumber >= INITIAL_CLASS_SERIAL_NUMBER);
+    int classIndex = clazz->serialNumber-INITIAL_CLASS_SERIAL_NUMBER;
+    if (gDvm.initiatingLoaderList != NULL &&
+        classIndex < ZYGOTE_CLASS_CUTOFF) {
+        return &(gDvm.initiatingLoaderList[classIndex]);
+    } else {
+        return &(clazz->initiatingLoaderList);
+    }
+}
+
+/*
+ * Determine if "loader" appears in clazz' initiating loader list.
+ *
+ * The class hash table lock must be held when calling here, since
+ * it's also used when updating a class' initiating loader list.
+ *
+ * TODO: switch to some sort of lock-free data structure so we don't have
+ * to grab the lock to do a lookup.  Among other things, this would improve
+ * the speed of compareDescriptorClasses().
+ */
+bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader)
+{
+    /*
+     * The bootstrap class loader can't be just an initiating loader for
+     * anything (it's always the defining loader if the class is visible
+     * to it).  We don't put defining loaders in the initiating list.
+     */
+    if (loader == NULL)
+        return false;
+
+    /*
+     * Scan the list for a match.  The list is expected to be short.
+     */
+    /* Cast to remove the const from clazz, but use const loaderList */
+    ClassObject* nonConstClazz = (ClassObject*) clazz;
+    const InitiatingLoaderList *loaderList =
+        dvmGetInitiatingLoaderList(nonConstClazz);
+    int i;
+    for (i = loaderList->initiatingLoaderCount-1; i >= 0; --i) {
+        if (loaderList->initiatingLoaders[i] == loader) {
+            //ALOGI("+++ found initiating match %p in %s",
+            //    loader, clazz->descriptor);
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Add "loader" to clazz's initiating loader set, unless it's the defining
+ * class loader.
+ *
+ * In the common case this will be a short list, so we don't need to do
+ * anything too fancy here.
+ *
+ * This locks gDvm.loadedClasses for synchronization, so don't hold it
+ * when calling here.
+ */
+void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader)
+{
+    if (loader != clazz->classLoader) {
+        assert(loader != NULL);
+
+        LOGVV("Adding %p to '%s' init list", loader, clazz->descriptor);
+        dvmHashTableLock(gDvm.loadedClasses);
+
+        /*
+         * Make sure nobody snuck in.  The penalty for adding twice is
+         * pretty minor, and probably outweighs the O(n^2) hit for
+         * checking before every add, so we may not want to do this.
+         */
+        //if (dvmLoaderInInitiatingList(clazz, loader)) {
+        //    ALOGW("WOW: simultaneous add of initiating class loader");
+        //    goto bail_unlock;
+        //}
+
+        /*
+         * The list never shrinks, so we just keep a count of the
+         * number of elements in it, and reallocate the buffer when
+         * we run off the end.
+         *
+         * The pointer is initially NULL, so we *do* want to call realloc
+         * when count==0.
+         */
+        InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+        if ((loaderList->initiatingLoaderCount & (kInitLoaderInc-1)) == 0) {
+            Object** newList;
+
+            newList = (Object**) realloc(loaderList->initiatingLoaders,
+                        (loaderList->initiatingLoaderCount + kInitLoaderInc)
+                         * sizeof(Object*));
+            if (newList == NULL) {
+                /* this is mainly a cache, so it's not the EotW */
+                assert(false);
+                goto bail_unlock;
+            }
+            loaderList->initiatingLoaders = newList;
+
+            //ALOGI("Expanded init list to %d (%s)",
+            //    loaderList->initiatingLoaderCount+kInitLoaderInc,
+            //    clazz->descriptor);
+        }
+        loaderList->initiatingLoaders[loaderList->initiatingLoaderCount++] =
+            loader;
+
+bail_unlock:
+        dvmHashTableUnlock(gDvm.loadedClasses);
+    }
+}
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Entries in the class hash table are stored as { descriptor, d-loader }
+ * tuples.  If the hashed class descriptor matches the requested descriptor,
+ * and the hashed defining class loader matches the requested class
+ * loader, we're good.  If only the descriptor matches, we check to see if the
+ * loader is in the hashed class' initiating loader list.  If so, we
+ * can return "true" immediately and skip some of the loadClass melodrama.
+ *
+ * The caller must lock the hash table before calling here.
+ *
+ * Returns 0 if a matching entry is found, nonzero otherwise.
+ */
+static int hashcmpClassByCrit(const void* vclazz, const void* vcrit)
+{
+    const ClassObject* clazz = (const ClassObject*) vclazz;
+    const ClassMatchCriteria* pCrit = (const ClassMatchCriteria*) vcrit;
+    bool match;
+
+    match = (strcmp(clazz->descriptor, pCrit->descriptor) == 0 &&
+             (clazz->classLoader == pCrit->loader ||
+              (pCrit->loader != NULL &&
+               dvmLoaderInInitiatingList(clazz, pCrit->loader)) ));
+    //if (match)
+    //    ALOGI("+++ %s %p matches existing %s %p",
+    //        pCrit->descriptor, pCrit->loader,
+    //        clazz->descriptor, clazz->classLoader);
+    return !match;
+}
+
+/*
+ * Like hashcmpClassByCrit, but passing in a fully-formed ClassObject
+ * instead of a ClassMatchCriteria.
+ */
+static int hashcmpClassByClass(const void* vclazz, const void* vaddclazz)
+{
+    const ClassObject* clazz = (const ClassObject*) vclazz;
+    const ClassObject* addClazz = (const ClassObject*) vaddclazz;
+    bool match;
+
+    match = (strcmp(clazz->descriptor, addClazz->descriptor) == 0 &&
+             (clazz->classLoader == addClazz->classLoader ||
+              (addClazz->classLoader != NULL &&
+               dvmLoaderInInitiatingList(clazz, addClazz->classLoader)) ));
+    return !match;
+}
+
+/*
+ * Search through the hash table to find an entry with a matching descriptor
+ * and an initiating class loader that matches "loader".
+ *
+ * The table entries are hashed on descriptor only, because they're unique
+ * on *defining* class loader, not *initiating* class loader.  This isn't
+ * great, because it guarantees we will have to probe when multiple
+ * class loaders are used.
+ *
+ * Note this does NOT try to load a class; it just finds a class that
+ * has already been loaded.
+ *
+ * If "unprepOkay" is set, this will return classes that have been added
+ * to the hash table but are not yet fully loaded and linked.  Otherwise,
+ * such classes are ignored.  (The only place that should set "unprepOkay"
+ * is findClassNoInit(), which will wait for the prep to finish.)
+ *
+ * Returns NULL if not found.
+ */
+ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
+    bool unprepOkay)
+{
+    ClassMatchCriteria crit;
+    void* found;
+    u4 hash;
+
+    crit.descriptor = descriptor;
+    crit.loader = loader;
+    hash = dvmComputeUtf8Hash(descriptor);
+
+    LOGVV("threadid=%d: dvmLookupClass searching for '%s' %p",
+        dvmThreadSelf()->threadId, descriptor, loader);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    found = dvmHashTableLookup(gDvm.loadedClasses, hash, &crit,
+                hashcmpClassByCrit, false);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    /*
+     * The class has been added to the hash table but isn't ready for use.
+     * We're going to act like we didn't see it, so that the caller will
+     * go through the full "find class" path, which includes locking the
+     * object and waiting until it's ready.  We could do that lock/wait
+     * here, but this is an extremely rare case, and it's simpler to have
+     * the wait-for-class code centralized.
+     */
+    if (found && !unprepOkay && !dvmIsClassLinked((ClassObject*)found)) {
+        ALOGV("Ignoring not-yet-ready %s, using slow path",
+            ((ClassObject*)found)->descriptor);
+        found = NULL;
+    }
+
+    return (ClassObject*) found;
+}
+
+/*
+ * Add a new class to the hash table.
+ *
+ * The class is considered "new" if it doesn't match on both the class
+ * descriptor and the defining class loader.
+ *
+ * TODO: we should probably have separate hash tables for each
+ * ClassLoader. This could speed up dvmLookupClass and
+ * other common operations. It does imply a VM-visible data structure
+ * for each ClassLoader object with loaded classes, which we don't
+ * have yet.
+ */
+bool dvmAddClassToHash(ClassObject* clazz)
+{
+    void* found;
+    u4 hash;
+
+    hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    found = dvmHashTableLookup(gDvm.loadedClasses, hash, clazz,
+                hashcmpClassByClass, true);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    ALOGV("+++ dvmAddClassToHash '%s' %p (isnew=%d) --> %p",
+        clazz->descriptor, clazz->classLoader,
+        (found == (void*) clazz), clazz);
+
+    //dvmCheckClassTablePerf();
+
+    /* can happen if two threads load the same class simultaneously */
+    return (found == (void*) clazz);
+}
+
+#if 0
+/*
+ * Compute hash value for a class.
+ */
+u4 hashcalcClass(const void* item)
+{
+    return dvmComputeUtf8Hash(((const ClassObject*) item)->descriptor);
+}
+
+/*
+ * Check the performance of the "loadedClasses" hash table.
+ */
+void dvmCheckClassTablePerf()
+{
+    dvmHashTableLock(gDvm.loadedClasses);
+    dvmHashTableProbeCount(gDvm.loadedClasses, hashcalcClass,
+        hashcmpClassByClass);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+#endif
+
+/*
+ * Remove a class object from the hash table.
+ */
+static void removeClassFromHash(ClassObject* clazz)
+{
+    ALOGV("+++ removeClassFromHash '%s'", clazz->descriptor);
+
+    u4 hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    if (!dvmHashTableRemove(gDvm.loadedClasses, hash, clazz))
+        ALOGW("Hash table remove failed on class '%s'", clazz->descriptor);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+
+/*
+ * ===========================================================================
+ *      Class creation
+ * ===========================================================================
+ */
+
+/*
+ * Set clazz->serialNumber to the next available value.
+ *
+ * This usually happens *very* early in class creation, so don't expect
+ * anything else in the class to be ready.
+ */
+void dvmSetClassSerialNumber(ClassObject* clazz)
+{
+    assert(clazz->serialNumber == 0);
+    clazz->serialNumber = android_atomic_inc(&gDvm.classSerialNumber);
+}
+
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded and initialized if it has not already been.
+ * If necessary, the superclass will be loaded.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClass(const char* descriptor, Object* loader)
+{
+    ClassObject* clazz;
+
+    clazz = dvmFindClassNoInit(descriptor, loader);
+    if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+        /* initialize class */
+        if (!dvmInitClass(clazz)) {
+            /* init failed; leave it in the list, marked as bad */
+            assert(dvmCheckException(dvmThreadSelf()));
+            assert(clazz->status == CLASS_ERROR);
+            return NULL;
+        }
+    }
+
+    return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded if it has not already been, as will its
+ * superclass.  It will not be initialized.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClassNoInit(const char* descriptor,
+        Object* loader)
+{
+    assert(descriptor != NULL);
+    //assert(loader != NULL);
+
+    LOGVV("FindClassNoInit '%s' %p", descriptor, loader);
+
+    if (*descriptor == '[') {
+        /*
+         * Array class.  Find in table, generate if not found.
+         */
+        return dvmFindArrayClass(descriptor, loader);
+    } else {
+        /*
+         * Regular class.  Find in table, load if not found.
+         */
+        if (loader != NULL) {
+            return findClassFromLoaderNoInit(descriptor, loader);
+        } else {
+            return dvmFindSystemClassNoInit(descriptor);
+        }
+    }
+}
+
+/*
+ * Load the named class (by descriptor) from the specified class
+ * loader.  This calls out to let the ClassLoader object do its thing.
+ *
+ * Returns with NULL and an exception raised on error.
+ */
+static ClassObject* findClassFromLoaderNoInit(const char* descriptor,
+    Object* loader)
+{
+    //ALOGI("##### findClassFromLoaderNoInit (%s,%p)",
+    //        descriptor, loader);
+
+    Thread* self = dvmThreadSelf();
+
+    assert(loader != NULL);
+
+    /*
+     * Do we already have it?
+     *
+     * The class loader code does the "is it already loaded" check as
+     * well.  However, this call is much faster than calling through
+     * interpreted code.  Doing this does mean that in the common case
+     * (365 out of 420 calls booting the sim) we're doing the
+     * lookup-by-descriptor twice.  It appears this is still a win, so
+     * I'm keeping it in.
+     */
+    ClassObject* clazz = dvmLookupClass(descriptor, loader, false);
+    if (clazz != NULL) {
+        LOGVV("Already loaded: %s %p", descriptor, loader);
+        return clazz;
+    } else {
+        LOGVV("Not already loaded: %s %p", descriptor, loader);
+    }
+
+    char* dotName = NULL;
+    StringObject* nameObj = NULL;
+
+    /* convert "Landroid/debug/Stuff;" to "android.debug.Stuff" */
+    dotName = dvmDescriptorToDot(descriptor);
+    if (dotName == NULL) {
+        dvmThrowOutOfMemoryError(NULL);
+        return NULL;
+    }
+    nameObj = dvmCreateStringFromCstr(dotName);
+    if (nameObj == NULL) {
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+
+    dvmMethodTraceClassPrepBegin();
+
+    /*
+     * Invoke loadClass().  This will probably result in a couple of
+     * exceptions being thrown, because the ClassLoader.loadClass()
+     * implementation eventually calls VMClassLoader.loadClass to see if
+     * the bootstrap class loader can find it before doing its own load.
+     */
+    LOGVV("--- Invoking loadClass(%s, %p)", dotName, loader);
+    {
+        const Method* loadClass =
+            loader->clazz->vtable[gDvm.voffJavaLangClassLoader_loadClass];
+        JValue result;
+        dvmCallMethod(self, loadClass, loader, &result, nameObj);
+        clazz = (ClassObject*) result.l;
+
+        dvmMethodTraceClassPrepEnd();
+        Object* excep = dvmGetException(self);
+        if (excep != NULL) {
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("NOTE: loadClass '%s' %p threw exception %s",
+                 dotName, loader, excep->clazz->descriptor);
+#endif
+            dvmAddTrackedAlloc(excep, self);
+            dvmClearException(self);
+            dvmThrowChainedNoClassDefFoundError(descriptor, excep);
+            dvmReleaseTrackedAlloc(excep, self);
+            clazz = NULL;
+            goto bail;
+        } else if (clazz == NULL) {
+            ALOGW("ClassLoader returned NULL w/o exception pending");
+            dvmThrowNullPointerException("ClassLoader returned null");
+            goto bail;
+        }
+    }
+
+    /* not adding clazz to tracked-alloc list, because it's a ClassObject */
+
+    dvmAddInitiatingLoader(clazz, loader);
+
+    LOGVV("--- Successfully loaded %s %p (thisldr=%p clazz=%p)",
+        descriptor, clazz->classLoader, loader, clazz);
+
+bail:
+    dvmReleaseTrackedAlloc((Object*)nameObj, NULL);
+    free(dotName);
+    return clazz;
+}
+
+/*
+ * Load the named class (by descriptor) from the specified DEX file.
+ * Used by class loaders to instantiate a class object from a
+ * VM-managed DEX.
+ */
+ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
+    Object* classLoader)
+{
+    assert(pDvmDex != NULL);
+
+    return findClassNoInit(descriptor, classLoader, pDvmDex);
+}
+
+
+/*
+ * Find the named class (by descriptor), scanning through the
+ * bootclasspath if it hasn't already been loaded.
+ *
+ * "descriptor" looks like "Landroid/debug/Stuff;".
+ *
+ * Uses NULL as the defining class loader.
+ */
+ClassObject* dvmFindSystemClass(const char* descriptor)
+{
+    ClassObject* clazz;
+
+    clazz = dvmFindSystemClassNoInit(descriptor);
+    if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+        /* initialize class */
+        if (!dvmInitClass(clazz)) {
+            /* init failed; leave it in the list, marked as bad */
+            assert(dvmCheckException(dvmThreadSelf()));
+            assert(clazz->status == CLASS_ERROR);
+            return NULL;
+        }
+    }
+
+    return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), searching for it in the
+ * bootclasspath.
+ *
+ * On failure, this returns NULL with an exception raised.
+ */
+ClassObject* dvmFindSystemClassNoInit(const char* descriptor)
+{
+    return findClassNoInit(descriptor, NULL, NULL);
+}
+
+/*
+ * Find the named class (by descriptor). If it's not already loaded,
+ * we load it and link it, but don't execute <clinit>. (The VM has
+ * specific limitations on which events can cause initialization.)
+ *
+ * If "pDexFile" is NULL, we will search the bootclasspath for an entry.
+ *
+ * On failure, this returns NULL with an exception raised.
+ *
+ * TODO: we need to return an indication of whether we loaded the class or
+ * used an existing definition.  If somebody deliberately tries to load a
+ * class twice in the same class loader, they should get a LinkageError,
+ * but inadvertent simultaneous class references should "just work".
+ */
+static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
+    DvmDex* pDvmDex)
+{
+    Thread* self = dvmThreadSelf();
+    ClassObject* clazz;
+    bool profilerNotified = false;
+
+    if (loader != NULL) {
+        LOGVV("#### findClassNoInit(%s,%p,%p)", descriptor, loader,
+            pDvmDex->pDexFile);
+    }
+
+    /*
+     * We don't expect an exception to be raised at this point.  The
+     * exception handling code is good about managing this.  This *can*
+     * happen if a JNI lookup fails and the JNI code doesn't do any
+     * error checking before doing another class lookup, so we may just
+     * want to clear this and restore it on exit.  If we don't, some kinds
+     * of failures can't be detected without rearranging other stuff.
+     *
+     * Most often when we hit this situation it means that something is
+     * broken in the VM or in JNI code, so I'm keeping it in place (and
+     * making it an informative abort rather than an assert).
+     */
+    if (dvmCheckException(self)) {
+        ALOGE("Class lookup %s attempted with exception pending", descriptor);
+        ALOGW("Pending exception is:");
+        dvmLogExceptionStackTrace();
+        dvmDumpAllThreads(false);
+        dvmAbort();
+    }
+
+    clazz = dvmLookupClass(descriptor, loader, true);
+    if (clazz == NULL) {
+        const DexClassDef* pClassDef;
+
+        dvmMethodTraceClassPrepBegin();
+        profilerNotified = true;
+
+#if LOG_CLASS_LOADING
+        u8 startTime = dvmGetThreadCpuTimeNsec();
+#endif
+
+        if (pDvmDex == NULL) {
+            assert(loader == NULL);     /* shouldn't be here otherwise */
+            pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
+        } else {
+            pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+        }
+
+        if (pDvmDex == NULL || pClassDef == NULL) {
+            if (gDvm.noClassDefFoundErrorObj != NULL) {
+                /* usual case -- use prefabricated object */
+                dvmSetException(self, gDvm.noClassDefFoundErrorObj);
+            } else {
+                /* dexopt case -- can't guarantee prefab (core.jar) */
+                dvmThrowNoClassDefFoundError(descriptor);
+            }
+            goto bail;
+        }
+
+        /* found a match, try to load it */
+        clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
+        if (dvmCheckException(self)) {
+            /* class was found but had issues */
+            if (clazz != NULL) {
+                dvmFreeClassInnards(clazz);
+                dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+            }
+            goto bail;
+        }
+
+        /*
+         * Lock the class while we link it so other threads must wait for us
+         * to finish.  Set the "initThreadId" so we can identify recursive
+         * invocation.  (Note all accesses to initThreadId here are
+         * guarded by the class object's lock.)
+         */
+        dvmLockObject(self, (Object*) clazz);
+        clazz->initThreadId = self->threadId;
+
+        /*
+         * Add to hash table so lookups succeed.
+         *
+         * [Are circular references possible when linking a class?]
+         */
+        assert(clazz->classLoader == loader);
+        if (!dvmAddClassToHash(clazz)) {
+            /*
+             * Another thread must have loaded the class after we
+             * started but before we finished.  Discard what we've
+             * done and leave some hints for the GC.
+             *
+             * (Yes, this happens.)
+             */
+            //ALOGW("WOW: somebody loaded %s simultaneously", descriptor);
+            clazz->initThreadId = 0;
+            dvmUnlockObject(self, (Object*) clazz);
+
+            /* Let the GC free the class.
+             */
+            dvmFreeClassInnards(clazz);
+            dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+            /* Grab the winning class.
+             */
+            clazz = dvmLookupClass(descriptor, loader, true);
+            assert(clazz != NULL);
+            goto got_class;
+        }
+        dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+#if LOG_CLASS_LOADING
+        logClassLoadWithTime('>', clazz, startTime);
+#endif
+        /*
+         * Prepare and resolve.
+         */
+        if (!dvmLinkClass(clazz)) {
+            assert(dvmCheckException(self));
+
+            /* Make note of the error and clean up the class.
+             */
+            removeClassFromHash(clazz);
+            clazz->status = CLASS_ERROR;
+            dvmFreeClassInnards(clazz);
+
+            /* Let any waiters know.
+             */
+            clazz->initThreadId = 0;
+            dvmObjectNotifyAll(self, (Object*) clazz);
+            dvmUnlockObject(self, (Object*) clazz);
+
+#if LOG_CLASS_LOADING
+            ALOG(LOG_INFO, "DVMLINK FAILED FOR CLASS ", "%s in %s",
+                clazz->descriptor, get_process_name());
+
+            /*
+             * TODO: It would probably be better to use a new type code here (instead of '<') to
+             * indicate the failure.  This change would require a matching change in the parser
+             * and analysis code in frameworks/base/tools/preload.
+             */
+            logClassLoad('<', clazz);
+#endif
+            clazz = NULL;
+            if (gDvm.optimizing) {
+                /* happens with "external" libs */
+                ALOGV("Link of class '%s' failed", descriptor);
+            } else {
+                ALOGW("Link of class '%s' failed", descriptor);
+            }
+            goto bail;
+        }
+        dvmObjectNotifyAll(self, (Object*) clazz);
+        dvmUnlockObject(self, (Object*) clazz);
+
+        /*
+         * Add class stats to global counters.
+         *
+         * TODO: these should probably be atomic ops.
+         */
+        gDvm.numLoadedClasses++;
+        gDvm.numDeclaredMethods +=
+            clazz->virtualMethodCount + clazz->directMethodCount;
+        gDvm.numDeclaredInstFields += clazz->ifieldCount;
+        gDvm.numDeclaredStaticFields += clazz->sfieldCount;
+
+        /*
+         * Cache pointers to basic classes.  We want to use these in
+         * various places, and it's easiest to initialize them on first
+         * use rather than trying to force them to initialize (startup
+         * ordering makes it weird).
+         */
+        if (gDvm.classJavaLangObject == NULL &&
+            strcmp(descriptor, "Ljava/lang/Object;") == 0)
+        {
+            /* It should be impossible to get here with anything
+             * but the bootclasspath loader.
+             */
+            assert(loader == NULL);
+            gDvm.classJavaLangObject = clazz;
+        }
+
+#if LOG_CLASS_LOADING
+        logClassLoad('<', clazz);
+#endif
+
+    } else {
+got_class:
+        if (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+            /*
+             * We can race with other threads for class linking.  We should
+             * never get here recursively; doing so indicates that two
+             * classes have circular dependencies.
+             *
+             * One exception: we force discovery of java.lang.Class in
+             * dvmLinkClass(), and Class has Object as its superclass.  So
+             * if the first thing we ever load is Object, we will init
+             * Object->Class->Object.  The easiest way to avoid this is to
+             * ensure that Object is never the first thing we look up, so
+             * we get Foo->Class->Object instead.
+             */
+            dvmLockObject(self, (Object*) clazz);
+            if (!dvmIsClassLinked(clazz) &&
+                clazz->initThreadId == self->threadId)
+            {
+                ALOGW("Recursive link on class %s", clazz->descriptor);
+                dvmUnlockObject(self, (Object*) clazz);
+                dvmThrowClassCircularityError(clazz->descriptor);
+                clazz = NULL;
+                goto bail;
+            }
+            //ALOGI("WAITING  for '%s' (owner=%d)",
+            //    clazz->descriptor, clazz->initThreadId);
+            while (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+                dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+            }
+            dvmUnlockObject(self, (Object*) clazz);
+        }
+        if (clazz->status == CLASS_ERROR) {
+            /*
+             * Somebody else tried to load this and failed.  We need to raise
+             * an exception and report failure.
+             */
+            throwEarlierClassFailure(clazz);
+            clazz = NULL;
+            goto bail;
+        }
+    }
+
+    /* check some invariants */
+    assert(dvmIsClassLinked(clazz));
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(clazz->clazz == gDvm.classJavaLangClass);
+    assert(dvmIsClassObject(clazz));
+    assert(clazz == gDvm.classJavaLangObject || clazz->super != NULL);
+    if (!dvmIsInterfaceClass(clazz)) {
+        //ALOGI("class=%s vtableCount=%d, virtualMeth=%d",
+        //    clazz->descriptor, clazz->vtableCount,
+        //    clazz->virtualMethodCount);
+        assert(clazz->vtableCount >= clazz->virtualMethodCount);
+    }
+
+bail:
+    if (profilerNotified)
+        dvmMethodTraceClassPrepEnd();
+    assert(clazz != NULL || dvmCheckException(self));
+    return clazz;
+}
+
+/*
+ * Helper for loadClassFromDex, which takes a DexClassDataHeader and
+ * encoded data pointer in addition to the other arguments.
+ */
+static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
+    const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
+    const u1* pEncodedData, Object* classLoader)
+{
+    ClassObject* newClass = NULL;
+    const DexFile* pDexFile;
+    const char* descriptor;
+    int i;
+
+    pDexFile = pDvmDex->pDexFile;
+    descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
+
+    /*
+     * Make sure the aren't any "bonus" flags set, since we use them for
+     * runtime state.
+     */
+    if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
+        ALOGW("Invalid file flags in class %s: %04x",
+            descriptor, pClassDef->accessFlags);
+        return NULL;
+    }
+
+    /*
+     * Allocate storage for the class object on the GC heap, so that other
+     * objects can have references to it.  We bypass the usual mechanism
+     * (allocObject), because we don't have all the bits and pieces yet.
+     *
+     * Note that we assume that java.lang.Class does not override
+     * finalize().
+     */
+    /* TODO: Can there be fewer special checks in the usual path? */
+    assert(descriptor != NULL);
+    if (classLoader == NULL &&
+        strcmp(descriptor, "Ljava/lang/Class;") == 0) {
+        assert(gDvm.classJavaLangClass != NULL);
+        newClass = gDvm.classJavaLangClass;
+    } else {
+        size_t size = classObjectSize(pHeader->staticFieldsSize);
+        newClass = (ClassObject*) dvmMalloc(size, ALLOC_NON_MOVING);
+    }
+    if (newClass == NULL)
+        return NULL;
+
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptor = descriptor;
+    assert(newClass->descriptorAlloc == NULL);
+    SET_CLASS_FLAG(newClass, pClassDef->accessFlags);
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, classLoader),
+                      (Object *)classLoader);
+    newClass->pDvmDex = pDvmDex;
+    newClass->primitiveType = PRIM_NOT;
+    newClass->status = CLASS_IDX;
+
+    /*
+     * Stuff the superclass index into the object pointer field.  The linker
+     * pulls it out and replaces it with a resolved ClassObject pointer.
+     * I'm doing it this way (rather than having a dedicated superclassIdx
+     * field) to save a few bytes of overhead per class.
+     *
+     * newClass->super is not traversed or freed by dvmFreeClassInnards, so
+     * this is safe.
+     */
+    assert(sizeof(u4) == sizeof(ClassObject*)); /* 32-bit check */
+    newClass->super = (ClassObject*) pClassDef->superclassIdx;
+
+    /*
+     * Stuff class reference indices into the pointer fields.
+     *
+     * The elements of newClass->interfaces are not traversed or freed by
+     * dvmFreeClassInnards, so this is GC-safe.
+     */
+    const DexTypeList* pInterfacesList;
+    pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);
+    if (pInterfacesList != NULL) {
+        newClass->interfaceCount = pInterfacesList->size;
+        newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader,
+                newClass->interfaceCount * sizeof(ClassObject*));
+
+        for (i = 0; i < newClass->interfaceCount; i++) {
+            const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i);
+            newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx;
+        }
+        dvmLinearReadOnly(classLoader, newClass->interfaces);
+    }
+
+    /* load field definitions */
+
+    /*
+     * Over-allocate the class object and append static field info
+     * onto the end.  It's fixed-size and known at alloc time.  This
+     * seems to increase zygote sharing.  Heap compaction will have to
+     * be careful if it ever tries to move ClassObject instances,
+     * because we pass Field pointers around internally. But at least
+     * now these Field pointers are in the object heap.
+     */
+
+    if (pHeader->staticFieldsSize != 0) {
+        /* static fields stay on system heap; field data isn't "write once" */
+        int count = (int) pHeader->staticFieldsSize;
+        u4 lastIndex = 0;
+        DexField field;
+
+        newClass->sfieldCount = count;
+        for (i = 0; i < count; i++) {
+            dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+            loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
+        }
+    }
+
+    if (pHeader->instanceFieldsSize != 0) {
+        int count = (int) pHeader->instanceFieldsSize;
+        u4 lastIndex = 0;
+        DexField field;
+
+        newClass->ifieldCount = count;
+        newClass->ifields = (InstField*) dvmLinearAlloc(classLoader,
+                count * sizeof(InstField));
+        for (i = 0; i < count; i++) {
+            dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+            loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);
+        }
+        dvmLinearReadOnly(classLoader, newClass->ifields);
+    }
+
+    /*
+     * Load method definitions.  We do this in two batches, direct then
+     * virtual.
+     *
+     * If register maps have already been generated for this class, and
+     * precise GC is enabled, we pull out pointers to them.  We know that
+     * they were streamed to the DEX file in the same order in which the
+     * methods appear.
+     *
+     * If the class wasn't pre-verified, the maps will be generated when
+     * the class is verified during class initialization.
+     */
+    u4 classDefIdx = dexGetIndexForClassDef(pDexFile, pClassDef);
+    const void* classMapData;
+    u4 numMethods;
+
+    if (gDvm.preciseGc) {
+        classMapData =
+            dvmRegisterMapGetClassData(pDexFile, classDefIdx, &numMethods);
+
+        /* sanity check */
+        if (classMapData != NULL &&
+            pHeader->directMethodsSize + pHeader->virtualMethodsSize != numMethods)
+        {
+            ALOGE("ERROR: in %s, direct=%d virtual=%d, maps have %d",
+                newClass->descriptor, pHeader->directMethodsSize,
+                pHeader->virtualMethodsSize, numMethods);
+            assert(false);
+            classMapData = NULL;        /* abandon */
+        }
+    } else {
+        classMapData = NULL;
+    }
+
+    if (pHeader->directMethodsSize != 0) {
+        int count = (int) pHeader->directMethodsSize;
+        u4 lastIndex = 0;
+        DexMethod method;
+
+        newClass->directMethodCount = count;
+        newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,
+                count * sizeof(Method));
+        for (i = 0; i < count; i++) {
+            dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+            loadMethodFromDex(newClass, &method, &newClass->directMethods[i]);
+            if (classMapData != NULL) {
+                const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
+                if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
+                    newClass->directMethods[i].registerMap = pMap;
+                    /* TODO: add rigorous checks */
+                    assert((newClass->directMethods[i].registersSize+7) / 8 ==
+                        newClass->directMethods[i].registerMap->regWidth);
+                }
+            }
+        }
+        dvmLinearReadOnly(classLoader, newClass->directMethods);
+    }
+
+    if (pHeader->virtualMethodsSize != 0) {
+        int count = (int) pHeader->virtualMethodsSize;
+        u4 lastIndex = 0;
+        DexMethod method;
+
+        newClass->virtualMethodCount = count;
+        newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,
+                count * sizeof(Method));
+        for (i = 0; i < count; i++) {
+            dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+            loadMethodFromDex(newClass, &method, &newClass->virtualMethods[i]);
+            if (classMapData != NULL) {
+                const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
+                if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
+                    newClass->virtualMethods[i].registerMap = pMap;
+                    /* TODO: add rigorous checks */
+                    assert((newClass->virtualMethods[i].registersSize+7) / 8 ==
+                        newClass->virtualMethods[i].registerMap->regWidth);
+                }
+            }
+        }
+        dvmLinearReadOnly(classLoader, newClass->virtualMethods);
+    }
+
+    newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return newClass;
+}
+
+/*
+ * Try to load the indicated class from the specified DEX file.
+ *
+ * This is effectively loadClass()+defineClass() for a DexClassDef.  The
+ * loading was largely done when we crunched through the DEX.
+ *
+ * Returns NULL on failure.  If we locate the class but encounter an error
+ * while processing it, an appropriate exception is thrown.
+ */
+static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
+    const DexClassDef* pClassDef, Object* classLoader)
+{
+    ClassObject* result;
+    DexClassDataHeader header;
+    const u1* pEncodedData;
+    const DexFile* pDexFile;
+
+    assert((pDvmDex != NULL) && (pClassDef != NULL));
+    pDexFile = pDvmDex->pDexFile;
+
+    if (gDvm.verboseClass) {
+        ALOGV("CLASS: loading '%s'...",
+            dexGetClassDescriptor(pDexFile, pClassDef));
+    }
+
+    pEncodedData = dexGetClassData(pDexFile, pClassDef);
+
+    if (pEncodedData != NULL) {
+        dexReadClassDataHeader(&pEncodedData, &header);
+    } else {
+        // Provide an all-zeroes header for the rest of the loading.
+        memset(&header, 0, sizeof(header));
+    }
+
+    result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
+            classLoader);
+
+    if (gDvm.verboseClass && (result != NULL)) {
+        ALOGI("[Loaded %s from DEX %p (cl=%p)]",
+            result->descriptor, pDvmDex, classLoader);
+    }
+
+    return result;
+}
+
+/*
+ * Free anything in a ClassObject that was allocated on the system heap.
+ *
+ * The ClassObject itself is allocated on the GC heap, so we leave it for
+ * the garbage collector.
+ *
+ * NOTE: this may be called with a partially-constructed object.
+ * NOTE: there is no particular ordering imposed, so don't go poking at
+ * superclasses.
+ */
+void dvmFreeClassInnards(ClassObject* clazz)
+{
+    void *tp;
+    int i;
+
+    if (clazz == NULL)
+        return;
+
+    assert(clazz->clazz == gDvm.classJavaLangClass);
+    assert(dvmIsClassObject(clazz));
+
+    /* Guarantee that dvmFreeClassInnards can be called on a given
+     * class multiple times by clearing things out as we free them.
+     * We don't make any attempt at real atomicity here; higher
+     * levels need to make sure that no two threads can free the
+     * same ClassObject at the same time.
+     *
+     * TODO: maybe just make it so the GC will never free the
+     * innards of an already-freed class.
+     *
+     * TODO: this #define isn't MT-safe -- the compiler could rearrange it.
+     */
+#define NULL_AND_FREE(p) \
+    do { \
+        if ((p) != NULL) { \
+            tp = (p); \
+            (p) = NULL; \
+            free(tp); \
+        } \
+    } while (0)
+#define NULL_AND_LINEAR_FREE(p) \
+    do { \
+        if ((p) != NULL) { \
+            tp = (p); \
+            (p) = NULL; \
+            dvmLinearFree(clazz->classLoader, tp); \
+        } \
+    } while (0)
+
+    /* arrays just point at Object's vtable; don't free vtable in this case.
+     */
+    clazz->vtableCount = -1;
+    if (clazz->vtable == gDvm.classJavaLangObject->vtable) {
+        clazz->vtable = NULL;
+    } else {
+        NULL_AND_LINEAR_FREE(clazz->vtable);
+    }
+
+    clazz->descriptor = NULL;
+    NULL_AND_FREE(clazz->descriptorAlloc);
+
+    if (clazz->directMethods != NULL) {
+        Method *directMethods = clazz->directMethods;
+        int directMethodCount = clazz->directMethodCount;
+        clazz->directMethods = NULL;
+        clazz->directMethodCount = -1;
+        dvmLinearReadWrite(clazz->classLoader, directMethods);
+        for (i = 0; i < directMethodCount; i++) {
+            freeMethodInnards(&directMethods[i]);
+        }
+        dvmLinearReadOnly(clazz->classLoader, directMethods);
+        dvmLinearFree(clazz->classLoader, directMethods);
+    }
+    if (clazz->virtualMethods != NULL) {
+        Method *virtualMethods = clazz->virtualMethods;
+        int virtualMethodCount = clazz->virtualMethodCount;
+        clazz->virtualMethodCount = -1;
+        clazz->virtualMethods = NULL;
+        dvmLinearReadWrite(clazz->classLoader, virtualMethods);
+        for (i = 0; i < virtualMethodCount; i++) {
+            freeMethodInnards(&virtualMethods[i]);
+        }
+        dvmLinearReadOnly(clazz->classLoader, virtualMethods);
+        dvmLinearFree(clazz->classLoader, virtualMethods);
+    }
+
+    InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+    loaderList->initiatingLoaderCount = -1;
+    NULL_AND_FREE(loaderList->initiatingLoaders);
+
+    clazz->interfaceCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->interfaces);
+
+    clazz->iftableCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->iftable);
+
+    clazz->ifviPoolCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->ifviPool);
+
+    clazz->sfieldCount = -1;
+    /* The sfields are attached to the ClassObject, and will be freed
+     * with it. */
+
+    clazz->ifieldCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->ifields);
+
+#undef NULL_AND_FREE
+#undef NULL_AND_LINEAR_FREE
+}
+
+/*
+ * Free anything in a Method that was allocated on the system heap.
+ *
+ * The containing class is largely torn down by this point.
+ */
+static void freeMethodInnards(Method* meth)
+{
+#if 0
+    free(meth->exceptions);
+    free(meth->lines);
+    free(meth->locals);
+#endif
+
+    /*
+     * Some register maps are allocated on the heap, either because of late
+     * verification or because we're caching an uncompressed form.
+     */
+    const RegisterMap* pMap = meth->registerMap;
+    if (pMap != NULL && dvmRegisterMapGetOnHeap(pMap)) {
+        dvmFreeRegisterMap((RegisterMap*) pMap);
+        meth->registerMap = NULL;
+    }
+
+    /*
+     * We may have copied the instructions.
+     */
+    if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+        DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+        dvmLinearFree(meth->clazz->classLoader, methodDexCode);
+    }
+}
+
+/*
+ * Clone a Method, making new copies of anything that will be freed up
+ * by freeMethodInnards().  This is used for "miranda" methods.
+ */
+static void cloneMethod(Method* dst, const Method* src)
+{
+    if (src->registerMap != NULL) {
+        ALOGE("GLITCH: only expected abstract methods here");
+        ALOGE("        cloning %s.%s", src->clazz->descriptor, src->name);
+        dvmAbort();
+    }
+    memcpy(dst, src, sizeof(Method));
+}
+
+/*
+ * Pull the interesting pieces out of a DexMethod.
+ *
+ * The DEX file isn't going anywhere, so we don't need to make copies of
+ * the code area.
+ */
+static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,
+    Method* meth)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexMethodId* pMethodId;
+    const DexCode* pDexCode;
+
+    pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+
+    meth->name = dexStringById(pDexFile, pMethodId->nameIdx);
+    dexProtoSetFromMethodId(&meth->prototype, pDexFile, pMethodId);
+    meth->shorty = dexProtoGetShorty(&meth->prototype);
+    meth->accessFlags = pDexMethod->accessFlags;
+    meth->clazz = clazz;
+    meth->jniArgInfo = 0;
+
+    if (dvmCompareNameDescriptorAndMethod("finalize", "()V", meth) == 0) {
+        /*
+         * The Enum class declares a "final" finalize() method to
+         * prevent subclasses from introducing a finalizer.  We don't
+         * want to set the finalizable flag for Enum or its subclasses,
+         * so we check for it here.
+         *
+         * We also want to avoid setting it on Object, but it's easier
+         * to just strip that out later.
+         */
+        if (clazz->classLoader != NULL ||
+            strcmp(clazz->descriptor, "Ljava/lang/Enum;") != 0)
+        {
+            SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+        }
+    }
+
+    pDexCode = dexGetCode(pDexFile, pDexMethod);
+    if (pDexCode != NULL) {
+        /* integer constants, copy over for faster access */
+        meth->registersSize = pDexCode->registersSize;
+        meth->insSize = pDexCode->insSize;
+        meth->outsSize = pDexCode->outsSize;
+
+        /* pointer to code area */
+        meth->insns = pDexCode->insns;
+    } else {
+        /*
+         * We don't have a DexCode block, but we still want to know how
+         * much space is needed for the arguments (so we don't have to
+         * compute it later).  We also take this opportunity to compute
+         * JNI argument info.
+         *
+         * We do this for abstract methods as well, because we want to
+         * be able to substitute our exception-throwing "stub" in.
+         */
+        int argsSize = dvmComputeMethodArgsSize(meth);
+        if (!dvmIsStaticMethod(meth))
+            argsSize++;
+        meth->registersSize = meth->insSize = argsSize;
+        assert(meth->outsSize == 0);
+        assert(meth->insns == NULL);
+
+        if (dvmIsNativeMethod(meth)) {
+            meth->nativeFunc = dvmResolveNativeMethod;
+            meth->jniArgInfo = computeJniArgInfo(&meth->prototype);
+        }
+    }
+}
+
+#if 0       /* replaced with private/read-write mapping */
+/*
+ * We usually map bytecode directly out of the DEX file, which is mapped
+ * shared read-only.  If we want to be able to modify it, we have to make
+ * a new copy.
+ *
+ * Once copied, the code will be in the LinearAlloc region, which may be
+ * marked read-only.
+ *
+ * The bytecode instructions are embedded inside a DexCode structure, so we
+ * need to copy all of that.  (The dvmGetMethodCode function backs up the
+ * instruction pointer to find the start of the DexCode.)
+ */
+void dvmMakeCodeReadWrite(Method* meth)
+{
+    DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+
+    if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+        dvmLinearReadWrite(meth->clazz->classLoader, methodDexCode);
+        return;
+    }
+
+    assert(!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth));
+
+    size_t dexCodeSize = dexGetDexCodeSize(methodDexCode);
+    ALOGD("Making a copy of %s.%s code (%d bytes)",
+        meth->clazz->descriptor, meth->name, dexCodeSize);
+
+    DexCode* newCode =
+        (DexCode*) dvmLinearAlloc(meth->clazz->classLoader, dexCodeSize);
+    memcpy(newCode, methodDexCode, dexCodeSize);
+
+    meth->insns = newCode->insns;
+    SET_METHOD_FLAG(meth, METHOD_ISWRITABLE);
+}
+
+/*
+ * Mark the bytecode read-only.
+ *
+ * If the contents of the DexCode haven't actually changed, we could revert
+ * to the original shared page.
+ */
+void dvmMakeCodeReadOnly(Method* meth)
+{
+    DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+    ALOGV("+++ marking %p read-only", methodDexCode);
+    dvmLinearReadOnly(meth->clazz->classLoader, methodDexCode);
+}
+#endif
+
+
+/*
+ * jniArgInfo (32-bit int) layout:
+ *   SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *   S - if set, do things the hard way (scan the signature)
+ *   R - return-type enumeration
+ *   H - target-specific hints
+ *
+ * This info is used at invocation time by dvmPlatformInvoke.  In most
+ * cases, the target-specific hints allow dvmPlatformInvoke to avoid
+ * having to fully parse the signature.
+ *
+ * The return-type bits are always set, even if target-specific hint bits
+ * are unavailable.
+ */
+static int computeJniArgInfo(const DexProto* proto)
+{
+    const char* sig = dexProtoGetShorty(proto);
+    int returnType, jniArgInfo;
+    u4 hints;
+
+    /* The first shorty character is the return type. */
+    switch (*(sig++)) {
+    case 'V':
+        returnType = DALVIK_JNI_RETURN_VOID;
+        break;
+    case 'F':
+        returnType = DALVIK_JNI_RETURN_FLOAT;
+        break;
+    case 'D':
+        returnType = DALVIK_JNI_RETURN_DOUBLE;
+        break;
+    case 'J':
+        returnType = DALVIK_JNI_RETURN_S8;
+        break;
+    case 'Z':
+    case 'B':
+        returnType = DALVIK_JNI_RETURN_S1;
+        break;
+    case 'C':
+        returnType = DALVIK_JNI_RETURN_U2;
+        break;
+    case 'S':
+        returnType = DALVIK_JNI_RETURN_S2;
+        break;
+    default:
+        returnType = DALVIK_JNI_RETURN_S4;
+        break;
+    }
+
+    jniArgInfo = returnType << DALVIK_JNI_RETURN_SHIFT;
+
+    hints = dvmPlatformInvokeHints(proto);
+
+    if (hints & DALVIK_JNI_NO_ARG_INFO) {
+        jniArgInfo |= DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        assert((hints & DALVIK_JNI_RETURN_MASK) == 0);
+        jniArgInfo |= hints;
+    }
+
+    return jniArgInfo;
+}
+
+/*
+ * Load information about a static field.
+ *
+ * This also "prepares" static fields by initializing them
+ * to their "standard default values".
+ */
+static void loadSFieldFromDex(ClassObject* clazz,
+    const DexField* pDexSField, StaticField* sfield)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexFieldId* pFieldId;
+
+    pFieldId = dexGetFieldId(pDexFile, pDexSField->fieldIdx);
+
+    sfield->clazz = clazz;
+    sfield->name = dexStringById(pDexFile, pFieldId->nameIdx);
+    sfield->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    sfield->accessFlags = pDexSField->accessFlags;
+
+    /* Static object field values are set to "standard default values"
+     * (null or 0) until the class is initialized.  We delay loading
+     * constant values from the class until that time.
+     */
+    //sfield->value.j = 0;
+    assert(sfield->value.j == 0LL);     // cleared earlier with calloc
+}
+
+/*
+ * Load information about an instance field.
+ */
+static void loadIFieldFromDex(ClassObject* clazz,
+    const DexField* pDexIField, InstField* ifield)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexFieldId* pFieldId;
+
+    pFieldId = dexGetFieldId(pDexFile, pDexIField->fieldIdx);
+
+    ifield->clazz = clazz;
+    ifield->name = dexStringById(pDexFile, pFieldId->nameIdx);
+    ifield->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    ifield->accessFlags = pDexIField->accessFlags;
+#ifndef NDEBUG
+    assert(ifield->byteOffset == 0);    // cleared earlier with calloc
+    ifield->byteOffset = -1;    // make it obvious if we fail to set later
+#endif
+}
+
+/*
+ * Cache java.lang.ref.Reference fields and methods.
+ */
+static bool precacheReferenceOffsets(ClassObject* clazz)
+{
+    int i;
+
+    /* We trick the GC object scanner by not counting
+     * java.lang.ref.Reference.referent as an object
+     * field.  It will get explicitly scanned as part
+     * of the reference-walking process.
+     *
+     * Find the object field named "referent" and put it
+     * just after the list of object reference fields.
+     */
+    dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+    for (i = 0; i < clazz->ifieldRefCount; i++) {
+        InstField *pField = &clazz->ifields[i];
+        if (strcmp(pField->name, "referent") == 0) {
+            int targetIndex;
+
+            /* Swap this field with the last object field.
+             */
+            targetIndex = clazz->ifieldRefCount - 1;
+            if (i != targetIndex) {
+                InstField *swapField = &clazz->ifields[targetIndex];
+                InstField tmpField;
+                int tmpByteOffset;
+
+                /* It's not currently strictly necessary
+                 * for the fields to be in byteOffset order,
+                 * but it's more predictable that way.
+                 */
+                tmpByteOffset = swapField->byteOffset;
+                swapField->byteOffset = pField->byteOffset;
+                pField->byteOffset = tmpByteOffset;
+
+                tmpField = *swapField;
+                *swapField = *pField;
+                *pField = tmpField;
+            }
+
+            /* One fewer object field (wink wink).
+             */
+            clazz->ifieldRefCount--;
+            i--;        /* don't trip "didn't find it" test if field was last */
+            break;
+        }
+    }
+    dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+    if (i == clazz->ifieldRefCount) {
+        ALOGE("Unable to reorder 'referent' in %s", clazz->descriptor);
+        return false;
+    }
+
+    /*
+     * Now that the above has been done, it is safe to cache
+     * info about the class.
+     */
+    if (!dvmFindReferenceMembers(clazz)) {
+        ALOGE("Trouble with Reference setup");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Set the bitmap of reference offsets, refOffsets, from the ifields
+ * list.
+ */
+static void computeRefOffsets(ClassObject* clazz)
+{
+    if (clazz->super != NULL) {
+        clazz->refOffsets = clazz->super->refOffsets;
+    } else {
+        clazz->refOffsets = 0;
+    }
+    /*
+     * If our superclass overflowed, we don't stand a chance.
+     */
+    if (clazz->refOffsets != CLASS_WALK_SUPER) {
+        InstField *f;
+        int i;
+
+        /* All of the fields that contain object references
+         * are guaranteed to be at the beginning of the ifields list.
+         */
+        f = clazz->ifields;
+        const int ifieldRefCount = clazz->ifieldRefCount;
+        for (i = 0; i < ifieldRefCount; i++) {
+          /*
+           * Note that, per the comment on struct InstField,
+           * f->byteOffset is the offset from the beginning of
+           * obj, not the offset into obj->instanceData.
+           */
+          assert(f->byteOffset >= (int) CLASS_SMALLEST_OFFSET);
+          assert((f->byteOffset & (CLASS_OFFSET_ALIGNMENT - 1)) == 0);
+          if (CLASS_CAN_ENCODE_OFFSET(f->byteOffset)) {
+              u4 newBit = CLASS_BIT_FROM_OFFSET(f->byteOffset);
+              assert(newBit != 0);
+              clazz->refOffsets |= newBit;
+          } else {
+              clazz->refOffsets = CLASS_WALK_SUPER;
+              break;
+          }
+          f++;
+        }
+    }
+}
+
+
+/*
+ * Link (prepare and resolve).  Verification is deferred until later.
+ *
+ * This converts symbolic references into pointers.  It's independent of
+ * the source file format.
+ *
+ * If clazz->status is CLASS_IDX, then clazz->super and interfaces[] are
+ * holding class reference indices rather than pointers.  The class
+ * references will be resolved during link.  (This is done when
+ * loading from DEX to avoid having to create additional storage to
+ * pass the indices around.)
+ *
+ * Returns "false" with an exception pending on failure.
+ */
+bool dvmLinkClass(ClassObject* clazz)
+{
+    u4 superclassIdx = 0;
+    u4 *interfaceIdxArray = NULL;
+    bool okay = false;
+    int i;
+
+    assert(clazz != NULL);
+    assert(clazz->descriptor != NULL);
+    assert(clazz->status == CLASS_IDX || clazz->status == CLASS_LOADED);
+    if (gDvm.verboseClass)
+        ALOGV("CLASS: linking '%s'...", clazz->descriptor);
+
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(clazz->clazz == gDvm.classJavaLangClass);
+    assert(dvmIsClassObject(clazz));
+    if (clazz->classLoader == NULL &&
+        (strcmp(clazz->descriptor, "Ljava/lang/Class;") == 0))
+    {
+        if (gDvm.classJavaLangClass->ifieldCount > CLASS_FIELD_SLOTS) {
+            ALOGE("java.lang.Class has %d instance fields (expected at most %d)",
+                 gDvm.classJavaLangClass->ifieldCount, CLASS_FIELD_SLOTS);
+            dvmAbort();
+        }
+        if (gDvm.classJavaLangClass->sfieldCount != CLASS_SFIELD_SLOTS) {
+            ALOGE("java.lang.Class has %d static fields (expected %d)",
+                 gDvm.classJavaLangClass->sfieldCount, CLASS_SFIELD_SLOTS);
+            dvmAbort();
+        }
+    }
+
+    /* "Resolve" the class.
+     *
+     * At this point, clazz's reference fields may contain Dex file
+     * indices instead of direct object references.  Proxy objects are
+     * an exception, and may be the only exception.  We need to
+     * translate those indices into real references, and let the GC
+     * look inside this ClassObject.
+     */
+    if (clazz->status == CLASS_IDX) {
+        if (clazz->interfaceCount > 0) {
+            /* Copy u4 DEX idx values out of the ClassObject* array
+             * where we stashed them.
+             */
+            assert(sizeof(*interfaceIdxArray) == sizeof(*clazz->interfaces));
+            size_t len = clazz->interfaceCount * sizeof(*interfaceIdxArray);
+            interfaceIdxArray = (u4*)malloc(len);
+            if (interfaceIdxArray == NULL) {
+                ALOGW("Unable to allocate memory to link %s", clazz->descriptor);
+                goto bail;
+            }
+            memcpy(interfaceIdxArray, clazz->interfaces, len);
+
+            dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
+            memset(clazz->interfaces, 0, len);
+            dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+        }
+
+        assert(sizeof(superclassIdx) == sizeof(clazz->super));
+        superclassIdx = (u4) clazz->super;
+        clazz->super = NULL;
+        /* After this line, clazz will be fair game for the GC. The
+         * superclass and interfaces are all NULL.
+         */
+        clazz->status = CLASS_LOADED;
+
+        if (superclassIdx != kDexNoIndex) {
+            ClassObject* super = dvmResolveClass(clazz, superclassIdx, false);
+            if (super == NULL) {
+                assert(dvmCheckException(dvmThreadSelf()));
+                if (gDvm.optimizing) {
+                    /* happens with "external" libs */
+                    ALOGV("Unable to resolve superclass of %s (%d)",
+                         clazz->descriptor, superclassIdx);
+                } else {
+                    ALOGW("Unable to resolve superclass of %s (%d)",
+                         clazz->descriptor, superclassIdx);
+                }
+                goto bail;
+            }
+            dvmSetFieldObject((Object *)clazz,
+                              OFFSETOF_MEMBER(ClassObject, super),
+                              (Object *)super);
+        }
+
+        if (clazz->interfaceCount > 0) {
+            /* Resolve the interfaces implemented directly by this class. */
+            assert(interfaceIdxArray != NULL);
+            dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
+            for (i = 0; i < clazz->interfaceCount; i++) {
+                assert(interfaceIdxArray[i] != kDexNoIndex);
+                clazz->interfaces[i] =
+                    dvmResolveClass(clazz, interfaceIdxArray[i], false);
+                if (clazz->interfaces[i] == NULL) {
+                    const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+
+                    assert(dvmCheckException(dvmThreadSelf()));
+                    dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+
+                    const char* classDescriptor;
+                    classDescriptor =
+                        dexStringByTypeIdx(pDexFile, interfaceIdxArray[i]);
+                    if (gDvm.optimizing) {
+                        /* happens with "external" libs */
+                        ALOGV("Failed resolving %s interface %d '%s'",
+                             clazz->descriptor, interfaceIdxArray[i],
+                             classDescriptor);
+                    } else {
+                        ALOGI("Failed resolving %s interface %d '%s'",
+                             clazz->descriptor, interfaceIdxArray[i],
+                             classDescriptor);
+                    }
+                    goto bail;
+                }
+
+                /* are we allowed to implement this interface? */
+                if (!dvmCheckClassAccess(clazz, clazz->interfaces[i])) {
+                    dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+                    ALOGW("Interface '%s' is not accessible to '%s'",
+                         clazz->interfaces[i]->descriptor, clazz->descriptor);
+                    dvmThrowIllegalAccessError("interface not accessible");
+                    goto bail;
+                }
+                LOGVV("+++  found interface '%s'",
+                      clazz->interfaces[i]->descriptor);
+            }
+            dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+        }
+    }
+    /*
+     * There are now Class references visible to the GC in super and
+     * interfaces.
+     */
+
+    /*
+     * All classes have a direct superclass, except for
+     * java/lang/Object and primitive classes. Primitive classes are
+     * are created CLASS_INITIALIZED, so won't get here.
+     */
+    assert(clazz->primitiveType == PRIM_NOT);
+    if (strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0) {
+        if (clazz->super != NULL) {
+            /* TODO: is this invariant true for all java/lang/Objects,
+             * regardless of the class loader?  For now, assume it is.
+             */
+            dvmThrowClassFormatError("java.lang.Object has a superclass");
+            goto bail;
+        }
+
+        /* Don't finalize objects whose classes use the
+         * default (empty) Object.finalize().
+         */
+        CLEAR_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+    } else {
+        if (clazz->super == NULL) {
+            dvmThrowLinkageError("no superclass defined");
+            goto bail;
+        }
+        /* verify */
+        if (dvmIsFinalClass(clazz->super)) {
+            ALOGW("Superclass of '%s' is final '%s'",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowIncompatibleClassChangeError("superclass is final");
+            goto bail;
+        } else if (dvmIsInterfaceClass(clazz->super)) {
+            ALOGW("Superclass of '%s' is interface '%s'",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowIncompatibleClassChangeError("superclass is an interface");
+            goto bail;
+        } else if (!dvmCheckClassAccess(clazz, clazz->super)) {
+            ALOGW("Superclass of '%s' (%s) is not accessible",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowIllegalAccessError("superclass not accessible");
+            goto bail;
+        }
+
+        /* Inherit finalizability from the superclass.  If this
+         * class also overrides finalize(), its CLASS_ISFINALIZABLE
+         * bit will already be set.
+         */
+        if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISFINALIZABLE)) {
+            SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+        }
+
+        /* See if this class descends from java.lang.Reference
+         * and set the class flags appropriately.
+         */
+        if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISREFERENCE)) {
+            u4 superRefFlags;
+
+            /* We've already determined the reference type of this
+             * inheritance chain.  Inherit reference-ness from the superclass.
+             */
+            superRefFlags = GET_CLASS_FLAG_GROUP(clazz->super,
+                    CLASS_ISREFERENCE |
+                    CLASS_ISWEAKREFERENCE |
+                    CLASS_ISFINALIZERREFERENCE |
+                    CLASS_ISPHANTOMREFERENCE);
+            SET_CLASS_FLAG(clazz, superRefFlags);
+        } else if (clazz->classLoader == NULL &&
+                clazz->super->classLoader == NULL &&
+                strcmp(clazz->super->descriptor,
+                       "Ljava/lang/ref/Reference;") == 0)
+        {
+            u4 refFlags;
+
+            /* This class extends Reference, which means it should
+             * be one of the magic Soft/Weak/PhantomReference classes.
+             */
+            refFlags = CLASS_ISREFERENCE;
+            if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/SoftReference;") == 0)
+            {
+                /* Only CLASS_ISREFERENCE is set for soft references.
+                 */
+            } else if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/WeakReference;") == 0)
+            {
+                refFlags |= CLASS_ISWEAKREFERENCE;
+            } else if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/FinalizerReference;") == 0)
+            {
+                refFlags |= CLASS_ISFINALIZERREFERENCE;
+            }  else if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/PhantomReference;") == 0)
+            {
+                refFlags |= CLASS_ISPHANTOMREFERENCE;
+            } else {
+                /* No-one else is allowed to inherit directly
+                 * from Reference.
+                 */
+//xxx is this the right exception?  better than an assertion.
+                dvmThrowLinkageError("illegal inheritance from Reference");
+                goto bail;
+            }
+
+            /* The class should not have any reference bits set yet.
+             */
+            assert(GET_CLASS_FLAG_GROUP(clazz,
+                    CLASS_ISREFERENCE |
+                    CLASS_ISWEAKREFERENCE |
+                    CLASS_ISFINALIZERREFERENCE |
+                    CLASS_ISPHANTOMREFERENCE) == 0);
+
+            SET_CLASS_FLAG(clazz, refFlags);
+        }
+    }
+
+    /*
+     * Populate vtable.
+     */
+    if (dvmIsInterfaceClass(clazz)) {
+        /* no vtable; just set the method indices */
+        int count = clazz->virtualMethodCount;
+
+        if (count != (u2) count) {
+            ALOGE("Too many methods (%d) in interface '%s'", count,
+                 clazz->descriptor);
+            goto bail;
+        }
+
+        dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+        for (i = 0; i < count; i++)
+            clazz->virtualMethods[i].methodIndex = (u2) i;
+
+        dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    } else {
+        if (!createVtable(clazz)) {
+            ALOGW("failed creating vtable");
+            goto bail;
+        }
+    }
+
+    /*
+     * Populate interface method tables.  Can alter the vtable.
+     */
+    if (!createIftable(clazz))
+        goto bail;
+
+    /*
+     * Insert special-purpose "stub" method implementations.
+     */
+    if (!insertMethodStubs(clazz))
+        goto bail;
+
+    /*
+     * Compute instance field offsets and, hence, the size of the object.
+     */
+    if (!computeFieldOffsets(clazz))
+        goto bail;
+
+    /*
+     * Cache field and method info for the class Reference (as loaded
+     * by the boot classloader). This has to happen after the call to
+     * computeFieldOffsets().
+     */
+    if ((clazz->classLoader == NULL)
+            && (strcmp(clazz->descriptor, "Ljava/lang/ref/Reference;") == 0)) {
+        if (!precacheReferenceOffsets(clazz)) {
+            ALOGE("failed pre-caching Reference offsets");
+            dvmThrowInternalError(NULL);
+            goto bail;
+        }
+    }
+
+    /*
+     * Compact the offsets the GC has to examine into a bitmap, if
+     * possible.  (This has to happen after Reference.referent is
+     * massaged in precacheReferenceOffsets.)
+     */
+    computeRefOffsets(clazz);
+
+    /*
+     * Done!
+     */
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED))
+        clazz->status = CLASS_VERIFIED;
+    else
+        clazz->status = CLASS_RESOLVED;
+    okay = true;
+    if (gDvm.verboseClass)
+        ALOGV("CLASS: linked '%s'", clazz->descriptor);
+
+    /*
+     * We send CLASS_PREPARE events to the debugger from here.  The
+     * definition of "preparation" is creating the static fields for a
+     * class and initializing them to the standard default values, but not
+     * executing any code (that comes later, during "initialization").
+     *
+     * We did the static prep in loadSFieldFromDex() while loading the class.
+     *
+     * The class has been prepared and resolved but possibly not yet verified
+     * at this point.
+     */
+    if (gDvm.debuggerActive) {
+        dvmDbgPostClassPrepare(clazz);
+    }
+
+bail:
+    if (!okay) {
+        clazz->status = CLASS_ERROR;
+        if (!dvmCheckException(dvmThreadSelf())) {
+            dvmThrowVirtualMachineError(NULL);
+        }
+    }
+    if (interfaceIdxArray != NULL) {
+        free(interfaceIdxArray);
+    }
+
+    return okay;
+}
+
+/*
+ * Create the virtual method table.
+ *
+ * The top part of the table is a copy of the table from our superclass,
+ * with our local methods overriding theirs.  The bottom part of the table
+ * has any new methods we defined.
+ */
+static bool createVtable(ClassObject* clazz)
+{
+    bool result = false;
+    int maxCount;
+    int i;
+
+    if (clazz->super != NULL) {
+        //ALOGI("SUPER METHODS %d %s->%s", clazz->super->vtableCount,
+        //    clazz->descriptor, clazz->super->descriptor);
+    }
+
+    /* the virtual methods we define, plus the superclass vtable size */
+    maxCount = clazz->virtualMethodCount;
+    if (clazz->super != NULL) {
+        maxCount += clazz->super->vtableCount;
+    } else {
+        /* TODO: is this invariant true for all java/lang/Objects,
+         * regardless of the class loader?  For now, assume it is.
+         */
+        assert(strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0);
+    }
+    //ALOGD("+++ max vmethods for '%s' is %d", clazz->descriptor, maxCount);
+
+    /*
+     * Over-allocate the table, then realloc it down if necessary.  So
+     * long as we don't allocate anything in between we won't cause
+     * fragmentation, and reducing the size should be unlikely to cause
+     * a buffer copy.
+     */
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+    clazz->vtable = (Method**) dvmLinearAlloc(clazz->classLoader,
+                        sizeof(Method*) * maxCount);
+    if (clazz->vtable == NULL)
+        goto bail;
+
+    if (clazz->super != NULL) {
+        int actualCount;
+
+        memcpy(clazz->vtable, clazz->super->vtable,
+            sizeof(*(clazz->vtable)) * clazz->super->vtableCount);
+        actualCount = clazz->super->vtableCount;
+
+        /*
+         * See if any of our virtual methods override the superclass.
+         */
+        for (i = 0; i < clazz->virtualMethodCount; i++) {
+            Method* localMeth = &clazz->virtualMethods[i];
+            int si;
+
+            for (si = 0; si < clazz->super->vtableCount; si++) {
+                Method* superMeth = clazz->vtable[si];
+
+                if (dvmCompareMethodNamesAndProtos(localMeth, superMeth) == 0) {
+                    // We should have an access check here, but some apps rely on us not
+                    // checking access: http://b/7301030
+                    bool isAccessible = dvmCheckMethodAccess(clazz, superMeth);
+                    if (dvmIsFinalMethod(superMeth)) {
+                        ALOGE("Method %s.%s overrides final %s.%s",
+                              localMeth->clazz->descriptor, localMeth->name,
+                              superMeth->clazz->descriptor, superMeth->name);
+                        goto bail;
+                    }
+
+                    // Warn if we just spotted code relying on this bug...
+                    if (!isAccessible) {
+                        ALOGW("method %s.%s incorrectly overrides "
+                              "package-private method with same name in %s",
+                              localMeth->clazz->descriptor, localMeth->name,
+                              superMeth->clazz->descriptor);
+                    }
+
+                    clazz->vtable[si] = localMeth;
+                    localMeth->methodIndex = (u2) si;
+                    //ALOGV("+++   override %s.%s (slot %d)",
+                    //    clazz->descriptor, localMeth->name, si);
+                    break;
+                }
+            }
+
+            if (si == clazz->super->vtableCount) {
+                /* not an override, add to end */
+                clazz->vtable[actualCount] = localMeth;
+                localMeth->methodIndex = (u2) actualCount;
+                actualCount++;
+
+                //ALOGV("+++   add method %s.%s",
+                //    clazz->descriptor, localMeth->name);
+            }
+        }
+
+        if (actualCount != (u2) actualCount) {
+            ALOGE("Too many methods (%d) in class '%s'", actualCount,
+                 clazz->descriptor);
+            goto bail;
+        }
+
+        assert(actualCount <= maxCount);
+
+        if (actualCount < maxCount) {
+            assert(clazz->vtable != NULL);
+            dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+            clazz->vtable = (Method **)dvmLinearRealloc(clazz->classLoader,
+                clazz->vtable, sizeof(*(clazz->vtable)) * actualCount);
+            if (clazz->vtable == NULL) {
+                ALOGE("vtable realloc failed");
+                goto bail;
+            } else {
+                LOGVV("+++  reduced vtable from %d to %d",
+                    maxCount, actualCount);
+            }
+        }
+
+        clazz->vtableCount = actualCount;
+    } else {
+        /* java/lang/Object case */
+        int count = clazz->virtualMethodCount;
+        if (count != (u2) count) {
+            ALOGE("Too many methods (%d) in base class '%s'", count,
+                 clazz->descriptor);
+            goto bail;
+        }
+
+        for (i = 0; i < count; i++) {
+            clazz->vtable[i] = &clazz->virtualMethods[i];
+            clazz->virtualMethods[i].methodIndex = (u2) i;
+        }
+        clazz->vtableCount = clazz->virtualMethodCount;
+    }
+
+    result = true;
+
+bail:
+    dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    return result;
+}
+
+/*
+ * Create and populate "iftable".
+ *
+ * The set of interfaces we support is the combination of the interfaces
+ * we implement directly and those implemented by our superclass.  Each
+ * interface can have one or more "superinterfaces", which we must also
+ * support.  For speed we flatten the tree out.
+ *
+ * We might be able to speed this up when there are lots of interfaces
+ * by merge-sorting the class pointers and binary-searching when removing
+ * duplicates.  We could also drop the duplicate removal -- it's only
+ * there to reduce the memory footprint.
+ *
+ * Because of "Miranda methods", this may reallocate clazz->virtualMethods.
+ *
+ * Returns "true" on success.
+ */
+static bool createIftable(ClassObject* clazz)
+{
+    bool result = false;
+    bool zapIftable = false;
+    bool zapVtable = false;
+    bool zapIfvipool = false;
+    int poolOffset = 0, poolSize = 0;
+    Method** mirandaList = NULL;
+    int mirandaCount = 0, mirandaAlloc = 0;
+
+    int superIfCount;
+    if (clazz->super != NULL)
+        superIfCount = clazz->super->iftableCount;
+    else
+        superIfCount = 0;
+
+    int ifCount = superIfCount;
+    ifCount += clazz->interfaceCount;
+    for (int i = 0; i < clazz->interfaceCount; i++)
+        ifCount += clazz->interfaces[i]->iftableCount;
+
+    LOGVV("INTF: class '%s' direct w/supra=%d super=%d total=%d",
+        clazz->descriptor, ifCount - superIfCount, superIfCount, ifCount);
+
+    if (ifCount == 0) {
+        assert(clazz->iftableCount == 0);
+        assert(clazz->iftable == NULL);
+        return true;
+    }
+
+    /*
+     * Create a table with enough space for all interfaces, and copy the
+     * superclass' table in.
+     */
+    clazz->iftable = (InterfaceEntry*) dvmLinearAlloc(clazz->classLoader,
+                        sizeof(InterfaceEntry) * ifCount);
+    zapIftable = true;
+    memset(clazz->iftable, 0x00, sizeof(InterfaceEntry) * ifCount);
+    if (superIfCount != 0) {
+        memcpy(clazz->iftable, clazz->super->iftable,
+            sizeof(InterfaceEntry) * superIfCount);
+    }
+
+    /*
+     * Create a flattened interface hierarchy of our immediate interfaces.
+     */
+    int idx = superIfCount;
+
+    for (int i = 0; i < clazz->interfaceCount; i++) {
+        ClassObject* interf = clazz->interfaces[i];
+        assert(interf != NULL);
+
+        /* make sure this is still an interface class */
+        if (!dvmIsInterfaceClass(interf)) {
+            ALOGW("Class '%s' implements non-interface '%s'",
+                clazz->descriptor, interf->descriptor);
+            dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+                clazz->descriptor);
+            goto bail;
+        }
+
+        /* add entry for this interface */
+        clazz->iftable[idx++].clazz = interf;
+
+        /* add entries for the interface's superinterfaces */
+        for (int j = 0; j < interf->iftableCount; j++) {
+            int k;
+            ClassObject *cand;
+
+            cand = interf->iftable[j].clazz;
+
+            /*
+             * Check if this interface was already added and add only if new.
+             * This is to avoid a potential blowup in the number of
+             * interfaces for sufficiently complicated interface hierarchies.
+             * This has quadratic runtime in the number of interfaces.
+             * However, in common cases with little interface inheritance, this
+             * doesn't make much of a difference.
+             */
+            for (k = 0; k < idx; k++)
+                if (clazz->iftable[k].clazz == cand)
+                    break;
+
+            if (k == idx)
+                clazz->iftable[idx++].clazz = cand;
+        }
+    }
+
+    assert(idx <= ifCount);
+
+    /*
+     * Adjust the ifCount. We could reallocate the interface memory here,
+     * but it's probably not worth the effort, the important thing here
+     * is to avoid the interface blowup and keep the ifCount low.
+     */
+    if (false) {
+        if (idx != ifCount) {
+            int newIfCount = idx;
+            InterfaceEntry* oldmem = clazz->iftable;
+
+            clazz->iftable = (InterfaceEntry*) dvmLinearAlloc(clazz->classLoader,
+                            sizeof(InterfaceEntry) * newIfCount);
+            memcpy(clazz->iftable, oldmem, sizeof(InterfaceEntry) * newIfCount);
+            dvmLinearFree(clazz->classLoader, oldmem);
+        }
+    }
+
+    ifCount = idx;
+    clazz->iftableCount = ifCount;
+
+    /*
+     * If we're an interface, we don't need the vtable pointers, so
+     * we're done.  If this class doesn't implement an interface that our
+     * superclass doesn't have, then we again have nothing to do.
+     */
+    if (dvmIsInterfaceClass(clazz) || superIfCount == ifCount) {
+        //dvmDumpClass(clazz, kDumpClassFullDetail);
+        result = true;
+        goto bail;
+    }
+
+    /*
+     * When we're handling invokeinterface, we probably have an object
+     * whose type is an interface class rather than a concrete class.  We
+     * need to convert the method reference into a vtable index.  So, for
+     * every entry in "iftable", we create a list of vtable indices.
+     *
+     * Because our vtable encompasses the superclass vtable, we can use
+     * the vtable indices from our superclass for all of the interfaces
+     * that weren't directly implemented by us.
+     *
+     * Each entry in "iftable" has a pointer to the start of its set of
+     * vtable offsets.  The iftable entries in the superclass point to
+     * storage allocated in the superclass, and the iftable entries added
+     * for this class point to storage allocated in this class.  "iftable"
+     * is flat for fast access in a class and all of its subclasses, but
+     * "ifviPool" is only created for the topmost implementor.
+     */
+    for (int i = superIfCount; i < ifCount; i++) {
+        /*
+         * Note it's valid for an interface to have no methods (e.g.
+         * java/io/Serializable).
+         */
+        LOGVV("INTF: pool: %d from %s",
+            clazz->iftable[i].clazz->virtualMethodCount,
+            clazz->iftable[i].clazz->descriptor);
+        poolSize += clazz->iftable[i].clazz->virtualMethodCount;
+    }
+
+    if (poolSize == 0) {
+        LOGVV("INTF: didn't find any new interfaces with methods");
+        result = true;
+        goto bail;
+    }
+
+    clazz->ifviPoolCount = poolSize;
+    clazz->ifviPool = (int*) dvmLinearAlloc(clazz->classLoader,
+                        poolSize * sizeof(int*));
+    zapIfvipool = true;
+
+    /*
+     * Fill in the vtable offsets for the interfaces that weren't part of
+     * our superclass.
+     */
+    for (int i = superIfCount; i < ifCount; i++) {
+        ClassObject* interface;
+        int methIdx;
+
+        clazz->iftable[i].methodIndexArray = clazz->ifviPool + poolOffset;
+        interface = clazz->iftable[i].clazz;
+        poolOffset += interface->virtualMethodCount;    // end here
+
+        /*
+         * For each method listed in the interface's method list, find the
+         * matching method in our class's method list.  We want to favor the
+         * subclass over the superclass, which just requires walking
+         * back from the end of the vtable.  (This only matters if the
+         * superclass defines a private method and this class redefines
+         * it -- otherwise it would use the same vtable slot.  In Dalvik
+         * those don't end up in the virtual method table, so it shouldn't
+         * matter which direction we go.  We walk it backward anyway.)
+         *
+         *
+         * Suppose we have the following arrangement:
+         *   public interface MyInterface
+         *     public boolean inInterface();
+         *   public abstract class MirandaAbstract implements MirandaInterface
+         *     //public abstract boolean inInterface(); // not declared!
+         *     public boolean inAbstract() { stuff }    // in vtable
+         *   public class MirandClass extends MirandaAbstract
+         *     public boolean inInterface() { stuff }
+         *     public boolean inAbstract() { stuff }    // in vtable
+         *
+         * The javac compiler happily compiles MirandaAbstract even though
+         * it doesn't declare all methods from its interface.  When we try
+         * to set up a vtable for MirandaAbstract, we find that we don't
+         * have an slot for inInterface.  To prevent this, we synthesize
+         * abstract method declarations in MirandaAbstract.
+         *
+         * We have to expand vtable and update some things that point at it,
+         * so we accumulate the method list and do it all at once below.
+         */
+        for (methIdx = 0; methIdx < interface->virtualMethodCount; methIdx++) {
+            Method* imeth = &interface->virtualMethods[methIdx];
+            int j;
+
+            IF_LOGVV() {
+                char* desc = dexProtoCopyMethodDescriptor(&imeth->prototype);
+                LOGVV("INTF:  matching '%s' '%s'", imeth->name, desc);
+                free(desc);
+            }
+
+            for (j = clazz->vtableCount-1; j >= 0; j--) {
+                if (dvmCompareMethodNamesAndProtos(imeth, clazz->vtable[j])
+                    == 0)
+                {
+                    LOGVV("INTF:   matched at %d", j);
+                    if (!dvmIsAbstractMethod(clazz->vtable[j]) &&
+                        !dvmIsPublicMethod(clazz->vtable[j]))
+                    {
+                        ALOGW("Implementation of %s.%s is not public",
+                            clazz->descriptor, clazz->vtable[j]->name);
+                        dvmThrowIllegalAccessError(
+                            "interface implementation not public");
+                        goto bail;
+                    }
+                    clazz->iftable[i].methodIndexArray[methIdx] = j;
+                    break;
+                }
+            }
+            if (j < 0) {
+                IF_ALOGV() {
+                    char* desc =
+                        dexProtoCopyMethodDescriptor(&imeth->prototype);
+                    ALOGV("No match for '%s' '%s' in '%s' (creating miranda)",
+                            imeth->name, desc, clazz->descriptor);
+                    free(desc);
+                }
+                //dvmThrowRuntimeException("Miranda!");
+                //return false;
+
+                if (mirandaCount == mirandaAlloc) {
+                    mirandaAlloc += 8;
+                    if (mirandaList == NULL) {
+                        mirandaList = (Method**)dvmLinearAlloc(
+                                        clazz->classLoader,
+                                        mirandaAlloc * sizeof(Method*));
+                    } else {
+                        dvmLinearReadOnly(clazz->classLoader, mirandaList);
+                        mirandaList = (Method**)dvmLinearRealloc(
+                                clazz->classLoader,
+                                mirandaList, mirandaAlloc * sizeof(Method*));
+                    }
+                    assert(mirandaList != NULL);    // mem failed + we leaked
+                }
+
+                /*
+                 * These may be redundant (e.g. method with same name and
+                 * signature declared in two interfaces implemented by the
+                 * same abstract class).  We can squeeze the duplicates
+                 * out here.
+                 */
+                int mir;
+                for (mir = 0; mir < mirandaCount; mir++) {
+                    if (dvmCompareMethodNamesAndProtos(
+                            mirandaList[mir], imeth) == 0)
+                    {
+                        IF_LOGVV() {
+                            char* desc = dexProtoCopyMethodDescriptor(
+                                    &imeth->prototype);
+                            LOGVV("MIRANDA dupe: %s and %s %s%s",
+                                mirandaList[mir]->clazz->descriptor,
+                                imeth->clazz->descriptor,
+                                imeth->name, desc);
+                            free(desc);
+                        }
+                        break;
+                    }
+                }
+
+                /* point the iftable at a phantom slot index */
+                clazz->iftable[i].methodIndexArray[methIdx] =
+                    clazz->vtableCount + mir;
+                LOGVV("MIRANDA: %s points at slot %d",
+                    imeth->name, clazz->vtableCount + mir);
+
+                /* if non-duplicate among Mirandas, add to Miranda list */
+                if (mir == mirandaCount) {
+                    //ALOGV("MIRANDA: holding '%s' in slot %d",
+                    //    imeth->name, mir);
+                    mirandaList[mirandaCount++] = imeth;
+                }
+            }
+        }
+    }
+
+    if (mirandaCount != 0) {
+        static const int kManyMirandas = 150;   /* arbitrary */
+        Method* newVirtualMethods;
+        Method* meth;
+        int oldMethodCount, oldVtableCount;
+
+        for (int i = 0; i < mirandaCount; i++) {
+            LOGVV("MIRANDA %d: %s.%s", i,
+                mirandaList[i]->clazz->descriptor, mirandaList[i]->name);
+        }
+        if (mirandaCount > kManyMirandas) {
+            /*
+             * Some obfuscators like to create an interface with a huge
+             * pile of methods, declare classes as implementing it, and then
+             * only define a couple of methods.  This leads to a rather
+             * massive collection of Miranda methods and a lot of wasted
+             * space, sometimes enough to blow out the LinearAlloc cap.
+             */
+            ALOGD("Note: class %s has %d unimplemented (abstract) methods",
+                clazz->descriptor, mirandaCount);
+        }
+
+        /*
+         * We found methods in one or more interfaces for which we do not
+         * have vtable entries.  We have to expand our virtualMethods
+         * table (which might be empty) to hold some new entries.
+         */
+        if (clazz->virtualMethods == NULL) {
+            newVirtualMethods = (Method*) dvmLinearAlloc(clazz->classLoader,
+                sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+        } else {
+            //dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+            newVirtualMethods = (Method*) dvmLinearRealloc(clazz->classLoader,
+                clazz->virtualMethods,
+                sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+        }
+        if (newVirtualMethods != clazz->virtualMethods) {
+            /*
+             * Table was moved in memory.  We have to run through the
+             * vtable and fix the pointers.  The vtable entries might be
+             * pointing at superclasses, so we flip it around: run through
+             * all locally-defined virtual methods, and fix their entries
+             * in the vtable.  (This would get really messy if sub-classes
+             * had already been loaded.)
+             *
+             * Reminder: clazz->virtualMethods and clazz->virtualMethodCount
+             * hold the virtual methods declared by this class.  The
+             * method's methodIndex is the vtable index, and is the same
+             * for all sub-classes (and all super classes in which it is
+             * defined).  We're messing with these because the Miranda
+             * stuff makes it look like the class actually has an abstract
+             * method declaration in it.
+             */
+            LOGVV("MIRANDA fixing vtable pointers");
+            dvmLinearReadWrite(clazz->classLoader, clazz->vtable);
+            Method* meth = newVirtualMethods;
+            for (int i = 0; i < clazz->virtualMethodCount; i++, meth++)
+                clazz->vtable[meth->methodIndex] = meth;
+            dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+        }
+
+        oldMethodCount = clazz->virtualMethodCount;
+        clazz->virtualMethods = newVirtualMethods;
+        clazz->virtualMethodCount += mirandaCount;
+
+        dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+        /*
+         * We also have to expand the vtable.
+         */
+        assert(clazz->vtable != NULL);
+        clazz->vtable = (Method**) dvmLinearRealloc(clazz->classLoader,
+                        clazz->vtable,
+                        sizeof(Method*) * (clazz->vtableCount + mirandaCount));
+        if (clazz->vtable == NULL) {
+            assert(false);
+            goto bail;
+        }
+        zapVtable = true;
+
+        oldVtableCount = clazz->vtableCount;
+        clazz->vtableCount += mirandaCount;
+
+        /*
+         * Now we need to create the fake methods.  We clone the abstract
+         * method definition from the interface and then replace a few
+         * things.
+         *
+         * The Method will be an "abstract native", with nativeFunc set to
+         * dvmAbstractMethodStub().
+         */
+        meth = clazz->virtualMethods + oldMethodCount;
+        for (int i = 0; i < mirandaCount; i++, meth++) {
+            dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+            cloneMethod(meth, mirandaList[i]);
+            meth->clazz = clazz;
+            meth->accessFlags |= ACC_MIRANDA;
+            meth->methodIndex = (u2) (oldVtableCount + i);
+            dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+            /* point the new vtable entry at the new method */
+            clazz->vtable[oldVtableCount + i] = meth;
+        }
+
+        dvmLinearReadOnly(clazz->classLoader, mirandaList);
+        dvmLinearFree(clazz->classLoader, mirandaList);
+
+    }
+
+    /*
+     * TODO?
+     * Sort the interfaces by number of declared methods.  All we really
+     * want is to get the interfaces with zero methods at the end of the
+     * list, so that when we walk through the list during invoke-interface
+     * we don't examine interfaces that can't possibly be useful.
+     *
+     * The set will usually be small, so a simple insertion sort works.
+     *
+     * We have to be careful not to change the order of two interfaces
+     * that define the same method.  (Not a problem if we only move the
+     * zero-method interfaces to the end.)
+     *
+     * PROBLEM:
+     * If we do this, we will no longer be able to identify super vs.
+     * current class interfaces by comparing clazz->super->iftableCount.  This
+     * breaks anything that only wants to find interfaces declared directly
+     * by the class (dvmFindStaticFieldHier, ReferenceType.Interfaces,
+     * dvmDbgOutputAllInterfaces, etc).  Need to provide a workaround.
+     *
+     * We can sort just the interfaces implemented directly by this class,
+     * but that doesn't seem like it would provide much of an advantage.  I'm
+     * not sure this is worthwhile.
+     *
+     * (This has been made largely obsolete by the interface cache mechanism.)
+     */
+
+    //dvmDumpClass(clazz);
+
+    result = true;
+
+bail:
+    if (zapIftable)
+        dvmLinearReadOnly(clazz->classLoader, clazz->iftable);
+    if (zapVtable)
+        dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+    if (zapIfvipool)
+        dvmLinearReadOnly(clazz->classLoader, clazz->ifviPool);
+    return result;
+}
+
+
+/*
+ * Provide "stub" implementations for methods without them.
+ *
+ * Currently we provide an implementation for all abstract methods that
+ * throws an AbstractMethodError exception.  This allows us to avoid an
+ * explicit check for abstract methods in every virtual call.
+ *
+ * NOTE: for Miranda methods, the method declaration is a clone of what
+ * was found in the interface class.  That copy may already have had the
+ * function pointer filled in, so don't be surprised if it's not NULL.
+ *
+ * NOTE: this sets the "native" flag, giving us an "abstract native" method,
+ * which is nonsensical.  Need to make sure that this doesn't escape the
+ * VM.  We can either mask it out in reflection calls, or copy "native"
+ * into the high 16 bits of accessFlags and check that internally.
+ */
+static bool insertMethodStubs(ClassObject* clazz)
+{
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+    Method* meth;
+    int i;
+
+    meth = clazz->virtualMethods;
+    for (i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+        if (dvmIsAbstractMethod(meth)) {
+            assert(meth->insns == NULL);
+            assert(meth->nativeFunc == NULL ||
+                meth->nativeFunc == (DalvikBridgeFunc)dvmAbstractMethodStub);
+
+            meth->accessFlags |= ACC_NATIVE;
+            meth->nativeFunc = (DalvikBridgeFunc) dvmAbstractMethodStub;
+        }
+    }
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    return true;
+}
+
+
+/*
+ * Swap two instance fields.
+ */
+static inline void swapField(InstField* pOne, InstField* pTwo)
+{
+    InstField swap;
+
+    LOGVV("  --- swap '%s' and '%s'", pOne->name, pTwo->name);
+    swap = *pOne;
+    *pOne = *pTwo;
+    *pTwo = swap;
+}
+
+/*
+ * Assign instance fields to u4 slots.
+ *
+ * The top portion of the instance field area is occupied by the superclass
+ * fields, the bottom by the fields for this class.
+ *
+ * "long" and "double" fields occupy two adjacent slots.  On some
+ * architectures, 64-bit quantities must be 64-bit aligned, so we need to
+ * arrange fields (or introduce padding) to ensure this.  We assume the
+ * fields of the topmost superclass (i.e. Object) are 64-bit aligned, so
+ * we can just ensure that the offset is "even".  To avoid wasting space,
+ * we want to move non-reference 32-bit fields into gaps rather than
+ * creating pad words.
+ *
+ * In the worst case we will waste 4 bytes, but because objects are
+ * allocated on >= 64-bit boundaries, those bytes may well be wasted anyway
+ * (assuming this is the most-derived class).
+ *
+ * Pad words are not represented in the field table, so the field table
+ * itself does not change size.
+ *
+ * The number of field slots determines the size of the object, so we
+ * set that here too.
+ *
+ * This function feels a little more complicated than I'd like, but it
+ * has the property of moving the smallest possible set of fields, which
+ * should reduce the time required to load a class.
+ *
+ * NOTE: reference fields *must* come first, or precacheReferenceOffsets()
+ * will break.
+ */
+static bool computeFieldOffsets(ClassObject* clazz)
+{
+    int fieldOffset;
+    int i, j;
+
+    dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+
+    if (clazz->super != NULL)
+        fieldOffset = clazz->super->objectSize;
+    else
+        fieldOffset = OFFSETOF_MEMBER(DataObject, instanceData);
+
+    LOGVV("--- computeFieldOffsets '%s'", clazz->descriptor);
+
+    //ALOGI("OFFSETS fieldCount=%d", clazz->ifieldCount);
+    //ALOGI("dataobj, instance: %d", offsetof(DataObject, instanceData));
+    //ALOGI("classobj, access: %d", offsetof(ClassObject, accessFlags));
+    //ALOGI("super=%p, fieldOffset=%d", clazz->super, fieldOffset);
+
+    /*
+     * Start by moving all reference fields to the front.
+     */
+    clazz->ifieldRefCount = 0;
+    j = clazz->ifieldCount - 1;
+    for (i = 0; i < clazz->ifieldCount; i++) {
+        InstField* pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c != '[' && c != 'L') {
+            /* This isn't a reference field; see if any reference fields
+             * follow this one.  If so, we'll move it to this position.
+             * (quicksort-style partitioning)
+             */
+            while (j > i) {
+                InstField* refField = &clazz->ifields[j--];
+                char rc = refField->signature[0];
+
+                if (rc == '[' || rc == 'L') {
+                    /* Here's a reference field that follows at least one
+                     * non-reference field.  Swap it with the current field.
+                     * (When this returns, "pField" points to the reference
+                     * field, and "refField" points to the non-ref field.)
+                     */
+                    swapField(pField, refField);
+
+                    /* Fix the signature.
+                     */
+                    c = rc;
+
+                    clazz->ifieldRefCount++;
+                    break;
+                }
+            }
+            /* We may or may not have swapped a field.
+             */
+        } else {
+            /* This is a reference field.
+             */
+            clazz->ifieldRefCount++;
+        }
+
+        /*
+         * If we've hit the end of the reference fields, break.
+         */
+        if (c != '[' && c != 'L')
+            break;
+
+        pField->byteOffset = fieldOffset;
+        fieldOffset += sizeof(u4);
+        LOGVV("  --- offset1 '%s'=%d", pField->name,pField->byteOffset);
+    }
+
+    /*
+     * Now we want to pack all of the double-wide fields together.  If we're
+     * not aligned, though, we want to shuffle one 32-bit field into place.
+     * If we can't find one, we'll have to pad it.
+     */
+    if (i != clazz->ifieldCount && (fieldOffset & 0x04) != 0) {
+        LOGVV("  +++ not aligned");
+
+        InstField* pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c != 'J' && c != 'D') {
+            /*
+             * The field that comes next is 32-bit, so just advance past it.
+             */
+            assert(c != '[' && c != 'L');
+            pField->byteOffset = fieldOffset;
+            fieldOffset += sizeof(u4);
+            i++;
+            LOGVV("  --- offset2 '%s'=%d",
+                pField->name, pField->byteOffset);
+        } else {
+            /*
+             * Next field is 64-bit, so search for a 32-bit field we can
+             * swap into it.
+             */
+            bool found = false;
+            j = clazz->ifieldCount - 1;
+            while (j > i) {
+                InstField* singleField = &clazz->ifields[j--];
+                char rc = singleField->signature[0];
+
+                if (rc != 'J' && rc != 'D') {
+                    swapField(pField, singleField);
+                    //c = rc;
+                    LOGVV("  +++ swapped '%s' for alignment",
+                        pField->name);
+                    pField->byteOffset = fieldOffset;
+                    fieldOffset += sizeof(u4);
+                    LOGVV("  --- offset3 '%s'=%d",
+                        pField->name, pField->byteOffset);
+                    found = true;
+                    i++;
+                    break;
+                }
+            }
+            if (!found) {
+                ALOGV("  +++ inserting pad field in '%s'", clazz->descriptor);
+                fieldOffset += sizeof(u4);
+            }
+        }
+    }
+
+    /*
+     * Alignment is good, shuffle any double-wide fields forward, and
+     * finish assigning field offsets to all fields.
+     */
+    assert(i == clazz->ifieldCount || (fieldOffset & 0x04) == 0);
+    j = clazz->ifieldCount - 1;
+    for ( ; i < clazz->ifieldCount; i++) {
+        InstField* pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c != 'D' && c != 'J') {
+            /* This isn't a double-wide field; see if any double fields
+             * follow this one.  If so, we'll move it to this position.
+             * (quicksort-style partitioning)
+             */
+            while (j > i) {
+                InstField* doubleField = &clazz->ifields[j--];
+                char rc = doubleField->signature[0];
+
+                if (rc == 'D' || rc == 'J') {
+                    /* Here's a double-wide field that follows at least one
+                     * non-double field.  Swap it with the current field.
+                     * (When this returns, "pField" points to the reference
+                     * field, and "doubleField" points to the non-double field.)
+                     */
+                    swapField(pField, doubleField);
+                    c = rc;
+
+                    break;
+                }
+            }
+            /* We may or may not have swapped a field.
+             */
+        } else {
+            /* This is a double-wide field, leave it be.
+             */
+        }
+
+        pField->byteOffset = fieldOffset;
+        LOGVV("  --- offset4 '%s'=%d", pField->name,pField->byteOffset);
+        fieldOffset += sizeof(u4);
+        if (c == 'J' || c == 'D')
+            fieldOffset += sizeof(u4);
+    }
+
+#ifndef NDEBUG
+    /* Make sure that all reference fields appear before
+     * non-reference fields, and all double-wide fields are aligned.
+     */
+    j = 0;  // seen non-ref
+    for (i = 0; i < clazz->ifieldCount; i++) {
+        InstField *pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c == 'D' || c == 'J') {
+            assert((pField->byteOffset & 0x07) == 0);
+        }
+
+        if (c != '[' && c != 'L') {
+            if (!j) {
+                assert(i == clazz->ifieldRefCount);
+                j = 1;
+            }
+        } else if (j) {
+            assert(false);
+        }
+    }
+    if (!j) {
+        assert(clazz->ifieldRefCount == clazz->ifieldCount);
+    }
+#endif
+
+    /*
+     * We map a C struct directly on top of java/lang/Class objects.  Make
+     * sure we left enough room for the instance fields.
+     */
+    assert(!dvmIsTheClassClass(clazz) || (size_t)fieldOffset <
+        OFFSETOF_MEMBER(ClassObject, instanceData) + sizeof(clazz->instanceData));
+
+    clazz->objectSize = fieldOffset;
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+    return true;
+}
+
+/*
+ * The class failed to initialize on a previous attempt, so we want to throw
+ * a NoClassDefFoundError (v2 2.17.5).  The exception to this rule is if we
+ * failed in verification, in which case v2 5.4.1 says we need to re-throw
+ * the previous error.
+ */
+static void throwEarlierClassFailure(ClassObject* clazz)
+{
+    ALOGI("Rejecting re-init on previously-failed class %s v=%p",
+        clazz->descriptor, clazz->verifyErrorClass);
+
+    if (clazz->verifyErrorClass == NULL) {
+        dvmThrowNoClassDefFoundError(clazz->descriptor);
+    } else {
+        dvmThrowExceptionWithClassMessage(clazz->verifyErrorClass,
+            clazz->descriptor);
+    }
+}
+
+/*
+ * Initialize any static fields whose values are stored in
+ * the DEX file.  This must be done during class initialization.
+ */
+static void initSFields(ClassObject* clazz)
+{
+    Thread* self = dvmThreadSelf(); /* for dvmReleaseTrackedAlloc() */
+    DexFile* pDexFile;
+    const DexClassDef* pClassDef;
+    const DexEncodedArray* pValueList;
+    EncodedArrayIterator iterator;
+    int i;
+
+    if (clazz->sfieldCount == 0) {
+        return;
+    }
+    if (clazz->pDvmDex == NULL) {
+        /* generated class; any static fields should already be set up */
+        ALOGV("Not initializing static fields in %s", clazz->descriptor);
+        return;
+    }
+    pDexFile = clazz->pDvmDex->pDexFile;
+
+    pClassDef = dexFindClass(pDexFile, clazz->descriptor);
+    assert(pClassDef != NULL);
+
+    pValueList = dexGetStaticValuesList(pDexFile, pClassDef);
+    if (pValueList == NULL) {
+        return;
+    }
+
+    dvmEncodedArrayIteratorInitialize(&iterator, pValueList, clazz);
+
+    /*
+     * Iterate over the initial values array, setting the corresponding
+     * static field for each array element.
+     */
+
+    for (i = 0; dvmEncodedArrayIteratorHasNext(&iterator); i++) {
+        AnnotationValue value;
+        bool parsed = dvmEncodedArrayIteratorGetNext(&iterator, &value);
+        StaticField* sfield = &clazz->sfields[i];
+        const char* descriptor = sfield->signature;
+        bool isObj = false;
+
+        if (! parsed) {
+            /*
+             * TODO: Eventually verification should attempt to ensure
+             * that this can't happen at least due to a data integrity
+             * problem.
+             */
+            ALOGE("Static initializer parse failed for %s at index %d",
+                    clazz->descriptor, i);
+            dvmAbort();
+        }
+
+        /* Verify that the value we got was of a valid type. */
+
+        switch (descriptor[0]) {
+            case 'Z': parsed = (value.type == kDexAnnotationBoolean); break;
+            case 'B': parsed = (value.type == kDexAnnotationByte);    break;
+            case 'C': parsed = (value.type == kDexAnnotationChar);    break;
+            case 'S': parsed = (value.type == kDexAnnotationShort);   break;
+            case 'I': parsed = (value.type == kDexAnnotationInt);     break;
+            case 'J': parsed = (value.type == kDexAnnotationLong);    break;
+            case 'F': parsed = (value.type == kDexAnnotationFloat);   break;
+            case 'D': parsed = (value.type == kDexAnnotationDouble);  break;
+            case '[': parsed = (value.type == kDexAnnotationNull);    break;
+            case 'L': {
+                switch (value.type) {
+                    case kDexAnnotationNull: {
+                        /* No need for further tests. */
+                        break;
+                    }
+                    case kDexAnnotationString: {
+                        parsed =
+                            (strcmp(descriptor, "Ljava/lang/String;") == 0);
+                        isObj = true;
+                        break;
+                    }
+                    case kDexAnnotationType: {
+                        parsed =
+                            (strcmp(descriptor, "Ljava/lang/Class;") == 0);
+                        isObj = true;
+                        break;
+                    }
+                    default: {
+                        parsed = false;
+                        break;
+                    }
+                }
+                break;
+            }
+            default: {
+                parsed = false;
+                break;
+            }
+        }
+
+        if (parsed) {
+            /*
+             * All's well, so store the value.
+             */
+            if (isObj) {
+                dvmSetStaticFieldObject(sfield, (Object*)value.value.l);
+                dvmReleaseTrackedAlloc((Object*)value.value.l, self);
+            } else {
+                /*
+                 * Note: This always stores the full width of a
+                 * JValue, even though most of the time only the first
+                 * word is needed.
+                 */
+                sfield->value = value.value;
+            }
+        } else {
+            /*
+             * Something up above had a problem. TODO: See comment
+             * above the switch about verfication.
+             */
+            ALOGE("Bogus static initialization: value type %d in field type "
+                    "%s for %s at index %d",
+                value.type, descriptor, clazz->descriptor, i);
+            dvmAbort();
+        }
+    }
+}
+
+
+/*
+ * Determine whether "descriptor" yields the same class object in the
+ * context of clazz1 and clazz2.
+ *
+ * The caller must hold gDvm.loadedClasses.
+ *
+ * Returns "true" if they match.
+ */
+static bool compareDescriptorClasses(const char* descriptor,
+    const ClassObject* clazz1, const ClassObject* clazz2)
+{
+    ClassObject* result1;
+    ClassObject* result2;
+
+    /*
+     * Do the first lookup by name.
+     */
+    result1 = dvmFindClassNoInit(descriptor, clazz1->classLoader);
+
+    /*
+     * We can skip a second lookup by name if the second class loader is
+     * in the initiating loader list of the class object we retrieved.
+     * (This means that somebody already did a lookup of this class through
+     * the second loader, and it resolved to the same class.)  If it's not
+     * there, we may simply not have had an opportunity to add it yet, so
+     * we do the full lookup.
+     *
+     * The initiating loader test should catch the majority of cases
+     * (in particular, the zillions of references to String/Object).
+     *
+     * Unfortunately we're still stuck grabbing a mutex to do the lookup.
+     *
+     * For this to work, the superclass/interface should be the first
+     * argument, so that way if it's from the bootstrap loader this test
+     * will work.  (The bootstrap loader, by definition, never shows up
+     * as the initiating loader of a class defined by some other loader.)
+     */
+    dvmHashTableLock(gDvm.loadedClasses);
+    bool isInit = dvmLoaderInInitiatingList(result1, clazz2->classLoader);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    if (isInit) {
+        //printf("%s(obj=%p) / %s(cl=%p): initiating\n",
+        //    result1->descriptor, result1,
+        //    clazz2->descriptor, clazz2->classLoader);
+        return true;
+    } else {
+        //printf("%s(obj=%p) / %s(cl=%p): RAW\n",
+        //    result1->descriptor, result1,
+        //    clazz2->descriptor, clazz2->classLoader);
+        result2 = dvmFindClassNoInit(descriptor, clazz2->classLoader);
+    }
+
+    if (result1 == NULL || result2 == NULL) {
+        dvmClearException(dvmThreadSelf());
+        if (result1 == result2) {
+            /*
+             * Neither class loader could find this class.  Apparently it
+             * doesn't exist.
+             *
+             * We can either throw some sort of exception now, or just
+             * assume that it'll fail later when something actually tries
+             * to use the class.  For strict handling we should throw now,
+             * because a "tricky" class loader could start returning
+             * something later, and a pair of "tricky" loaders could set
+             * us up for confusion.
+             *
+             * I'm not sure if we're allowed to complain about nonexistent
+             * classes in method signatures during class init, so for now
+             * this will just return "true" and let nature take its course.
+             */
+            return true;
+        } else {
+            /* only one was found, so clearly they're not the same */
+            return false;
+        }
+    }
+
+    return result1 == result2;
+}
+
+/*
+ * For every component in the method descriptor, resolve the class in the
+ * context of the two classes and compare the results.
+ *
+ * For best results, the "superclass" class should be first.
+ *
+ * Returns "true" if the classes match, "false" otherwise.
+ */
+static bool checkMethodDescriptorClasses(const Method* meth,
+    const ClassObject* clazz1, const ClassObject* clazz2)
+{
+    DexParameterIterator iterator;
+    const char* descriptor;
+
+    /* walk through the list of parameters */
+    dexParameterIteratorInit(&iterator, &meth->prototype);
+    while (true) {
+        descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (descriptor == NULL)
+            break;
+
+        if (descriptor[0] == 'L' || descriptor[0] == '[') {
+            /* non-primitive type */
+            if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+                return false;
+        }
+    }
+
+    /* check the return type */
+    descriptor = dexProtoGetReturnType(&meth->prototype);
+    if (descriptor[0] == 'L' || descriptor[0] == '[') {
+        if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+            return false;
+    }
+    return true;
+}
+
+/*
+ * Validate the descriptors in the superclass and interfaces.
+ *
+ * What we need to do is ensure that the classes named in the method
+ * descriptors in our ancestors and ourselves resolve to the same class
+ * objects.  We can get conflicts when the classes come from different
+ * class loaders, and the resolver comes up with different results for
+ * the same class name in different contexts.
+ *
+ * An easy way to cause the problem is to declare a base class that uses
+ * class Foo in a method signature (e.g. as the return type).  Then,
+ * define a subclass and a different version of Foo, and load them from a
+ * different class loader.  If the subclass overrides the method, it will
+ * have a different concept of what Foo is than its parent does, so even
+ * though the method signature strings are identical, they actually mean
+ * different things.
+ *
+ * A call to the method through a base-class reference would be treated
+ * differently than a call to the method through a subclass reference, which
+ * isn't the way polymorphism works, so we have to reject the subclass.
+ * If the subclass doesn't override the base method, then there's no
+ * problem, because calls through base-class references and subclass
+ * references end up in the same place.
+ *
+ * We don't need to check to see if an interface's methods match with its
+ * superinterface's methods, because you can't instantiate an interface
+ * and do something inappropriate with it.  If interface I1 extends I2
+ * and is implemented by C, and I1 and I2 are in separate class loaders
+ * and have conflicting views of other classes, we will catch the conflict
+ * when we process C.  Anything that implements I1 is doomed to failure,
+ * but we don't need to catch that while processing I1.
+ *
+ * On failure, throws an exception and returns "false".
+ */
+static bool validateSuperDescriptors(const ClassObject* clazz)
+{
+    int i;
+
+    if (dvmIsInterfaceClass(clazz))
+        return true;
+
+    /*
+     * Start with the superclass-declared methods.
+     */
+    if (clazz->super != NULL &&
+        clazz->classLoader != clazz->super->classLoader)
+    {
+        /*
+         * Walk through every overridden method and compare resolved
+         * descriptor components.  We pull the Method structs out of
+         * the vtable.  It doesn't matter whether we get the struct from
+         * the parent or child, since we just need the UTF-8 descriptor,
+         * which must match.
+         *
+         * We need to do this even for the stuff inherited from Object,
+         * because it's possible that the new class loader has redefined
+         * a basic class like String.
+         *
+         * We don't need to check stuff defined in a superclass because
+         * it was checked when the superclass was loaded.
+         */
+        const Method* meth;
+
+        //printf("Checking %s %p vs %s %p\n",
+        //    clazz->descriptor, clazz->classLoader,
+        //    clazz->super->descriptor, clazz->super->classLoader);
+        for (i = clazz->super->vtableCount - 1; i >= 0; i--) {
+            meth = clazz->vtable[i];
+            if (meth != clazz->super->vtable[i] &&
+                !checkMethodDescriptorClasses(meth, clazz->super, clazz))
+            {
+                ALOGW("Method mismatch: %s in %s (cl=%p) and super %s (cl=%p)",
+                    meth->name, clazz->descriptor, clazz->classLoader,
+                    clazz->super->descriptor, clazz->super->classLoader);
+                dvmThrowLinkageError(
+                    "Classes resolve differently in superclass");
+                return false;
+            }
+        }
+    }
+
+    /*
+     * Check the methods defined by this class against the interfaces it
+     * implements.  If we inherited the implementation from a superclass,
+     * we have to check it against the superclass (which might be in a
+     * different class loader).  If the superclass also implements the
+     * interface, we could skip the check since by definition it was
+     * performed when the class was loaded.
+     */
+    for (i = 0; i < clazz->iftableCount; i++) {
+        const InterfaceEntry* iftable = &clazz->iftable[i];
+
+        if (clazz->classLoader != iftable->clazz->classLoader) {
+            const ClassObject* iface = iftable->clazz;
+            int j;
+
+            for (j = 0; j < iface->virtualMethodCount; j++) {
+                const Method* meth;
+                int vtableIndex;
+
+                vtableIndex = iftable->methodIndexArray[j];
+                meth = clazz->vtable[vtableIndex];
+
+                if (!checkMethodDescriptorClasses(meth, iface, meth->clazz)) {
+                    ALOGW("Method mismatch: %s in %s (cl=%p) and "
+                            "iface %s (cl=%p)",
+                        meth->name, clazz->descriptor, clazz->classLoader,
+                        iface->descriptor, iface->classLoader);
+                    dvmThrowLinkageError(
+                        "Classes resolve differently in interface");
+                    return false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Returns true if the class is being initialized by us (which means that
+ * calling dvmInitClass will return immediately after fiddling with locks).
+ * Returns false if it's not being initialized, or if it's being
+ * initialized by another thread.
+ *
+ * The value for initThreadId is always set to "self->threadId", by the
+ * thread doing the initializing.  If it was done by the current thread,
+ * we are guaranteed to see "initializing" and our thread ID, even on SMP.
+ * If it was done by another thread, the only bad situation is one in
+ * which we see "initializing" and a stale copy of our own thread ID
+ * while another thread is actually handling init.
+ *
+ * The initThreadId field is used during class linking, so it *is*
+ * possible to have a stale value floating around.  We need to ensure
+ * that memory accesses happen in the correct order.
+ */
+bool dvmIsClassInitializing(const ClassObject* clazz)
+{
+    const int32_t* addr = (const int32_t*)(const void*)&clazz->status;
+    int32_t value = android_atomic_acquire_load(addr);
+    ClassStatus status = static_cast<ClassStatus>(value);
+    return (status == CLASS_INITIALIZING &&
+            clazz->initThreadId == dvmThreadSelf()->threadId);
+}
+
+/*
+ * If a class has not been initialized, do so by executing the code in
+ * <clinit>.  The sequence is described in the VM spec v2 2.17.5.
+ *
+ * It is possible for multiple threads to arrive here simultaneously, so
+ * we need to lock the class while we check stuff.  We know that no
+ * interpreted code has access to the class yet, so we can use the class's
+ * monitor lock.
+ *
+ * We will often be called recursively, e.g. when the <clinit> code resolves
+ * one of its fields, the field resolution will try to initialize the class.
+ * In that case we will return "true" even though the class isn't actually
+ * ready to go.  The ambiguity can be resolved with dvmIsClassInitializing().
+ * (TODO: consider having this return an enum to avoid the extra call --
+ * return -1 on failure, 0 on success, 1 on still-initializing.  Looks like
+ * dvmIsClassInitializing() is always paired with *Initialized())
+ *
+ * This can get very interesting if a class has a static field initialized
+ * to a new instance of itself.  <clinit> will end up calling <init> on
+ * the members it is initializing, which is fine unless it uses the contents
+ * of static fields to initialize instance fields.  This will leave the
+ * static-referenced objects in a partially initialized state.  This is
+ * reasonably rare and can sometimes be cured with proper field ordering.
+ *
+ * On failure, returns "false" with an exception raised.
+ *
+ * -----
+ *
+ * It is possible to cause a deadlock by having a situation like this:
+ *   class A { static { sleep(10000); new B(); } }
+ *   class B { static { sleep(10000); new A(); } }
+ *   new Thread() { public void run() { new A(); } }.start();
+ *   new Thread() { public void run() { new B(); } }.start();
+ * This appears to be expected under the spec.
+ *
+ * The interesting question is what to do if somebody calls Thread.interrupt()
+ * on one of the deadlocked threads.  According to the VM spec, they're both
+ * sitting in "wait".  Should the interrupt code quietly raise the
+ * "interrupted" flag, or should the "wait" return immediately with an
+ * exception raised?
+ *
+ * This gets a little murky.  The VM spec says we call "wait", and the
+ * spec for Thread.interrupt says Object.wait is interruptible.  So it
+ * seems that, if we get unlucky and interrupt class initialization, we
+ * are expected to throw (which gets converted to ExceptionInInitializerError
+ * since InterruptedException is checked).
+ *
+ * There are a couple of problems here.  First, all threads are expected to
+ * present a consistent view of class initialization, so we can't have it
+ * fail in one thread and succeed in another.  Second, once a class fails
+ * to initialize, it must *always* fail.  This means that a stray interrupt()
+ * call could render a class unusable for the lifetime of the VM.
+ *
+ * In most cases -- the deadlock example above being a counter-example --
+ * the interrupting thread can't tell whether the target thread handled
+ * the initialization itself or had to wait while another thread did the
+ * work.  Refusing to interrupt class initialization is, in most cases,
+ * not something that a program can reliably detect.
+ *
+ * On the assumption that interrupting class initialization is highly
+ * undesirable in most circumstances, and that failing to do so does not
+ * deviate from the spec in a meaningful way, we don't allow class init
+ * to be interrupted by Thread.interrupt().
+ */
+bool dvmInitClass(ClassObject* clazz)
+{
+    u8 startWhen = 0;
+
+#if LOG_CLASS_LOADING
+    bool initializedByUs = false;
+#endif
+
+    Thread* self = dvmThreadSelf();
+    const Method* method;
+
+    dvmLockObject(self, (Object*) clazz);
+    assert(dvmIsClassLinked(clazz) || clazz->status == CLASS_ERROR);
+
+    /*
+     * If the class hasn't been verified yet, do so now.
+     */
+    if (clazz->status < CLASS_VERIFIED) {
+        /*
+         * If we're in an "erroneous" state, throw an exception and bail.
+         */
+        if (clazz->status == CLASS_ERROR) {
+            throwEarlierClassFailure(clazz);
+            goto bail_unlock;
+        }
+
+        assert(clazz->status == CLASS_RESOLVED);
+        assert(!IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+
+        if (gDvm.classVerifyMode == VERIFY_MODE_NONE ||
+            (gDvm.classVerifyMode == VERIFY_MODE_REMOTE &&
+             clazz->classLoader == NULL))
+        {
+            /* advance to "verified" state */
+            ALOGV("+++ not verifying class %s (cl=%p)",
+                clazz->descriptor, clazz->classLoader);
+            clazz->status = CLASS_VERIFIED;
+            goto noverify;
+        }
+
+        if (!gDvm.optimizing)
+            ALOGV("+++ late verify on %s", clazz->descriptor);
+
+        /*
+         * We're not supposed to optimize an unverified class, but during
+         * development this mode was useful.  We can't verify an optimized
+         * class because the optimization process discards information.
+         */
+        if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) {
+            ALOGW("Class '%s' was optimized without verification; "
+                 "not verifying now",
+                clazz->descriptor);
+            ALOGW("  ('rm /data/dalvik-cache/*' and restart to fix this)");
+            goto verify_failed;
+        }
+
+        clazz->status = CLASS_VERIFYING;
+        if (!dvmVerifyClass(clazz)) {
+verify_failed:
+            dvmThrowVerifyError(clazz->descriptor);
+            dvmSetFieldObject((Object*) clazz,
+                OFFSETOF_MEMBER(ClassObject, verifyErrorClass),
+                (Object*) dvmGetException(self)->clazz);
+            clazz->status = CLASS_ERROR;
+            goto bail_unlock;
+        }
+
+        clazz->status = CLASS_VERIFIED;
+    }
+noverify:
+
+    /*
+     * We need to ensure that certain instructions, notably accesses to
+     * volatile fields, are replaced before any code is executed.  This
+     * must happen even if DEX optimizations are disabled.
+     *
+     * The only exception to this rule is that we don't want to do this
+     * during dexopt.  We don't generally initialize classes at all
+     * during dexopt, but because we're loading classes we need Class and
+     * Object (and possibly some Throwable stuff if a class isn't found).
+     * If optimizations are disabled, we don't want to output optimized
+     * instructions at this time.  This means we will be executing <clinit>
+     * code with un-fixed volatiles, but we're only doing it for a few
+     * system classes, and dexopt runs single-threaded.
+     */
+    if (!IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED) && !gDvm.optimizing) {
+        ALOGV("+++ late optimize on %s (pv=%d)",
+            clazz->descriptor, IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+        bool essentialOnly = (gDvm.dexOptMode != OPTIMIZE_MODE_FULL);
+        dvmOptimizeClass(clazz, essentialOnly);
+        SET_CLASS_FLAG(clazz, CLASS_ISOPTIMIZED);
+    }
+
+    /* update instruction stream now that verification + optimization is done */
+    dvmFlushBreakpoints(clazz);
+
+    if (clazz->status == CLASS_INITIALIZED)
+        goto bail_unlock;
+
+    while (clazz->status == CLASS_INITIALIZING) {
+        /* we caught somebody else in the act; was it us? */
+        if (clazz->initThreadId == self->threadId) {
+            //ALOGV("HEY: found a recursive <clinit>");
+            goto bail_unlock;
+        }
+
+        if (dvmCheckException(self)) {
+            ALOGW("GLITCH: exception pending at start of class init");
+            dvmAbort();
+        }
+
+        /*
+         * Wait for the other thread to finish initialization.  We pass
+         * "false" for the "interruptShouldThrow" arg so it doesn't throw
+         * an exception on interrupt.
+         */
+        dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+
+        /*
+         * When we wake up, repeat the test for init-in-progress.  If there's
+         * an exception pending (only possible if "interruptShouldThrow"
+         * was set), bail out.
+         */
+        if (dvmCheckException(self)) {
+            ALOGI("Class init of '%s' failing with wait() exception",
+                clazz->descriptor);
+            /*
+             * TODO: this is bogus, because it means the two threads have a
+             * different idea of the class status.  We need to flag the
+             * class as bad and ensure that the initializer thread respects
+             * our notice.  If we get lucky and wake up after the class has
+             * finished initialization but before being woken, we have to
+             * swallow the exception, perhaps raising thread->interrupted
+             * to preserve semantics.
+             *
+             * Since we're not currently allowing interrupts, this should
+             * never happen and we don't need to fix this.
+             */
+            assert(false);
+            dvmThrowExceptionInInitializerError();
+            clazz->status = CLASS_ERROR;
+            goto bail_unlock;
+        }
+        if (clazz->status == CLASS_INITIALIZING) {
+            ALOGI("Waiting again for class init");
+            continue;
+        }
+        assert(clazz->status == CLASS_INITIALIZED ||
+               clazz->status == CLASS_ERROR);
+        if (clazz->status == CLASS_ERROR) {
+            /*
+             * The caller wants an exception, but it was thrown in a
+             * different thread.  Synthesize one here.
+             */
+            dvmThrowUnsatisfiedLinkError(
+                "(<clinit> failed, see exception in other thread)");
+        }
+        goto bail_unlock;
+    }
+
+    /* see if we failed previously */
+    if (clazz->status == CLASS_ERROR) {
+        // might be wise to unlock before throwing; depends on which class
+        // it is that we have locked
+        dvmUnlockObject(self, (Object*) clazz);
+        throwEarlierClassFailure(clazz);
+        return false;
+    }
+
+    if (gDvm.allocProf.enabled) {
+        startWhen = dvmGetRelativeTimeNsec();
+    }
+
+    /*
+     * We're ready to go, and have exclusive access to the class.
+     *
+     * Before we start initialization, we need to do one extra bit of
+     * validation: make sure that the methods declared here match up
+     * with our superclass and interfaces.  We know that the UTF-8
+     * descriptors match, but classes from different class loaders can
+     * have the same name.
+     *
+     * We do this now, rather than at load/link time, for the same reason
+     * that we defer verification.
+     *
+     * It's unfortunate that we need to do this at all, but we risk
+     * mixing reference types with identical names (see Dalvik test 068).
+     */
+    if (!validateSuperDescriptors(clazz)) {
+        assert(dvmCheckException(self));
+        clazz->status = CLASS_ERROR;
+        goto bail_unlock;
+    }
+
+    /*
+     * Let's initialize this thing.
+     *
+     * We unlock the object so that other threads can politely sleep on
+     * our mutex with Object.wait(), instead of hanging or spinning trying
+     * to grab our mutex.
+     */
+    assert(clazz->status < CLASS_INITIALIZING);
+
+#if LOG_CLASS_LOADING
+    // We started initializing.
+    logClassLoad('+', clazz);
+    initializedByUs = true;
+#endif
+
+    /* order matters here, esp. interaction with dvmIsClassInitializing */
+    clazz->initThreadId = self->threadId;
+    android_atomic_release_store(CLASS_INITIALIZING,
+                                 (int32_t*)(void*)&clazz->status);
+    dvmUnlockObject(self, (Object*) clazz);
+
+    /* init our superclass */
+    if (clazz->super != NULL && clazz->super->status != CLASS_INITIALIZED) {
+        assert(!dvmIsInterfaceClass(clazz));
+        if (!dvmInitClass(clazz->super)) {
+            assert(dvmCheckException(self));
+            clazz->status = CLASS_ERROR;
+            /* wake up anybody who started waiting while we were unlocked */
+            dvmLockObject(self, (Object*) clazz);
+            goto bail_notify;
+        }
+    }
+
+    /* Initialize any static fields whose values are
+     * stored in the Dex file.  This should include all of the
+     * simple "final static" fields, which are required to
+     * be initialized first. (vmspec 2 sec 2.17.5 item 8)
+     * More-complicated final static fields should be set
+     * at the beginning of <clinit>;  all we can do is trust
+     * that the compiler did the right thing.
+     */
+    initSFields(clazz);
+
+    /* Execute any static initialization code.
+     */
+    method = dvmFindDirectMethodByDescriptor(clazz, "<clinit>", "()V");
+    if (method == NULL) {
+        LOGVV("No <clinit> found for %s", clazz->descriptor);
+    } else {
+        LOGVV("Invoking %s.<clinit>", clazz->descriptor);
+        JValue unused;
+        dvmCallMethod(self, method, NULL, &unused);
+    }
+
+    if (dvmCheckException(self)) {
+        /*
+         * We've had an exception thrown during static initialization.  We
+         * need to throw an ExceptionInInitializerError, but we want to
+         * tuck the original exception into the "cause" field.
+         */
+        ALOGW("Exception %s thrown while initializing %s",
+            (dvmGetException(self)->clazz)->descriptor, clazz->descriptor);
+        dvmThrowExceptionInInitializerError();
+        //ALOGW("+++ replaced");
+
+        dvmLockObject(self, (Object*) clazz);
+        clazz->status = CLASS_ERROR;
+    } else {
+        /* success! */
+        dvmLockObject(self, (Object*) clazz);
+        clazz->status = CLASS_INITIALIZED;
+        LOGVV("Initialized class: %s", clazz->descriptor);
+
+        /*
+         * Update alloc counters.  TODO: guard with mutex.
+         */
+        if (gDvm.allocProf.enabled && startWhen != 0) {
+            u8 initDuration = dvmGetRelativeTimeNsec() - startWhen;
+            gDvm.allocProf.classInitTime += initDuration;
+            self->allocProf.classInitTime += initDuration;
+            gDvm.allocProf.classInitCount++;
+            self->allocProf.classInitCount++;
+        }
+    }
+
+bail_notify:
+    /*
+     * Notify anybody waiting on the object.
+     */
+    dvmObjectNotifyAll(self, (Object*) clazz);
+
+bail_unlock:
+
+#if LOG_CLASS_LOADING
+    if (initializedByUs) {
+        // We finished initializing.
+        logClassLoad('-', clazz);
+    }
+#endif
+
+    dvmUnlockObject(self, (Object*) clazz);
+
+    return (clazz->status != CLASS_ERROR);
+}
+
+/*
+ * Replace method->nativeFunc and method->insns with new values.  This is
+ * commonly performed after successful resolution of a native method.
+ *
+ * There are three basic states:
+ *  (1) (initial) nativeFunc = dvmResolveNativeMethod, insns = NULL
+ *  (2) (internal native) nativeFunc = <impl>, insns = NULL
+ *  (3) (JNI) nativeFunc = JNI call bridge, insns = <impl>
+ *
+ * nativeFunc must never be NULL for a native method.
+ *
+ * The most common transitions are (1)->(2) and (1)->(3).  The former is
+ * atomic, since only one field is updated; the latter is not, but since
+ * dvmResolveNativeMethod ignores the "insns" field we just need to make
+ * sure the update happens in the correct order.
+ *
+ * A transition from (2)->(1) would work fine, but (3)->(1) will not,
+ * because both fields change.  If we did this while a thread was executing
+ * in the call bridge, we could null out the "insns" field right before
+ * the bridge tried to call through it.  So, once "insns" is set, we do
+ * not allow it to be cleared.  A NULL value for the "insns" argument is
+ * treated as "do not change existing value".
+ */
+void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func,
+    const u2* insns)
+{
+    ClassObject* clazz = method->clazz;
+
+    assert(func != NULL);
+
+    /* just open up both; easier that way */
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+    if (insns != NULL) {
+        /* update both, ensuring that "insns" is observed first */
+        method->insns = insns;
+        android_atomic_release_store((int32_t) func,
+            (volatile int32_t*)(void*) &method->nativeFunc);
+    } else {
+        /* only update nativeFunc */
+        method->nativeFunc = func;
+    }
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * Add a RegisterMap to a Method.  This is done when we verify the class
+ * and compute the register maps at class initialization time (i.e. when
+ * we don't have a pre-generated map).  This means "pMap" is on the heap
+ * and should be freed when the Method is discarded.
+ */
+void dvmSetRegisterMap(Method* method, const RegisterMap* pMap)
+{
+    ClassObject* clazz = method->clazz;
+
+    if (method->registerMap != NULL) {
+        /* unexpected during class loading, okay on first use (uncompress) */
+        ALOGV("NOTE: registerMap already set for %s.%s",
+            method->clazz->descriptor, method->name);
+        /* keep going */
+    }
+    assert(!dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method));
+
+    /* might be virtual or direct */
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+    method->registerMap = pMap;
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * dvmHashForeach callback.  A nonzero return value causes foreach to
+ * bail out.
+ */
+static int findClassCallback(void* vclazz, void* arg)
+{
+    ClassObject* clazz = (ClassObject*)vclazz;
+    const char* descriptor = (const char*) arg;
+
+    if (strcmp(clazz->descriptor, descriptor) == 0)
+        return (int) clazz;
+    return 0;
+}
+
+/*
+ * Find a loaded class by descriptor. Returns the first one found.
+ * Because there can be more than one if class loaders are involved,
+ * this is not an especially good API. (Currently only used by the
+ * debugger and "checking" JNI.)
+ *
+ * "descriptor" should have the form "Ljava/lang/Class;" or
+ * "[Ljava/lang/Class;", i.e. a descriptor and not an internal-form
+ * class name.
+ */
+ClassObject* dvmFindLoadedClass(const char* descriptor)
+{
+    int result;
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    result = dvmHashForeach(gDvm.loadedClasses, findClassCallback,
+            (void*) descriptor);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    return (ClassObject*) result;
+}
+
+/*
+ * Retrieve the system (a/k/a application) class loader.
+ *
+ * The caller must call dvmReleaseTrackedAlloc on the result.
+ */
+Object* dvmGetSystemClassLoader()
+{
+    Thread* self = dvmThreadSelf();
+    ClassObject* clClass = gDvm.classJavaLangClassLoader;
+
+    if (!dvmIsClassInitialized(clClass) && !dvmInitClass(clClass))
+        return NULL;
+
+    JValue result;
+    dvmCallMethod(self, gDvm.methJavaLangClassLoader_getSystemClassLoader,
+        NULL, &result);
+    Object* loader = (Object*)result.l;
+    dvmAddTrackedAlloc(loader, self);
+    return loader;
+}
+
+
+/*
+ * This is a dvmHashForeach callback.
+ */
+static int dumpClass(void* vclazz, void* varg)
+{
+    const ClassObject* clazz = (const ClassObject*) vclazz;
+    const ClassObject* super;
+    int flags = (int) varg;
+    char* desc;
+    int i;
+
+    if (clazz == NULL) {
+        ALOGI("dumpClass: ignoring request to dump null class");
+        return 0;
+    }
+
+    if ((flags & kDumpClassFullDetail) == 0) {
+        bool showInit = (flags & kDumpClassInitialized) != 0;
+        bool showLoader = (flags & kDumpClassClassLoader) != 0;
+        const char* initStr;
+
+        initStr = dvmIsClassInitialized(clazz) ? "true" : "false";
+
+        if (showInit && showLoader)
+            ALOGI("%s %p %s", clazz->descriptor, clazz->classLoader, initStr);
+        else if (showInit)
+            ALOGI("%s %s", clazz->descriptor, initStr);
+        else if (showLoader)
+            ALOGI("%s %p", clazz->descriptor, clazz->classLoader);
+        else
+            ALOGI("%s", clazz->descriptor);
+
+        return 0;
+    }
+
+    /* clazz->super briefly holds the superclass index during class prep */
+    if ((u4)clazz->super > 0x10000 && (u4) clazz->super != (u4)-1)
+        super = clazz->super;
+    else
+        super = NULL;
+
+    ALOGI("----- %s '%s' cl=%p ser=0x%08x -----",
+        dvmIsInterfaceClass(clazz) ? "interface" : "class",
+        clazz->descriptor, clazz->classLoader, clazz->serialNumber);
+    ALOGI("  objectSize=%d (%d from super)", (int) clazz->objectSize,
+        super != NULL ? (int) super->objectSize : -1);
+    ALOGI("  access=0x%04x.%04x", clazz->accessFlags >> 16,
+        clazz->accessFlags & JAVA_FLAGS_MASK);
+    if (super != NULL)
+        ALOGI("  super='%s' (cl=%p)", super->descriptor, super->classLoader);
+    if (dvmIsArrayClass(clazz)) {
+        ALOGI("  dimensions=%d elementClass=%s",
+            clazz->arrayDim, clazz->elementClass->descriptor);
+    }
+    if (clazz->iftableCount > 0) {
+        ALOGI("  interfaces (%d):", clazz->iftableCount);
+        for (i = 0; i < clazz->iftableCount; i++) {
+            InterfaceEntry* ent = &clazz->iftable[i];
+            int j;
+
+            ALOGI("    %2d: %s (cl=%p)",
+                i, ent->clazz->descriptor, ent->clazz->classLoader);
+
+            /* enable when needed */
+            if (false && ent->methodIndexArray != NULL) {
+                for (j = 0; j < ent->clazz->virtualMethodCount; j++)
+                    ALOGI("      %2d: %d %s %s",
+                        j, ent->methodIndexArray[j],
+                        ent->clazz->virtualMethods[j].name,
+                        clazz->vtable[ent->methodIndexArray[j]]->name);
+            }
+        }
+    }
+    if (!dvmIsInterfaceClass(clazz)) {
+        ALOGI("  vtable (%d entries, %d in super):", clazz->vtableCount,
+            super != NULL ? super->vtableCount : 0);
+        for (i = 0; i < clazz->vtableCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(&clazz->vtable[i]->prototype);
+            ALOGI("    %s%2d: %p %20s %s",
+                (i != clazz->vtable[i]->methodIndex) ? "*** " : "",
+                (u4) clazz->vtable[i]->methodIndex, clazz->vtable[i],
+                clazz->vtable[i]->name, desc);
+            free(desc);
+        }
+        ALOGI("  direct methods (%d entries):", clazz->directMethodCount);
+        for (i = 0; i < clazz->directMethodCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(
+                    &clazz->directMethods[i].prototype);
+            ALOGI("    %2d: %20s %s", i, clazz->directMethods[i].name,
+                desc);
+            free(desc);
+        }
+    } else {
+        ALOGI("  interface methods (%d):", clazz->virtualMethodCount);
+        for (i = 0; i < clazz->virtualMethodCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(
+                    &clazz->virtualMethods[i].prototype);
+            ALOGI("    %2d: %2d %20s %s", i,
+                (u4) clazz->virtualMethods[i].methodIndex,
+                clazz->virtualMethods[i].name,
+                desc);
+            free(desc);
+        }
+    }
+    if (clazz->sfieldCount > 0) {
+        ALOGI("  static fields (%d entries):", clazz->sfieldCount);
+        for (i = 0; i < clazz->sfieldCount; i++) {
+            ALOGI("    %2d: %20s %s", i, clazz->sfields[i].name,
+                clazz->sfields[i].signature);
+        }
+    }
+    if (clazz->ifieldCount > 0) {
+        ALOGI("  instance fields (%d entries):", clazz->ifieldCount);
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            ALOGI("    %2d: %20s %s", i, clazz->ifields[i].name,
+                clazz->ifields[i].signature);
+        }
+    }
+    return 0;
+}
+
+/*
+ * Dump the contents of a single class.
+ *
+ * Pass kDumpClassFullDetail into "flags" to get lots of detail.
+ */
+void dvmDumpClass(const ClassObject* clazz, int flags)
+{
+    dumpClass((void*) clazz, (void*) flags);
+}
+
+/*
+ * Dump the contents of all classes.
+ */
+void dvmDumpAllClasses(int flags)
+{
+    dvmHashTableLock(gDvm.loadedClasses);
+    dvmHashForeach(gDvm.loadedClasses, dumpClass, (void*) flags);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the number of loaded classes
+ */
+int dvmGetNumLoadedClasses()
+{
+    int count;
+    dvmHashTableLock(gDvm.loadedClasses);
+    count = dvmHashTableNumEntries(gDvm.loadedClasses);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+    return count;
+}
+
+/*
+ * Write some statistics to the log file.
+ */
+void dvmDumpLoaderStats(const char* msg)
+{
+    ALOGV("VM stats (%s): cls=%d/%d meth=%d ifld=%d sfld=%d linear=%d",
+        msg, gDvm.numLoadedClasses, dvmHashTableNumEntries(gDvm.loadedClasses),
+        gDvm.numDeclaredMethods, gDvm.numDeclaredInstFields,
+        gDvm.numDeclaredStaticFields, gDvm.pBootLoaderAlloc->curOffset);
+#ifdef COUNT_PRECISE_METHODS
+    ALOGI("GC precise methods: %d",
+        dvmPointerSetGetCount(gDvm.preciseMethods));
+#endif
+}
+
+/*
+ * ===========================================================================
+ *      Method Prototypes and Descriptors
+ * ===========================================================================
+ */
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(). The
+ * name is considered the "major" order and the prototype the "minor"
+ * order. The prototypes are compared as if by dvmCompareMethodProtos().
+ */
+int dvmCompareMethodNamesAndProtos(const Method* method1,
+        const Method* method2)
+{
+    int result = strcmp(method1->name, method2->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dvmCompareMethodProtos(method1, method2);
+}
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(), ignoring
+ * the return value. The name is considered the "major" order and the
+ * prototype the "minor" order. The prototypes are compared as if by
+ * dvmCompareMethodArgProtos().
+ */
+int dvmCompareMethodNamesAndParameterProtos(const Method* method1,
+        const Method* method2)
+{
+    int result = strcmp(method1->name, method2->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dvmCompareMethodParameterProtos(method1, method2);
+}
+
+/*
+ * Compare a (name, prototype) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameProtoAndMethod(const char* name,
+    const DexProto* proto, const Method* method)
+{
+    int result = strcmp(name, method->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dexProtoCompare(proto, &method->prototype);
+}
+
+/*
+ * Compare a (name, method descriptor) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameDescriptorAndMethod(const char* name,
+    const char* descriptor, const Method* method)
+{
+    int result = strcmp(name, method->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dvmCompareDescriptorAndMethodProto(descriptor, method);
+}
diff --git a/vm/oo/Class.h b/vm/oo/Class.h
new file mode 100644
index 0000000..349c666
--- /dev/null
+++ b/vm/oo/Class.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Class loader.
+ */
+#ifndef DALVIK_OO_CLASS_H_
+#define DALVIK_OO_CLASS_H_
+
+/*
+ * The classpath and bootclasspath differ in that only the latter is
+ * consulted when looking for classes needed by the VM.  When searching
+ * for an arbitrary class definition, we start with the bootclasspath,
+ * look for optional packages (a/k/a standard extensions), and then try
+ * the classpath.
+ *
+ * In Dalvik, a class can be found in one of two ways:
+ *  - in a .dex file
+ *  - in a .dex file named specifically "classes.dex", which is held
+ *    inside a jar file
+ *
+ * These two may be freely intermixed in a classpath specification.
+ * Ordering is significant.
+ */
+enum ClassPathEntryKind {
+    kCpeUnknown = 0,
+    kCpeJar,
+    kCpeDex,
+    kCpeLastEntry       /* used as sentinel at end of array */
+};
+
+struct ClassPathEntry {
+    ClassPathEntryKind kind;
+    char*   fileName;
+    void*   ptr;            /* JarFile* or DexFile* */
+};
+
+bool dvmClassStartup(void);
+void dvmClassShutdown(void);
+bool dvmPrepBootClassPath(bool isNormalStart);
+
+/*
+ * Boot class path accessors, for class loader getResources().
+ */
+int dvmGetBootPathSize(void);
+StringObject* dvmGetBootPathResource(const char* name, int idx);
+void dvmDumpBootClassPath(void);
+
+/*
+ * Determine whether "path" is a member of "cpe".
+ */
+bool dvmClassPathContains(const ClassPathEntry* cpe, const char* path);
+
+/*
+ * Set clazz->serialNumber to the next available value.
+ */
+void dvmSetClassSerialNumber(ClassObject* clazz);
+
+/*
+ * Find the class object representing the primitive type with the
+ * given descriptor. This returns NULL if the given type character
+ * is invalid.
+ */
+ClassObject* dvmFindPrimitiveClass(char type);
+
+/*
+ * Find the class with the given descriptor.  Load it if it hasn't already
+ * been.
+ *
+ * "loader" is the initiating class loader.
+ */
+ClassObject* dvmFindClass(const char* descriptor, Object* loader);
+ClassObject* dvmFindClassNoInit(const char* descriptor, Object* loader);
+
+/*
+ * Like dvmFindClass, but only for system classes.
+ */
+ClassObject* dvmFindSystemClass(const char* descriptor);
+ClassObject* dvmFindSystemClassNoInit(const char* descriptor);
+
+/*
+ * Find a loaded class by descriptor. Returns the first one found.
+ * Because there can be more than one if class loaders are involved,
+ * this is not an especially good API. (Currently only used by the
+ * debugger and "checking" JNI.)
+ *
+ * "descriptor" should have the form "Ljava/lang/Class;" or
+ * "[Ljava/lang/Class;", i.e. a descriptor and not an internal-form
+ * class name.
+ */
+ClassObject* dvmFindLoadedClass(const char* descriptor);
+
+/*
+ * Load the named class (by descriptor) from the specified DEX file.
+ * Used by class loaders to instantiate a class object from a
+ * VM-managed DEX.
+ */
+ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
+    Object* classLoader);
+
+/*
+ * Link a loaded class.  Normally done as part of one of the "find class"
+ * variations, this is only called explicitly for synthetic class
+ * generation (e.g. reflect.Proxy).
+ */
+bool dvmLinkClass(ClassObject* clazz);
+
+/*
+ * Determine if a class has been initialized.
+ */
+INLINE bool dvmIsClassInitialized(const ClassObject* clazz) {
+    return (clazz->status == CLASS_INITIALIZED);
+}
+bool dvmIsClassInitializing(const ClassObject* clazz);
+
+/*
+ * Initialize a class.
+ */
+extern "C" bool dvmInitClass(ClassObject* clazz);
+
+/*
+ * Retrieve the system class loader.
+ */
+Object* dvmGetSystemClassLoader(void);
+
+/*
+ * Utility functions.
+ */
+ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
+    bool unprepOkay);
+void dvmFreeClassInnards(ClassObject* clazz);
+bool dvmAddClassToHash(ClassObject* clazz);
+void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader);
+bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader);
+
+/*
+ * Update method's "nativeFunc" and "insns".  If "insns" is NULL, the
+ * current method->insns value is not changed.
+ */
+void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func, const u2* insns);
+
+/*
+ * Set the method's "registerMap" field.
+ */
+void dvmSetRegisterMap(Method* method, const RegisterMap* pMap);
+
+/*
+ * Make a method's DexCode (which includes the bytecode) read-write or
+ * read-only.  The conversion to read-write may involve making a new copy
+ * of the DexCode, and in normal operation the read-only state is not
+ * actually enforced.
+ */
+void dvmMakeCodeReadWrite(Method* meth);
+void dvmMakeCodeReadOnly(Method* meth);
+
+/*
+ * During DEX optimizing, add an extra DEX to the bootstrap class path.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex);
+
+/*
+ * Debugging.
+ */
+void dvmDumpClass(const ClassObject* clazz, int flags);
+void dvmDumpAllClasses(int flags);
+void dvmDumpLoaderStats(const char* msg);
+int  dvmGetNumLoadedClasses();
+
+/* flags for dvmDumpClass / dvmDumpAllClasses */
+#define kDumpClassFullDetail    1
+#define kDumpClassClassLoader   (1 << 1)
+#define kDumpClassInitialized   (1 << 2)
+
+
+/*
+ * Store a copy of the method prototype descriptor string
+ * for the given method into the given DexStringCache, returning the
+ * stored string for convenience.
+ */
+INLINE char* dvmCopyDescriptorStringFromMethod(const Method* method,
+        DexStringCache *pCache)
+{
+    const char* result =
+        dexProtoGetMethodDescriptor(&method->prototype, pCache);
+    return dexStringCacheEnsureCopy(pCache, result);
+}
+
+/*
+ * Compute the number of argument words (u4 units) required by the
+ * given method's prototype. For example, if the method descriptor is
+ * "(IJ)D", this would return 3 (one for the int, two for the long;
+ * return value isn't relevant).
+ */
+INLINE int dvmComputeMethodArgsSize(const Method* method)
+{
+    return dexProtoComputeArgsSize(&method->prototype);
+}
+
+/*
+ * Compare the two method prototypes. The two prototypes are compared
+ * as if by strcmp() on the result of dexProtoGetMethodDescriptor().
+ */
+INLINE int dvmCompareMethodProtos(const Method* method1,
+        const Method* method2)
+{
+    return dexProtoCompare(&method1->prototype, &method2->prototype);
+}
+
+/*
+ * Compare the two method prototypes, considering only the parameters
+ * (i.e. ignoring the return types). The two prototypes are compared
+ * as if by strcmp() on the result of dexProtoGetMethodDescriptor().
+ */
+INLINE int dvmCompareMethodParameterProtos(const Method* method1,
+        const Method* method2)
+{
+    return dexProtoCompareParameters(&method1->prototype, &method2->prototype);
+}
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(). The
+ * name is considered the "major" order and the prototype the "minor"
+ * order. The prototypes are compared as if by dexProtoGetMethodDescriptor().
+ */
+int dvmCompareMethodNamesAndProtos(const Method* method1,
+        const Method* method2);
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(), ignoring
+ * the return type. The name is considered the "major" order and the
+ * prototype the "minor" order. The prototypes are compared as if by
+ * dexProtoGetMethodDescriptor().
+ */
+int dvmCompareMethodNamesAndParameterProtos(const Method* method1,
+        const Method* method2);
+
+/*
+ * Compare a method descriptor string with the prototype of a method,
+ * as if by converting the descriptor to a DexProto and comparing it
+ * with dexProtoCompare().
+ */
+INLINE int dvmCompareDescriptorAndMethodProto(const char* descriptor,
+    const Method* method)
+{
+    // Sense is reversed.
+    return -dexProtoCompareToDescriptor(&method->prototype, descriptor);
+}
+
+/*
+ * Compare a (name, prototype) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameProtoAndMethod(const char* name,
+    const DexProto* proto, const Method* method);
+
+/*
+ * Compare a (name, method descriptor) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameDescriptorAndMethod(const char* name,
+    const char* descriptor, const Method* method);
+
+/*
+ * Returns the size of the given class object in bytes.
+ */
+size_t dvmClassObjectSize(const ClassObject *clazz);
+
+#endif  // DALVIK_OO_CLASS_H_
diff --git a/vm/oo/Object.cpp b/vm/oo/Object.cpp
new file mode 100644
index 0000000..cd3ae64
--- /dev/null
+++ b/vm/oo/Object.cpp
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Operations on an Object.
+ */
+#include "Dalvik.h"
+
+/*
+ * Find a matching field, in the current class only.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+InstField* dvmFindInstanceField(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    InstField* pField;
+    int i;
+
+    assert(clazz != NULL);
+
+    /*
+     * Find a field with a matching name and signature.  The Java programming
+     * language does not allow you to have two fields with the same name
+     * and different types, but the Java VM spec does allow it, so we can't
+     * bail out early when the name matches.
+     */
+    pField = clazz->ifields;
+    for (i = 0; i < clazz->ifieldCount; i++, pField++) {
+        if (strcmp(fieldName, pField->name) == 0 &&
+            strcmp(signature, pField->signature) == 0)
+        {
+            return pField;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * Searching through interfaces isn't necessary, because interface fields
+ * are inherently public/static/final.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+InstField* dvmFindInstanceFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    InstField* pField;
+
+    /*
+     * Search for a match in the current class.
+     */
+    pField = dvmFindInstanceField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+
+    if (clazz->super != NULL)
+        return dvmFindInstanceFieldHier(clazz->super, fieldName, signature);
+    else
+        return NULL;
+}
+
+
+/*
+ * Find a matching field, in this class or an interface.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+StaticField* dvmFindStaticField(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    const StaticField* pField;
+    int i;
+
+    assert(clazz != NULL);
+
+    /*
+     * Find a field with a matching name and signature.  As with instance
+     * fields, the VM allows you to have two fields with the same name so
+     * long as they have different types.
+     */
+    pField = &clazz->sfields[0];
+    for (i = 0; i < clazz->sfieldCount; i++, pField++) {
+        if (strcmp(fieldName, pField->name) == 0 &&
+            strcmp(signature, pField->signature) == 0)
+        {
+            return (StaticField*) pField;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+StaticField* dvmFindStaticFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    StaticField* pField;
+
+    /*
+     * Search for a match in the current class.
+     */
+    pField = dvmFindStaticField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+
+    /*
+     * See if it's in any of our interfaces.  We don't check interfaces
+     * inherited from the superclass yet.
+     *
+     * (Note the set may have been stripped down because of redundancy with
+     * the superclass; see notes in createIftable.)
+     */
+    int i = 0;
+    if (clazz->super != NULL) {
+        assert(clazz->iftableCount >= clazz->super->iftableCount);
+        i = clazz->super->iftableCount;
+    }
+    for ( ; i < clazz->iftableCount; i++) {
+        ClassObject* iface = clazz->iftable[i].clazz;
+        pField = dvmFindStaticField(iface, fieldName, signature);
+        if (pField != NULL)
+            return pField;
+    }
+
+    if (clazz->super != NULL)
+        return dvmFindStaticFieldHier(clazz->super, fieldName, signature);
+    else
+        return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * We scan both the static and instance field lists in the class.  If it's
+ * not found there, we check the direct interfaces, and then recursively
+ * scan the superclasses.  This is the order prescribed in the VM spec
+ * (v2 5.4.3.2).
+ *
+ * In most cases we know that we're looking for either a static or an
+ * instance field and there's no value in searching through both types.
+ * During verification we need to recognize and reject certain unusual
+ * situations, and we won't see them unless we walk the lists this way.
+ */
+Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName,
+    const char* signature)
+{
+    Field* pField;
+
+    /*
+     * Search for a match in the current class.  Which set we scan first
+     * doesn't really matter.
+     */
+    pField = (Field*) dvmFindStaticField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+    pField = (Field*) dvmFindInstanceField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+
+    /*
+     * See if it's in any of our interfaces.  We don't check interfaces
+     * inherited from the superclass yet.
+     */
+    int i = 0;
+    if (clazz->super != NULL) {
+        assert(clazz->iftableCount >= clazz->super->iftableCount);
+        i = clazz->super->iftableCount;
+    }
+    for ( ; i < clazz->iftableCount; i++) {
+        ClassObject* iface = clazz->iftable[i].clazz;
+        pField = (Field*) dvmFindStaticField(iface, fieldName, signature);
+        if (pField != NULL)
+            return pField;
+    }
+
+    if (clazz->super != NULL)
+        return dvmFindFieldHier(clazz->super, fieldName, signature);
+    else
+        return NULL;
+}
+
+
+/*
+ * Compare the given name, return type, and argument types with the contents
+ * of the given method. This returns 0 if they are equal and non-zero if not.
+ */
+static inline int compareMethodHelper(Method* method, const char* methodName,
+    const char* returnType, size_t argCount, const char** argTypes)
+{
+    DexParameterIterator iterator;
+    const DexProto* proto;
+
+    if (strcmp(methodName, method->name) != 0) {
+        return 1;
+    }
+
+    proto = &method->prototype;
+
+    if (strcmp(returnType, dexProtoGetReturnType(proto)) != 0) {
+        return 1;
+    }
+
+    if (dexProtoGetParameterCount(proto) != argCount) {
+        return 1;
+    }
+
+    dexParameterIteratorInit(&iterator, proto);
+
+    for (/*argCount*/; argCount != 0; argCount--, argTypes++) {
+        const char* argType = *argTypes;
+        const char* paramType = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (paramType == NULL) {
+            /* Param list ended early; no match */
+            break;
+        } else if (strcmp(argType, paramType) != 0) {
+            /* Types aren't the same; no match. */
+            break;
+        }
+    }
+
+    if (argCount == 0) {
+        /* We ran through all the given arguments... */
+        if (dexParameterIteratorNextDescriptor(&iterator) == NULL) {
+            /* ...and through all the method's arguments; success! */
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Get the count of arguments in the given method descriptor string,
+ * and also find a pointer to the return type.
+ */
+static inline size_t countArgsAndFindReturnType(const char* descriptor,
+    const char** pReturnType)
+{
+    size_t count = 0;
+    bool bogus = false;
+    bool done = false;
+
+    assert(*descriptor == '(');
+    descriptor++;
+
+    while (!done) {
+        switch (*descriptor) {
+            case 'B': case 'C': case 'D': case 'F':
+            case 'I': case 'J': case 'S': case 'Z': {
+                count++;
+                break;
+            }
+            case '[': {
+                do {
+                    descriptor++;
+                } while (*descriptor == '[');
+                /*
+                 * Don't increment count, as it will be taken care of
+                 * by the next iteration. Also, decrement descriptor
+                 * to compensate for the increment below the switch.
+                 */
+                descriptor--;
+                break;
+            }
+            case 'L': {
+                do {
+                    descriptor++;
+                } while ((*descriptor != ';') && (*descriptor != '\0'));
+                count++;
+                if (*descriptor == '\0') {
+                    /* Bogus descriptor. */
+                    done = true;
+                    bogus = true;
+                }
+                break;
+            }
+            case ')': {
+                /*
+                 * Note: The loop will exit after incrementing descriptor
+                 * one more time, so it then points at the return type.
+                 */
+                done = true;
+                break;
+            }
+            default: {
+                /* Bogus descriptor. */
+                done = true;
+                bogus = true;
+                break;
+            }
+        }
+
+        descriptor++;
+    }
+
+    if (bogus) {
+        *pReturnType = NULL;
+        return 0;
+    }
+
+    *pReturnType = descriptor;
+    return count;
+}
+
+/*
+ * Copy the argument types into the given array using the given buffer
+ * for the contents.
+ */
+static inline void copyTypes(char* buffer, const char** argTypes,
+    size_t argCount, const char* descriptor)
+{
+    size_t i;
+    char c;
+
+    /* Skip the '('. */
+    descriptor++;
+
+    for (i = 0; i < argCount; i++) {
+        argTypes[i] = buffer;
+
+        /* Copy all the array markers and one extra character. */
+        do {
+            c = *(descriptor++);
+            *(buffer++) = c;
+        } while (c == '[');
+
+        if (c == 'L') {
+            /* Copy the rest of a class name. */
+            do {
+                c = *(descriptor++);
+                *(buffer++) = c;
+            } while (c != ';');
+        }
+
+        *(buffer++) = '\0';
+    }
+}
+
+/*
+ * Look for a match in the given class. Returns the match if found
+ * or NULL if not.
+ */
+static Method* findMethodInListByDescriptor(const ClassObject* clazz,
+    bool findVirtual, bool isHier, const char* name, const char* descriptor)
+{
+    const char* returnType;
+    size_t argCount = countArgsAndFindReturnType(descriptor, &returnType);
+
+    if (returnType == NULL) {
+        ALOGW("Bogus method descriptor: %s", descriptor);
+        return NULL;
+    }
+
+    /*
+     * Make buffer big enough for all the argument type characters and
+     * one '\0' per argument. The "- 2" is because "returnType -
+     * descriptor" includes two parens.
+     */
+    char buffer[argCount + (returnType - descriptor) - 2];
+    const char* argTypes[argCount];
+
+    copyTypes(buffer, argTypes, argCount, descriptor);
+
+    while (clazz != NULL) {
+        Method* methods;
+        size_t methodCount;
+        size_t i;
+
+        if (findVirtual) {
+            methods = clazz->virtualMethods;
+            methodCount = clazz->virtualMethodCount;
+        } else {
+            methods = clazz->directMethods;
+            methodCount = clazz->directMethodCount;
+        }
+
+        for (i = 0; i < methodCount; i++) {
+            Method* method = &methods[i];
+            if (compareMethodHelper(method, name, returnType, argCount,
+                            argTypes) == 0) {
+                return method;
+            }
+        }
+
+        if (! isHier) {
+            break;
+        }
+
+        clazz = clazz->super;
+    }
+
+    return NULL;
+}
+
+/*
+ * Look for a match in the given clazz. Returns the match if found
+ * or NULL if not.
+ *
+ * "wantedType" should be METHOD_VIRTUAL or METHOD_DIRECT to indicate the
+ * list to search through.  If the match can come from either list, use
+ * MATCH_UNKNOWN to scan both.
+ */
+static Method* findMethodInListByProto(const ClassObject* clazz,
+    MethodType wantedType, bool isHier, const char* name, const DexProto* proto)
+{
+    while (clazz != NULL) {
+        int i;
+
+        /*
+         * Check the virtual and/or direct method lists.
+         */
+        if (wantedType == METHOD_VIRTUAL || wantedType == METHOD_UNKNOWN) {
+            for (i = 0; i < clazz->virtualMethodCount; i++) {
+                Method* method = &clazz->virtualMethods[i];
+                if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) {
+                    return method;
+                }
+            }
+        }
+        if (wantedType == METHOD_DIRECT || wantedType == METHOD_UNKNOWN) {
+            for (i = 0; i < clazz->directMethodCount; i++) {
+                Method* method = &clazz->directMethods[i];
+                if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) {
+                    return method;
+                }
+            }
+        }
+
+        if (! isHier) {
+            break;
+        }
+
+        clazz = clazz->super;
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a "virtual" method in a class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, true, false,
+            methodName, descriptor);
+
+    // TODO? - throw IncompatibleClassChangeError if a match is
+    // found in the directMethods list, rather than NotFoundError.
+    // Note we could have been called by dvmFindVirtualMethodHier though.
+}
+
+
+/*
+ * Find a "virtual" method in a class, knowing only the name.  This is
+ * only useful in limited circumstances, e.g. when searching for a member
+ * of an annotation class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodByName(const ClassObject* clazz,
+    const char* methodName)
+{
+    Method* methods = clazz->virtualMethods;
+    int methodCount = clazz->virtualMethodCount;
+    int i;
+
+    for (i = 0; i < methodCount; i++) {
+        if (strcmp(methods[i].name, methodName) == 0)
+            return &methods[i];
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a "virtual" method in a class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_VIRTUAL, false, methodName,
+            proto);
+}
+
+/*
+ * Find a "virtual" method in a class.  If we don't find it, try the
+ * superclass.  Does not examine interfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, true, true,
+            methodName, descriptor);
+}
+
+/*
+ * Find a "virtual" method in a class.  If we don't find it, try the
+ * superclass.  Does not examine interfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_VIRTUAL, true, methodName,
+            proto);
+}
+
+/*
+ * Find a method in an interface.  Searches superinterfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindInterfaceMethodHierByDescriptor(const ClassObject* iface,
+    const char* methodName, const char* descriptor)
+{
+    Method* resMethod = dvmFindVirtualMethodByDescriptor(iface,
+        methodName, descriptor);
+    if (resMethod == NULL) {
+        /* scan superinterfaces and superclass interfaces */
+        int i;
+        for (i = 0; i < iface->iftableCount; i++) {
+            resMethod = dvmFindVirtualMethodByDescriptor(iface->iftable[i].clazz,
+                methodName, descriptor);
+            if (resMethod != NULL)
+                break;
+        }
+    }
+    return resMethod;
+}
+
+/*
+ * Find a method in an interface.  Searches superinterfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindInterfaceMethodHier(const ClassObject* iface,
+    const char* methodName, const DexProto* proto)
+{
+    Method* resMethod = dvmFindVirtualMethod(iface, methodName, proto);
+    if (resMethod == NULL) {
+        /* scan superinterfaces and superclass interfaces */
+        int i;
+        for (i = 0; i < iface->iftableCount; i++) {
+            resMethod = dvmFindVirtualMethod(iface->iftable[i].clazz,
+                methodName, proto);
+            if (resMethod != NULL)
+                break;
+        }
+    }
+    return resMethod;
+}
+
+/*
+ * Find a "direct" method (static, private, or "<*init>").
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, false, false,
+            methodName, descriptor);
+}
+
+/*
+ * Find a "direct" method.  If we don't find it, try the superclass.  This
+ * is only appropriate for static methods, but will work for all direct
+ * methods.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, false, true,
+            methodName, descriptor);
+}
+
+/*
+ * Find a "direct" method (static or "<*init>").
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_DIRECT, false, methodName,
+            proto);
+}
+
+/*
+ * Find a "direct" method in a class.  If we don't find it, try the
+ * superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_DIRECT, true, methodName,
+            proto);
+}
+
+/*
+ * Find a virtual or static method in a class.  If we don't find it, try the
+ * superclass.  This is compatible with the VM spec (v2 5.4.3.3) method
+ * search order, but it stops short of scanning through interfaces (which
+ * should be done after this function completes).
+ *
+ * In most cases we know that we're looking for either a static or an
+ * instance field and there's no value in searching through both types.
+ * During verification we need to recognize and reject certain unusual
+ * situations, and we won't see them unless we walk the lists this way.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_UNKNOWN, true, methodName,
+            proto);
+}
+
+
+/*
+ * We have a method pointer for a method in "clazz", but it might be
+ * pointing to a method in a derived class.  We want to find the actual entry
+ * from the class' vtable.  If "clazz" is an interface, we have to do a
+ * little more digging.
+ *
+ * For "direct" methods (private / constructor), we just return the
+ * original Method.
+ *
+ * (This is used for reflection and JNI "call method" calls.)
+ */
+const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
+    const Method* meth)
+{
+    Method* actualMeth;
+    int methodIndex;
+
+    if (dvmIsDirectMethod(meth)) {
+        /* no vtable entry for these */
+        assert(!dvmIsStaticMethod(meth));
+        return meth;
+    }
+
+    /*
+     * If the method was declared in an interface, we need to scan through
+     * the class' list of interfaces for it, and find the vtable index
+     * from that.
+     *
+     * TODO: use the interface cache.
+     */
+    if (dvmIsInterfaceClass(meth->clazz)) {
+        int i;
+
+        for (i = 0; i < clazz->iftableCount; i++) {
+            if (clazz->iftable[i].clazz == meth->clazz)
+                break;
+        }
+        if (i == clazz->iftableCount) {
+            dvmThrowIncompatibleClassChangeError(
+                "invoking method from interface not implemented by class");
+            return NULL;
+        }
+
+        methodIndex = clazz->iftable[i].methodIndexArray[meth->methodIndex];
+    } else {
+        methodIndex = meth->methodIndex;
+    }
+
+    assert(methodIndex >= 0 && methodIndex < clazz->vtableCount);
+    actualMeth = clazz->vtable[methodIndex];
+
+    /*
+     * Make sure there's code to execute.
+     */
+    if (dvmIsAbstractMethod(actualMeth)) {
+        dvmThrowAbstractMethodError(NULL);
+        return NULL;
+    }
+    assert(!dvmIsMirandaMethod(actualMeth));
+
+    return actualMeth;
+}
+
+/*
+ * Get the source file for a method.
+ */
+const char* dvmGetMethodSourceFile(const Method* meth)
+{
+    /*
+     * TODO: A method's debug info can override the default source
+     * file for a class, so we should account for that possibility
+     * here.
+     */
+    return meth->clazz->sourceFile;
+}
+
+/*
+ * Dump some information about an object.
+ */
+void dvmDumpObject(const Object* obj)
+{
+    ClassObject* clazz;
+    int i;
+
+    if (obj == NULL || obj->clazz == NULL) {
+        ALOGW("Null or malformed object not dumped");
+        return;
+    }
+
+    clazz = obj->clazz;
+    ALOGD("----- Object dump: %p (%s, %d bytes) -----",
+        obj, clazz->descriptor, (int) clazz->objectSize);
+    //printHexDump(obj, clazz->objectSize);
+    ALOGD("  Fields:");
+    while (clazz != NULL) {
+        ALOGD("    -- %s", clazz->descriptor);
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            const InstField* pField = &clazz->ifields[i];
+            char type = pField->signature[0];
+
+            if (type == 'F' || type == 'D') {
+                double dval;
+
+                if (type == 'F')
+                    dval = dvmGetFieldFloat(obj, pField->byteOffset);
+                else
+                    dval = dvmGetFieldDouble(obj, pField->byteOffset);
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%d %.3f", i,
+                    pField->name, pField->signature,
+                    pField->accessFlags, pField->byteOffset, dval);
+            } else {
+                u8 lval;
+
+                if (type == 'J')
+                    lval = dvmGetFieldLong(obj, pField->byteOffset);
+                else if (type == 'Z')
+                    lval = dvmGetFieldBoolean(obj, pField->byteOffset);
+                else
+                    lval = dvmGetFieldInt(obj, pField->byteOffset);
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%d 0x%08llx", i,
+                    pField->name, pField->signature,
+                    pField->accessFlags, pField->byteOffset, lval);
+            }
+        }
+
+        clazz = clazz->super;
+    }
+    if (dvmIsClassObject(obj)) {
+        ALOGD("  Static fields:");
+        const StaticField* sfields = &((ClassObject *)obj)->sfields[0];
+        for (i = 0; i < ((ClassObject *)obj)->sfieldCount; ++i) {
+            const StaticField* pField = &sfields[i];
+            size_t byteOffset = (size_t)pField - (size_t)sfields;
+            char type = pField->signature[0];
+
+            if (type == 'F' || type == 'D') {
+                double dval;
+
+                if (type == 'F')
+                    dval = pField->value.f;
+                else
+                    dval = pField->value.d;
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%zd %.3f", i,
+                     pField->name, pField->signature,
+                     pField->accessFlags, byteOffset, dval);
+            } else {
+                u8 lval;
+
+                if (type == 'J')
+                    lval = pField->value.j;
+                else if (type == 'Z')
+                    lval = pField->value.z;
+                else
+                    lval = pField->value.i;
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%zd 0x%08llx", i,
+                     pField->name, pField->signature,
+                     pField->accessFlags, byteOffset, lval);
+            }
+        }
+    }
+}
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
new file mode 100644
index 0000000..4e6103a
--- /dev/null
+++ b/vm/oo/Object.h
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Declaration of the fundamental Object type and refinements thereof, plus
+ * some functions for manipulating them.
+ */
+#ifndef DALVIK_OO_OBJECT_H_
+#define DALVIK_OO_OBJECT_H_
+
+#include <stddef.h>
+#include "Atomic.h"
+
+/* fwd decl */
+struct DataObject;
+struct InitiatingLoaderList;
+struct ClassObject;
+struct StringObject;
+struct ArrayObject;
+struct Method;
+struct ExceptionEntry;
+struct LineNumEntry;
+struct StaticField;
+struct InstField;
+struct Field;
+struct RegisterMap;
+
+/*
+ * Native function pointer type.
+ *
+ * "args[0]" holds the "this" pointer for virtual methods.
+ *
+ * The "Bridge" form is a super-set of the "Native" form; in many places
+ * they are used interchangeably.  Currently, all functions have all
+ * arguments passed in, but some functions only care about the first two.
+ * Passing extra arguments to a C function is (mostly) harmless.
+ */
+typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult,
+    const Method* method, struct Thread* self);
+typedef void (*DalvikNativeFunc)(const u4* args, JValue* pResult);
+
+
+/* vm-internal access flags and related definitions */
+enum AccessFlags {
+    ACC_MIRANDA         = 0x8000,       // method (internal to VM)
+    JAVA_FLAGS_MASK     = 0xffff,       // bits set from Java sources (low 16)
+};
+
+/* Use the top 16 bits of the access flags field for
+ * other class flags.  Code should use the *CLASS_FLAG*()
+ * macros to set/get these flags.
+ */
+enum ClassFlags {
+    CLASS_ISFINALIZABLE        = (1<<31), // class/ancestor overrides finalize()
+    CLASS_ISARRAY              = (1<<30), // class is a "[*"
+    CLASS_ISOBJECTARRAY        = (1<<29), // class is a "[L*" or "[[*"
+    CLASS_ISCLASS              = (1<<28), // class is *the* class Class
+
+    CLASS_ISREFERENCE          = (1<<27), // class is a soft/weak/phantom ref
+                                          // only ISREFERENCE is set --> soft
+    CLASS_ISWEAKREFERENCE      = (1<<26), // class is a weak reference
+    CLASS_ISFINALIZERREFERENCE = (1<<25), // class is a finalizer reference
+    CLASS_ISPHANTOMREFERENCE   = (1<<24), // class is a phantom reference
+
+    CLASS_MULTIPLE_DEFS        = (1<<23), // DEX verifier: defs in multiple DEXs
+
+    /* unlike the others, these can be present in the optimized DEX file */
+    CLASS_ISOPTIMIZED          = (1<<17), // class may contain opt instrs
+    CLASS_ISPREVERIFIED        = (1<<16), // class has been pre-verified
+};
+
+/* 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.
+ */
+enum MethodFlags {
+    METHOD_ISWRITABLE       = (1<<31),  // the method's code is writable
+};
+
+/*
+ * Get/set method flags.
+ */
+#define SET_METHOD_FLAG(method, flag) \
+    do { (method)->accessFlags |= (flag); } while (0)
+
+#define CLEAR_METHOD_FLAG(method, flag) \
+    do { (method)->accessFlags &= ~(flag); } while (0)
+
+#define IS_METHOD_FLAG_SET(method, flag) \
+    (((method)->accessFlags & (flag)) != 0)
+
+#define GET_METHOD_FLAG_GROUP(method, flags) \
+    ((u4)((method)->accessFlags & (flags)))
+
+/* current state of the class, increasing as we progress */
+enum ClassStatus {
+    CLASS_ERROR         = -1,
+
+    CLASS_NOTREADY      = 0,
+    CLASS_IDX           = 1,    /* loaded, DEX idx in super or ifaces */
+    CLASS_LOADED        = 2,    /* DEX idx values resolved */
+    CLASS_RESOLVED      = 3,    /* part of linking */
+    CLASS_VERIFYING     = 4,    /* in the process of being verified */
+    CLASS_VERIFIED      = 5,    /* logically part of linking; done pre-init */
+    CLASS_INITIALIZING  = 6,    /* class init in progress */
+    CLASS_INITIALIZED   = 7,    /* ready to go */
+};
+
+/*
+ * Definitions for packing refOffsets in ClassObject.
+ */
+/*
+ * A magic value for refOffsets. Ignore the bits and walk the super
+ * chain when this is the value.
+ * [This is an unlikely "natural" value, since it would be 30 non-ref instance
+ * fields followed by 2 ref instance fields.]
+ */
+#define CLASS_WALK_SUPER ((unsigned int)(3))
+#define CLASS_SMALLEST_OFFSET (sizeof(struct Object))
+#define CLASS_BITS_PER_WORD (sizeof(unsigned long int) * 8)
+#define CLASS_OFFSET_ALIGNMENT 4
+#define CLASS_HIGH_BIT ((unsigned int)1 << (CLASS_BITS_PER_WORD - 1))
+/*
+ * Given an offset, return the bit number which would encode that offset.
+ * Local use only.
+ */
+#define _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) \
+    (((unsigned int)(byteOffset) - CLASS_SMALLEST_OFFSET) / \
+     CLASS_OFFSET_ALIGNMENT)
+/*
+ * Is the given offset too large to be encoded?
+ */
+#define CLASS_CAN_ENCODE_OFFSET(byteOffset) \
+    (_CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) < CLASS_BITS_PER_WORD)
+/*
+ * Return a single bit, encoding the offset.
+ * Undefined if the offset is too large, as defined above.
+ */
+#define CLASS_BIT_FROM_OFFSET(byteOffset) \
+    (CLASS_HIGH_BIT >> _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset))
+/*
+ * Return an offset, given a bit number as returned from CLZ.
+ */
+#define CLASS_OFFSET_FROM_CLZ(rshift) \
+    (((int)(rshift) * CLASS_OFFSET_ALIGNMENT) + CLASS_SMALLEST_OFFSET)
+
+
+/*
+ * Used for iftable in ClassObject.
+ */
+struct InterfaceEntry {
+    /* pointer to interface class */
+    ClassObject*    clazz;
+
+    /*
+     * Index into array of vtable offsets.  This points into the ifviPool,
+     * which holds the vtables for all interfaces declared by this class.
+     */
+    int*            methodIndexArray;
+};
+
+
+
+/*
+ * There are three types of objects:
+ *  Class objects - an instance of java.lang.Class
+ *  Array objects - an object created with a "new array" instruction
+ *  Data objects - an object that is neither of the above
+ *
+ * We also define String objects.  At present they're equivalent to
+ * DataObject, but that may change.  (Either way, they make some of the
+ * code more obvious.)
+ *
+ * All objects have an Object header followed by type-specific data.
+ */
+struct Object {
+    /* ptr to class object */
+    ClassObject*    clazz;
+
+    /*
+     * A word containing either a "thin" lock or a "fat" monitor.  See
+     * the comments in Sync.c for a description of its layout.
+     */
+    u4              lock;
+};
+
+/*
+ * Properly initialize an Object.
+ * void DVM_OBJECT_INIT(Object *obj, ClassObject *clazz_)
+ */
+#define DVM_OBJECT_INIT(obj, clazz_) \
+    dvmSetFieldObject(obj, OFFSETOF_MEMBER(Object, clazz), clazz_)
+
+/*
+ * Data objects have an Object header followed by their instance data.
+ */
+struct DataObject : Object {
+    /* variable #of u4 slots; u8 uses 2 slots */
+    u4              instanceData[1];
+};
+
+/*
+ * Strings are used frequently enough that we may want to give them their
+ * own unique type.
+ *
+ * Using a dedicated type object to access the instance data provides a
+ * performance advantage but makes the java/lang/String.java implementation
+ * fragile.
+ *
+ * Currently this is just equal to DataObject, and we pull the fields out
+ * like we do for any other object.
+ */
+struct StringObject : Object {
+    /* variable #of u4 slots; u8 uses 2 slots */
+    u4              instanceData[1];
+
+    /** Returns this string's length in characters. */
+    int length() const;
+
+    /**
+     * Returns this string's length in bytes when encoded as modified UTF-8.
+     * Does not include a terminating NUL byte.
+     */
+    int utfLength() const;
+
+    /** Returns this string's char[] as an ArrayObject. */
+    ArrayObject* array() const;
+
+    /** Returns this string's char[] as a u2*. */
+    const u2* chars() const;
+};
+
+
+/*
+ * Array objects have these additional fields.
+ *
+ * We don't currently store the size of each element.  Usually it's implied
+ * by the instruction.  If necessary, the width can be derived from
+ * the first char of obj->clazz->descriptor.
+ */
+struct ArrayObject : Object {
+    /* number of elements; immutable after init */
+    u4              length;
+
+    /*
+     * Array contents; actual size is (length * sizeof(type)).  This is
+     * declared as u8 so that the compiler inserts any necessary padding
+     * (e.g. for EABI); the actual allocation may be smaller than 8 bytes.
+     */
+    u8              contents[1];
+};
+
+/*
+ * For classes created early and thus probably in the zygote, the
+ * InitiatingLoaderList is kept in gDvm. Later classes use the structure in
+ * Object Class. This helps keep zygote pages shared.
+ */
+struct InitiatingLoaderList {
+    /* a list of initiating loader Objects; grown and initialized on demand */
+    Object**  initiatingLoaders;
+    /* count of loaders in the above list */
+    int       initiatingLoaderCount;
+};
+
+/*
+ * Generic field header.  We pass this around when we want a generic Field
+ * pointer (e.g. for reflection stuff).  Testing the accessFlags for
+ * ACC_STATIC allows a proper up-cast.
+ */
+struct Field {
+    ClassObject*    clazz;          /* class in which the field is declared */
+    const char*     name;
+    const char*     signature;      /* e.g. "I", "[C", "Landroid/os/Debug;" */
+    u4              accessFlags;
+};
+
+/*
+ * Static field.
+ */
+struct StaticField : Field {
+    JValue          value;          /* initially set from DEX for primitives */
+};
+
+/*
+ * Instance field.
+ */
+struct InstField : Field {
+    /*
+     * This field indicates the byte offset from the beginning of the
+     * (Object *) to the actual instance data; e.g., byteOffset==0 is
+     * the same as the object pointer (bug!), and byteOffset==4 is 4
+     * bytes farther.
+     */
+    int             byteOffset;
+};
+
+/*
+ * This defines the amount of space we leave for field slots in the
+ * java.lang.Class definition.  If we alter the class to have more than
+ * this many fields, the VM will abort at startup.
+ */
+#define CLASS_FIELD_SLOTS   4
+
+/*
+ * Class objects have many additional fields.  This is used for both
+ * classes and interfaces, including synthesized classes (arrays and
+ * primitive types).
+ *
+ * Class objects are unusual in that they have some fields allocated with
+ * the system malloc (or LinearAlloc), rather than on the GC heap.  This is
+ * handy during initialization, but does require special handling when
+ * discarding java.lang.Class objects.
+ *
+ * The separation of methods (direct vs. virtual) and fields (class vs.
+ * instance) used in Dalvik works out pretty well.  The only time it's
+ * annoying is when enumerating or searching for things with reflection.
+ */
+struct ClassObject : Object {
+    /* leave space for instance data; we could access fields directly if we
+       freeze the definition of java/lang/Class */
+    u4              instanceData[CLASS_FIELD_SLOTS];
+
+    /* UTF-8 descriptor for the class; from constant pool, or on heap
+       if generated ("[C") */
+    const char*     descriptor;
+    char*           descriptorAlloc;
+
+    /* access flags; low 16 bits are defined by VM spec */
+    u4              accessFlags;
+
+    /* VM-unique class serial number, nonzero, set very early */
+    u4              serialNumber;
+
+    /* DexFile from which we came; needed to resolve constant pool entries */
+    /* (will be NULL for VM-generated, e.g. arrays and primitive classes) */
+    DvmDex*         pDvmDex;
+
+    /* state of class initialization */
+    ClassStatus     status;
+
+    /* if class verify fails, we must return same error on subsequent tries */
+    ClassObject*    verifyErrorClass;
+
+    /* threadId, used to check for recursive <clinit> invocation */
+    u4              initThreadId;
+
+    /*
+     * Total object size; used when allocating storage on gc heap.  (For
+     * interfaces and abstract classes this will be zero.)
+     */
+    size_t          objectSize;
+
+    /* arrays only: class object for base element, for instanceof/checkcast
+       (for String[][][], this will be String) */
+    ClassObject*    elementClass;
+
+    /* arrays only: number of dimensions, e.g. int[][] is 2 */
+    int             arrayDim;
+
+    /* primitive type index, or PRIM_NOT (-1); set for generated prim classes */
+    PrimitiveType   primitiveType;
+
+    /* superclass, or NULL if this is java.lang.Object */
+    ClassObject*    super;
+
+    /* defining class loader, or NULL for the "bootstrap" system loader */
+    Object*         classLoader;
+
+    /* initiating class loader list */
+    /* NOTE: for classes with low serialNumber, these are unused, and the
+       values are kept in a table in gDvm. */
+    InitiatingLoaderList initiatingLoaderList;
+
+    /* array of interfaces this class implements directly */
+    int             interfaceCount;
+    ClassObject**   interfaces;
+
+    /* static, private, and <init> methods */
+    int             directMethodCount;
+    Method*         directMethods;
+
+    /* virtual methods defined in this class; invoked through vtable */
+    int             virtualMethodCount;
+    Method*         virtualMethods;
+
+    /*
+     * Virtual method table (vtable), for use by "invoke-virtual".  The
+     * vtable from the superclass is copied in, and virtual methods from
+     * our class either replace those from the super or are appended.
+     */
+    int             vtableCount;
+    Method**        vtable;
+
+    /*
+     * Interface table (iftable), one entry per interface supported by
+     * this class.  That means one entry for each interface we support
+     * directly, indirectly via superclass, or indirectly via
+     * superinterface.  This will be null if neither we nor our superclass
+     * implement any interfaces.
+     *
+     * Why we need this: given "class Foo implements Face", declare
+     * "Face faceObj = new Foo()".  Invoke faceObj.blah(), where "blah" is
+     * part of the Face interface.  We can't easily use a single vtable.
+     *
+     * For every interface a concrete class implements, we create a list of
+     * virtualMethod indices for the methods in the interface.
+     */
+    int             iftableCount;
+    InterfaceEntry* iftable;
+
+    /*
+     * The interface vtable indices for iftable get stored here.  By placing
+     * them all in a single pool for each class that implements interfaces,
+     * we decrease the number of allocations.
+     */
+    int             ifviPoolCount;
+    int*            ifviPool;
+
+    /* instance fields
+     *
+     * These describe the layout of the contents of a DataObject-compatible
+     * Object.  Note that only the fields directly defined by this class
+     * are listed in ifields;  fields defined by a superclass are listed
+     * in the superclass's ClassObject.ifields.
+     *
+     * All instance fields that refer to objects are guaranteed to be
+     * at the beginning of the field list.  ifieldRefCount specifies
+     * the number of reference fields.
+     */
+    int             ifieldCount;
+    int             ifieldRefCount; // number of fields that are object refs
+    InstField*      ifields;
+
+    /* bitmap of offsets of ifields */
+    u4 refOffsets;
+
+    /* source file name, if known */
+    const char*     sourceFile;
+
+    /* static fields */
+    int             sfieldCount;
+    StaticField     sfields[0]; /* MUST be last item */
+};
+
+/*
+ * A method.  We create one of these for every method in every class
+ * we load, so try to keep the size to a minimum.
+ *
+ * Much of this comes from and could be accessed in the data held in shared
+ * memory.  We hold it all together here for speed.  Everything but the
+ * pointers could be held in a shared table generated by the optimizer;
+ * if we're willing to convert them to offsets and take the performance
+ * hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we
+ * could move everything but "nativeFunc".
+ */
+struct Method {
+    /* the class we are a part of */
+    ClassObject*    clazz;
+
+    /* access flags; low 16 bits are defined by spec (could be u2?) */
+    u4              accessFlags;
+
+    /*
+     * For concrete virtual methods, this is the offset of the method
+     * in "vtable".
+     *
+     * For abstract methods in an interface class, this is the offset
+     * of the method in "iftable[n]->methodIndexArray".
+     */
+    u2             methodIndex;
+
+    /*
+     * Method bounds; not needed for an abstract method.
+     *
+     * For a native method, we compute the size of the argument list, and
+     * set "insSize" and "registerSize" equal to it.
+     */
+    u2              registersSize;  /* ins + locals */
+    u2              outsSize;
+    u2              insSize;
+
+    /* method name, e.g. "<init>" or "eatLunch" */
+    const char*     name;
+
+    /*
+     * Method prototype descriptor string (return and argument types).
+     *
+     * TODO: This currently must specify the DexFile as well as the proto_ids
+     * index, because generated Proxy classes don't have a DexFile.  We can
+     * remove the DexFile* and reduce the size of this struct if we generate
+     * a DEX for proxies.
+     */
+    DexProto        prototype;
+
+    /* short-form method descriptor string */
+    const char*     shorty;
+
+    /*
+     * The remaining items are not used for abstract or native methods.
+     * (JNI is currently hijacking "insns" as a function pointer, set
+     * after the first call.  For internal-native this stays null.)
+     */
+
+    /* the actual code */
+    const u2*       insns;          /* instructions, in memory-mapped .dex */
+
+    /* JNI: cached argument and return-type hints */
+    int             jniArgInfo;
+
+    /*
+     * JNI: native method ptr; could be actual function or a JNI bridge.  We
+     * don't currently discriminate between DalvikBridgeFunc and
+     * DalvikNativeFunc; the former takes an argument superset (i.e. two
+     * extra args) which will be ignored.  If necessary we can use
+     * insns==NULL to detect JNI bridge vs. internal native.
+     */
+    DalvikBridgeFunc nativeFunc;
+
+    /*
+     * JNI: true if this static non-synchronized native method (that has no
+     * reference arguments) needs a JNIEnv* and jclass/jobject. Libcore
+     * uses this.
+     */
+    bool fastJni;
+
+    /*
+     * JNI: true if this method has no reference arguments. This lets the JNI
+     * bridge avoid scanning the shorty for direct pointers that need to be
+     * converted to local references.
+     *
+     * TODO: replace this with a list of indexes of the reference arguments.
+     */
+    bool noRef;
+
+    /*
+     * JNI: true if we should log entry and exit. This is the only way
+     * developers can log the local references that are passed into their code.
+     * Used for debugging JNI problems in third-party code.
+     */
+    bool shouldTrace;
+
+    /*
+     * Register map data, if available.  This will point into the DEX file
+     * if the data was computed during pre-verification, or into the
+     * linear alloc area if not.
+     */
+    const RegisterMap* registerMap;
+
+    /* set if method was called during method profiling */
+    bool            inProfile;
+};
+
+
+/*
+ * Find a method within a class.  The superclass is not searched.
+ */
+Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* signature);
+Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* signature);
+Method* dvmFindVirtualMethodByName(const ClassObject* clazz,
+    const char* methodName);
+Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto);
+Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto);
+
+
+/*
+ * Find a method within a class hierarchy.
+ */
+Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor);
+Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* signature);
+Method* dvmFindDirectMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto);
+Method* dvmFindVirtualMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto);
+Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto);
+
+/*
+ * Find a method in an interface hierarchy.
+ */
+Method* dvmFindInterfaceMethodHierByDescriptor(const ClassObject* iface,
+    const char* methodName, const char* descriptor);
+Method* dvmFindInterfaceMethodHier(const ClassObject* iface,
+    const char* methodName, const DexProto* proto);
+
+/*
+ * Find the implementation of "meth" in "clazz".
+ *
+ * Returns NULL and throws an exception if not found.
+ */
+const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
+    const Method* meth);
+
+/*
+ * Get the source file associated with a method.
+ */
+extern "C" const char* dvmGetMethodSourceFile(const Method* meth);
+
+/*
+ * Find a field within a class.  The superclass is not searched.
+ */
+InstField* dvmFindInstanceField(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+StaticField* dvmFindStaticField(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+
+/*
+ * Find a field in a class/interface hierarchy.
+ */
+InstField* dvmFindInstanceFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+StaticField* dvmFindStaticFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName,
+    const char* signature);
+
+/*
+ * Find a field and return the byte offset from the object pointer.  Only
+ * searches the specified class, not the superclass.
+ *
+ * Returns -1 on failure.
+ */
+INLINE int dvmFindFieldOffset(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    InstField* pField = dvmFindInstanceField(clazz, fieldName, signature);
+    if (pField == NULL)
+        return -1;
+    else
+        return pField->byteOffset;
+}
+
+/*
+ * Helpers.
+ */
+INLINE bool dvmIsPublicMethod(const Method* method) {
+    return (method->accessFlags & ACC_PUBLIC) != 0;
+}
+INLINE bool dvmIsPrivateMethod(const Method* method) {
+    return (method->accessFlags & ACC_PRIVATE) != 0;
+}
+INLINE bool dvmIsStaticMethod(const Method* method) {
+    return (method->accessFlags & ACC_STATIC) != 0;
+}
+INLINE bool dvmIsSynchronizedMethod(const Method* method) {
+    return (method->accessFlags & ACC_SYNCHRONIZED) != 0;
+}
+INLINE bool dvmIsDeclaredSynchronizedMethod(const Method* method) {
+    return (method->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+}
+INLINE bool dvmIsFinalMethod(const Method* method) {
+    return (method->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsNativeMethod(const Method* method) {
+    return (method->accessFlags & ACC_NATIVE) != 0;
+}
+INLINE bool dvmIsAbstractMethod(const Method* method) {
+    return (method->accessFlags & ACC_ABSTRACT) != 0;
+}
+INLINE bool dvmIsSyntheticMethod(const Method* method) {
+    return (method->accessFlags & ACC_SYNTHETIC) != 0;
+}
+INLINE bool dvmIsMirandaMethod(const Method* method) {
+    return (method->accessFlags & ACC_MIRANDA) != 0;
+}
+INLINE bool dvmIsConstructorMethod(const Method* method) {
+    return *method->name == '<';
+}
+/* Dalvik puts private, static, and constructors into non-virtual table */
+INLINE bool dvmIsDirectMethod(const Method* method) {
+    return dvmIsPrivateMethod(method) ||
+           dvmIsStaticMethod(method) ||
+           dvmIsConstructorMethod(method);
+}
+/* Get whether the given method has associated bytecode. This is the
+ * case for methods which are neither native nor abstract. */
+INLINE bool dvmIsBytecodeMethod(const Method* method) {
+    return (method->accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0;
+}
+
+INLINE bool dvmIsProtectedField(const Field* field) {
+    return (field->accessFlags & ACC_PROTECTED) != 0;
+}
+INLINE bool dvmIsStaticField(const Field* field) {
+    return (field->accessFlags & ACC_STATIC) != 0;
+}
+INLINE bool dvmIsFinalField(const Field* field) {
+    return (field->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsVolatileField(const Field* field) {
+    return (field->accessFlags & ACC_VOLATILE) != 0;
+}
+
+INLINE bool dvmIsInterfaceClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_INTERFACE) != 0;
+}
+INLINE bool dvmIsPublicClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_PUBLIC) != 0;
+}
+INLINE bool dvmIsFinalClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsAbstractClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_ABSTRACT) != 0;
+}
+INLINE bool dvmIsAnnotationClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_ANNOTATION) != 0;
+}
+INLINE bool dvmIsPrimitiveClass(const ClassObject* clazz) {
+    return clazz->primitiveType != PRIM_NOT;
+}
+
+/* linked, here meaning prepared and resolved */
+INLINE bool dvmIsClassLinked(const ClassObject* clazz) {
+    return clazz->status >= CLASS_RESOLVED;
+}
+/* has class been verified? */
+INLINE bool dvmIsClassVerified(const ClassObject* clazz) {
+    return clazz->status >= CLASS_VERIFIED;
+}
+
+/*
+ * Return whether the given object is an instance of Class.
+ */
+INLINE bool dvmIsClassObject(const Object* obj) {
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    return IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISCLASS);
+}
+
+/*
+ * Return whether the given object is the class Class (that is, the
+ * unique class which is an instance of itself).
+ */
+INLINE bool dvmIsTheClassClass(const ClassObject* clazz) {
+    assert(clazz != NULL);
+    return IS_CLASS_FLAG_SET(clazz, CLASS_ISCLASS);
+}
+
+/*
+ * Get the associated code struct for a method. This returns NULL
+ * for non-bytecode methods.
+ */
+INLINE const DexCode* dvmGetMethodCode(const Method* meth) {
+    if (dvmIsBytecodeMethod(meth)) {
+        /*
+         * The insns field for a bytecode method actually points at
+         * &(DexCode.insns), so we can subtract back to get at the
+         * DexCode in front.
+         */
+        return (const DexCode*)
+            (((const u1*) meth->insns) - offsetof(DexCode, insns));
+    } else {
+        return NULL;
+    }
+}
+
+/*
+ * Get the size of the insns associated with a method. This returns 0
+ * for non-bytecode methods.
+ */
+INLINE u4 dvmGetMethodInsnsSize(const Method* meth) {
+    const DexCode* pCode = dvmGetMethodCode(meth);
+    return (pCode == NULL) ? 0 : pCode->insnsSize;
+}
+
+/* debugging */
+void dvmDumpObject(const Object* obj);
+
+#endif  // DALVIK_OO_OBJECT_H_
diff --git a/vm/oo/ObjectInlines.h b/vm/oo/ObjectInlines.h
new file mode 100644
index 0000000..eb2e962
--- /dev/null
+++ b/vm/oo/ObjectInlines.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Helper functions to access data fields in Objects.
+ */
+#ifndef DALVIK_OO_OBJECTINLINES_H_
+#define DALVIK_OO_OBJECTINLINES_H_
+
+/*
+ * Store a single value in the array, and if the value isn't null,
+ * note in the write barrier.
+ */
+INLINE void dvmSetObjectArrayElement(const ArrayObject* obj, int index,
+                                     Object* val) {
+    ((Object **)(void *)(obj)->contents)[index] = val;
+    if (val != NULL) {
+        dvmWriteBarrierArray(obj, index, index + 1);
+    }
+}
+
+
+/*
+ * Field access functions.  Pass in the word offset from Field->byteOffset.
+ *
+ * We guarantee that long/double field data is 64-bit aligned, so it's safe
+ * to access them with ldrd/strd on ARM.
+ *
+ * The VM treats all fields as 32 or 64 bits, so the field set functions
+ * write 32 bits even if the underlying type is smaller.
+ *
+ * Setting Object types to non-null values includes a call to the
+ * write barrier.
+ */
+#define BYTE_OFFSET(_ptr, _offset)  ((void*) (((u1*)(_ptr)) + (_offset)))
+
+INLINE JValue* dvmFieldPtr(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset));
+}
+
+INLINE bool dvmGetFieldBoolean(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->z;
+}
+INLINE s1 dvmGetFieldByte(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->b;
+}
+INLINE s2 dvmGetFieldShort(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->s;
+}
+INLINE u2 dvmGetFieldChar(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->c;
+}
+INLINE s4 dvmGetFieldInt(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->i;
+}
+INLINE s8 dvmGetFieldLong(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->j;
+}
+INLINE float dvmGetFieldFloat(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->f;
+}
+INLINE double dvmGetFieldDouble(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->d;
+}
+INLINE Object* dvmGetFieldObject(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->l;
+}
+INLINE bool dvmGetFieldBooleanVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (bool)android_atomic_acquire_load(ptr);
+}
+INLINE s1 dvmGetFieldByteVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (s1)android_atomic_acquire_load(ptr);
+}
+INLINE s2 dvmGetFieldShortVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (s2)android_atomic_acquire_load(ptr);
+}
+INLINE u2 dvmGetFieldCharVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (u2)android_atomic_acquire_load(ptr);
+}
+INLINE s4 dvmGetFieldIntVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return android_atomic_acquire_load(ptr);
+}
+INLINE float dvmGetFieldFloatVolatile(const Object* obj, int offset) {
+    union { s4 ival; float fval; } alias;
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    alias.ival = android_atomic_acquire_load(ptr);
+    return alias.fval;
+}
+INLINE s8 dvmGetFieldLongVolatile(const Object* obj, int offset) {
+    const s8* addr = (const s8*)BYTE_OFFSET(obj, offset);
+    s8 val = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return val;
+}
+INLINE double dvmGetFieldDoubleVolatile(const Object* obj, int offset) {
+    union { s8 lval; double dval; } alias;
+    const s8* addr = (const s8*)BYTE_OFFSET(obj, offset);
+    alias.lval = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return alias.dval;
+}
+INLINE Object* dvmGetFieldObjectVolatile(const Object* obj, int offset) {
+    Object** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l;
+    return (Object*)android_atomic_acquire_load((int32_t*)ptr);
+}
+
+INLINE void dvmSetFieldBoolean(Object* obj, int offset, bool val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldByte(Object* obj, int offset, s1 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldShort(Object* obj, int offset, s2 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldChar(Object* obj, int offset, u2 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldInt(Object* obj, int offset, s4 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldFloat(Object* obj, int offset, float val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->f = val;
+}
+INLINE void dvmSetFieldLong(Object* obj, int offset, s8 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->j = val;
+}
+INLINE void dvmSetFieldDouble(Object* obj, int offset, double val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->d = val;
+}
+INLINE void dvmSetFieldObject(Object* obj, int offset, Object* val) {
+    JValue* lhs = (JValue*)BYTE_OFFSET(obj, offset);
+    lhs->l = val;
+    if (val != NULL) {
+        dvmWriteBarrierField(obj, &lhs->l);
+    }
+}
+INLINE void dvmSetFieldIntVolatile(Object* obj, int offset, s4 val) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    /*
+     * TODO: add an android_atomic_synchronization_store() function and
+     * use it in the 32-bit volatile set handlers.  On some platforms we
+     * can use a fast atomic instruction and avoid the barriers.
+     */
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+}
+INLINE void dvmSetFieldBooleanVolatile(Object* obj, int offset, bool val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldByteVolatile(Object* obj, int offset, s1 val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldShortVolatile(Object* obj, int offset, s2 val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldCharVolatile(Object* obj, int offset, u2 val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldFloatVolatile(Object* obj, int offset, float val) {
+    union { s4 ival; float fval; } alias;
+    alias.fval = val;
+    dvmSetFieldIntVolatile(obj, offset, alias.ival);
+}
+INLINE void dvmSetFieldLongVolatile(Object* obj, int offset, s8 val) {
+    s8* addr = (s8*)BYTE_OFFSET(obj, offset);
+    dvmQuasiAtomicSwap64Sync(val, addr);
+}
+INLINE void dvmSetFieldDoubleVolatile(Object* obj, int offset, double val) {
+    union { s8 lval; double dval; } alias;
+    alias.dval = val;
+    dvmSetFieldLongVolatile(obj, offset, alias.lval);
+}
+INLINE void dvmSetFieldObjectVolatile(Object* obj, int offset, Object* val) {
+    Object** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l;
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+    if (val != NULL) {
+        dvmWriteBarrierField(obj, ptr);
+    }
+}
+
+/*
+ * Static field access functions.
+ */
+INLINE JValue* dvmStaticFieldPtr(const StaticField* sfield) {
+    return (JValue*)&sfield->value;
+}
+
+INLINE bool dvmGetStaticFieldBoolean(const StaticField* sfield) {
+    return sfield->value.z;
+}
+INLINE s1 dvmGetStaticFieldByte(const StaticField* sfield) {
+    return sfield->value.b;
+}
+INLINE s2 dvmGetStaticFieldShort(const StaticField* sfield) {
+    return sfield->value.s;
+}
+INLINE u2 dvmGetStaticFieldChar(const StaticField* sfield) {
+    return sfield->value.c;
+}
+INLINE s4 dvmGetStaticFieldInt(const StaticField* sfield) {
+    return sfield->value.i;
+}
+INLINE float dvmGetStaticFieldFloat(const StaticField* sfield) {
+    return sfield->value.f;
+}
+INLINE s8 dvmGetStaticFieldLong(const StaticField* sfield) {
+    return sfield->value.j;
+}
+INLINE double dvmGetStaticFieldDouble(const StaticField* sfield) {
+    return sfield->value.d;
+}
+INLINE Object* dvmGetStaticFieldObject(const StaticField* sfield) {
+    return sfield->value.l;
+}
+INLINE bool dvmGetStaticFieldBooleanVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (bool)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s1 dvmGetStaticFieldByteVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (s1)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s2 dvmGetStaticFieldShortVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (s2)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE u2 dvmGetStaticFieldCharVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (u2)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s4 dvmGetStaticFieldIntVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return android_atomic_acquire_load((s4*)ptr);
+}
+INLINE float dvmGetStaticFieldFloatVolatile(const StaticField* sfield) {
+    union { s4 ival; float fval; } alias;
+    const s4* ptr = &(sfield->value.i);
+    alias.ival = android_atomic_acquire_load((s4*)ptr);
+    return alias.fval;
+}
+INLINE s8 dvmGetStaticFieldLongVolatile(const StaticField* sfield) {
+    const s8* addr = &sfield->value.j;
+    s8 val = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return val;
+}
+INLINE double dvmGetStaticFieldDoubleVolatile(const StaticField* sfield) {
+    union { s8 lval; double dval; } alias;
+    const s8* addr = &sfield->value.j;
+    alias.lval = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return alias.dval;
+}
+INLINE Object* dvmGetStaticFieldObjectVolatile(const StaticField* sfield) {
+    Object* const* ptr = &(sfield->value.l);
+    return (Object*)android_atomic_acquire_load((int32_t*)ptr);
+}
+
+INLINE void dvmSetStaticFieldBoolean(StaticField* sfield, bool val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldByte(StaticField* sfield, s1 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldShort(StaticField* sfield, s2 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldChar(StaticField* sfield, u2 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldInt(StaticField* sfield, s4 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldFloat(StaticField* sfield, float val) {
+    sfield->value.f = val;
+}
+INLINE void dvmSetStaticFieldLong(StaticField* sfield, s8 val) {
+    sfield->value.j = val;
+}
+INLINE void dvmSetStaticFieldDouble(StaticField* sfield, double val) {
+    sfield->value.d = val;
+}
+INLINE void dvmSetStaticFieldObject(StaticField* sfield, Object* val) {
+    sfield->value.l = val;
+    if (val != NULL) {
+        dvmWriteBarrierField(sfield->clazz, &sfield->value.l);
+    }
+}
+INLINE void dvmSetStaticFieldIntVolatile(StaticField* sfield, s4 val) {
+    s4* ptr = &sfield->value.i;
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+}
+INLINE void dvmSetStaticFieldBooleanVolatile(StaticField* sfield, bool val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldByteVolatile(StaticField* sfield, s1 val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldShortVolatile(StaticField* sfield, s2 val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldCharVolatile(StaticField* sfield, u2 val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldFloatVolatile(StaticField* sfield, float val) {
+    union { s4 ival; float fval; } alias;
+    alias.fval = val;
+    dvmSetStaticFieldIntVolatile(sfield, alias.ival);
+}
+INLINE void dvmSetStaticFieldLongVolatile(StaticField* sfield, s8 val) {
+    s8* addr = &sfield->value.j;
+    dvmQuasiAtomicSwap64Sync(val, addr);
+}
+INLINE void dvmSetStaticFieldDoubleVolatile(StaticField* sfield, double val) {
+    union { s8 lval; double dval; } alias;
+    alias.dval = val;
+    dvmSetStaticFieldLongVolatile(sfield, alias.lval);
+}
+INLINE void dvmSetStaticFieldObjectVolatile(StaticField* sfield, Object* val) {
+    Object** ptr = &(sfield->value.l);
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+    if (val != NULL) {
+        dvmWriteBarrierField(sfield->clazz, &sfield->value.l);
+    }
+}
+
+#endif  // DALVIK_OO_OBJECTINLINES_H_
diff --git a/vm/oo/Resolve.cpp b/vm/oo/Resolve.cpp
new file mode 100644
index 0000000..ab3de5b
--- /dev/null
+++ b/vm/oo/Resolve.cpp
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Resolve classes, methods, fields, and strings.
+ *
+ * According to the VM spec (v2 5.5), classes may be initialized by use
+ * of the "new", "getstatic", "putstatic", or "invokestatic" instructions.
+ * If we are resolving a static method or static field, we make the
+ * initialization check here.
+ *
+ * (NOTE: the verifier has its own resolve functions, which can be invoked
+ * if a class isn't pre-verified.  Those functions must not update the
+ * "resolved stuff" tables for static fields and methods, because they do
+ * not perform initialization.)
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+
+/*
+ * Find the class corresponding to "classIdx", which maps to a class name
+ * string.  It might be in the same DEX file as "referrer", in a different
+ * DEX file, generated by a class loader, or generated by the VM (e.g.
+ * array classes).
+ *
+ * Because the DexTypeId is associated with the referring class' DEX file,
+ * we may have to resolve the same class more than once if it's referred
+ * to from classes in multiple DEX files.  This is a necessary property for
+ * DEX files associated with different class loaders.
+ *
+ * We cache a copy of the lookup in the DexFile's "resolved class" table,
+ * so future references to "classIdx" are faster.
+ *
+ * Note that "referrer" may be in the process of being linked.
+ *
+ * Traditional VMs might do access checks here, but in Dalvik the class
+ * "constant pool" is shared between all classes in the DEX file.  We rely
+ * on the verifier to do the checks for us.
+ *
+ * Does not initialize the class.
+ *
+ * "fromUnverifiedConstant" should only be set if this call is the direct
+ * result of executing a "const-class" or "instance-of" instruction, which
+ * use class constants not resolved by the bytecode verifier.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,
+    bool fromUnverifiedConstant)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const char* className;
+
+    /*
+     * Check the table first -- this gets called from the other "resolve"
+     * methods.
+     */
+    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+    if (resClass != NULL)
+        return resClass;
+
+    LOGVV("--- resolving class %u (referrer=%s cl=%p)",
+        classIdx, referrer->descriptor, referrer->classLoader);
+
+    /*
+     * Class hasn't been loaded yet, or is in the process of being loaded
+     * and initialized now.  Try to get a copy.  If we find one, put the
+     * pointer in the DexTypeId.  There isn't a race condition here --
+     * 32-bit writes are guaranteed atomic on all target platforms.  Worst
+     * case we have two threads storing the same value.
+     *
+     * If this is an array class, we'll generate it here.
+     */
+    className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+    if (className[0] != '\0' && className[1] == '\0') {
+        /* primitive type */
+        resClass = dvmFindPrimitiveClass(className[0]);
+    } else {
+        resClass = dvmFindClassNoInit(className, referrer->classLoader);
+    }
+
+    if (resClass != NULL) {
+        /*
+         * If the referrer was pre-verified, the resolved class must come
+         * from the same DEX or from a bootstrap class.  The pre-verifier
+         * makes assumptions that could be invalidated by a wacky class
+         * loader.  (See the notes at the top of oo/Class.c.)
+         *
+         * The verifier does *not* fail a class for using a const-class
+         * or instance-of instruction referring to an unresolveable class,
+         * because the result of the instruction is simply a Class object
+         * or boolean -- there's no need to resolve the class object during
+         * verification.  Instance field and virtual method accesses can
+         * break dangerously if we get the wrong class, but const-class and
+         * instance-of are only interesting at execution time.  So, if we
+         * we got here as part of executing one of the "unverified class"
+         * instructions, we skip the additional check.
+         *
+         * Ditto for class references from annotations and exception
+         * handler lists.
+         */
+        if (!fromUnverifiedConstant &&
+            IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED))
+        {
+            ClassObject* resClassCheck = resClass;
+            if (dvmIsArrayClass(resClassCheck))
+                resClassCheck = resClassCheck->elementClass;
+
+            if (referrer->pDvmDex != resClassCheck->pDvmDex &&
+                resClassCheck->classLoader != NULL)
+            {
+                ALOGW("Class resolved by unexpected DEX:"
+                     " %s(%p):%p ref [%s] %s(%p):%p",
+                    referrer->descriptor, referrer->classLoader,
+                    referrer->pDvmDex,
+                    resClass->descriptor, resClassCheck->descriptor,
+                    resClassCheck->classLoader, resClassCheck->pDvmDex);
+                ALOGW("(%s had used a different %s during pre-verification)",
+                    referrer->descriptor, resClass->descriptor);
+                dvmThrowIllegalAccessError(
+                    "Class ref in pre-verified class resolved to unexpected "
+                    "implementation");
+                return NULL;
+            }
+        }
+
+        LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d",
+            resClass->descriptor, referrer->descriptor, referrer->pDvmDex,
+            referrer->classLoader, classIdx);
+
+        /*
+         * Add what we found to the list so we can skip the class search
+         * next time through.
+         *
+         * TODO: should we be doing this when fromUnverifiedConstant==true?
+         * (see comments at top of oo/Class.c)
+         */
+        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+    } else {
+        /* not found, exception should be raised */
+        LOGVV("Class not found: %s",
+            dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+        assert(dvmCheckException(dvmThreadSelf()));
+    }
+
+    return resClass;
+}
+
+
+/*
+ * Find the method corresponding to "methodRef".
+ *
+ * We use "referrer" to find the DexFile with the constant pool that
+ * "methodRef" is an index into.  We also use its class loader.  The method
+ * being resolved may very well be in a different DEX file.
+ *
+ * If this is a static method, we ensure that the method's class is
+ * initialized.
+ */
+Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
+    MethodType methodType)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexMethodId* pMethodId;
+    Method* resMethod;
+
+    assert(methodType != METHOD_INTERFACE);
+
+    LOGVV("--- resolving method %u (referrer=%s)", methodIdx,
+        referrer->descriptor);
+    pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+    resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
+    if (resClass == NULL) {
+        /* can't find the class that the method is a part of */
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+    if (dvmIsInterfaceClass(resClass)) {
+        /* method is part of an interface */
+        dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+                resClass->descriptor);
+        return NULL;
+    }
+
+    const char* name = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+    /*
+     * We need to chase up the class hierarchy to find methods defined
+     * in super-classes.  (We only want to check the current class
+     * if we're looking for a constructor; since DIRECT calls are only
+     * for constructors and private methods, we don't want to walk up.)
+     */
+    if (methodType == METHOD_DIRECT) {
+        resMethod = dvmFindDirectMethod(resClass, name, &proto);
+    } else if (methodType == METHOD_STATIC) {
+        resMethod = dvmFindDirectMethodHier(resClass, name, &proto);
+    } else {
+        resMethod = dvmFindVirtualMethodHier(resClass, name, &proto);
+    }
+
+    if (resMethod == NULL) {
+        std::string msg;
+        msg += resClass->descriptor;
+        msg += ".";
+        msg += name;
+        dvmThrowNoSuchMethodError(msg.c_str());
+        return NULL;
+    }
+
+    LOGVV("--- found method %d (%s.%s)",
+        methodIdx, resClass->descriptor, resMethod->name);
+
+    /* see if this is a pure-abstract method */
+    if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+        dvmThrowAbstractMethodError(name);
+        return NULL;
+    }
+
+    /*
+     * If we're the first to resolve this class, we need to initialize
+     * it now.  Only necessary for METHOD_STATIC.
+     */
+    if (methodType == METHOD_STATIC) {
+        if (!dvmIsClassInitialized(resMethod->clazz) &&
+            !dvmInitClass(resMethod->clazz))
+        {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        } else {
+            assert(!dvmCheckException(dvmThreadSelf()));
+        }
+    } else {
+        /*
+         * Edge case: if the <clinit> for a class creates an instance
+         * of itself, we will call <init> on a class that is still being
+         * initialized by us.
+         */
+        assert(dvmIsClassInitialized(resMethod->clazz) ||
+               dvmIsClassInitializing(resMethod->clazz));
+    }
+
+    /*
+     * If the class has been initialized, add a pointer to our data structure
+     * so we don't have to jump through the hoops again.  If this is a
+     * static method and the defining class is still initializing (i.e. this
+     * thread is executing <clinit>), don't do the store, otherwise other
+     * threads could call the method without waiting for class init to finish.
+     */
+    if (methodType == METHOD_STATIC && !dvmIsClassInitialized(resMethod->clazz))
+    {
+        LOGVV("--- not caching resolved method %s.%s (class init=%d/%d)",
+            resMethod->clazz->descriptor, resMethod->name,
+            dvmIsClassInitializing(resMethod->clazz),
+            dvmIsClassInitialized(resMethod->clazz));
+    } else {
+        dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    return resMethod;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexMethodId* pMethodId;
+    Method* resMethod;
+
+    LOGVV("--- resolving interface method %d (referrer=%s)",
+        methodIdx, referrer->descriptor);
+    pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+    resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
+    if (resClass == NULL) {
+        /* can't find the class that the method is a part of */
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+    if (!dvmIsInterfaceClass(resClass)) {
+        /* whoops */
+        dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+                resClass->descriptor);
+        return NULL;
+    }
+
+    /*
+     * This is the first time the method has been resolved.  Set it in our
+     * resolved-method structure.  It always resolves to the same thing,
+     * so looking it up and storing it doesn't create a race condition.
+     *
+     * If we scan into the interface's superclass -- which is always
+     * java/lang/Object -- we will catch things like:
+     *   interface I ...
+     *   I myobj = (something that implements I)
+     *   myobj.hashCode()
+     * However, the Method->methodIndex will be an offset into clazz->vtable,
+     * rather than an offset into clazz->iftable.  The invoke-interface
+     * code can test to see if the method returned is abstract or concrete,
+     * and use methodIndex accordingly.  I'm not doing this yet because
+     * (a) we waste time in an unusual case, and (b) we're probably going
+     * to fix it in the DEX optimizer.
+     *
+     * We do need to scan the superinterfaces, in case we're invoking a
+     * superinterface method on an interface reference.  The class in the
+     * DexTypeId is for the static type of the object, not the class in
+     * which the method is first defined.  We have the full, flattened
+     * list in "iftable".
+     */
+    const char* methodName =
+        dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+    LOGVV("+++ looking for '%s' in resClass='%s'", methodName, resClass->descriptor);
+    resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto);
+    if (resMethod == NULL) {
+        std::string msg;
+        msg += resClass->descriptor;
+        msg += ".";
+        msg += methodName;
+        dvmThrowNoSuchMethodError(msg.c_str());
+        return NULL;
+    }
+
+    LOGVV("--- found interface method %d (%s.%s)",
+        methodIdx, resClass->descriptor, resMethod->name);
+
+    /* we're expecting this to be abstract */
+    assert(dvmIsAbstractMethod(resMethod));
+
+    /* interface methods are always public; no need to check access */
+
+    /*
+     * The interface class *may* be initialized.  According to VM spec
+     * v2 2.17.4, the interfaces a class refers to "need not" be initialized
+     * when the class is initialized.
+     *
+     * It isn't necessary for an interface class to be initialized before
+     * we resolve methods on that interface.
+     *
+     * We choose not to do the initialization now.
+     */
+    //assert(dvmIsClassInitialized(resMethod->clazz));
+
+    /*
+     * Add a pointer to our data structure so we don't have to jump
+     * through the hoops again.
+     *
+     * As noted above, no need to worry about whether the interface that
+     * defines the method has been or is currently executing <clinit>.
+     */
+    dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+
+    return resMethod;
+}
+
+/*
+ * Resolve an instance field reference.
+ *
+ * Returns NULL and throws an exception on error (no such field, illegal
+ * access).
+ */
+InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexFieldId* pFieldId;
+    InstField* resField;
+
+    LOGVV("--- resolving field %u (referrer=%s cl=%p)",
+        ifieldIdx, referrer->descriptor, referrer->classLoader);
+
+    pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+    /*
+     * Find the field's class.
+     */
+    resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
+    if (resClass == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    resField = dvmFindInstanceFieldHier(resClass,
+        dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+        dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+    if (resField == NULL) {
+        dvmThrowNoSuchFieldError(
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+        return NULL;
+    }
+
+    /*
+     * Class must be initialized by now (unless verifier is buggy).  We
+     * could still be in the process of initializing it if the field
+     * access is from a static initializer.
+     */
+    assert(dvmIsClassInitialized(resField->clazz) ||
+           dvmIsClassInitializing(resField->clazz));
+
+    /*
+     * The class is initialized (or initializing), the field has been
+     * found.  Add a pointer to our data structure so we don't have to
+     * jump through the hoops again.
+     *
+     * Anything that uses the resolved table entry must have an instance
+     * of the class, so any class init activity has already happened (or
+     * been deliberately bypassed when <clinit> created an instance).
+     * So it's always okay to update the table.
+     */
+    dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField);
+    LOGVV("    field %u is %s.%s",
+        ifieldIdx, resField->clazz->descriptor, resField->name);
+
+    return resField;
+}
+
+/*
+ * Resolve a static field reference.  The DexFile format doesn't distinguish
+ * between static and instance field references, so the "resolved" pointer
+ * in the Dex struct will have the wrong type.  We trivially cast it here.
+ *
+ * Causes the field's class to be initialized.
+ */
+StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexFieldId* pFieldId;
+    StaticField* resField;
+
+    pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+    /*
+     * Find the field's class.
+     */
+    resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
+    if (resClass == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    resField = dvmFindStaticFieldHier(resClass,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+                dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+    if (resField == NULL) {
+        dvmThrowNoSuchFieldError(
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+        return NULL;
+    }
+
+    /*
+     * If we're the first to resolve the field in which this class resides,
+     * we need to do it now.  Note that, if the field was inherited from
+     * a superclass, it is not necessarily the same as "resClass".
+     */
+    if (!dvmIsClassInitialized(resField->clazz) &&
+        !dvmInitClass(resField->clazz))
+    {
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    /*
+     * If the class has been initialized, add a pointer to our data structure
+     * so we don't have to jump through the hoops again.  If it's still
+     * initializing (i.e. this thread is executing <clinit>), don't do
+     * the store, otherwise other threads could use the field without waiting
+     * for class init to finish.
+     */
+    if (dvmIsClassInitialized(resField->clazz)) {
+        dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+    } else {
+        LOGVV("--- not caching resolved field %s.%s (class init=%d/%d)",
+            resField->clazz->descriptor, resField->name,
+            dvmIsClassInitializing(resField->clazz),
+            dvmIsClassInitialized(resField->clazz));
+    }
+
+    return resField;
+}
+
+
+/*
+ * Resolve a string reference.
+ *
+ * Finding the string is easy.  We need to return a reference to a
+ * java/lang/String object, not a bunch of characters, which means the
+ * first time we get here we need to create an interned string.
+ */
+StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    StringObject* strObj;
+    StringObject* internStrObj;
+    const char* utf8;
+    u4 utf16Size;
+
+    LOGVV("+++ resolving string, referrer is %s", referrer->descriptor);
+
+    /*
+     * Create a UTF-16 version so we can trivially compare it to what's
+     * already interned.
+     */
+    utf8 = dexStringAndSizeById(pDvmDex->pDexFile, stringIdx, &utf16Size);
+    strObj = dvmCreateStringFromCstrAndLength(utf8, utf16Size);
+    if (strObj == NULL) {
+        /* ran out of space in GC heap? */
+        assert(dvmCheckException(dvmThreadSelf()));
+        goto bail;
+    }
+
+    /*
+     * Add it to the intern list.  The return value is the one in the
+     * intern list, which (due to race conditions) may or may not be
+     * the one we just created.  The intern list is synchronized, so
+     * there will be only one "live" version.
+     *
+     * By requesting an immortal interned string, we guarantee that
+     * the returned object will never be collected by the GC.
+     *
+     * A NULL return here indicates some sort of hashing failure.
+     */
+    internStrObj = dvmLookupImmortalInternedString(strObj);
+    dvmReleaseTrackedAlloc((Object*) strObj, NULL);
+    strObj = internStrObj;
+    if (strObj == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        goto bail;
+    }
+
+    /* save a reference so we can go straight to the object next time */
+    dvmDexSetResolvedString(pDvmDex, stringIdx, strObj);
+
+bail:
+    return strObj;
+}
+
+/*
+ * For debugging: return a string representing the methodType.
+ */
+const char* dvmMethodTypeStr(MethodType methodType)
+{
+    switch (methodType) {
+    case METHOD_DIRECT:         return "direct";
+    case METHOD_STATIC:         return "static";
+    case METHOD_VIRTUAL:        return "virtual";
+    case METHOD_INTERFACE:      return "interface";
+    case METHOD_UNKNOWN:        return "UNKNOWN";
+    }
+    assert(false);
+    return "BOGUS";
+}
diff --git a/vm/oo/Resolve.h b/vm/oo/Resolve.h
new file mode 100644
index 0000000..56a35b7
--- /dev/null
+++ b/vm/oo/Resolve.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Resolve "constant pool" references into pointers to VM structs.
+ */
+#ifndef DALVIK_OO_RESOLVE_H_
+#define DALVIK_OO_RESOLVE_H_
+
+/*
+ * "Direct" and "virtual" methods are stored independently.  The type of call
+ * used to invoke the method determines which list we search, and whether
+ * we travel up into superclasses.
+ *
+ * (<clinit>, <init>, and methods declared "private" or "static" are stored
+ * in the "direct" list.  All others are stored in the "virtual" list.)
+ */
+enum MethodType {
+    METHOD_UNKNOWN  = 0,
+    METHOD_DIRECT,      // <init>, private
+    METHOD_STATIC,      // static
+    METHOD_VIRTUAL,     // virtual, super
+    METHOD_INTERFACE    // interface
+};
+
+/*
+ * Resolve a class, given the referring class and a constant pool index
+ * for the DexTypeId.
+ *
+ * Does not initialize the class.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" ClassObject* dvmResolveClass(const ClassObject* referrer,
+                                        u4 classIdx,
+                                        bool fromUnverifiedConstant);
+
+/*
+ * Resolve a direct, static, or virtual method.
+ *
+ * Can cause the method's class to be initialized if methodType is
+ * METHOD_STATIC.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
+                                    MethodType methodType);
+
+/*
+ * Resolve an interface method.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx);
+
+/*
+ * Resolve an instance field.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" InstField* dvmResolveInstField(const ClassObject* referrer,
+                                          u4 ifieldIdx);
+
+/*
+ * Resolve a static field.
+ *
+ * Causes the field's class to be initialized.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" StaticField* dvmResolveStaticField(const ClassObject* referrer,
+                                              u4 sfieldIdx);
+
+/*
+ * Resolve a "const-string" reference.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx);
+
+/*
+ * Return debug string constant for enum.
+ */
+const char* dvmMethodTypeStr(MethodType methodType);
+
+#endif  // DALVIK_OO_RESOLVE_H_
diff --git a/vm/oo/TypeCheck.cpp b/vm/oo/TypeCheck.cpp
new file mode 100644
index 0000000..5228512
--- /dev/null
+++ b/vm/oo/TypeCheck.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * instanceof, checkcast, etc.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * I think modern C mandates that the results of a boolean expression are
+ * 0 or 1.  If not, or we suddenly turn into C++ and bool != int, use this.
+ */
+#define BOOL_TO_INT(x)  (x)
+//#define BOOL_TO_INT(x)  ((x) ? 1 : 0)
+
+/*
+ * Number of entries in instanceof cache.  MUST be a power of 2.
+ */
+#define INSTANCEOF_CACHE_SIZE   1024
+
+
+/*
+ * Allocate cache.
+ */
+bool dvmInstanceofStartup()
+{
+    gDvm.instanceofCache = dvmAllocAtomicCache(INSTANCEOF_CACHE_SIZE);
+    if (gDvm.instanceofCache == NULL)
+        return false;
+    return true;
+}
+
+/*
+ * Discard the cache.
+ */
+void dvmInstanceofShutdown()
+{
+    dvmFreeAtomicCache(gDvm.instanceofCache);
+}
+
+
+/*
+ * Determine whether "sub" is an instance of "clazz", where both of these
+ * are array classes.
+ *
+ * Consider an array class, e.g. Y[][], where Y is a subclass of X.
+ *   Y[][] instanceof Y[][]        --> true (identity)
+ *   Y[][] instanceof X[][]        --> true (element superclass)
+ *   Y[][] instanceof Y            --> false
+ *   Y[][] instanceof Y[]          --> false
+ *   Y[][] instanceof Object       --> true (everything is an object)
+ *   Y[][] instanceof Object[]     --> true
+ *   Y[][] instanceof Object[][]   --> true
+ *   Y[][] instanceof Object[][][] --> false (too many []s)
+ *   Y[][] instanceof Serializable     --> true (all arrays are Serializable)
+ *   Y[][] instanceof Serializable[]   --> true
+ *   Y[][] instanceof Serializable[][] --> false (unless Y is Serializable)
+ *
+ * Don't forget about primitive types.
+ *   int[] instanceof Object[]     --> false
+ *
+ * "subElemClass" is sub->elementClass.
+ *
+ * "subDim" is usually just sub->dim, but for some kinds of checks we want
+ * to pass in a non-array class and pretend that it's an array.
+ */
+static int isArrayInstanceOfArray(const ClassObject* subElemClass, int subDim,
+    const ClassObject* clazz)
+{
+    //assert(dvmIsArrayClass(sub));
+    assert(dvmIsArrayClass(clazz));
+
+    /* "If T is an array type TC[]... one of the following must be true:
+     *   TC and SC are the same primitive type.
+     *   TC and SC are reference types and type SC can be cast to TC [...]."
+     *
+     * We need the class objects for the array elements.  For speed we
+     * tucked them into the class object.
+     */
+    assert(subDim > 0 && clazz->arrayDim > 0);
+    if (subDim == clazz->arrayDim) {
+        /*
+         * See if "sub" is an instance of "clazz".  This handles the
+         * interfaces, java.lang.Object, superclassing, etc.
+         */
+        return dvmInstanceof(subElemClass, clazz->elementClass);
+    } else if (subDim > clazz->arrayDim) {
+        /*
+         * The thing we might be an instance of has fewer dimensions.  It
+         * must be an Object or array of Object, or a standard array
+         * interface or array of standard array interfaces (the standard
+         * interfaces being java/lang/Cloneable and java/io/Serializable).
+         */
+        if (dvmIsInterfaceClass(clazz->elementClass)) {
+            /*
+             * See if the class implements its base element.  We know the
+             * base element is an interface; if the array class implements
+             * it, we know it's a standard array interface.
+             */
+            return dvmImplements(clazz, clazz->elementClass);
+        } else {
+            /*
+             * See if this is an array of Object, Object[], etc.  We know
+             * that the superclass of an array is always Object, so we
+             * just compare the element type to that.
+             */
+            return (clazz->elementClass == clazz->super);
+        }
+    } else {
+        /*
+         * Too many []s.
+         */
+        return false;
+    }
+}
+
+/*
+ * Determine whether "sub" is a sub-class of "clazz", where "sub" is an
+ * array class.
+ *
+ * "clazz" could be an array class, interface, or simple class.
+ */
+static int isArrayInstanceOf(const ClassObject* sub, const ClassObject* clazz)
+{
+    assert(dvmIsArrayClass(sub));
+
+    /* "If T is an interface type, T must be one of the interfaces
+     * implemented by arrays."
+     *
+     * I'm not checking that here, because dvmInstanceof tests for
+     * interfaces first, and the generic dvmImplements stuff should
+     * work correctly.
+     */
+    assert(!dvmIsInterfaceClass(clazz));     /* make sure */
+
+    /* "If T is a class type, then T must be Object."
+     *
+     * The superclass of an array is always java.lang.Object, so just
+     * compare against that.
+     */
+    if (!dvmIsArrayClass(clazz))
+        return BOOL_TO_INT(clazz == sub->super);
+
+    /*
+     * If T is an array type TC[] ...
+     */
+    return isArrayInstanceOfArray(sub->elementClass, sub->arrayDim, clazz);
+}
+
+
+/*
+ * Returns 1 (true) if "clazz" is an implementation of "interface".
+ *
+ * "clazz" could be a class or an interface.
+ */
+int dvmImplements(const ClassObject* clazz, const ClassObject* interface)
+{
+    int i;
+
+    assert(dvmIsInterfaceClass(interface));
+
+    /*
+     * All interfaces implemented directly and by our superclass, and
+     * recursively all super-interfaces of those interfaces, are listed
+     * in "iftable", so we can just do a linear scan through that.
+     */
+    for (i = 0; i < clazz->iftableCount; i++) {
+        if (clazz->iftable[i].clazz == interface)
+            return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Determine whether or not we can put an object into an array, based on
+ * the class hierarchy.  The object might itself by an array, which means
+ * we have to pay attention to the array instanceof rules.
+ *
+ * Note that "objectClass" could be an array, but objectClass->elementClass
+ * is always a non-array type.
+ */
+bool dvmCanPutArrayElement(const ClassObject* objectClass,
+    const ClassObject* arrayClass)
+{
+    if (dvmIsArrayClass(objectClass)) {
+        /*
+         * We're stuffing an array into an array.  We want to see if the
+         * elements of "arrayClass" are compatible with "objectClass".
+         * We bump up the number of dimensions in "objectClass" so that we
+         * can compare the two directly.
+         */
+        return isArrayInstanceOfArray(objectClass->elementClass,
+                    objectClass->arrayDim + 1, arrayClass);
+    } else {
+        /*
+         * We're putting a non-array element into an array.  We need to
+         * test to see if the elements are compatible.  The easiest way
+         * to do that is to "arrayify" it and use the standard array
+         * compatibility check.
+         */
+        return isArrayInstanceOfArray(objectClass, 1, arrayClass);
+    }
+}
+
+
+/*
+ * Perform the instanceof calculation.
+ */
+static inline int isInstanceof(const ClassObject* instance,
+    const ClassObject* clazz)
+{
+    if (dvmIsInterfaceClass(clazz)) {
+        return dvmImplements(instance, clazz);
+    } else if (dvmIsArrayClass(instance)) {
+        return isArrayInstanceOf(instance, clazz);
+    } else {
+        return dvmIsSubClass(instance, clazz);
+    }
+}
+
+
+/*
+ * Do the instanceof calculation, pulling the result from the cache if
+ * possible.
+ */
+int dvmInstanceofNonTrivial(const ClassObject* instance,
+    const ClassObject* clazz)
+{
+#define ATOMIC_CACHE_CALC isInstanceof(instance, clazz)
+#define ATOMIC_CACHE_NULL_ALLOWED true
+    return ATOMIC_CACHE_LOOKUP(gDvm.instanceofCache,
+                INSTANCEOF_CACHE_SIZE, instance, clazz);
+#undef ATOMIC_CACHE_CALC
+}
diff --git a/vm/oo/TypeCheck.h b/vm/oo/TypeCheck.h
new file mode 100644
index 0000000..c3f19ea
--- /dev/null
+++ b/vm/oo/TypeCheck.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * instanceof, checkcast, etc.
+ */
+#ifndef DALVIK_OO_TYPECHECK_H_
+#define DALVIK_OO_TYPECHECK_H_
+
+/* VM startup/shutdown */
+bool dvmInstanceofStartup(void);
+void dvmInstanceofShutdown(void);
+
+
+/* used by dvmInstanceof; don't call */
+extern "C" int dvmInstanceofNonTrivial(const ClassObject* instance,
+                                       const ClassObject* clazz);
+
+/*
+ * Determine whether "instance" is an instance of "clazz".
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+INLINE int dvmInstanceof(const ClassObject* instance, const ClassObject* clazz)
+{
+    if (instance == clazz) {
+        if (CALC_CACHE_STATS)
+            gDvm.instanceofCache->trivial++;
+        return 1;
+    } else
+        return dvmInstanceofNonTrivial(instance, clazz);
+}
+
+/*
+ * Determine whether a class implements an interface.
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+int dvmImplements(const ClassObject* clazz, const ClassObject* interface);
+
+/*
+ * Determine whether "sub" is a sub-class of "clazz".
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+INLINE int dvmIsSubClass(const ClassObject* sub, const ClassObject* clazz) {
+    do {
+        /*printf("###### sub='%s' clazz='%s'\n", sub->name, clazz->name);*/
+        if (sub == clazz)
+            return 1;
+        sub = sub->super;
+    } while (sub != NULL);
+
+    return 0;
+}
+
+/*
+ * Determine whether or not we can store an object into an array, based
+ * on the classes of the two.
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+extern "C" bool dvmCanPutArrayElement(const ClassObject* elemClass,
+    const ClassObject* arrayClass);
+
+#endif  // DALVIK_OO_TYPECHECK_H_
diff --git a/vm/os/android.cpp b/vm/os/android.cpp
new file mode 100644
index 0000000..24ebd5a
--- /dev/null
+++ b/vm/os/android.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "os.h"
+
+#include "Dalvik.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <cutils/sched_policy.h>
+#include <utils/threads.h>
+
+/*
+ * Conversion map for "nice" values.
+ *
+ * We use Android thread priority constants to be consistent with the rest
+ * of the system.  In some cases adjacent entries may overlap.
+ */
+static const int kNiceValues[10] = {
+    ANDROID_PRIORITY_LOWEST,                /* 1 (MIN_PRIORITY) */
+    ANDROID_PRIORITY_BACKGROUND + 6,
+    ANDROID_PRIORITY_BACKGROUND + 3,
+    ANDROID_PRIORITY_BACKGROUND,
+    ANDROID_PRIORITY_NORMAL,                /* 5 (NORM_PRIORITY) */
+    ANDROID_PRIORITY_NORMAL - 2,
+    ANDROID_PRIORITY_NORMAL - 4,
+    ANDROID_PRIORITY_URGENT_DISPLAY + 3,
+    ANDROID_PRIORITY_URGENT_DISPLAY + 2,
+    ANDROID_PRIORITY_URGENT_DISPLAY         /* 10 (MAX_PRIORITY) */
+};
+
+void os_changeThreadPriority(Thread* thread, int newPriority)
+{
+    if (newPriority < 1 || newPriority > 10) {
+        ALOGW("bad priority %d", newPriority);
+        newPriority = 5;
+    }
+
+    int newNice = kNiceValues[newPriority-1];
+    pid_t pid = thread->systemTid;
+
+    if (newNice >= ANDROID_PRIORITY_BACKGROUND) {
+        set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
+    } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
+        set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
+    }
+
+    if (setpriority(PRIO_PROCESS, pid, newNice) != 0) {
+        std::string threadName(dvmGetThreadName(thread));
+        ALOGI("setPriority(%d) '%s' to prio=%d(n=%d) failed: %s",
+        pid, threadName.c_str(), newPriority, newNice, strerror(errno));
+    } else {
+        ALOGV("setPriority(%d) to prio=%d(n=%d)", pid, newPriority, newNice);
+    }
+}
+
+int os_getThreadPriorityFromSystem()
+{
+    errno = 0;
+    int sysprio = getpriority(PRIO_PROCESS, 0);
+    if (sysprio == -1 && errno != 0) {
+        ALOGW("getpriority() failed: %s", strerror(errno));
+        return THREAD_NORM_PRIORITY;
+    }
+
+    int jprio = THREAD_MIN_PRIORITY;
+    for (int i = 0; i < NELEM(kNiceValues); i++) {
+        if (sysprio >= kNiceValues[i]) {
+            break;
+        }
+        jprio++;
+    }
+    if (jprio > THREAD_MAX_PRIORITY) {
+        jprio = THREAD_MAX_PRIORITY;
+    }
+    return jprio;
+}
+
+int os_raiseThreadPriority()
+{
+    /* Get the priority (the "nice" value) of the current thread.  The
+     * getpriority() call can legitimately return -1, so we have to
+     * explicitly test errno.
+     */
+    errno = 0;
+    int oldThreadPriority = getpriority(PRIO_PROCESS, 0);
+    if (errno != 0) {
+        ALOGI("getpriority(self) failed: %s", strerror(errno));
+    } else if (oldThreadPriority > ANDROID_PRIORITY_NORMAL) {
+        /* Current value is numerically greater than "normal", which
+         * in backward UNIX terms means lower priority.
+         */
+        if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) {
+            set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
+        }
+        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) {
+            ALOGI("Unable to elevate priority from %d to %d",
+                    oldThreadPriority, ANDROID_PRIORITY_NORMAL);
+        } else {
+            /*
+             * The priority has been elevated.  Return the old value
+             * so the caller can restore it later.
+             */
+            ALOGV("Elevating priority from %d to %d",
+                    oldThreadPriority, ANDROID_PRIORITY_NORMAL);
+            return oldThreadPriority;
+        }
+    }
+    return INT_MAX;
+}
+
+void os_lowerThreadPriority(int oldThreadPriority)
+{
+    if (setpriority(PRIO_PROCESS, 0, oldThreadPriority) != 0) {
+        ALOGW("Unable to reset priority to %d: %s",
+                oldThreadPriority, strerror(errno));
+    } else {
+        ALOGV("Reset priority to %d", oldThreadPriority);
+    }
+    if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) {
+        set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
+    }
+}
diff --git a/vm/os/linux.cpp b/vm/os/linux.cpp
new file mode 100644
index 0000000..172cd05
--- /dev/null
+++ b/vm/os/linux.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "os.h"
+
+#include "Dalvik.h"
+
+int os_raiseThreadPriority()
+{
+    return 0;
+}
+
+void os_lowerThreadPriority(int oldThreadPriority)
+{
+    // Do nothing.
+}
+
+void os_changeThreadPriority(Thread* thread, int newPriority)
+{
+    // Do nothing.
+}
+
+int os_getThreadPriorityFromSystem()
+{
+    return THREAD_NORM_PRIORITY;
+}
diff --git a/vm/os/os.h b/vm/os/os.h
new file mode 100644
index 0000000..19e2a61
--- /dev/null
+++ b/vm/os/os.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+struct Thread;
+
+/*
+ * Raises the scheduling priority of the current thread.  Returns the
+ * original priority if successful, or INT_MAX on failure.
+ * Use os_lowerThreadPriority to undo.
+ *
+ * TODO: does the GC really need this?
+ */
+int os_raiseThreadPriority();
+
+/*
+ * Sets the current thread scheduling priority. Used to undo the effects
+ * of an earlier call to os_raiseThreadPriority.
+ *
+ * TODO: does the GC really need this?
+ */
+void os_lowerThreadPriority(int oldThreadPriority);
+
+/*
+ * Changes the priority of a system thread to match that of the Thread object.
+ *
+ * We map a priority value from 1-10 to Linux "nice" values, where lower
+ * numbers indicate higher priority.
+ */
+void os_changeThreadPriority(Thread* thread, int newPriority);
+
+/*
+ * Returns the thread priority for the current thread by querying the system.
+ * This is useful when attaching a thread through JNI.
+ *
+ * Returns a value from 1 to 10 (compatible with java.lang.Thread values).
+ */
+int os_getThreadPriorityFromSystem();
diff --git a/vm/reflect/Annotation.cpp b/vm/reflect/Annotation.cpp
new file mode 100644
index 0000000..7b36d51
--- /dev/null
+++ b/vm/reflect/Annotation.cpp
@@ -0,0 +1,2293 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Annotations.
+ *
+ * We're not expecting to make much use of runtime annotations, so speed vs.
+ * space choices are weighted heavily toward small size.
+ *
+ * It would have been nice to treat "system" annotations in the same way
+ * we do "real" annotations, but that doesn't work.  The chief difficulty
+ * is that some of them have member types that are not legal in annotations,
+ * such as Method and Annotation.  Another source of pain comes from the
+ * AnnotationDefault annotation, which by virtue of being an annotation
+ * could itself have default values, requiring some additional checks to
+ * prevent recursion.
+ *
+ * It's simpler, and more efficient, to handle the system annotations
+ * entirely inside the VM.  There are empty classes defined for the system
+ * annotation types, but their only purpose is to allow the system
+ * annotations to share name space with standard annotations.
+ */
+#include "Dalvik.h"
+
+// fwd
+static Object* processEncodedAnnotation(const ClassObject* clazz,\
+    const u1** pPtr);
+static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr);
+
+/*
+ * System annotation descriptors.
+ */
+static const char* kDescrAnnotationDefault
+                                    = "Ldalvik/annotation/AnnotationDefault;";
+static const char* kDescrEnclosingClass
+                                    = "Ldalvik/annotation/EnclosingClass;";
+static const char* kDescrEnclosingMethod
+                                    = "Ldalvik/annotation/EnclosingMethod;";
+static const char* kDescrInnerClass = "Ldalvik/annotation/InnerClass;";
+static const char* kDescrMemberClasses
+                                    = "Ldalvik/annotation/MemberClasses;";
+static const char* kDescrSignature  = "Ldalvik/annotation/Signature;";
+static const char* kDescrThrows     = "Ldalvik/annotation/Throws;";
+
+/*
+ * Read an unsigned LEB128 value from a buffer.  Advances "pBuf".
+ */
+static u4 readUleb128(const u1** pBuf)
+{
+    u4 result = 0;
+    int shift = 0;
+    const u1* buf = *pBuf;
+    u1 val;
+
+    do {
+        /*
+         * Worst-case on bad data is we read too much data and return a bogus
+         * result.  Safe to assume that we will encounter a byte with its
+         * high bit clear before the end of the mapped file.
+         */
+        assert(shift < 32);
+
+        val = *buf++;
+        result |= (val & 0x7f) << shift;
+        shift += 7;
+    } while ((val & 0x80) != 0);
+
+    *pBuf = buf;
+    return result;
+}
+
+/*
+ * Get the annotations directory item.
+ */
+static const DexAnnotationsDirectoryItem* getAnnoDirectory(DexFile* pDexFile,
+    const ClassObject* clazz)
+{
+    const DexClassDef* pClassDef;
+
+    /*
+     * Find the class def in the DEX file.  For better performance we should
+     * stash this in the ClassObject.
+     */
+    pClassDef = dexFindClass(pDexFile, clazz->descriptor);
+    assert(pClassDef != NULL);
+    return dexGetAnnotationsDirectoryItem(pDexFile, pClassDef);
+}
+
+/*
+ * Return a zero-length array of Annotation objects.
+ *
+ * TODO: this currently allocates a new array each time, but I think we
+ * can get away with returning a canonical copy.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+static ArrayObject* emptyAnnoArray()
+{
+    return dvmAllocArrayByClass(
+        gDvm.classJavaLangAnnotationAnnotationArray, 0, ALLOC_DEFAULT);
+}
+
+/*
+ * Return an array of empty arrays of Annotation objects.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+static ArrayObject* emptyAnnoArrayArray(int numElements)
+{
+    Thread* self = dvmThreadSelf();
+    ArrayObject* arr;
+    int i;
+
+    arr = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArrayArray,
+            numElements, ALLOC_DEFAULT);
+    if (arr != NULL) {
+        ArrayObject** elems = (ArrayObject**)(void*)arr->contents;
+        for (i = 0; i < numElements; i++) {
+            elems[i] = emptyAnnoArray();
+            dvmReleaseTrackedAlloc((Object*)elems[i], self);
+        }
+    }
+
+    return arr;
+}
+
+/*
+ * Read a signed integer.  "zwidth" is the zero-based byte count.
+ */
+static s4 readSignedInt(const u1* ptr, int zwidth)
+{
+    s4 val = 0;
+    int i;
+
+    for (i = zwidth; i >= 0; --i)
+        val = ((u4)val >> 8) | (((s4)*ptr++) << 24);
+    val >>= (3 - zwidth) * 8;
+
+    return val;
+}
+
+/*
+ * Read an unsigned integer.  "zwidth" is the zero-based byte count,
+ * "fillOnRight" indicates which side we want to zero-fill from.
+ */
+static u4 readUnsignedInt(const u1* ptr, int zwidth, bool fillOnRight)
+{
+    u4 val = 0;
+    int i;
+
+    if (!fillOnRight) {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u4)*ptr++) << 24);
+        val >>= (3 - zwidth) * 8;
+    } else {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u4)*ptr++) << 24);
+    }
+    return val;
+}
+
+/*
+ * Read a signed long.  "zwidth" is the zero-based byte count.
+ */
+static s8 readSignedLong(const u1* ptr, int zwidth)
+{
+    s8 val = 0;
+    int i;
+
+    for (i = zwidth; i >= 0; --i)
+        val = ((u8)val >> 8) | (((s8)*ptr++) << 56);
+    val >>= (7 - zwidth) * 8;
+
+    return val;
+}
+
+/*
+ * Read an unsigned long.  "zwidth" is the zero-based byte count,
+ * "fillOnRight" indicates which side we want to zero-fill from.
+ */
+static u8 readUnsignedLong(const u1* ptr, int zwidth, bool fillOnRight)
+{
+    u8 val = 0;
+    int i;
+
+    if (!fillOnRight) {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u8)*ptr++) << 56);
+        val >>= (7 - zwidth) * 8;
+    } else {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u8)*ptr++) << 56);
+    }
+    return val;
+}
+
+
+/*
+ * ===========================================================================
+ *      Element extraction
+ * ===========================================================================
+ */
+
+/*
+ * An annotation in "clazz" refers to a method by index.  This just gives
+ * us the name of the class and the name and signature of the method.  We
+ * need to find the method's class, and then find the method within that
+ * class.  If the method has been resolved before, we can just use the
+ * results of the previous lookup.
+ *
+ * Normally we do this as part of method invocation in the interpreter, which
+ * provides us with a bit of context: is it virtual or direct, do we need
+ * to initialize the class because it's a static method, etc.  We don't have
+ * that information here, so we have to do a bit of searching.
+ *
+ * Returns NULL if the method was not found (exception may be pending).
+ */
+static Method* resolveAmbiguousMethod(const ClassObject* referrer, u4 methodIdx)
+{
+    DexFile* pDexFile;
+    ClassObject* resClass;
+    Method* resMethod;
+    const DexMethodId* pMethodId;
+    const char* name;
+
+    /* if we've already resolved this method, return it */
+    resMethod = dvmDexGetResolvedMethod(referrer->pDvmDex, methodIdx);
+    if (resMethod != NULL)
+        return resMethod;
+
+    pDexFile = referrer->pDvmDex->pDexFile;
+    pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    resClass = dvmResolveClass(referrer, pMethodId->classIdx, true);
+    if (resClass == NULL) {
+        /* note exception will be pending */
+        ALOGD("resolveAmbiguousMethod: unable to find class %d", methodIdx);
+        return NULL;
+    }
+    if (dvmIsInterfaceClass(resClass)) {
+        /* method is part of an interface -- not expecting that */
+        ALOGD("resolveAmbiguousMethod: method in interface?");
+        return NULL;
+    }
+
+    // TODO - consider a method access flag that indicates direct vs. virtual
+    name = dexStringById(pDexFile, pMethodId->nameIdx);
+
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+
+    if (name[0] == '<') {
+        /*
+         * Constructor or class initializer.  Only need to examine the
+         * "direct" list, and don't need to look up the class hierarchy.
+         */
+        resMethod = dvmFindDirectMethod(resClass, name, &proto);
+    } else {
+        /*
+         * Do a hierarchical scan for direct and virtual methods.
+         *
+         * This uses the search order from the VM spec (v2 5.4.3.3), which
+         * seems appropriate here.
+         */
+        resMethod = dvmFindMethodHier(resClass, name, &proto);
+    }
+
+    return resMethod;
+}
+
+/*
+ * constants for processAnnotationValue indicating what style of
+ * result is wanted
+ */
+enum AnnotationResultStyle {
+    kAllObjects,         /* return everything as an object */
+    kAllRaw,             /* return everything as a raw value or index */
+    kPrimitivesOrObjects /* return primitives as-is but the rest as objects */
+};
+
+/*
+ * Recursively process an annotation value.
+ *
+ * "clazz" is the class on which the annotations are defined.  It may be
+ * NULL when "resultStyle" is "kAllRaw".
+ *
+ * If "resultStyle" is "kAllObjects", the result will always be an Object of an
+ * appropriate type (in pValue->value.l).  For primitive types, the usual
+ * wrapper objects will be created.
+ *
+ * If "resultStyle" is "kAllRaw", numeric constants are stored directly into
+ * "pValue", and indexed values like String and Method are returned as
+ * indexes.  Complex values like annotations and arrays are not handled.
+ *
+ * If "resultStyle" is "kPrimitivesOrObjects", numeric constants are stored
+ * directly into "pValue", and everything else is constructed as an Object
+ * of appropriate type (in pValue->value.l).
+ *
+ * The caller must call dvmReleaseTrackedAlloc on returned objects, when
+ * using "kAllObjects" or "kPrimitivesOrObjects".
+ *
+ * Returns "true" on success, "false" if the value could not be processed
+ * or an object could not be allocated.  On allocation failure an exception
+ * will be raised.
+ */
+static bool processAnnotationValue(const ClassObject* clazz,
+    const u1** pPtr, AnnotationValue* pValue,
+    AnnotationResultStyle resultStyle)
+{
+    Thread* self = dvmThreadSelf();
+    Object* elemObj = NULL;
+    bool setObject = false;
+    const u1* ptr = *pPtr;
+    u1 valueType, valueArg;
+    int width;
+    u4 idx;
+
+    valueType = *ptr++;
+    valueArg = valueType >> kDexAnnotationValueArgShift;
+    width = valueArg + 1;       /* assume, correct later */
+
+    ALOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]",
+        valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
+        (ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
+
+    pValue->type = valueType & kDexAnnotationValueTypeMask;
+
+    switch (valueType & kDexAnnotationValueTypeMask) {
+    case kDexAnnotationByte:
+        pValue->value.i = (s1) readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('B'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationShort:
+        pValue->value.i = (s2) readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('S'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationChar:
+        pValue->value.i = (u2) readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('C'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationInt:
+        pValue->value.i = readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('I'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationLong:
+        pValue->value.j = readSignedLong(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('J'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationFloat:
+        pValue->value.i = readUnsignedInt(ptr, valueArg, true);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('F'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationDouble:
+        pValue->value.j = readUnsignedLong(ptr, valueArg, true);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('D'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationBoolean:
+        pValue->value.i = (valueArg != 0);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('Z'));
+            setObject = true;
+        }
+        width = 0;
+        break;
+
+    case kDexAnnotationString:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            elemObj = (Object*) dvmResolveString(clazz, idx);
+            setObject = true;
+            if (elemObj == NULL)
+                return false;
+            dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+        }
+        break;
+    case kDexAnnotationType:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            elemObj = (Object*) dvmResolveClass(clazz, idx, true);
+            setObject = true;
+            if (elemObj == NULL) {
+                /* we're expected to throw a TypeNotPresentException here */
+                DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+                const char* desc = dexStringByTypeIdx(pDexFile, idx);
+                dvmClearException(self);
+                dvmThrowTypeNotPresentException(desc);
+                return false;
+            } else {
+                dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+            }
+        }
+        break;
+    case kDexAnnotationMethod:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            Method* meth = resolveAmbiguousMethod(clazz, idx);
+            if (meth == NULL)
+                return false;
+            elemObj = dvmCreateReflectObjForMethod(clazz, meth);
+            setObject = true;
+            if (elemObj == NULL)
+                return false;
+        }
+        break;
+    case kDexAnnotationField:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        assert(false);      // TODO
+        break;
+    case kDexAnnotationEnum:
+        /* enum values are the contents of a static field */
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            StaticField* sfield;
+
+            sfield = dvmResolveStaticField(clazz, idx);
+            if (sfield == NULL) {
+                return false;
+            } else {
+                assert(sfield->clazz->descriptor[0] == 'L');
+                elemObj = sfield->value.l;
+                setObject = true;
+                dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+            }
+        }
+        break;
+    case kDexAnnotationArray:
+        /*
+         * encoded_array format, which is a size followed by a stream
+         * of annotation_value.
+         *
+         * We create an array of Object, populate it, and return it.
+         */
+        if (resultStyle == kAllRaw) {
+            return false;
+        } else {
+            ArrayObject* newArray;
+            u4 size, count;
+
+            size = readUleb128(&ptr);
+            LOGVV("--- annotation array, size is %u at %p", size, ptr);
+            newArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
+                size, ALLOC_DEFAULT);
+            if (newArray == NULL) {
+                ALOGE("annotation element array alloc failed (%d)", size);
+                return false;
+            }
+
+            AnnotationValue avalue;
+            for (count = 0; count < size; count++) {
+                if (!processAnnotationValue(clazz, &ptr, &avalue,
+                                kAllObjects)) {
+                    dvmReleaseTrackedAlloc((Object*)newArray, self);
+                    return false;
+                }
+                Object* obj = (Object*)avalue.value.l;
+                dvmSetObjectArrayElement(newArray, count, obj);
+                dvmReleaseTrackedAlloc(obj, self);
+            }
+
+            elemObj = (Object*) newArray;
+            setObject = true;
+        }
+        width = 0;
+        break;
+    case kDexAnnotationAnnotation:
+        /* encoded_annotation format */
+        if (resultStyle == kAllRaw)
+            return false;
+        elemObj = processEncodedAnnotation(clazz, &ptr);
+        setObject = true;
+        if (elemObj == NULL)
+            return false;
+        dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+        width = 0;
+        break;
+    case kDexAnnotationNull:
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = 0;
+        } else {
+            assert(elemObj == NULL);
+            setObject = true;
+        }
+        width = 0;
+        break;
+    default:
+        ALOGE("Bad annotation element value byte 0x%02x (0x%02x)",
+            valueType, valueType & kDexAnnotationValueTypeMask);
+        assert(false);
+        return false;
+    }
+
+    ptr += width;
+
+    *pPtr = ptr;
+    if (setObject)
+        pValue->value.l = elemObj;
+    return true;
+}
+
+
+/*
+ * For most object types, we have nothing to do here, and we just return
+ * "valueObj".
+ *
+ * For an array annotation, the type of the extracted object will always
+ * be java.lang.Object[], but we want it to match the type that the
+ * annotation member is expected to return.  In some cases this may
+ * involve un-boxing primitive values.
+ *
+ * We allocate a second array with the correct type, then copy the data
+ * over.  This releases the tracked allocation on "valueObj" and returns
+ * a new, tracked object.
+ *
+ * On failure, this releases the tracking on "valueObj" and returns NULL
+ * (allowing the call to say "foo = convertReturnType(foo, ..)").
+ */
+static Object* convertReturnType(Object* valueObj, ClassObject* methodReturn)
+{
+    if (valueObj == NULL ||
+        !dvmIsArray((ArrayObject*)valueObj) || !dvmIsArrayClass(methodReturn))
+    {
+        return valueObj;
+    }
+
+    Thread* self = dvmThreadSelf();
+    ClassObject* srcElemClass;
+    ClassObject* dstElemClass;
+
+    /*
+     * We always extract kDexAnnotationArray into Object[], so we expect to
+     * find that here.  This means we can skip the FindClass on
+     * (valueObj->clazz->descriptor+1, valueObj->clazz->classLoader).
+     */
+    if (strcmp(valueObj->clazz->descriptor, "[Ljava/lang/Object;") != 0) {
+        ALOGE("Unexpected src type class (%s)", valueObj->clazz->descriptor);
+        return NULL;
+    }
+    srcElemClass = gDvm.classJavaLangObject;
+
+    /*
+     * Skip past the '[' to get element class name.  Note this is not always
+     * the same as methodReturn->elementClass.
+     */
+    char firstChar = methodReturn->descriptor[1];
+    if (firstChar == 'L' || firstChar == '[') {
+        dstElemClass = dvmFindClass(methodReturn->descriptor+1,
+            methodReturn->classLoader);
+    } else {
+        dstElemClass = dvmFindPrimitiveClass(firstChar);
+    }
+    ALOGV("HEY: converting valueObj from [%s to [%s",
+        srcElemClass->descriptor, dstElemClass->descriptor);
+
+    ArrayObject* srcArray = (ArrayObject*) valueObj;
+    u4 length = srcArray->length;
+    ArrayObject* newArray;
+
+    newArray = dvmAllocArrayByClass(methodReturn, length, ALLOC_DEFAULT);
+    if (newArray == NULL) {
+        ALOGE("Failed creating duplicate annotation class (%s %d)",
+            methodReturn->descriptor, length);
+        goto bail;
+    }
+
+    bool success;
+    if (dstElemClass->primitiveType == PRIM_NOT) {
+        success = dvmCopyObjectArray(newArray, srcArray, dstElemClass);
+    } else {
+        success = dvmUnboxObjectArray(newArray, srcArray, dstElemClass);
+    }
+    if (!success) {
+        ALOGE("Annotation array copy failed");
+        dvmReleaseTrackedAlloc((Object*)newArray, self);
+        newArray = NULL;
+        goto bail;
+    }
+
+bail:
+    /* replace old, return new */
+    dvmReleaseTrackedAlloc(valueObj, self);
+    return (Object*) newArray;
+}
+
+/*
+ * Create a new AnnotationMember.
+ *
+ * "clazz" is the class on which the annotations are defined.  "pPtr"
+ * points to a pointer into the annotation data.  "annoClass" is the
+ * annotation's class.
+ *
+ * We extract the annotation's value, create a new AnnotationMember object,
+ * and construct it.
+ *
+ * Returns NULL on failure; an exception may or may not be raised.
+ */
+static Object* createAnnotationMember(const ClassObject* clazz,
+    const ClassObject* annoClass, const u1** pPtr)
+{
+    Thread* self = dvmThreadSelf();
+    const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    StringObject* nameObj = NULL;
+    Object* valueObj = NULL;
+    Object* newMember = NULL;
+    Object* methodObj = NULL;
+    ClassObject* methodReturn = NULL;
+    u4 elementNameIdx;
+    const char* name;
+    AnnotationValue avalue;
+    JValue unused;
+    bool failed = true;
+
+    elementNameIdx = readUleb128(pPtr);
+
+    if (!processAnnotationValue(clazz, pPtr, &avalue, kAllObjects)) {
+        ALOGW("Failed processing annotation value");
+        goto bail;
+    }
+    valueObj = (Object*)avalue.value.l;
+
+    /* new member to hold the element */
+    newMember =
+        dvmAllocObject(gDvm.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) {
+            ALOGW("WARNING: could not find annotation member %s in %s",
+                name, annoClass->descriptor);
+        } else {
+            methodObj = dvmCreateReflectObjForMethod(annoClass, annoMeth);
+            methodReturn = dvmGetBoxedReturnType(annoMeth);
+        }
+    }
+    if (newMember == NULL || nameObj == NULL || methodObj == NULL ||
+        methodReturn == NULL)
+    {
+        ALOGE("Failed creating annotation element (m=%p n=%p a=%p r=%p)",
+            newMember, nameObj, methodObj, methodReturn);
+        goto bail;
+    }
+
+    /* convert the return type, if necessary */
+    valueObj = convertReturnType(valueObj, methodReturn);
+    if (valueObj == NULL)
+        goto bail;
+
+    /* call 4-argument constructor */
+    dvmCallMethod(self, gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init,
+        newMember, &unused, nameObj, valueObj, methodReturn, methodObj);
+    if (dvmCheckException(self)) {
+        ALOGD("Failed constructing annotation element");
+        goto bail;
+    }
+
+    failed = false;
+
+bail:
+    /* release tracked allocations */
+    dvmReleaseTrackedAlloc(newMember, self);
+    dvmReleaseTrackedAlloc((Object*)nameObj, self);
+    dvmReleaseTrackedAlloc(valueObj, self);
+    dvmReleaseTrackedAlloc(methodObj, self);
+    if (failed)
+        return NULL;
+    else
+        return newMember;
+}
+
+/*
+ * Create a new Annotation object from what we find in the annotation item.
+ *
+ * "clazz" is the class on which the annotations are defined.  "pPtr"
+ * points to a pointer into the annotation data.
+ *
+ * We use the AnnotationFactory class to create the annotation for us.  The
+ * method we call is:
+ *
+ *  public static Annotation createAnnotation(
+ *      Class<? extends Annotation> annotationType,
+ *      AnnotationMember[] elements)
+ *
+ * Returns a new Annotation, which will NOT be in the local ref table and
+ * not referenced elsewhere, so store it away soon.  On failure, returns NULL
+ * with an exception raised.
+ */
+static Object* processEncodedAnnotation(const ClassObject* clazz,
+    const u1** pPtr)
+{
+    Thread* self = dvmThreadSelf();
+    Object* newAnno = NULL;
+    ArrayObject* elementArray = NULL;
+    const ClassObject* annoClass;
+    const u1* ptr;
+    u4 typeIdx, size, count;
+
+    ptr = *pPtr;
+    typeIdx = readUleb128(&ptr);
+    size = readUleb128(&ptr);
+
+    LOGVV("----- processEnc ptr=%p type=%d size=%d", ptr, typeIdx, size);
+
+    annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx);
+    if (annoClass == NULL) {
+        annoClass = dvmResolveClass(clazz, typeIdx, true);
+        if (annoClass == NULL) {
+            ALOGE("Unable to resolve %s annotation class %d",
+                clazz->descriptor, typeIdx);
+            assert(dvmCheckException(self));
+            dvmClearException(self);
+            return NULL;
+        }
+    }
+
+    ALOGV("----- processEnc ptr=%p [0x%06x]  typeIdx=%d size=%d class=%s",
+        *pPtr, *pPtr - (u1*) clazz->pDvmDex->pDexFile->baseAddr,
+        typeIdx, size, annoClass->descriptor);
+
+    /*
+     * Elements are parsed out and stored in an array.  The Harmony
+     * constructor wants an array with just the declared elements --
+     * default values get merged in later.
+     */
+    JValue result;
+
+    if (size > 0) {
+        elementArray = dvmAllocArrayByClass(
+            gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray,
+            size, ALLOC_DEFAULT);
+        if (elementArray == NULL) {
+            ALOGE("failed to allocate annotation member array (%d elements)",
+                size);
+            goto bail;
+        }
+    }
+
+    /*
+     * "ptr" points to a byte stream with "size" occurrences of
+     * annotation_element.
+     */
+    for (count = 0; count < size; count++) {
+        Object* newMember = createAnnotationMember(clazz, annoClass, &ptr);
+        if (newMember == NULL)
+            goto bail;
+
+        /* add it to the array */
+        dvmSetObjectArrayElement(elementArray, count, newMember);
+    }
+
+    dvmCallMethod(self,
+        gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation,
+        NULL, &result, annoClass, elementArray);
+    if (dvmCheckException(self)) {
+        ALOGD("Failed creating an annotation");
+        //dvmLogExceptionStackTrace();
+        goto bail;
+    }
+
+    newAnno = (Object*)result.l;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) elementArray, NULL);
+    *pPtr = ptr;
+    if (newAnno == NULL && !dvmCheckException(self)) {
+        /* make sure an exception is raised */
+        dvmThrowRuntimeException("failure in processEncodedAnnotation");
+    }
+    return newAnno;
+}
+
+/*
+ * Run through an annotation set and convert each entry into an Annotation
+ * object.
+ *
+ * Returns an array of Annotation objects, or NULL with an exception raised
+ * on alloc failure.
+ */
+static ArrayObject* processAnnotationSet(const ClassObject* clazz,
+    const DexAnnotationSetItem* pAnnoSet, int visibility)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationItem* pAnnoItem;
+
+    /* we need these later; make sure they're initialized */
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory))
+        dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember))
+        dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
+
+    /* count up the number of visible elements */
+    size_t count = 0;
+    for (size_t i = 0; i < pAnnoSet->size; ++i) {
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility == visibility) {
+            count++;
+        }
+    }
+
+    ArrayObject* annoArray = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArray,
+                                                  count, ALLOC_DEFAULT);
+    if (annoArray == NULL) {
+        return NULL;
+    }
+
+    /*
+     * Generate Annotation objects.  We must put them into the array
+     * immediately (or add them to the tracked ref table).
+     * We may not be able to resolve all annotations, and should just
+     * ignore those we can't.
+     */
+    u4 dstIndex = 0;
+    for (int i = 0; i < (int) pAnnoSet->size; i++) {
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility != visibility)
+            continue;
+        const u1* ptr = pAnnoItem->annotation;
+        Object *anno = processEncodedAnnotation(clazz, &ptr);
+        if (anno != NULL) {
+            dvmSetObjectArrayElement(annoArray, dstIndex, anno);
+            ++dstIndex;
+        }
+    }
+
+    // If we got as many as we expected, we're done...
+    if (dstIndex == count) {
+        return annoArray;
+    }
+
+    // ...otherwise we need to trim the trailing nulls.
+    ArrayObject* trimmedArray = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArray,
+                                                     dstIndex, ALLOC_DEFAULT);
+    if (trimmedArray == NULL) {
+        return NULL;
+    }
+    for (size_t i = 0; i < dstIndex; ++i) {
+        Object** src = (Object**)(void*) annoArray->contents;
+        dvmSetObjectArrayElement(trimmedArray, i, src[i]);
+    }
+    dvmReleaseTrackedAlloc((Object*) annoArray, NULL);
+    return trimmedArray;
+}
+
+/*
+ * Return the annotation item of the specified type in the annotation set, or
+ * NULL if the set contains no annotation of that type.
+ */
+static const DexAnnotationItem* getAnnotationItemFromAnnotationSet(
+        const ClassObject* clazz, const DexAnnotationSetItem* pAnnoSet,
+        int visibility, const ClassObject* annotationClazz)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationItem* pAnnoItem;
+    int i;
+    const ClassObject* annoClass;
+    const u1* ptr;
+    u4 typeIdx;
+
+    /* we need these later; make sure they're initialized */
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory))
+        dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember))
+        dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
+
+    for (i = 0; i < (int) pAnnoSet->size; i++) {
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility != visibility)
+            continue;
+
+        ptr = pAnnoItem->annotation;
+        typeIdx = readUleb128(&ptr);
+
+        annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx);
+        if (annoClass == NULL) {
+            annoClass = dvmResolveClass(clazz, typeIdx, true);
+            if (annoClass == NULL) {
+                ALOGE("Unable to resolve %s annotation class %d",
+                      clazz->descriptor, typeIdx);
+                Thread* self = dvmThreadSelf();
+                assert(dvmCheckException(self));
+                dvmClearException(self);
+                continue;
+            }
+        }
+
+        if (annoClass == annotationClazz) {
+            return pAnnoItem;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Return the Annotation object of the specified type in the annotation set, or
+ * NULL if the set contains no annotation of that type.
+ */
+static Object* getAnnotationObjectFromAnnotationSet(const ClassObject* clazz,
+        const DexAnnotationSetItem* pAnnoSet, int visibility,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, visibility, annotationClazz);
+    if (pAnnoItem == NULL) {
+        return NULL;
+    }
+    const u1* ptr = pAnnoItem->annotation;
+    return processEncodedAnnotation(clazz, &ptr);
+}
+
+/*
+ * ===========================================================================
+ *      Skipping and scanning
+ * ===========================================================================
+ */
+
+/*
+ * Skip past an annotation value.
+ *
+ * "clazz" is the class on which the annotations are defined.
+ *
+ * Returns "true" on success, "false" on parsing failure.
+ */
+static bool skipAnnotationValue(const ClassObject* clazz, const u1** pPtr)
+{
+    const u1* ptr = *pPtr;
+    u1 valueType, valueArg;
+    int width;
+
+    valueType = *ptr++;
+    valueArg = valueType >> kDexAnnotationValueArgShift;
+    width = valueArg + 1;       /* assume */
+
+    ALOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]",
+        valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
+        (ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
+
+    switch (valueType & kDexAnnotationValueTypeMask) {
+    case kDexAnnotationByte:        break;
+    case kDexAnnotationShort:       break;
+    case kDexAnnotationChar:        break;
+    case kDexAnnotationInt:         break;
+    case kDexAnnotationLong:        break;
+    case kDexAnnotationFloat:       break;
+    case kDexAnnotationDouble:      break;
+    case kDexAnnotationString:      break;
+    case kDexAnnotationType:        break;
+    case kDexAnnotationMethod:      break;
+    case kDexAnnotationField:       break;
+    case kDexAnnotationEnum:        break;
+
+    case kDexAnnotationArray:
+        /* encoded_array format */
+        {
+            u4 size = readUleb128(&ptr);
+            while (size--) {
+                if (!skipAnnotationValue(clazz, &ptr))
+                    return false;
+            }
+        }
+        width = 0;
+        break;
+    case kDexAnnotationAnnotation:
+        /* encoded_annotation format */
+        if (!skipEncodedAnnotation(clazz, &ptr))
+            return false;
+        width = 0;
+        break;
+    case kDexAnnotationBoolean:
+    case kDexAnnotationNull:
+        width = 0;
+        break;
+    default:
+        ALOGE("Bad annotation element value byte 0x%02x", valueType);
+        assert(false);
+        return false;
+    }
+
+    ptr += width;
+
+    *pPtr = ptr;
+    return true;
+}
+
+/*
+ * Skip past an encoded annotation.  Mainly useful for annotations embedded
+ * in other annotations.
+ */
+static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr)
+{
+    const u1* ptr;
+    u4 size;
+
+    ptr = *pPtr;
+    (void) readUleb128(&ptr);
+    size = readUleb128(&ptr);
+
+    /*
+     * "ptr" points to a byte stream with "size" occurrences of
+     * annotation_element.
+     */
+    while (size--) {
+        (void) readUleb128(&ptr);
+
+        if (!skipAnnotationValue(clazz, &ptr))
+            return false;
+    }
+
+    *pPtr = ptr;
+    return true;
+}
+
+
+/*
+ * Compare the name of the class in the DEX file to the supplied descriptor.
+ * Return value is equivalent to strcmp.
+ */
+static int compareClassDescriptor(DexFile* pDexFile, u4 typeIdx,
+    const char* descriptor)
+{
+    const char* str = dexStringByTypeIdx(pDexFile, typeIdx);
+
+    return strcmp(str, descriptor);
+}
+
+/*
+ * Search through the annotation set for an annotation with a matching
+ * descriptor.
+ *
+ * Comparing the string descriptor is slower than comparing an integer class
+ * index.  If annotation lists are expected to be long, we could look up
+ * the class' index by name from the DEX file, rather than doing a class
+ * lookup and string compare on each entry.  (Note the index will be
+ * different for each DEX file, so we can't cache annotation class indices
+ * globally.)
+ */
+static const DexAnnotationItem* searchAnnotationSet(const ClassObject* clazz,
+    const DexAnnotationSetItem* pAnnoSet, const char* descriptor,
+    int visibility)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationItem* result = NULL;
+    u4 typeIdx;
+    int i;
+
+    //printf("##### searchAnnotationSet %s %d\n", descriptor, visibility);
+
+    for (i = 0; i < (int) pAnnoSet->size; i++) {
+        const DexAnnotationItem* pAnnoItem;
+
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility != visibility)
+            continue;
+        const u1* ptr = pAnnoItem->annotation;
+        typeIdx = readUleb128(&ptr);
+
+        if (compareClassDescriptor(pDexFile, typeIdx, descriptor) == 0) {
+            //printf("#####  match on %x/%p at %d\n", typeIdx, pDexFile, i);
+            result = pAnnoItem;
+            break;
+        }
+    }
+
+    return result;
+}
+
+/*
+ * Find an annotation value in the annotation_item whose name matches "name".
+ * A pointer to the annotation_value is returned, or NULL if it's not found.
+ */
+static const u1* searchEncodedAnnotation(const ClassObject* clazz,
+    const u1* ptr, const char* name)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    u4 typeIdx, size;
+
+    typeIdx = readUleb128(&ptr);
+    size = readUleb128(&ptr);
+    //printf("#####   searching ptr=%p type=%u size=%u\n", ptr, typeIdx, size);
+
+    while (size--) {
+        u4 elementNameIdx;
+        const char* elemName;
+
+        elementNameIdx = readUleb128(&ptr);
+        elemName = dexStringById(pDexFile, elementNameIdx);
+        if (strcmp(name, elemName) == 0) {
+            //printf("#####   item match on %s\n", name);
+            return ptr;     /* points to start of value */
+        }
+
+        skipAnnotationValue(clazz, &ptr);
+    }
+
+    //printf("#####   no item match on %s\n", name);
+    return NULL;
+}
+
+#define GAV_FAILED  ((Object*) 0x10000001)
+
+/*
+ * Extract an encoded annotation value from the field specified by "annoName".
+ *
+ * "expectedType" is an annotation value type, e.g. kDexAnnotationString.
+ * "debugAnnoName" is only used in debug messages.
+ *
+ * Returns GAV_FAILED on failure.  If an allocation failed, an exception
+ * will be raised.
+ */
+static Object* getAnnotationValue(const ClassObject* clazz,
+    const DexAnnotationItem* pAnnoItem, const char* annoName,
+    int expectedType, const char* debugAnnoName)
+{
+    const u1* ptr;
+    AnnotationValue avalue;
+
+    /* find the annotation */
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, annoName);
+    if (ptr == NULL) {
+        ALOGW("%s annotation lacks '%s' member", debugAnnoName, annoName);
+        return GAV_FAILED;
+    }
+
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects))
+        return GAV_FAILED;
+
+    /* make sure it has the expected format */
+    if (avalue.type != expectedType) {
+        ALOGW("%s %s has wrong type (0x%02x, expected 0x%02x)",
+            debugAnnoName, annoName, avalue.type, expectedType);
+        return GAV_FAILED;
+    }
+
+    return (Object*)avalue.value.l;
+}
+
+
+/*
+ * Find the Signature attribute and extract its value.  (Signatures can
+ * be found in annotations on classes, constructors, methods, and fields.)
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if not found.  On memory alloc failure, returns NULL with an
+ * exception raised.
+ */
+static ArrayObject* getSignatureValue(const ClassObject* clazz,
+    const DexAnnotationSetItem* pAnnoSet)
+{
+    const DexAnnotationItem* pAnnoItem;
+    Object* obj;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrSignature,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The Signature annotation has one member, "String value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationArray,
+            "Signature");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(obj->clazz == gDvm.classJavaLangObjectArray);
+
+    return (ArrayObject*)obj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Class
+ * ===========================================================================
+ */
+
+/*
+ * Find the DexAnnotationSetItem for this class.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForClass(
+    const ClassObject* clazz)
+{
+    DexFile* pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+
+    if (clazz->pDvmDex == NULL)         /* generated class (Proxy, array) */
+        return NULL;
+
+    pDexFile = clazz->pDvmDex->pDexFile;
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir != NULL)
+        return dexGetClassAnnotationSet(pDexFile, pAnnoDir);
+    else
+        return NULL;
+}
+
+/*
+ * Return an array of Annotation objects for the class.  Returns an empty
+ * array if there are no annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * On allocation failure, this returns NULL with an exception raised.
+ */
+ArrayObject* dvmGetClassAnnotations(const ClassObject* clazz)
+{
+    ArrayObject* annoArray;
+    const DexAnnotationSetItem* pAnnoSet = NULL;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL) {
+        /* no annotations for anything in class, or no class annotations */
+        annoArray = emptyAnnoArray();
+    } else {
+        annoArray = processAnnotationSet(clazz, pAnnoSet,
+                        kDexVisibilityRuntime);
+    }
+
+    return annoArray;
+}
+
+/*
+ * Returns the annotation or NULL if it doesn't exist.
+ */
+Object* dvmGetClassAnnotation(const ClassObject* clazz,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    return getAnnotationObjectFromAnnotationSet(clazz, pAnnoSet,
+            kDexVisibilityRuntime, annotationClazz);
+}
+
+/*
+ * Returns true if the annotation exists.
+ */
+bool dvmIsClassAnnotationPresent(const ClassObject* clazz,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, kDexVisibilityRuntime, annotationClazz);
+    return (pAnnoItem != NULL);
+}
+
+/*
+ * Retrieve the Signature annotation, if any.  Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetClassSignatureAnnotation(const ClassObject* clazz)
+{
+    ArrayObject* signature = NULL;
+    const DexAnnotationSetItem* pAnnoSet;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet != NULL)
+        signature = getSignatureValue(clazz, pAnnoSet);
+
+    return signature;
+}
+
+/*
+ * Get the EnclosingMethod attribute from an annotation.  Returns a Method
+ * object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmGetEnclosingMethod(const ClassObject* clazz)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The EnclosingMethod annotation has one member, "Method value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationMethod,
+            "EnclosingMethod");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(obj->clazz == gDvm.classJavaLangReflectConstructor ||
+           obj->clazz == gDvm.classJavaLangReflectMethod);
+
+    return obj;
+}
+
+/*
+ * Find a class' enclosing class.  We return what we find in the
+ * EnclosingClass attribute.
+ *
+ * Returns a Class object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ClassObject* dvmGetDeclaringClass(const ClassObject* clazz)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The EnclosingClass annotation has one member, "Class value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
+            "EnclosingClass");
+    if (obj == GAV_FAILED)
+        return NULL;
+
+    assert(dvmIsClassObject(obj));
+    return (ClassObject*)obj;
+}
+
+/*
+ * Find a class' enclosing class.  We first search for an EnclosingClass
+ * attribute, and if that's not found we look for an EnclosingMethod.
+ *
+ * Returns a Class object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ClassObject* dvmGetEnclosingClass(const ClassObject* clazz)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
+        kDexVisibilitySystem);
+    if (pAnnoItem != NULL) {
+        /*
+         * The EnclosingClass annotation has one member, "Class value".
+         */
+        obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
+                "EnclosingClass");
+        if (obj != GAV_FAILED) {
+            assert(dvmIsClassObject(obj));
+            return (ClassObject*)obj;
+        }
+    }
+
+    /*
+     * That didn't work.  Look for an EnclosingMethod.
+     *
+     * We could create a java.lang.reflect.Method object and extract the
+     * declaringClass from it, but that's more work than we want to do.
+     * Instead, we find the "value" item and parse the index out ourselves.
+     */
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /* find the value member */
+    const u1* ptr;
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
+    if (ptr == NULL) {
+        ALOGW("EnclosingMethod annotation lacks 'value' member");
+        return NULL;
+    }
+
+    /* parse it, verify the type */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+        ALOGW("EnclosingMethod parse failed");
+        return NULL;
+    }
+    if (avalue.type != kDexAnnotationMethod) {
+        ALOGW("EnclosingMethod value has wrong type (0x%02x, expected 0x%02x)",
+            avalue.type, kDexAnnotationMethod);
+        return NULL;
+    }
+
+    /* pull out the method index and resolve the method */
+    Method* meth = resolveAmbiguousMethod(clazz, avalue.value.i);
+    if (meth == NULL)
+        return NULL;
+
+    ClassObject* methClazz = meth->clazz;
+    dvmAddTrackedAlloc((Object*) methClazz, NULL);      // balance the Release
+    return methClazz;
+}
+
+/*
+ * Get the EnclosingClass attribute from an annotation.  If found, returns
+ * "true".  A String with the original name of the class and the original
+ * access flags are returned through the arguments.  (The name will be NULL
+ * for an anonymous inner class.)
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+bool dvmGetInnerClass(const ClassObject* clazz, StringObject** pName,
+    int* pAccessFlags)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return false;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrInnerClass,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return false;
+
+    /*
+     * The InnerClass annotation has two members, "String name" and
+     * "int accessFlags".  We don't want to get the access flags as an
+     * Integer, so we process that as a simple value.
+     */
+    const u1* ptr;
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "name");
+    if (ptr == NULL) {
+        ALOGW("InnerClass annotation lacks 'name' member");
+        return false;
+    }
+
+    /* parse it into an Object */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
+        ALOGD("processAnnotationValue failed on InnerClass member 'name'");
+        return false;
+    }
+
+    /* make sure it has the expected format */
+    if (avalue.type != kDexAnnotationNull &&
+        avalue.type != kDexAnnotationString)
+    {
+        ALOGW("InnerClass name has bad type (0x%02x, expected STRING or NULL)",
+            avalue.type);
+        return false;
+    }
+
+    *pName = (StringObject*) avalue.value.l;
+    assert(*pName == NULL || (*pName)->clazz == gDvm.classJavaLangString);
+
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "accessFlags");
+    if (ptr == NULL) {
+        ALOGW("InnerClass annotation lacks 'accessFlags' member");
+        return false;
+    }
+
+    /* parse it, verify the type */
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+        ALOGW("InnerClass accessFlags parse failed");
+        return false;
+    }
+    if (avalue.type != kDexAnnotationInt) {
+        ALOGW("InnerClass value has wrong type (0x%02x, expected 0x%02x)",
+            avalue.type, kDexAnnotationInt);
+        return false;
+    }
+
+    *pAccessFlags = avalue.value.i;
+
+    return true;
+}
+
+/*
+ * Extract an array of Class objects from the MemberClasses annotation
+ * for this class.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if we don't find any member classes.
+ */
+ArrayObject* dvmGetDeclaredClasses(const ClassObject* clazz)
+{
+    const DexAnnotationSetItem* pAnnoSet;
+    const DexAnnotationItem* pAnnoItem;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrMemberClasses,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The MemberClasses annotation has one member, "Class[] value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value",
+            kDexAnnotationArray, "MemberClasses");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(dvmIsArray((ArrayObject*)obj));
+    obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
+    return (ArrayObject*)obj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Method (and Constructor)
+ * ===========================================================================
+ */
+
+/*
+ * Compare the attributes (class name, method name, method signature) of
+ * the specified method to "method".
+ */
+static int compareMethodStr(DexFile* pDexFile, u4 methodIdx,
+    const Method* method)
+{
+    const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    const char* str = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+    int result = strcmp(str, method->clazz->descriptor);
+
+    if (result == 0) {
+        str = dexStringById(pDexFile, pMethodId->nameIdx);
+        result = strcmp(str, method->name);
+        if (result == 0) {
+            DexProto proto;
+            dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+            result = dexProtoCompare(&proto, &method->prototype);
+        }
+    }
+
+    return result;
+}
+
+/*
+ * Given a method, determine the method's index.
+ *
+ * We could simply store this in the Method*, but that would cost 4 bytes
+ * per method.  Instead we plow through the DEX data.
+ *
+ * We have two choices: look through the class method data, or look through
+ * the global method_ids table.  The former is awkward because the method
+ * could have been defined in a superclass or interface.  The latter works
+ * out reasonably well because it's in sorted order, though we're still left
+ * doing a fair number of string comparisons.
+ */
+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);
+        ALOGE("Unable to find method %s.%s %s in DEX file!",
+            method->clazz->descriptor, method->name, desc);
+        free(desc);
+        dvmAbort();
+    }
+
+    return cur;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this method.
+ *
+ * Returns NULL if none found.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForMethod(
+    const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    DexFile* pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+    const DexMethodAnnotationsItem* pMethodList;
+    const DexAnnotationSetItem* pAnnoSet = NULL;
+
+    if (clazz->pDvmDex == NULL)         /* generated class (Proxy, array) */
+        return NULL;
+    pDexFile = clazz->pDvmDex->pDexFile;
+
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir != NULL) {
+        pMethodList = dexGetMethodAnnotations(pDexFile, pAnnoDir);
+        if (pMethodList != NULL) {
+            /*
+             * Run through the list and find a matching method.  We compare the
+             * method ref indices in the annotation list with the method's DEX
+             * method_idx value.
+             *
+             * TODO: use a binary search for long lists
+             *
+             * Alternate approach: for each entry in the annotations list,
+             * find the method definition in the DEX file and perform string
+             * comparisons on class name, method name, and signature.
+             */
+            u4 methodIdx = 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;
+}
+
+/*
+ * Returns the annotation or NULL if it doesn't exist.
+ */
+Object* dvmGetMethodAnnotation(const ClassObject* clazz, const Method* method,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    return getAnnotationObjectFromAnnotationSet(clazz, pAnnoSet,
+            kDexVisibilityRuntime, annotationClazz);
+}
+
+/*
+ * Returns true if the annotation exists.
+ */
+bool dvmIsMethodAnnotationPresent(const ClassObject* clazz,
+        const Method* method, const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, kDexVisibilityRuntime, annotationClazz);
+    return (pAnnoItem != NULL);
+}
+
+/*
+ * Retrieve the Signature annotation, if any.  Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetMethodSignatureAnnotation(const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    const DexAnnotationSetItem* pAnnoSet;
+    ArrayObject* signature = NULL;
+
+    pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet != NULL)
+        signature = getSignatureValue(clazz, pAnnoSet);
+
+    return signature;
+}
+
+/*
+ * Extract an array of exception classes from the "system" annotation list
+ * for this method.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if we don't find any exceptions for this method.
+ */
+ArrayObject* dvmGetMethodThrows(const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    const DexAnnotationSetItem* pAnnoSet;
+    const DexAnnotationItem* pAnnoItem;
+
+    /* find the set for this method */
+    pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet == NULL)
+        return NULL;        /* nothing for this method */
+
+    /* find the "Throws" annotation, if any */
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrThrows,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;        /* no Throws */
+
+    /*
+     * The Throws annotation has one member, "Class[] value".
+     */
+    Object* obj = getAnnotationValue(clazz, pAnnoItem, "value",
+        kDexAnnotationArray, "Throws");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(dvmIsArray((ArrayObject*)obj));
+    obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
+    return (ArrayObject*)obj;
+}
+
+/*
+ * Given an Annotation's method, find the default value, if any.
+ *
+ * If this is a CLASS annotation, and we can't find a match for the
+ * default class value, we need to throw a TypeNotPresentException.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmGetAnnotationDefaultValue(const Method* method)
+{
+    const ClassObject* clazz = method->clazz;
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+    const DexAnnotationSetItem* pAnnoSet = NULL;
+
+    /*
+     * The method's declaring class (the annotation) will have an
+     * AnnotationDefault "system" annotation associated with it if any
+     * of its methods have default values.  Start by finding the
+     * DexAnnotationItem associated with the class.
+     */
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir != NULL)
+        pAnnoSet = dexGetClassAnnotationSet(pDexFile, pAnnoDir);
+    if (pAnnoSet == NULL) {
+        /* no annotations for anything in class, or no class annotations */
+        return NULL;
+    }
+
+    /* find the "AnnotationDefault" annotation, if any */
+    const DexAnnotationItem* pAnnoItem;
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrAnnotationDefault,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL) {
+        /* no default values for any member in this annotation */
+        //printf("##### no default annotations for %s.%s\n",
+        //    method->clazz->descriptor, method->name);
+        return NULL;
+    }
+
+    /*
+     * The AnnotationDefault annotation has one member, "Annotation value".
+     * We need to pull that out.
+     */
+    const u1* ptr;
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
+    if (ptr == NULL) {
+        ALOGW("AnnotationDefault annotation lacks 'value'");
+        return NULL;
+    }
+    if ((*ptr & kDexAnnotationValueTypeMask) != kDexAnnotationAnnotation) {
+        ALOGW("AnnotationDefault value has wrong type (0x%02x)",
+            *ptr & kDexAnnotationValueTypeMask);
+        return NULL;
+    }
+
+    /*
+     * The value_type byte for VALUE_ANNOTATION is followed by
+     * encoded_annotation data.  We want to scan through it to find an
+     * entry whose name matches our method name.
+     */
+    ptr++;
+    ptr = searchEncodedAnnotation(clazz, ptr, method->name);
+    if (ptr == NULL)
+        return NULL;        /* no default annotation for this method */
+
+    /* got it, pull it out */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
+        ALOGD("processAnnotationValue failed on default for '%s'",
+            method->name);
+        return NULL;
+    }
+
+    /* convert the return type, if necessary */
+    ClassObject* methodReturn = dvmGetBoxedReturnType(method);
+    Object* obj = (Object*)avalue.value.l;
+    obj = convertReturnType(obj, methodReturn);
+
+    return obj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Field
+ * ===========================================================================
+ */
+
+/*
+ * Compare the attributes (class name, field name, field signature) of
+ * the specified field to "field".
+ */
+static int compareFieldStr(DexFile* pDexFile, u4 idx, const Field* field)
+{
+    const DexFieldId* pFieldId = dexGetFieldId(pDexFile, idx);
+    const char* str = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+    int result = strcmp(str, field->clazz->descriptor);
+
+    if (result == 0) {
+        str = dexStringById(pDexFile, pFieldId->nameIdx);
+        result = strcmp(str, field->name);
+        if (result == 0) {
+            str = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+            result = strcmp(str, field->signature);
+        }
+    }
+
+    return result;
+}
+
+/*
+ * Given a field, determine the field's index.
+ *
+ * This has the same tradeoffs as 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 */
+        ALOGE("Unable to find field %s.%s %s in DEX file!",
+            field->clazz->descriptor, field->name, field->signature);
+        dvmAbort();
+    }
+
+    return cur;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this field.
+ *
+ * Returns NULL if none found.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForField(const Field* field)
+{
+    ClassObject* clazz = field->clazz;
+    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;
+}
+
+/*
+ * Returns the annotation or NULL if it doesn't exist.
+ */
+Object* dvmGetFieldAnnotation(const ClassObject* clazz, const Field* field,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForField(field);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    return getAnnotationObjectFromAnnotationSet(clazz, pAnnoSet,
+            kDexVisibilityRuntime, annotationClazz);
+}
+
+/*
+ * Returns true if the annotation exists.
+ */
+bool dvmIsFieldAnnotationPresent(const ClassObject* clazz,
+        const Field* field, const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForField(field);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, kDexVisibilityRuntime, annotationClazz);
+    return (pAnnoItem != NULL);
+}
+
+/*
+ * Retrieve the Signature annotation, if any.  Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetFieldSignatureAnnotation(const Field* field)
+{
+    ClassObject* clazz = field->clazz;
+    const DexAnnotationSetItem* pAnnoSet;
+    ArrayObject* signature = NULL;
+
+    pAnnoSet = findAnnotationSetForField(field);
+    if (pAnnoSet != NULL)
+        signature = getSignatureValue(clazz, pAnnoSet);
+
+    return signature;
+}
+
+
+/*
+ * ===========================================================================
+ *      Parameter
+ * ===========================================================================
+ */
+
+/*
+ * We have an annotation_set_ref_list, which is essentially a list of
+ * entries that we pass to processAnnotationSet().
+ *
+ * The returned object must be released with dvmReleaseTrackedAlloc.
+ */
+static ArrayObject* processAnnotationSetRefList(const ClassObject* clazz,
+    const DexAnnotationSetRefList* pAnnoSetList, u4 count)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    ArrayObject* annoArrayArray = NULL;
+    u4 idx;
+
+    /* allocate an array of Annotation arrays to hold results */
+    annoArrayArray = dvmAllocArrayByClass(
+        gDvm.classJavaLangAnnotationAnnotationArrayArray, count, ALLOC_DEFAULT);
+    if (annoArrayArray == NULL) {
+        ALOGW("annotation set ref array alloc failed");
+        goto bail;
+    }
+
+    for (idx = 0; idx < count; idx++) {
+        Thread* self = dvmThreadSelf();
+        const DexAnnotationSetRefItem* pItem;
+        const DexAnnotationSetItem* pAnnoSet;
+        Object *annoSet;
+        DexAnnotationSetItem emptySet;
+        emptySet.size = 0;
+
+        pItem = dexGetParameterAnnotationSetRef(pAnnoSetList, idx);
+        pAnnoSet = dexGetSetRefItemItem(pDexFile, pItem);
+        if (pAnnoSet == NULL) {
+            pAnnoSet = &emptySet;
+        }
+
+        annoSet = (Object *)processAnnotationSet(clazz,
+                                                 pAnnoSet,
+                                                 kDexVisibilityRuntime);
+        if (annoSet == NULL) {
+            ALOGW("processAnnotationSet failed");
+            annoArrayArray = NULL;
+            goto bail;
+        }
+        dvmSetObjectArrayElement(annoArrayArray, idx, annoSet);
+        dvmReleaseTrackedAlloc((Object*) annoSet, self);
+    }
+
+bail:
+    return annoArrayArray;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this parameter.
+ *
+ * Returns NULL if none found.
+ */
+static const DexParameterAnnotationsItem* findAnnotationsItemForMethod(
+    const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    DexFile* pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+    const DexParameterAnnotationsItem* pParameterList;
+
+    if (clazz->pDvmDex == NULL)         /* generated class (Proxy, array) */
+        return NULL;
+
+    pDexFile = clazz->pDvmDex->pDexFile;
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir == NULL)
+        return NULL;
+
+    pParameterList = dexGetParameterAnnotations(pDexFile, pAnnoDir);
+    if (pParameterList == NULL)
+        return NULL;
+
+    /*
+     * Run through the list and find a matching method.  We compare the
+     * method ref indices in the annotation list with the method's DEX
+     * method_idx value.
+     *
+     * TODO: use a binary search for long lists
+     *
+     * Alternate approach: for each entry in the annotations list,
+     * find the method definition in the DEX file and perform string
+     * comparisons on class name, method name, and signature.
+     */
+    u4 methodIdx = 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) {
+        ALOGE("Failed to process array element %d from %p",
+                iterator->size - iterator->elementsLeft,
+                iterator->encodedArray);
+        iterator->elementsLeft = 0;
+        return false;
+    }
+
+    iterator->elementsLeft--;
+    return true;
+}
diff --git a/vm/reflect/Proxy.cpp b/vm/reflect/Proxy.cpp
new file mode 100644
index 0000000..57d32e7
--- /dev/null
+++ b/vm/reflect/Proxy.cpp
@@ -0,0 +1,1029 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of java.lang.reflect.Proxy.
+ *
+ * Traditionally this is implemented entirely in interpreted code,
+ * generating bytecode that defines the proxy class.  Dalvik doesn't
+ * currently support this approach, so we generate the class directly.  If
+ * we add support for DefineClass with standard classfiles we can
+ * eliminate this.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+// fwd
+static bool returnTypesAreCompatible(Method* baseMethod, Method* subMethod);
+static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,\
+    ArrayObject** pThrows, int* pMethodCount);
+static int copyWithoutDuplicates(Method** allMethods, int allCount,
+    Method** outMethods, ArrayObject* throws);
+static bool createExceptionClassList(const Method* method,
+    PointerSet** pThrows);
+static void updateExceptionClassList(const Method* method, PointerSet* throws);
+static void createConstructor(ClassObject* clazz, Method* meth);
+static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
+    const Method* srcMeth);
+static void proxyConstructor(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+static void proxyInvoker(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+static bool mustWrapException(const Method* method, const Object* throwable);
+
+/* private static fields in the Proxy class */
+#define kThrowsField    0
+#define kProxySFieldCount 1
+
+/*
+ * Generate a proxy class with the specified name, interfaces, and loader.
+ * "interfaces" is an array of class objects.
+ *
+ * The Proxy.getProxyClass() code has done the following:
+ *  - Verified that "interfaces" contains only interfaces
+ *  - Verified that no interface appears twice
+ *  - Prepended the package name to the class name if one or more
+ *    interfaces are non-public
+ *  - Searched for an existing instance of an appropriate Proxy class
+ *
+ * On failure we leave a partially-created class object sitting around,
+ * but the garbage collector will take care of it.
+ */
+ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
+    Object* loader)
+{
+    ClassObject* result = NULL;
+    ArrayObject* throws = NULL;
+
+    char* nameStr = dvmCreateCstrFromString(str);
+    if (nameStr == NULL) {
+        dvmThrowIllegalArgumentException("missing name");
+        return NULL;
+    }
+
+    ALOGV("+++ Generate proxy class '%s' %p from %d interface classes",
+        nameStr, loader, interfaces->length);
+
+
+    /*
+     * Characteristics of a Proxy class:
+     * - concrete class, public and final
+     * - superclass is java.lang.reflect.Proxy
+     * - implements all listed interfaces (req'd for instanceof)
+     * - has one method for each method in the interfaces (for duplicates,
+     *   the method in the earliest interface wins)
+     * - has one constructor (takes an InvocationHandler arg)
+     * - has overrides for hashCode, equals, and toString (these come first)
+     * - has one field, a reference to the InvocationHandler object, inherited
+     *   from Proxy
+     *
+     * TODO: set protection domain so it matches bootstrap classes.
+     *
+     * The idea here is to create a class object and fill in the details
+     * as we would in loadClassFromDex(), and then call dvmLinkClass() to do
+     * all the heavy lifting (notably populating the virtual and interface
+     * method tables).
+     */
+
+    /*
+     * Allocate storage for the class object and set some basic fields.
+     */
+    size_t newClassSize =
+        sizeof(ClassObject) + kProxySFieldCount * sizeof(StaticField);
+    ClassObject* newClass =
+        (ClassObject*) dvmMalloc(newClassSize, ALLOC_NON_MOVING);
+    if (newClass == NULL)
+        goto bail;
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptorAlloc = dvmNameToDescriptor(nameStr);
+    newClass->descriptor = newClass->descriptorAlloc;
+    SET_CLASS_FLAG(newClass, ACC_PUBLIC | ACC_FINAL);
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, super),
+                      (Object *)gDvm.classJavaLangReflectProxy);
+    newClass->primitiveType = PRIM_NOT;
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, classLoader),
+                      (Object *)loader);
+
+    /*
+     * Add direct method definitions.  We have one (the constructor).
+     */
+    newClass->directMethodCount = 1;
+    newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader,
+            1 * sizeof(Method));
+    createConstructor(newClass, &newClass->directMethods[0]);
+    dvmLinearReadOnly(newClass->classLoader, newClass->directMethods);
+
+    /*
+     * Add virtual method definitions.
+     */
+    {
+        /*
+         * Generate a temporary list of virtual methods.
+         */
+        int methodCount;
+        Method **methods;
+        if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) {
+            goto bail;
+        }
+        newClass->virtualMethodCount = methodCount;
+        size_t virtualMethodsSize = methodCount * sizeof(Method);
+        newClass->virtualMethods =
+            (Method*)dvmLinearAlloc(newClass->classLoader, virtualMethodsSize);
+        for (int i = 0; i < newClass->virtualMethodCount; i++) {
+            createHandlerMethod(newClass, &newClass->virtualMethods[i], methods[i]);
+        }
+        free(methods);
+        dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods);
+    }
+
+    /*
+     * Add interface list.
+     */
+    {
+        size_t interfaceCount = interfaces->length;
+        ClassObject** ifArray = (ClassObject**)(void*)interfaces->contents;
+        newClass->interfaceCount = interfaceCount;
+        size_t interfacesSize = sizeof(ClassObject*) * interfaceCount;
+        newClass->interfaces =
+            (ClassObject**)dvmLinearAlloc(newClass->classLoader, interfacesSize);
+        for (size_t i = 0; i < interfaceCount; i++)
+          newClass->interfaces[i] = ifArray[i];
+        dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
+    }
+
+    /*
+     * Static field list.  We have one private field, for our list of
+     * exceptions declared for each method.
+     */
+    assert(kProxySFieldCount == 1);
+    newClass->sfieldCount = kProxySFieldCount;
+    {
+        StaticField* sfield = &newClass->sfields[kThrowsField];
+        sfield->clazz = newClass;
+        sfield->name = "throws";
+        sfield->signature = "[[Ljava/lang/Throwable;";
+        sfield->accessFlags = ACC_STATIC | ACC_PRIVATE;
+        dvmSetStaticFieldObject(sfield, (Object*)throws);
+    }
+
+    /*
+     * Everything is ready. This class didn't come out of a DEX file
+     * so we didn't tuck any indexes into the class object.  We can
+     * advance to LOADED state immediately.
+     */
+    newClass->status = CLASS_LOADED;
+    if (!dvmLinkClass(newClass)) {
+        ALOGD("Proxy class link failed");
+        goto bail;
+    }
+
+    /*
+     * All good.  Add it to the hash table.  We should NOT see a collision
+     * here; if we do, it means the caller has screwed up and provided us
+     * with a duplicate name.
+     */
+    if (!dvmAddClassToHash(newClass)) {
+        ALOGE("ERROR: attempted to generate %s more than once",
+            newClass->descriptor);
+        goto bail;
+    }
+
+    result = newClass;
+
+bail:
+    free(nameStr);
+    if (result == NULL) {
+        /* must free innards explicitly if we didn't finish linking */
+        dvmFreeClassInnards(newClass);
+        if (!dvmCheckException(dvmThreadSelf())) {
+            /* throw something */
+            dvmThrowRuntimeException(NULL);
+        }
+    }
+
+    /* allow the GC to free these when nothing else has a reference */
+    dvmReleaseTrackedAlloc((Object*) throws, NULL);
+    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+    return result;
+}
+
+
+/*
+ * Generate a list of methods.  The Method pointers returned point to the
+ * abstract method definition from the appropriate interface, or to the
+ * virtual method definition in java.lang.Object.
+ *
+ * We also allocate an array of arrays of throwable classes, one for each
+ * method,so we can do some special handling of checked exceptions.  The
+ * caller must call ReleaseTrackedAlloc() on *pThrows.
+ */
+static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,
+    ArrayObject** pThrows, int* pMethodCount)
+{
+    ClassObject** classes;
+    ArrayObject* throws = NULL;
+    Method** methods = NULL;
+    Method** allMethods = NULL;
+    int numInterfaces, maxCount, actualCount, allCount;
+    bool result = false;
+    int i;
+
+    /*
+     * Get a maximum count so we can allocate storage.  We need the
+     * methods declared by each interface and all of its superinterfaces.
+     */
+    maxCount = 3;       // 3 methods in java.lang.Object
+    numInterfaces = interfaces->length;
+    classes = (ClassObject**)(void*)interfaces->contents;
+
+    for (i = 0; i < numInterfaces; i++, classes++) {
+        ClassObject* clazz = *classes;
+
+        LOGVV("---  %s virtualMethodCount=%d",
+            clazz->descriptor, clazz->virtualMethodCount);
+        maxCount += clazz->virtualMethodCount;
+
+        int j;
+        for (j = 0; j < clazz->iftableCount; j++) {
+            ClassObject* iclass = clazz->iftable[j].clazz;
+
+            LOGVV("---  +%s %d",
+                iclass->descriptor, iclass->virtualMethodCount);
+            maxCount += iclass->virtualMethodCount;
+        }
+    }
+
+    methods = (Method**) malloc(maxCount * sizeof(*methods));
+    allMethods = (Method**) malloc(maxCount * sizeof(*methods));
+    if (methods == NULL || allMethods == NULL)
+        goto bail;
+
+    /*
+     * First three entries are the java.lang.Object methods.
+     */
+    {
+      ClassObject* obj = gDvm.classJavaLangObject;
+      allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals];
+      allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode];
+      allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString];
+      allCount = 3;
+    }
+
+    /*
+     * Add the methods from each interface, in order.
+     */
+    classes = (ClassObject**)(void*)interfaces->contents;
+    for (i = 0; i < numInterfaces; i++, classes++) {
+        ClassObject* clazz = *classes;
+        int j;
+
+        for (j = 0; j < clazz->virtualMethodCount; j++) {
+            allMethods[allCount++] = &clazz->virtualMethods[j];
+        }
+
+        for (j = 0; j < clazz->iftableCount; j++) {
+            ClassObject* iclass = clazz->iftable[j].clazz;
+            int k;
+
+            for (k = 0; k < iclass->virtualMethodCount; k++) {
+                allMethods[allCount++] = &iclass->virtualMethods[k];
+            }
+        }
+    }
+    assert(allCount == maxCount);
+
+    /*
+     * Allocate some storage to hold the lists of throwables.  We need
+     * one entry per unique method, but it's convenient to allocate it
+     * ahead of the duplicate processing.
+     */
+    ClassObject* arrArrClass;
+    arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL);
+    if (arrArrClass == NULL)
+        goto bail;
+    throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT);
+
+    /*
+     * Identify and remove duplicates.
+     */
+    actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws);
+    if (actualCount < 0)
+        goto bail;
+
+    //ALOGI("gathered methods:");
+    //for (i = 0; i < actualCount; i++) {
+    //    ALOGI(" %d: %s.%s",
+    //        i, methods[i]->clazz->descriptor, methods[i]->name);
+    //}
+
+    *pMethods = methods;
+    *pMethodCount = actualCount;
+    *pThrows = throws;
+    result = true;
+
+bail:
+    free(allMethods);
+    if (!result) {
+        free(methods);
+        dvmReleaseTrackedAlloc((Object*)throws, NULL);
+    }
+    return result;
+}
+
+/*
+ * Identify and remove duplicates, where "duplicate" means it has the
+ * same name and arguments, but not necessarily the same return type.
+ *
+ * If duplicate methods have different return types, we want to use the
+ * first method whose return type is assignable from all other duplicate
+ * methods.  That is, if we have:
+ *   class base {...}
+ *   class sub extends base {...}
+ *   class subsub extends sub {...}
+ * Then we want to return the method that returns subsub, since callers
+ * to any form of the method will get a usable object back.
+ *
+ * All other duplicate methods are stripped out.
+ *
+ * This also populates the "throwLists" array with arrays of Class objects,
+ * one entry per method in "outMethods".  Methods that don't declare any
+ * throwables (or have no common throwables with duplicate methods) will
+ * have NULL entries.
+ *
+ * Returns the number of methods copied into "methods", or -1 on failure.
+ */
+static int copyWithoutDuplicates(Method** allMethods, int allCount,
+    Method** outMethods, ArrayObject* throwLists)
+{
+    int outCount = 0;
+    int i, j;
+
+    /*
+     * The plan is to run through all methods, checking all other methods
+     * for a duplicate.  If we find a match, we see if the other methods'
+     * return type is compatible/assignable with ours.  If the current
+     * method is assignable from all others, we copy it to the new list,
+     * and NULL out all other entries.  If not, we keep looking for a
+     * better version.
+     *
+     * If there are no duplicates, we copy the method and NULL the entry.
+     *
+     * At the end of processing, if we have any non-NULL entries, then we
+     * have bad duplicates and must exit with an exception.
+     */
+    for (i = 0; i < allCount; i++) {
+        bool best, dupe;
+
+        if (allMethods[i] == NULL)
+            continue;
+
+        /*
+         * Find all duplicates.  If any of the return types is not
+         * assignable to our return type, then we're not the best.
+         *
+         * We start from 0, not i, because we need to compare assignability
+         * the other direction even if we've compared these before.
+         */
+        dupe = false;
+        best = true;
+        for (j = 0; j < allCount; j++) {
+            if (i == j)
+                continue;
+            if (allMethods[j] == NULL)
+                continue;
+
+            if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
+                    allMethods[j]) == 0)
+            {
+                /*
+                 * Duplicate method, check return type.  If it's a primitive
+                 * type or void, the types must match exactly, or we throw
+                 * an exception now.
+                 */
+                ALOGV("MATCH on %s.%s and %s.%s",
+                    allMethods[i]->clazz->descriptor, allMethods[i]->name,
+                    allMethods[j]->clazz->descriptor, allMethods[j]->name);
+                dupe = true;
+                if (!returnTypesAreCompatible(allMethods[i], allMethods[j]))
+                    best = false;
+            }
+        }
+
+        /*
+         * If this is the best of a set of duplicates, copy it over and
+         * nuke all duplicates.
+         *
+         * While we do this, we create the set of exceptions declared to
+         * be thrown by all occurrences of the method.
+         */
+        if (dupe) {
+            if (best) {
+                ALOGV("BEST %d %s.%s -> %d", i,
+                    allMethods[i]->clazz->descriptor, allMethods[i]->name,
+                    outCount);
+
+                /* if we have exceptions, make a local copy */
+                PointerSet* commonThrows = NULL;
+                if (!createExceptionClassList(allMethods[i], &commonThrows))
+                    return -1;
+
+                /*
+                 * Run through one more time, erasing the duplicates.  (This
+                 * would go faster if we had marked them somehow.)
+                 */
+                for (j = 0; j < allCount; j++) {
+                    if (i == j)
+                        continue;
+                    if (allMethods[j] == NULL)
+                        continue;
+                    if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
+                            allMethods[j]) == 0)
+                    {
+                        ALOGV("DEL %d %s.%s", j,
+                            allMethods[j]->clazz->descriptor,
+                            allMethods[j]->name);
+
+                        /*
+                         * Update set to hold the intersection of method[i]'s
+                         * and method[j]'s throws.
+                         */
+                        if (commonThrows != NULL) {
+                            updateExceptionClassList(allMethods[j],
+                                commonThrows);
+                        }
+
+                        allMethods[j] = NULL;
+                    }
+                }
+
+                /*
+                 * If the set of Throwable classes isn't empty, create an
+                 * array of Class, copy them into it, and put the result
+                 * into the "throwLists" array.
+                 */
+                if (commonThrows != NULL &&
+                    dvmPointerSetGetCount(commonThrows) > 0)
+                {
+                    int commonCount = dvmPointerSetGetCount(commonThrows);
+                    ArrayObject* throwArray;
+                    Object** contents;
+                    int ent;
+
+                    throwArray = dvmAllocArrayByClass(
+                            gDvm.classJavaLangClassArray, commonCount,
+                            ALLOC_DEFAULT);
+                    if (throwArray == NULL) {
+                        ALOGE("common-throw array alloc failed");
+                        return -1;
+                    }
+
+                    contents = (Object**)(void*)throwArray->contents;
+                    for (ent = 0; ent < commonCount; ent++) {
+                        contents[ent] = (Object*)
+                            dvmPointerSetGetEntry(commonThrows, ent);
+                    }
+
+                    /* add it to the array of arrays */
+                    contents = (Object**)(void*)throwLists->contents;
+                    contents[outCount] = (Object*) throwArray;
+                    dvmReleaseTrackedAlloc((Object*) throwArray, NULL);
+                }
+
+                /* copy the winner and NULL it out */
+                outMethods[outCount++] = allMethods[i];
+                allMethods[i] = NULL;
+
+                dvmPointerSetFree(commonThrows);
+            } else {
+                ALOGV("BEST not %d", i);
+            }
+        } else {
+            /*
+             * Singleton.  Copy the entry and NULL it out.
+             */
+            ALOGV("COPY singleton %d %s.%s -> %d", i,
+                allMethods[i]->clazz->descriptor, allMethods[i]->name,
+                outCount);
+
+            /* keep track of our throwables */
+            ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]);
+            if (exceptionArray != NULL) {
+                Object** contents;
+
+                contents = (Object**)(void*)throwLists->contents;
+                contents[outCount] = (Object*) exceptionArray;
+                dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+            }
+
+            outMethods[outCount++] = allMethods[i];
+            allMethods[i] = NULL;
+        }
+    }
+
+    /*
+     * Check for stragglers.  If we find any, throw an exception.
+     */
+    for (i = 0; i < allCount; i++) {
+        if (allMethods[i] != NULL) {
+            ALOGV("BAD DUPE: %d %s.%s", i,
+                allMethods[i]->clazz->descriptor, allMethods[i]->name);
+            dvmThrowIllegalArgumentException(
+                "incompatible return types in proxied interfaces");
+            return -1;
+        }
+    }
+
+    return outCount;
+}
+
+
+/*
+ * Classes can declare to throw multiple exceptions in a hierarchy, e.g.
+ * IOException and FileNotFoundException.  Since we're only interested in
+ * knowing the set that can be thrown without requiring an extra wrapper,
+ * we can remove anything that is a subclass of something else in the list.
+ *
+ * The "mix" step we do next reduces things toward the most-derived class,
+ * so it's important that we start with the least-derived classes.
+ */
+static void reduceExceptionClassList(ArrayObject* exceptionArray)
+{
+    const ClassObject** classes =
+        (const ClassObject**)(void*)exceptionArray->contents;
+
+    /*
+     * Consider all pairs of classes.  If one is the subclass of the other,
+     * null out the subclass.
+     */
+    size_t len = exceptionArray->length;
+    for (size_t i = 0; i < len - 1; i++) {
+        if (classes[i] == NULL)
+            continue;
+        for (size_t j = i + 1; j < len; j++) {
+            if (classes[j] == NULL)
+                continue;
+
+            if (dvmInstanceof(classes[i], classes[j])) {
+                classes[i] = NULL;
+                break;      /* no more comparisons against classes[i] */
+            } else if (dvmInstanceof(classes[j], classes[i])) {
+                classes[j] = NULL;
+            }
+        }
+    }
+}
+
+/*
+ * Create a local array with a copy of the throwable classes declared by
+ * "method".  If no throws are declared, "*pSet" will be NULL.
+ *
+ * Returns "false" on allocation failure.
+ */
+static bool createExceptionClassList(const Method* method, PointerSet** pThrows)
+{
+    ArrayObject* exceptionArray = NULL;
+    bool result = false;
+
+    exceptionArray = dvmGetMethodThrows(method);
+    if (exceptionArray != NULL && exceptionArray->length > 0) {
+        /* reduce list, nulling out redundant entries */
+        reduceExceptionClassList(exceptionArray);
+
+        *pThrows = dvmPointerSetAlloc(exceptionArray->length);
+        if (*pThrows == NULL)
+            goto bail;
+
+        const ClassObject** contents;
+
+        contents = (const ClassObject**)(void*)exceptionArray->contents;
+        for (size_t i = 0; i < exceptionArray->length; i++) {
+            if (contents[i] != NULL)
+                dvmPointerSetAddEntry(*pThrows, contents[i]);
+        }
+    } else {
+        *pThrows = NULL;
+    }
+
+    result = true;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+    return result;
+}
+
+/*
+ * We need to compute the intersection of the arguments, i.e. remove
+ * anything from "throws" that isn't in the method's list of throws.
+ *
+ * If one class is a subclass of another, we want to keep just the subclass,
+ * moving toward the most-restrictive set.
+ *
+ * We assume these are all classes, and don't try to filter out interfaces.
+ */
+static void updateExceptionClassList(const Method* method, PointerSet* throws)
+{
+    int setSize = dvmPointerSetGetCount(throws);
+    if (setSize == 0)
+        return;
+
+    ArrayObject* exceptionArray = dvmGetMethodThrows(method);
+    if (exceptionArray == NULL) {
+        /* nothing declared, so intersection is empty */
+        dvmPointerSetClear(throws);
+        return;
+    }
+
+    /* reduce list, nulling out redundant entries */
+    reduceExceptionClassList(exceptionArray);
+
+    size_t mixLen = dvmPointerSetGetCount(throws);
+    const ClassObject* mixSet[mixLen];
+
+    size_t declLen = exceptionArray->length;
+    const ClassObject** declSet = (const ClassObject**)(void*)exceptionArray->contents;
+
+    /* grab a local copy to work on */
+    for (size_t i = 0; i < mixLen; i++) {
+        mixSet[i] = (ClassObject*)dvmPointerSetGetEntry(throws, i);
+    }
+
+    for (size_t i = 0; i < mixLen; i++) {
+        size_t j;
+        for (j = 0; j < declLen; j++) {
+            if (declSet[j] == NULL)
+                continue;
+
+            if (mixSet[i] == declSet[j]) {
+                /* match, keep this one */
+                break;
+            } else if (dvmInstanceof(mixSet[i], declSet[j])) {
+                /* mix is a subclass of a declared throwable, keep it */
+                break;
+            } else if (dvmInstanceof(declSet[j], mixSet[i])) {
+                /* mix is a superclass, replace it */
+                mixSet[i] = declSet[j];
+                break;
+            }
+        }
+
+        if (j == declLen) {
+            /* no match, remove entry by nulling it out */
+            mixSet[i] = NULL;
+        }
+    }
+
+    /* copy results back out; this eliminates duplicates as we go */
+    dvmPointerSetClear(throws);
+    for (size_t i = 0; i < mixLen; i++) {
+        if (mixSet[i] != NULL)
+            dvmPointerSetAddEntry(throws, mixSet[i]);
+    }
+
+    dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+}
+
+
+/*
+ * Check to see if the return types are compatible.
+ *
+ * If the return type is primitive or void, it must match exactly.
+ *
+ * If not, the type in "subMethod" must be assignable to the type in
+ * "baseMethod".
+ */
+static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod)
+{
+    const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype);
+    const char* subSig = dexProtoGetReturnType(&subMethod->prototype);
+    ClassObject* baseClass;
+    ClassObject* subClass;
+
+    if (baseSig[1] == '\0' || subSig[1] == '\0') {
+        /* at least one is primitive type */
+        return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]);
+    }
+
+    baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader);
+    subClass = dvmFindClass(subSig, subMethod->clazz->classLoader);
+    bool result = dvmInstanceof(subClass, baseClass);
+    return result;
+}
+
+/*
+ * Create a constructor for our Proxy class.  The constructor takes one
+ * argument, a java.lang.reflect.InvocationHandler.
+ */
+static void createConstructor(ClassObject* clazz, Method* meth)
+{
+    /*
+     * The constructor signatures (->prototype and ->shorty) need to
+     * be cloned from a method in a "real" DEX file. We declared the
+     * otherwise unused method Proxy.constructorPrototype() just for
+     * this purpose.
+     */
+
+    meth->clazz = clazz;
+    meth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
+    meth->name = "<init>";
+    meth->prototype =
+        gDvm.methJavaLangReflectProxy_constructorPrototype->prototype;
+    meth->shorty =
+        gDvm.methJavaLangReflectProxy_constructorPrototype->shorty;
+    // no pDexCode or pDexMethod
+
+    int argsSize = dvmComputeMethodArgsSize(meth) + 1;
+    meth->registersSize = meth->insSize = argsSize;
+
+    meth->nativeFunc = proxyConstructor;
+}
+
+/*
+ * Create a method in our Proxy class with the name and signature of
+ * the interface method it implements.
+ */
+static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
+    const Method* srcMeth)
+{
+    dstMeth->clazz = clazz;
+    dstMeth->insns = (u2*) srcMeth;
+    dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
+    dstMeth->name = srcMeth->name;
+    dstMeth->prototype = srcMeth->prototype;
+    dstMeth->shorty = srcMeth->shorty;
+    // no pDexCode or pDexMethod
+
+    int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1;
+    dstMeth->registersSize = dstMeth->insSize = argsSize;
+
+    dstMeth->nativeFunc = proxyInvoker;
+}
+
+/*
+ * Return a new Object[] array with the contents of "args".  We determine
+ * the number and types of values in "args" based on the method signature.
+ * Primitive types are boxed.
+ *
+ * Returns NULL if the method takes no arguments.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * On failure, returns with an appropriate exception raised.
+ */
+static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
+{
+    const char* desc = &method->shorty[1]; // [0] is the return type.
+
+    /* count args */
+    size_t argCount = dexProtoGetParameterCount(&method->prototype);
+
+    /* allocate storage */
+    ArrayObject* argArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
+        argCount, ALLOC_DEFAULT);
+    if (argArray == NULL)
+        return NULL;
+    Object** argObjects = (Object**)(void*)argArray->contents;
+
+    /*
+     * Fill in the array.
+     */
+
+    size_t srcIndex = 0;
+    size_t dstIndex = 0;
+    while (*desc != '\0') {
+        char descChar = *(desc++);
+        JValue value;
+
+        switch (descChar) {
+        case 'Z':
+        case 'C':
+        case 'F':
+        case 'B':
+        case 'S':
+        case 'I':
+            value.i = args[srcIndex++];
+            argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
+                dvmFindPrimitiveClass(descChar));
+            /* argObjects is tracked, don't need to hold this too */
+            dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
+            dstIndex++;
+            break;
+        case 'D':
+        case 'J':
+            value.j = dvmGetArgLong(args, srcIndex);
+            srcIndex += 2;
+            argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
+                dvmFindPrimitiveClass(descChar));
+            dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
+            dstIndex++;
+            break;
+        case '[':
+        case 'L':
+            argObjects[dstIndex++] = (Object*) args[srcIndex++];
+            break;
+        }
+    }
+
+    return argArray;
+}
+
+/*
+ * This is the constructor for a generated proxy object.  All we need to
+ * do is stuff "handler" into "h".
+ */
+static void proxyConstructor(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* obj = (Object*) args[0];
+    Object* handler = (Object*) args[1];
+
+    dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler);
+}
+
+/*
+ * This is the common message body for proxy methods.
+ *
+ * The method we're calling looks like:
+ *   public Object invoke(Object proxy, Method method, Object[] args)
+ *
+ * This means we have to create a Method object, box our arguments into
+ * a new Object[] array, make the call, and unbox the return value if
+ * necessary.
+ */
+static void proxyInvoker(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisObj = (Object*) args[0];
+    Object* methodObj = NULL;
+    ArrayObject* argArray = NULL;
+    Object* handler;
+    Method* invoke;
+    ClassObject* returnType;
+    JValue invokeResult;
+
+    /*
+     * Retrieve handler object for this proxy instance.  The field is
+     * defined in the superclass (Proxy).
+     */
+    handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h);
+
+    /*
+     * Find the invoke() method, looking in "this"s class.  (Because we
+     * start here we don't have to convert it to a vtable index and then
+     * index into this' vtable.)
+     */
+    invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
+            "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+    if (invoke == NULL) {
+        ALOGE("Unable to find invoke()");
+        dvmAbort();
+    }
+
+    ALOGV("invoke: %s.%s, this=%p, handler=%s",
+        method->clazz->descriptor, method->name,
+        thisObj, handler->clazz->descriptor);
+
+    /*
+     * Create a java.lang.reflect.Method object for this method.
+     *
+     * We don't want to use "method", because that's the concrete
+     * implementation in the proxy class.  We want the abstract Method
+     * from the declaring interface.  We have a pointer to it tucked
+     * away in the "insns" field.
+     *
+     * TODO: this could be cached for performance.
+     */
+    methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
+    if (methodObj == NULL) {
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+
+    /*
+     * Determine the return type from the signature.
+     *
+     * TODO: this could be cached for performance.
+     */
+    returnType = dvmGetBoxedReturnType(method);
+    if (returnType == NULL) {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGE("Could not determine return type for '%s'", desc);
+        free(desc);
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+    ALOGV("  return type will be %s", returnType->descriptor);
+
+    /*
+     * Convert "args" array into Object[] array, using the method
+     * signature to determine types.  If the method takes no arguments,
+     * we must pass null.
+     */
+    argArray = boxMethodArgs(method, args+1);
+    if (dvmCheckException(self))
+        goto bail;
+
+    /*
+     * Call h.invoke(proxy, method, args).
+     *
+     * We don't need to repackage exceptions, so if one has been thrown
+     * just jump to the end.
+     *
+     * We're not adding invokeResult.l to the tracked allocation list, but
+     * since we're just unboxing it or returning it to interpreted code
+     * that shouldn't be a problem.
+     */
+    dvmCallMethod(self, invoke, handler, &invokeResult,
+        thisObj, methodObj, argArray);
+    if (dvmCheckException(self)) {
+        Object* excep = dvmGetException(self);
+        if (mustWrapException(method, excep)) {
+            /* wrap with UndeclaredThrowableException */
+            dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;");
+        }
+        goto bail;
+    }
+
+    /*
+     * Unbox the return value.  If it's the wrong type, throw a
+     * ClassCastException.  If it's a null pointer and we need a
+     * primitive type, throw a NullPointerException.
+     */
+    if (returnType->primitiveType == PRIM_VOID) {
+        LOGVV("+++ ignoring return to void");
+    } else if (invokeResult.l == NULL) {
+        if (dvmIsPrimitiveClass(returnType)) {
+            dvmThrowNullPointerException(
+                "null result when primitive expected");
+            goto bail;
+        }
+        pResult->l = NULL;
+    } else {
+        if (!dvmUnboxPrimitive((Object*)invokeResult.l, returnType, pResult)) {
+            dvmThrowClassCastException(((Object*)invokeResult.l)->clazz,
+                    returnType);
+            goto bail;
+        }
+    }
+
+bail:
+    dvmReleaseTrackedAlloc(methodObj, self);
+    dvmReleaseTrackedAlloc((Object*)argArray, self);
+}
+
+/*
+ * Determine if it's okay for this method to throw this exception.  If
+ * an unchecked exception was thrown we immediately return false.  If
+ * checked, we have to ensure that this method and all of its duplicates
+ * have declared that they throw it.
+ */
+static bool mustWrapException(const Method* method, const Object* throwable)
+{
+    if (!dvmIsCheckedException(throwable))
+        return false;
+
+    const StaticField* sfield = &method->clazz->sfields[kThrowsField];
+    const ArrayObject* throws = (ArrayObject*) dvmGetStaticFieldObject(sfield);
+
+    int methodIndex = method - method->clazz->virtualMethods;
+    assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount);
+
+    const Object** contents = (const Object**)(void*)throws->contents;
+    const ArrayObject* methodThrows = (ArrayObject*) contents[methodIndex];
+
+    if (methodThrows == NULL) {
+        /* no throws declared, must wrap all checked exceptions */
+        return true;
+    }
+
+    size_t throwCount = methodThrows->length;
+    const ClassObject** classes =
+        (const ClassObject**)(void*)methodThrows->contents;
+
+    for (size_t i = 0; i < throwCount; i++) {
+        if (dvmInstanceof(throwable->clazz, classes[i])) {
+            /* this was declared, okay to throw */
+            return false;
+        }
+    }
+
+    /* no match in declared throws */
+    return true;
+}
diff --git a/vm/reflect/Reflect.cpp b/vm/reflect/Reflect.cpp
new file mode 100644
index 0000000..1d055c1
--- /dev/null
+++ b/vm/reflect/Reflect.cpp
@@ -0,0 +1,1275 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Basic reflection calls and utility functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * For some of the reflection stuff we need to un-box primitives, e.g.
+ * convert a java/lang/Integer to int or even a float.  We assume that
+ * the first instance field holds the value.
+ *
+ * To verify this, we either need to ensure that the class has only one
+ * instance field, or we need to look up the field by name and verify
+ * that it comes first.  The former is simpler, and should work.
+ */
+bool dvmValidateBoxClasses()
+{
+    static const char* classes[] = {
+        "Ljava/lang/Boolean;",
+        "Ljava/lang/Character;",
+        "Ljava/lang/Float;",
+        "Ljava/lang/Double;",
+        "Ljava/lang/Byte;",
+        "Ljava/lang/Short;",
+        "Ljava/lang/Integer;",
+        "Ljava/lang/Long;",
+        NULL
+    };
+    const char** ccp;
+
+    for (ccp = classes; *ccp != NULL; ccp++) {
+        ClassObject* clazz;
+
+        clazz = dvmFindClassNoInit(*ccp, NULL);
+        if (clazz == NULL) {
+            ALOGE("Couldn't find '%s'", *ccp);
+            return false;
+        }
+
+        if (clazz->ifieldCount != 1) {
+            ALOGE("Found %d instance fields in '%s'",
+                clazz->ifieldCount, *ccp);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Find the named class object.  We have to trim "*pSignature" down to just
+ * the first token, do the lookup, and then restore anything important
+ * that we've stomped on.
+ *
+ * "pSig" will be advanced to the start of the next token.
+ */
+static ClassObject* convertSignaturePartToClass(char** pSignature,
+    const ClassObject* defClass)
+{
+    ClassObject* clazz = NULL;
+    char* signature = *pSignature;
+
+    if (*signature == '[') {
+        /* looks like "[[[Landroid/debug/Stuff;"; we want the whole thing */
+        char savedChar;
+
+        while (*++signature == '[')
+            ;
+        if (*signature == 'L') {
+            while (*++signature != ';')
+                ;
+        }
+
+        /* advance past ';', and stomp on whatever comes next */
+        savedChar = *++signature;
+        *signature = '\0';
+        clazz = dvmFindArrayClass(*pSignature, defClass->classLoader);
+        *signature = savedChar;
+    } else if (*signature == 'L') {
+        /* looks like 'Landroid/debug/Stuff;"; we want the whole thing */
+        char savedChar;
+        while (*++signature != ';')
+            ;
+        savedChar = *++signature;
+        *signature = '\0';
+        clazz = dvmFindClassNoInit(*pSignature, defClass->classLoader);
+        *signature = savedChar;
+    } else {
+        clazz = dvmFindPrimitiveClass(*signature++);
+    }
+
+    if (clazz == NULL) {
+        ALOGW("Unable to match class for part: '%s'", *pSignature);
+    }
+    *pSignature = signature;
+    return clazz;
+}
+
+/*
+ * Convert the method signature to an array of classes.
+ *
+ * The tokenization process may mangle "*pSignature".  On return, it will
+ * be pointing at the closing ')'.
+ *
+ * "defClass" is the method's class, which is needed to make class loaders
+ * happy.
+ */
+static ArrayObject* convertSignatureToClassArray(char** pSignature,
+    ClassObject* defClass)
+{
+    char* signature = *pSignature;
+
+    assert(*signature == '(');
+    signature++;
+
+    /* count up the number of parameters */
+    size_t count = 0;
+    char* cp = signature;
+    while (*cp != ')') {
+        count++;
+
+        if (*cp == '[') {
+            while (*++cp == '[')
+                ;
+        }
+        if (*cp == 'L') {
+            while (*++cp != ';')
+                ;
+        }
+        cp++;
+    }
+    LOGVV("REFLECT found %d parameters in '%s'", count, *pSignature);
+
+    /* create an array to hold them */
+    ArrayObject* classArray = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+                     count, ALLOC_DEFAULT);
+    if (classArray == NULL)
+        return NULL;
+
+    /* fill it in */
+    cp = signature;
+    for (size_t i = 0; i < count; i++) {
+        ClassObject* clazz = convertSignaturePartToClass(&cp, defClass);
+        if (clazz == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+        LOGVV("REFLECT  %d: '%s'", i, clazz->descriptor);
+        dvmSetObjectArrayElement(classArray, i, (Object *)clazz);
+    }
+
+    *pSignature = cp;
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return classArray;
+}
+
+
+/*
+ * Convert a field pointer to a slot number.
+ *
+ * We use positive values starting from 0 for instance fields, negative
+ * values starting from -1 for static fields.
+ */
+static int fieldToSlot(const Field* field, const ClassObject* clazz)
+{
+    int slot;
+
+    if (dvmIsStaticField(field)) {
+        slot = (StaticField*)field - &clazz->sfields[0];
+        assert(slot >= 0 && slot < clazz->sfieldCount);
+        slot = -(slot+1);
+    } else {
+        slot = (InstField*)field - clazz->ifields;
+        assert(slot >= 0 && slot < clazz->ifieldCount);
+    }
+
+    return slot;
+}
+
+/*
+ * Convert a slot number to a field pointer.
+ */
+Field* dvmSlotToField(ClassObject* clazz, int slot)
+{
+    if (slot < 0) {
+        slot = -(slot+1);
+        assert(slot < clazz->sfieldCount);
+        return (Field*)(void*)&clazz->sfields[slot];
+    } else {
+        assert(slot < clazz->ifieldCount);
+        return (Field*)(void*)&clazz->ifields[slot];
+    }
+}
+
+/*
+ * Create a new java.lang.reflect.Field object from "field".
+ *
+ * The Field spec doesn't specify the constructor.  We're going to use the
+ * one from our existing class libs:
+ *
+ *  private Field(Class declaringClass, Class type, String name, int slot)
+ */
+static Object* createFieldObject(Field* field, const ClassObject* clazz)
+{
+    Object* result = NULL;
+    Object* fieldObj = NULL;
+    StringObject* nameObj = NULL;
+    ClassObject* type;
+    char* mangle;
+    char* cp;
+    int slot;
+
+    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())) {
+        ALOGD("Field class init threw exception");
+        goto bail;
+    }
+
+    result = fieldObj;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+    if (result == NULL)
+        dvmReleaseTrackedAlloc((Object*) fieldObj, NULL);
+    /* caller must dvmReleaseTrackedAlloc(result) */
+    return result;
+}
+
+/*
+ *
+ * Get an array with all fields declared by a class.
+ *
+ * This includes both static and instance fields.
+ */
+ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+        dvmInitClass(gDvm.classJavaLangReflectField);
+
+    /* count #of fields */
+    size_t count;
+    if (!publicOnly)
+        count = clazz->sfieldCount + clazz->ifieldCount;
+    else {
+        count = 0;
+        for (int i = 0; i < clazz->sfieldCount; i++) {
+            if ((clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0)
+                count++;
+        }
+        for (int i = 0; i < clazz->ifieldCount; i++) {
+            if ((clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0)
+                count++;
+        }
+    }
+
+    /* create the Field[] array */
+    ArrayObject* fieldArray =
+        dvmAllocArrayByClass(gDvm.classJavaLangReflectFieldArray, count, ALLOC_DEFAULT);
+    if (fieldArray == NULL)
+        return NULL;
+
+    /* populate */
+    size_t fieldCount = 0;
+    for (int i = 0; i < clazz->sfieldCount; i++) {
+        if (!publicOnly ||
+            (clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0)
+        {
+            Object* field = createFieldObject(&clazz->sfields[i], clazz);
+            if (field == NULL) {
+                goto fail;
+            }
+            dvmSetObjectArrayElement(fieldArray, fieldCount, field);
+            dvmReleaseTrackedAlloc(field, NULL);
+            ++fieldCount;
+        }
+    }
+    for (int i = 0; i < clazz->ifieldCount; i++) {
+        if (!publicOnly ||
+            (clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0)
+        {
+            Object* field = createFieldObject(&clazz->ifields[i], clazz);
+            if (field == NULL) {
+                goto fail;
+            }
+            dvmSetObjectArrayElement(fieldArray, fieldCount, field);
+            dvmReleaseTrackedAlloc(field, NULL);
+            ++fieldCount;
+        }
+    }
+
+    assert(fieldCount == fieldArray->length);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return fieldArray;
+
+fail:
+    dvmReleaseTrackedAlloc((Object*) fieldArray, NULL);
+    return NULL;
+}
+
+
+/*
+ * Convert a method pointer to a slot number.
+ *
+ * We use positive values starting from 0 for virtual methods, negative
+ * values starting from -1 for static methods.
+ */
+static int methodToSlot(const Method* meth)
+{
+    ClassObject* clazz = meth->clazz;
+    int slot;
+
+    if (dvmIsDirectMethod(meth)) {
+        slot = meth - clazz->directMethods;
+        assert(slot >= 0 && slot < clazz->directMethodCount);
+        slot = -(slot+1);
+    } else {
+        slot = meth - clazz->virtualMethods;
+        assert(slot >= 0 && slot < clazz->virtualMethodCount);
+    }
+
+    return slot;
+}
+
+/*
+ * Convert a slot number to a method pointer.
+ */
+Method* dvmSlotToMethod(ClassObject* clazz, int slot)
+{
+    if (slot < 0) {
+        slot = -(slot+1);
+        assert(slot < clazz->directMethodCount);
+        return &clazz->directMethods[slot];
+    } else {
+        assert(slot < clazz->virtualMethodCount);
+        return &clazz->virtualMethods[slot];
+    }
+}
+
+/*
+ * Create a new java/lang/reflect/Constructor object, using the contents of
+ * "meth" to construct it.
+ *
+ * The spec doesn't specify the constructor.  We're going to use the
+ * one from our existing class libs:
+ *
+ *  private Constructor (Class declaringClass, Class[] ptypes, Class[] extypes,
+ *      int slot)
+ */
+static Object* createConstructorObject(Method* meth)
+{
+    Object* result = NULL;
+    ArrayObject* params = NULL;
+    ArrayObject* exceptions = NULL;
+    Object* consObj;
+    DexStringCache mangle;
+    char* cp;
+    int slot;
+
+    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())) {
+        ALOGD("Constructor class init threw exception");
+        goto bail;
+    }
+
+    result = consObj;
+
+bail:
+    dexStringCacheRelease(&mangle);
+    dvmReleaseTrackedAlloc((Object*) params, NULL);
+    dvmReleaseTrackedAlloc((Object*) exceptions, NULL);
+    if (result == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        dvmReleaseTrackedAlloc(consObj, NULL);
+    }
+    /* caller must dvmReleaseTrackedAlloc(result) */
+    return result;
+}
+
+/*
+ * Get an array with all constructors declared by a class.
+ */
+ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor))
+        dvmInitClass(gDvm.classJavaLangReflectConstructor);
+
+    /*
+     * Ordinarily we init the class the first time we resolve a method.
+     * We're bypassing the normal resolution mechanism, so we init it here.
+     */
+    if (!dvmIsClassInitialized(clazz))
+        dvmInitClass(clazz);
+
+    /*
+     * Count up the #of relevant methods.
+     */
+    size_t count = 0;
+    for (int i = 0; i < clazz->directMethodCount; ++i) {
+        Method* meth = &clazz->directMethods[i];
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+        {
+            count++;
+        }
+    }
+
+    /*
+     * Create an array of Constructor objects.
+     */
+    ClassObject* arrayClass = gDvm.classJavaLangReflectConstructorArray;
+    ArrayObject* ctorArray = dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT);
+    if (ctorArray == NULL)
+        return NULL;
+
+    /*
+     * Fill out the array.
+     */
+    size_t ctorObjCount = 0;
+    for (int i = 0; i < clazz->directMethodCount; ++i) {
+        Method* meth = &clazz->directMethods[i];
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+        {
+            Object* ctorObj = createConstructorObject(meth);
+            if (ctorObj == NULL) {
+              dvmReleaseTrackedAlloc((Object*) ctorArray, NULL);
+              return NULL;
+            }
+            dvmSetObjectArrayElement(ctorArray, ctorObjCount, ctorObj);
+            ++ctorObjCount;
+            dvmReleaseTrackedAlloc(ctorObj, NULL);
+        }
+    }
+
+    assert(ctorObjCount == ctorArray->length);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return ctorArray;
+}
+
+/*
+ * Create a new java/lang/reflect/Method object, using the contents of
+ * "meth" to construct it.
+ *
+ * The spec doesn't specify the constructor.  We're going to use the
+ * one from our existing class libs:
+ *
+ *  private Method(Class declaring, Class[] paramTypes, Class[] exceptTypes,
+ *      Class returnType, String name, int slot)
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the result.
+ */
+Object* dvmCreateReflectMethodObject(const Method* meth)
+{
+    Object* result = NULL;
+    ArrayObject* params = NULL;
+    ArrayObject* exceptions = NULL;
+    StringObject* nameObj = NULL;
+    Object* methObj;
+    ClassObject* returnType;
+    DexStringCache mangle;
+    char* cp;
+    int slot;
+
+    if (dvmCheckException(dvmThreadSelf())) {
+        ALOGW("WARNING: dvmCreateReflectMethodObject called with "
+             "exception pending");
+        return NULL;
+    }
+
+    dexStringCacheInit(&mangle);
+
+    /* parent should guarantee init so we don't have to check on every call */
+    assert(dvmIsClassInitialized(gDvm.classJavaLangReflectMethod));
+
+    methObj = dvmAllocObject(gDvm.classJavaLangReflectMethod, ALLOC_DEFAULT);
+    if (methObj == NULL)
+        goto bail;
+
+    /*
+     * Convert the signature string into an array of classes representing
+     * the arguments, and a class for the return type.
+     */
+    cp = dvmCopyDescriptorStringFromMethod(meth, &mangle);
+    params = convertSignatureToClassArray(&cp, meth->clazz);
+    if (params == NULL)
+        goto bail;
+    assert(*cp == ')');
+    cp++;
+    returnType = convertSignaturePartToClass(&cp, meth->clazz);
+    if (returnType == NULL)
+        goto bail;
+
+    /*
+     * Create an array with one entry for every exception that the class
+     * is declared to throw.
+     */
+    exceptions = dvmGetMethodThrows(meth);
+    if (dvmCheckException(dvmThreadSelf()))
+        goto bail;
+
+    /* method name */
+    nameObj = dvmCreateStringFromCstr(meth->name);
+    if (nameObj == NULL)
+        goto bail;
+
+    slot = methodToSlot(meth);
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectMethod_init,
+        methObj, &unused, meth->clazz, params, exceptions, returnType,
+        nameObj, slot);
+    if (dvmCheckException(dvmThreadSelf())) {
+        ALOGD("Method class init threw exception");
+        goto bail;
+    }
+
+    result = methObj;
+
+bail:
+    dexStringCacheRelease(&mangle);
+    if (result == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+    }
+    dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+    dvmReleaseTrackedAlloc((Object*) params, NULL);
+    dvmReleaseTrackedAlloc((Object*) exceptions, NULL);
+    if (result == NULL)
+        dvmReleaseTrackedAlloc(methObj, NULL);
+    return result;
+}
+
+/*
+ * Get an array with all methods declared by a class.
+ *
+ * This includes both static and virtual methods, and can include private
+ * members if "publicOnly" is false.  It does not include Miranda methods,
+ * since those weren't declared in the class, or constructors.
+ */
+ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+        dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+    /*
+     * Count up the #of relevant methods.
+     *
+     * Ignore virtual Miranda methods and direct class/object constructors.
+     */
+    size_t count = 0;
+    Method* meth = clazz->virtualMethods;
+    for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            !dvmIsMirandaMethod(meth))
+        {
+            count++;
+        }
+    }
+    meth = clazz->directMethods;
+    for (int i = 0; i < clazz->directMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') {
+            count++;
+        }
+    }
+
+    /*
+     * Create an array of Method objects.
+     */
+    ArrayObject* methodArray =
+        dvmAllocArrayByClass(gDvm.classJavaLangReflectMethodArray, count, ALLOC_DEFAULT);
+    if (methodArray == NULL)
+        return NULL;
+
+    /*
+     * Fill out the array.
+     */
+    meth = clazz->virtualMethods;
+    size_t methObjCount = 0;
+    for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            !dvmIsMirandaMethod(meth))
+        {
+            Object* methObj = dvmCreateReflectMethodObject(meth);
+            if (methObj == NULL)
+                goto fail;
+            dvmSetObjectArrayElement(methodArray, methObjCount, methObj);
+            ++methObjCount;
+            dvmReleaseTrackedAlloc(methObj, NULL);
+        }
+    }
+    meth = clazz->directMethods;
+    for (int i = 0; i < clazz->directMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            meth->name[0] != '<')
+        {
+            Object* methObj = dvmCreateReflectMethodObject(meth);
+            if (methObj == NULL)
+                goto fail;
+            dvmSetObjectArrayElement(methodArray, methObjCount, methObj);
+            ++methObjCount;
+            dvmReleaseTrackedAlloc(methObj, NULL);
+        }
+    }
+
+    assert(methObjCount == methodArray->length);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return methodArray;
+
+fail:
+    dvmReleaseTrackedAlloc((Object*) methodArray, NULL);
+    return NULL;
+}
+
+/*
+ * Fills targetDescriptorCache with the descriptors of the classes in args.
+ * This is the concatenation of the descriptors with no other adornment,
+ * consistent with dexProtoGetParameterDescriptors.
+ */
+static void createTargetDescriptor(ArrayObject* args,
+    DexStringCache* targetDescriptorCache)
+{
+    ClassObject** argsArray = (ClassObject**)(void*)args->contents;
+    size_t length = 1; /* +1 for the terminating '\0' */
+    for (size_t i = 0; i < args->length; ++i) {
+        length += strlen(argsArray[i]->descriptor);
+    }
+
+    dexStringCacheAlloc(targetDescriptorCache, length);
+
+    char* at = (char*) targetDescriptorCache->value;
+    for (size_t i = 0; i < args->length; ++i) {
+        const char* descriptor = argsArray[i]->descriptor;
+        strcpy(at, descriptor);
+        at += strlen(descriptor);
+    }
+}
+
+static Object* findConstructorOrMethodInArray(int methodsCount, Method* methods,
+    const char* name, const char* parameterDescriptors)
+{
+    Method* method = NULL;
+    Method* result = NULL;
+    int i;
+
+    for (i = 0; i < methodsCount; ++i) {
+        method = &methods[i];
+        if (strcmp(name, method->name) != 0
+            || dvmIsMirandaMethod(method)
+            || dexProtoCompareToParameterDescriptors(&method->prototype,
+                    parameterDescriptors) != 0) {
+            continue;
+        }
+
+        result = method;
+
+        /*
+         * Covariant return types permit the class to define multiple
+         * methods with the same name and parameter types. Prefer to return
+         * a non-synthetic method in such situations. We may still return
+         * a synthetic method to handle situations like escalated visibility.
+         */
+        if (!dvmIsSyntheticMethod(method)) {
+            break;
+        }
+    }
+
+    if (result != NULL) {
+        return dvmCreateReflectObjForMethod(result->clazz, result);
+    }
+
+    return NULL;
+}
+
+/*
+ * Get the named method.
+ */
+Object* dvmGetDeclaredConstructorOrMethod(ClassObject* clazz,
+    StringObject* nameObj, ArrayObject* args)
+{
+    Object* result = NULL;
+    DexStringCache targetDescriptorCache;
+    char* name;
+    const char* targetDescriptor;
+
+    dexStringCacheInit(&targetDescriptorCache);
+
+    name = dvmCreateCstrFromString(nameObj);
+    createTargetDescriptor(args, &targetDescriptorCache);
+    targetDescriptor = targetDescriptorCache.value;
+
+    result = findConstructorOrMethodInArray(clazz->directMethodCount,
+        clazz->directMethods, name, targetDescriptor);
+    if (result == NULL) {
+        result = findConstructorOrMethodInArray(clazz->virtualMethodCount,
+            clazz->virtualMethods, name, targetDescriptor);
+    }
+
+    free(name);
+    dexStringCacheRelease(&targetDescriptorCache);
+    return result;
+}
+
+/*
+ * Get the named field.
+ */
+Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj)
+{
+    int i;
+    Object* fieldObj = NULL;
+    char* name = dvmCreateCstrFromString(nameObj);
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+        dvmInitClass(gDvm.classJavaLangReflectField);
+
+    for (i = 0; i < clazz->sfieldCount; i++) {
+        Field* field = &clazz->sfields[i];
+        if (strcmp(name, field->name) == 0) {
+            fieldObj = createFieldObject(field, clazz);
+            break;
+        }
+    }
+    if (fieldObj == NULL) {
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            Field* field = &clazz->ifields[i];
+            if (strcmp(name, field->name) == 0) {
+                fieldObj = createFieldObject(field, clazz);
+                break;
+            }
+        }
+    }
+
+    free(name);
+    return fieldObj;
+}
+
+/*
+ * Get all interfaces a class implements. If this is unable to allocate
+ * the result array, this raises an OutOfMemoryError and returns NULL.
+ */
+ArrayObject* dvmGetInterfaces(ClassObject* clazz)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+        dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+    /*
+     * Create an array of Class objects.
+     */
+    size_t count = clazz->interfaceCount;
+    ArrayObject* interfaceArray =
+        dvmAllocArrayByClass(gDvm.classJavaLangClassArray, count, ALLOC_DEFAULT);
+    if (interfaceArray == NULL)
+        return NULL;
+
+    /*
+     * Fill out the array.
+     */
+    memcpy(interfaceArray->contents, clazz->interfaces,
+           count * sizeof(Object *));
+    dvmWriteBarrierArray(interfaceArray, 0, count);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return interfaceArray;
+}
+
+/*
+ * Given a boxed primitive type, such as java/lang/Integer, return the
+ * primitive type index.
+ *
+ * Returns PRIM_NOT for void, since we never "box" that.
+ */
+static PrimitiveType getBoxedType(DataObject* arg)
+{
+    static const int kJavaLangLen = 11;     // strlen("Ljava/lang/")
+
+    if (arg == NULL)
+        return PRIM_NOT;
+
+    const char* name = arg->clazz->descriptor;
+
+    if (strncmp(name, "Ljava/lang/", kJavaLangLen) != 0)
+        return PRIM_NOT;
+
+    if (strcmp(name + kJavaLangLen, "Boolean;") == 0)
+        return PRIM_BOOLEAN;
+    if (strcmp(name + kJavaLangLen, "Character;") == 0)
+        return PRIM_CHAR;
+    if (strcmp(name + kJavaLangLen, "Float;") == 0)
+        return PRIM_FLOAT;
+    if (strcmp(name + kJavaLangLen, "Double;") == 0)
+        return PRIM_DOUBLE;
+    if (strcmp(name + kJavaLangLen, "Byte;") == 0)
+        return PRIM_BYTE;
+    if (strcmp(name + kJavaLangLen, "Short;") == 0)
+        return PRIM_SHORT;
+    if (strcmp(name + kJavaLangLen, "Integer;") == 0)
+        return PRIM_INT;
+    if (strcmp(name + kJavaLangLen, "Long;") == 0)
+        return PRIM_LONG;
+    return PRIM_NOT;
+}
+
+/*
+ * Convert primitive, boxed data from "srcPtr" to "dstPtr".
+ *
+ * Section v2 2.6 lists the various conversions and promotions.  We
+ * allow the "widening" and "identity" conversions, but don't allow the
+ * "narrowing" conversions.
+ *
+ * Allowed:
+ *  byte to short, int, long, float, double
+ *  short to int, long, float double
+ *  char to int, long, float, double
+ *  int to long, float, double
+ *  long to float, double
+ *  float to double
+ * Values of types byte, char, and short are "internally" widened to int.
+ *
+ * Returns the width in 32-bit words of the destination primitive, or
+ * -1 if the conversion is not allowed.
+ *
+ * TODO? use JValue rather than u4 pointers
+ */
+int dvmConvertPrimitiveValue(PrimitiveType srcType,
+    PrimitiveType dstType, const s4* srcPtr, s4* dstPtr)
+{
+    enum Conversion {
+        OK4, OK8, ItoJ, ItoD, JtoD, FtoD, ItoF, JtoF, bad
+    };
+
+    enum Conversion conv;
+#ifdef ARCH_HAVE_ALIGNED_DOUBLES
+    double ret;
+#endif
+
+    assert((srcType != PRIM_VOID) && (srcType != PRIM_NOT));
+    assert((dstType != PRIM_VOID) && (dstType != PRIM_NOT));
+
+    switch (dstType) {
+        case PRIM_BOOLEAN:
+        case PRIM_CHAR:
+        case PRIM_BYTE: {
+            conv = (srcType == dstType) ? OK4 : bad;
+            break;
+        }
+        case PRIM_SHORT: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_SHORT: conv = OK4; break;
+                default:         conv = bad; break;
+            }
+            break;
+        }
+        case PRIM_INT: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:   conv = OK4; break;
+                default:         conv = bad; break;
+            }
+            break;
+        }
+        case PRIM_LONG: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:   conv = ItoJ; break;
+                case PRIM_LONG:  conv = OK8;  break;
+                default:         conv = bad;  break;
+            }
+            break;
+        }
+        case PRIM_FLOAT: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:   conv = ItoF; break;
+                case PRIM_LONG:  conv = JtoF; break;
+                case PRIM_FLOAT: conv = OK4;  break;
+                default:         conv = bad;  break;
+            }
+            break;
+        }
+        case PRIM_DOUBLE: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:    conv = ItoD; break;
+                case PRIM_LONG:   conv = JtoD; break;
+                case PRIM_FLOAT:  conv = FtoD; break;
+                case PRIM_DOUBLE: conv = OK8;  break;
+                default:          conv = bad;  break;
+            }
+            break;
+        }
+        case PRIM_VOID:
+        case PRIM_NOT:
+        default: {
+            conv = bad;
+            break;
+        }
+    }
+
+    switch (conv) {
+        case OK4:  *dstPtr = *srcPtr;                                   return 1;
+        case OK8:  *(s8*) dstPtr = *(s8*)srcPtr;                        return 2;
+        case ItoJ: *(s8*) dstPtr = (s8) (*(s4*) srcPtr);                return 2;
+#ifndef ARCH_HAVE_ALIGNED_DOUBLES
+        case ItoD: *(double*) dstPtr = (double) (*(s4*) srcPtr);        return 2;
+        case JtoD: *(double*) dstPtr = (double) (*(long long*) srcPtr); return 2;
+        case FtoD: *(double*) dstPtr = (double) (*(float*) srcPtr);     return 2;
+#else
+        case ItoD: ret = (double) (*(s4*) srcPtr); memcpy(dstPtr, &ret, 8); return 2;
+        case JtoD: ret = (double) (*(long long*) srcPtr); memcpy(dstPtr, &ret, 8); return 2;
+        case FtoD: ret = (double) (*(float*) srcPtr); memcpy(dstPtr, &ret, 8); return 2;
+#endif
+        case ItoF: *(float*) dstPtr = (float) (*(int*) srcPtr);         return 1;
+        case JtoF: *(float*) dstPtr = (float) (*(long long*) srcPtr);   return 1;
+        case bad: {
+            ALOGV("illegal primitive conversion: '%s' to '%s'",
+                    dexGetPrimitiveTypeDescriptor(srcType),
+                    dexGetPrimitiveTypeDescriptor(dstType));
+            return -1;
+        }
+        default: {
+            dvmAbort();
+            return -1; // Keep the compiler happy.
+        }
+    }
+}
+
+/*
+ * Convert types and widen primitives.  Puts the value of "arg" into
+ * "destPtr".
+ *
+ * Returns the width of the argument in 32-bit words (1 or 2), or -1 on error.
+ */
+int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* destPtr)
+{
+    int retVal;
+
+    if (dvmIsPrimitiveClass(type)) {
+        /* e.g.: "arg" is java/lang/Float instance, "type" is VM float class */
+        PrimitiveType srcType;
+        s4* valuePtr;
+
+        srcType = getBoxedType(arg);
+        if (srcType == PRIM_NOT) {     // didn't pass a boxed primitive in
+            LOGVV("conv arg: type '%s' not boxed primitive",
+                arg->clazz->descriptor);
+            return -1;
+        }
+
+        /* assumes value is stored in first instance field */
+        valuePtr = (s4*) arg->instanceData;
+
+        retVal = dvmConvertPrimitiveValue(srcType, type->primitiveType,
+                    valuePtr, destPtr);
+    } else {
+        /* verify object is compatible */
+        if ((arg == NULL) || dvmInstanceof(arg->clazz, type)) {
+            *destPtr = (s4) arg;
+            retVal = 1;
+        } else {
+            LOGVV("Arg %p (%s) not compatible with %s",
+                arg, arg->clazz->descriptor, type->descriptor);
+            retVal = -1;
+        }
+    }
+
+    return retVal;
+}
+
+/*
+ * Create a wrapper object for a primitive data type.  If "returnType" is
+ * not primitive, this just casts "value" to an object and returns it.
+ *
+ * We could invoke the "toValue" method on the box types to take
+ * advantage of pre-created values, but running that through the
+ * interpreter is probably less efficient than just allocating storage here.
+ *
+ * The caller must call dvmReleaseTrackedAlloc on the result.
+ */
+DataObject* dvmBoxPrimitive(JValue value, ClassObject* returnType)
+{
+    ClassObject* wrapperClass;
+    DataObject* wrapperObj;
+    s4* dataPtr;
+    PrimitiveType typeIndex = returnType->primitiveType;
+    const char* classDescriptor;
+
+    if (typeIndex == PRIM_NOT) {
+        /* add to tracking table so return value is always in table */
+        if (value.l != NULL)
+            dvmAddTrackedAlloc((Object*)value.l, NULL);
+        return (DataObject*) value.l;
+    }
+
+    classDescriptor = dexGetBoxedTypeDescriptor(typeIndex);
+    if (classDescriptor == NULL) {
+        return NULL;
+    }
+
+    wrapperClass = dvmFindSystemClass(classDescriptor);
+    if (wrapperClass == NULL) {
+        ALOGW("Unable to find '%s'", classDescriptor);
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    wrapperObj = (DataObject*) dvmAllocObject(wrapperClass, ALLOC_DEFAULT);
+    if (wrapperObj == NULL)
+        return NULL;
+    dataPtr = (s4*) wrapperObj->instanceData;
+
+    /* assumes value is stored in first instance field */
+    /* (see dvmValidateBoxClasses) */
+    if (typeIndex == PRIM_LONG || typeIndex == PRIM_DOUBLE)
+        *(s8*)dataPtr = value.j;
+    else
+        *dataPtr = value.i;
+
+    return wrapperObj;
+}
+
+/*
+ * Unwrap a primitive data type, if necessary.
+ *
+ * If "returnType" is not primitive, we just tuck "value" into JValue and
+ * return it after verifying that it's the right type of object.
+ *
+ * Fails if the field is primitive and "value" is either not a boxed
+ * primitive or is of a type that cannot be converted.
+ *
+ * Returns "true" on success, "false" on failure.
+ */
+bool dvmUnboxPrimitive(Object* value, ClassObject* returnType,
+    JValue* pResult)
+{
+    PrimitiveType typeIndex = returnType->primitiveType;
+    PrimitiveType valueIndex;
+
+    if (typeIndex == PRIM_NOT) {
+        if (value != NULL && !dvmInstanceof(value->clazz, returnType)) {
+            ALOGD("wrong object type: %s %s",
+                value->clazz->descriptor, returnType->descriptor);
+            return false;
+        }
+        pResult->l = value;
+        return true;
+    } else if (typeIndex == PRIM_VOID) {
+        /* can't put anything into a void */
+        return false;
+    }
+
+    valueIndex = getBoxedType((DataObject*)value);
+    if (valueIndex == PRIM_NOT)
+        return false;
+
+    /* assumes value is stored in first instance field of "value" */
+    /* (see dvmValidateBoxClasses) */
+    if (dvmConvertPrimitiveValue(valueIndex, typeIndex,
+            (s4*) ((DataObject*)value)->instanceData, (s4*)pResult) < 0)
+    {
+        ALOGV("Prim conversion failed");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Find the return type in the signature, and convert it to a class
+ * object.  For primitive types we use a boxed class, for reference types
+ * we do a name lookup.
+ *
+ * On failure, we return NULL with an exception raised.
+ */
+ClassObject* dvmGetBoxedReturnType(const Method* meth)
+{
+    const char* sig = dexProtoGetReturnType(&meth->prototype);
+
+    switch (*sig) {
+    case 'Z':
+    case 'C':
+    case 'F':
+    case 'D':
+    case 'B':
+    case 'S':
+    case 'I':
+    case 'J':
+    case 'V':
+        return dvmFindPrimitiveClass(*sig);
+    case '[':
+    case 'L':
+        return dvmFindClass(sig, meth->clazz->classLoader);
+    default: {
+        /* should not have passed verification */
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        ALOGE("Bad return type in signature '%s'", desc);
+        free(desc);
+        dvmThrowInternalError(NULL);
+        return NULL;
+    }
+    }
+}
+
+
+/*
+ * JNI reflection support: convert reflection object to Field ptr.
+ */
+Field* dvmGetFieldFromReflectObj(Object* obj)
+{
+    ClassObject* clazz;
+    int slot;
+
+    assert(obj->clazz == gDvm.classJavaLangReflectField);
+    clazz = (ClassObject*)dvmGetFieldObject(obj,
+                                gDvm.offJavaLangReflectField_declClass);
+    slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectField_slot);
+
+    /* must initialize the class before returning a field ID */
+    if (!dvmInitClass(clazz))
+        return NULL;
+
+    return dvmSlotToField(clazz, slot);
+}
+
+/*
+ * JNI reflection support: convert reflection object to Method ptr.
+ */
+Method* dvmGetMethodFromReflectObj(Object* obj)
+{
+    ClassObject* clazz;
+    int slot;
+
+    if (obj->clazz == gDvm.classJavaLangReflectConstructor) {
+        clazz = (ClassObject*)dvmGetFieldObject(obj,
+                                gDvm.offJavaLangReflectConstructor_declClass);
+        slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectConstructor_slot);
+    } else if (obj->clazz == gDvm.classJavaLangReflectMethod) {
+        clazz = (ClassObject*)dvmGetFieldObject(obj,
+                                gDvm.offJavaLangReflectMethod_declClass);
+        slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectMethod_slot);
+    } else {
+        assert(false);
+        return NULL;
+    }
+
+    /* must initialize the class before returning a method ID */
+    if (!dvmInitClass(clazz))
+        return NULL;
+
+    return dvmSlotToMethod(clazz, slot);
+}
+
+/*
+ * JNI reflection support: convert Field to reflection object.
+ *
+ * The return value is a java.lang.reflect.Field.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+        dvmInitClass(gDvm.classJavaLangReflectField);
+
+    /* caller must dvmReleaseTrackedAlloc(result) */
+    return createFieldObject(field, clazz);
+}
+
+/*
+ * JNI reflection support: convert Method to reflection object.
+ *
+ * The returned object will be either a java.lang.reflect.Method or
+ * .Constructor, depending on whether "method" is a constructor.
+ *
+ * This is also used for certain "system" annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method)
+{
+    UNUSED_PARAMETER(clazz);
+
+    if (strcmp(method->name, "<init>") == 0) {
+        if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor))
+            dvmInitClass(gDvm.classJavaLangReflectConstructor);
+
+        return createConstructorObject(method);
+    } else {
+        if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+            dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+        return dvmCreateReflectMethodObject(method);
+    }
+}
diff --git a/vm/reflect/Reflect.h b/vm/reflect/Reflect.h
new file mode 100644
index 0000000..21bb08d
--- /dev/null
+++ b/vm/reflect/Reflect.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Basic reflection calls and utility functions.
+ */
+#ifndef DALVIK_REFLECT_REFLECT_H_
+#define DALVIK_REFLECT_REFLECT_H_
+
+/*
+ * During startup, validate the "box" classes, e.g. java/lang/Integer.
+ */
+bool dvmValidateBoxClasses();
+
+/*
+ * Get all fields declared by a class.
+ *
+ * Includes both class and instance fields.
+ */
+ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get the named field.
+ */
+Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj);
+
+/*
+ * Get all constructors declared by a class.
+ */
+ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get all methods declared by a class.
+ *
+ * This includes both static and virtual methods, and can include private
+ * members if "publicOnly" is false.  It does not include Miranda methods,
+ * since those weren't declared in the class, or constructors.
+ */
+ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get the named method.
+ */
+Object* dvmGetDeclaredConstructorOrMethod(ClassObject* clazz,
+    StringObject* nameObj, ArrayObject* args);
+
+/*
+ * Get all interfaces a class implements. If this is unable to allocate
+ * the result array, this raises an OutOfMemoryError and returns NULL.
+ */
+ArrayObject* dvmGetInterfaces(ClassObject* clazz);
+
+/*
+ * Convert slot numbers back to objects.
+ */
+Field* dvmSlotToField(ClassObject* clazz, int slot);
+Method* dvmSlotToMethod(ClassObject* clazz, int slot);
+
+/*
+ * Convert a primitive value, performing a widening conversion if necessary.
+ */
+int dvmConvertPrimitiveValue(PrimitiveType srcType,
+    PrimitiveType dstType, const s4* srcPtr, s4* dstPtr);
+
+/*
+ * Convert the argument to the specified type.
+ *
+ * Returns the width of the argument (1 for most types, 2 for J/D, -1 on
+ * error).
+ */
+int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* ins);
+
+/*
+ * Box a primitive value into an object.  If "returnType" is
+ * not primitive, this just returns "value" cast to an object.
+ */
+DataObject* dvmBoxPrimitive(JValue value, ClassObject* returnType);
+
+/*
+ * Unwrap a boxed primitive.  If "returnType" is not primitive, this just
+ * returns "value" cast into a JValue.
+ */
+bool dvmUnboxPrimitive(Object* value, ClassObject* returnType,
+    JValue* pResult);
+
+/*
+ * Return the class object that matches the method's signature.  For
+ * primitive types, returns the box class.
+ */
+ClassObject* dvmGetBoxedReturnType(const Method* meth);
+
+/*
+ * JNI reflection support.
+ */
+Field* dvmGetFieldFromReflectObj(Object* obj);
+Method* dvmGetMethodFromReflectObj(Object* obj);
+Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field);
+Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method);
+
+/*
+ * Quick test to determine if the method in question is a reflection call.
+ * Used for some stack parsing.  Currently defined as "the method's declaring
+ * class is java.lang.reflect.Method".
+ */
+INLINE bool dvmIsReflectionMethod(const Method* method)
+{
+    return (method->clazz == gDvm.classJavaLangReflectMethod);
+}
+
+/*
+ * Proxy class generation.
+ */
+ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
+    Object* loader);
+
+/*
+ * Create a new java.lang.reflect.Method object based on "meth".
+ */
+Object* dvmCreateReflectMethodObject(const Method* meth);
+
+/*
+ * Return an array of Annotation objects for the specified piece.  For method
+ * parameters this is an array of arrays of Annotation objects.
+ *
+ * Method also applies to Constructor.
+ */
+ArrayObject* dvmGetClassAnnotations(const ClassObject* clazz);
+ArrayObject* dvmGetMethodAnnotations(const Method* method);
+ArrayObject* dvmGetFieldAnnotations(const Field* field);
+ArrayObject* dvmGetParameterAnnotations(const Method* method);
+
+/*
+ * Return the annotation if it exists.
+ */
+Object* dvmGetClassAnnotation(const ClassObject* clazz, const ClassObject* annotationClazz);
+Object* dvmGetMethodAnnotation(const ClassObject* clazz, const Method* method,
+        const ClassObject* annotationClazz);
+Object* dvmGetFieldAnnotation(const ClassObject* clazz, const Field* method,
+        const ClassObject* annotationClazz);
+
+/*
+ * Return true if the annotation exists.
+ */
+bool dvmIsClassAnnotationPresent(const ClassObject* clazz, const ClassObject* annotationClazz);
+bool dvmIsMethodAnnotationPresent(const ClassObject* clazz, const Method* method,
+        const ClassObject* annotationClazz);
+bool dvmIsFieldAnnotationPresent(const ClassObject* clazz, const Field* method,
+        const ClassObject* annotationClazz);
+
+/*
+ * Find the default value for an annotation member.
+ */
+Object* dvmGetAnnotationDefaultValue(const Method* method);
+
+/*
+ * Get the list of thrown exceptions for a method.  Returns NULL if there
+ * are no exceptions listed.
+ */
+ArrayObject* dvmGetMethodThrows(const Method* method);
+
+/*
+ * Get the Signature annotation.
+ */
+ArrayObject* dvmGetClassSignatureAnnotation(const ClassObject* clazz);
+ArrayObject* dvmGetMethodSignatureAnnotation(const Method* method);
+ArrayObject* dvmGetFieldSignatureAnnotation(const Field* field);
+
+/*
+ * Get the EnclosingMethod attribute from an annotation.  Returns a Method
+ * object, or NULL.
+ */
+Object* dvmGetEnclosingMethod(const ClassObject* clazz);
+
+/*
+ * Return clazz's declaring class, or NULL if there isn't one.
+ */
+ClassObject* dvmGetDeclaringClass(const ClassObject* clazz);
+
+/*
+ * Return clazz's enclosing class, or NULL if there isn't one.
+ */
+ClassObject* dvmGetEnclosingClass(const ClassObject* clazz);
+
+/*
+ * Get the EnclosingClass attribute from an annotation.  If found, returns
+ * "true".  A String with the original name of the class and the original
+ * access flags are returned through the arguments.  (The name will be NULL
+ * for an anonymous inner class.)
+ */
+bool dvmGetInnerClass(const ClassObject* clazz, StringObject** pName,
+    int* pAccessFlags);
+
+/*
+ * Get an array of class objects from the MemberClasses annotation.  Returns
+ * NULL if none found.
+ */
+ArrayObject* dvmGetDeclaredClasses(const ClassObject* clazz);
+
+/*
+ * Used to pass values out of annotation (and encoded array) processing
+ * functions.
+ */
+struct AnnotationValue {
+    JValue  value;
+    u1      type;
+};
+
+
+/**
+ * Iterator structure for iterating over DexEncodedArray instances. The
+ * structure should be treated as opaque.
+ */
+struct EncodedArrayIterator {
+    const u1* cursor;                    /* current cursor */
+    u4 elementsLeft;                     /* number of elements left to read */
+    const DexEncodedArray* encodedArray; /* instance being iterated over */
+    u4 size;                             /* number of elements in instance */
+    const ClassObject* clazz;            /* class to resolve with respect to */
+};
+
+/**
+ * Initializes an encoded array iterator.
+ *
+ * @param iterator iterator to initialize
+ * @param encodedArray encoded array to iterate over
+ * @param clazz class to use when resolving strings and types
+ */
+void dvmEncodedArrayIteratorInitialize(EncodedArrayIterator* iterator,
+        const DexEncodedArray* encodedArray, const ClassObject* clazz);
+
+/**
+ * Returns whether there are more elements to be read.
+ */
+bool dvmEncodedArrayIteratorHasNext(const EncodedArrayIterator* iterator);
+
+/**
+ * Returns the next decoded value from the iterator, advancing its
+ * cursor. This returns primitive values in their corresponding union
+ * slots, and returns everything else (including nulls) as object
+ * references in the "l" union slot.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on any returned reference.
+ *
+ * @param value pointer to store decoded value into
+ * @returns true if a value was decoded and the cursor advanced; false if
+ * the last value had already been decoded or if there was a problem decoding
+ */
+bool dvmEncodedArrayIteratorGetNext(EncodedArrayIterator* iterator,
+        AnnotationValue* value);
+
+#endif  // DALVIK_REFLECT_REFLECT_H_
diff --git a/vm/test/AtomicTest.cpp b/vm/test/AtomicTest.cpp
new file mode 100644
index 0000000..c66b897
--- /dev/null
+++ b/vm/test/AtomicTest.cpp
@@ -0,0 +1,377 @@
+/*
+ * 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 int64_t wideCasTest = 0x6600000077000000LL;
+
+/*
+ * Get a relative time value.
+ */
+static int64_t getRelativeTimeNsec()
+{
+#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() __attribute__((noinline));
+static void decr() __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()
+{
+    incTest++;
+}
+static void decr()
+{
+    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
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            android_atomic_release_cas(7, 7, valuePtr);
+            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()
+{
+    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()
+{
+    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;
+    android_atomic_and(0xfffdaf96, &andTest);
+    android_atomic_or(0xdeaaeb00, &orTest);
+    if (android_atomic_release_cas(failingCasTest+1, failingCasTest-1,
+            &failingCasTest) == 0)
+        dvmFprintf(stdout, "failing test did not fail!\n");
+
+    dvmFprintf(stdout, "andTest = %#x\n", andTest);
+    dvmFprintf(stdout, "orTest = %#x\n", orTest);
+    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..e5acdfd
--- /dev/null
+++ b/vm/test/Test.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Internal unit tests.
+ */
+#ifndef DALVIK_TEST_TEST_H_
+#define DALVIK_TEST_TEST_H_
+
+bool dvmTestHash(void);
+bool dvmTestAtomicSpeed(void);
+bool dvmTestIndirectRefTable(void);
+
+#endif  // DALVIK_TEST_TEST_H_
diff --git a/vm/test/TestHash.cpp b/vm/test/TestHash.cpp
new file mode 100644
index 0000000..52e421b
--- /dev/null
+++ b/vm/test/TestHash.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Test the hash table functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+#ifndef NDEBUG
+
+#define kNumTestEntries 14
+
+/*
+ * Test foreach.
+ */
+static int printFunc(void* data, void* arg)
+{
+    //printf("  '%s'\n", (const char*) data);
+    // (should verify strings)
+
+    int* count = (int*) arg;
+    (*count)++;
+    return 0;
+}
+static void dumpForeach(HashTable* pTab)
+{
+    int count = 0;
+
+    //printf("Print from foreach:\n");
+    dvmHashForeach(pTab, printFunc, &count);
+    if (count != kNumTestEntries) {
+        ALOGE("TestHash foreach test failed");
+        assert(false);
+    }
+}
+
+/*
+ * Test iterator.
+ */
+static void dumpIterator(HashTable* pTab)
+{
+    int count = 0;
+
+    //printf("Print from iterator:\n");
+    HashIter iter;
+    for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        //const char* str = (const char*) dvmHashIterData(&iter);
+        //printf("  '%s'\n", str);
+        // (should verify strings)
+        count++;
+    }
+    if (count != kNumTestEntries) {
+        ALOGE("TestHash iterator test failed");
+        assert(false);
+    }
+}
+
+/*
+ * Some quick hash table tests.
+ */
+bool dvmTestHash()
+{
+    HashTable* pTab;
+    char tmpStr[64];
+    const char* str;
+    u4 hash;
+    int i;
+
+    ALOGV("TestHash BEGIN");
+
+    pTab = dvmHashTableCreate(dvmHashSize(12), free);
+    if (pTab == NULL)
+        return false;
+
+    dvmHashTableLock(pTab);
+
+    /* add some entries */
+    for (i = 0; i < kNumTestEntries; i++) {
+        sprintf(tmpStr, "entry %d", i);
+        hash = dvmComputeUtf8Hash(tmpStr);
+        dvmHashTableLookup(pTab, hash, strdup(tmpStr),
+            (HashCompareFunc) strcmp, true);
+    }
+
+    dvmHashTableUnlock(pTab);
+
+    /* make sure we can find all entries */
+    for (i = 0; i < kNumTestEntries; i++) {
+        sprintf(tmpStr, "entry %d", i);
+        hash = dvmComputeUtf8Hash(tmpStr);
+        str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr,
+                (HashCompareFunc) strcmp, false);
+        if (str == NULL) {
+            ALOGE("TestHash: failure: could not find '%s'", tmpStr);
+            /* return false */
+        }
+    }
+
+    /* make sure it behaves correctly when entry not found and !doAdd */
+    sprintf(tmpStr, "entry %d", 17);
+    hash = dvmComputeUtf8Hash(tmpStr);
+    str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr,
+            (HashCompareFunc) strcmp, false);
+    if (str == NULL) {
+        /* good */
+    } else {
+        ALOGE("TestHash found nonexistent string (improper add?)");
+    }
+
+    dumpForeach(pTab);
+    dumpIterator(pTab);
+
+    /* make sure they all get freed */
+    dvmHashTableFree(pTab);
+
+
+    /*
+     * Round 2: verify probing & tombstones.
+     */
+    pTab = dvmHashTableCreate(dvmHashSize(2), free);
+    if (pTab == NULL)
+        return false;
+
+    hash = 0;
+
+    /* two entries, same hash, different values */
+    const char* str1;
+    str1 = (char*) dvmHashTableLookup(pTab, hash, strdup("one"),
+            (HashCompareFunc) strcmp, true);
+    assert(str1 != NULL);
+    str = (const char*) dvmHashTableLookup(pTab, hash, strdup("two"),
+            (HashCompareFunc) strcmp, true);
+
+    /* remove the first one */
+    if (!dvmHashTableRemove(pTab, hash, (void*)str1))
+        ALOGE("TestHash failed to delete item");
+    else
+        free((void*)str1);     // "Remove" doesn't call the free func
+
+    /* make sure iterator doesn't included deleted entries */
+    int count = 0;
+    HashIter iter;
+    for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        count++;
+    }
+    if (count != 1) {
+        ALOGE("TestHash wrong number of entries (%d)", count);
+    }
+
+    /* see if we can find them */
+    str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"one",
+            (HashCompareFunc) strcmp,false);
+    if (str != NULL)
+        ALOGE("TestHash deleted entry has returned!");
+    str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"two",
+            (HashCompareFunc) strcmp,false);
+    if (str == NULL)
+        ALOGE("TestHash entry vanished");
+
+    /* force a table realloc to exercise tombstone removal */
+    for (i = 0; i < 20; i++) {
+        sprintf(tmpStr, "entry %d", i);
+        str = (const char*) dvmHashTableLookup(pTab, hash, strdup(tmpStr),
+                (HashCompareFunc) strcmp, true);
+        assert(str != NULL);
+    }
+
+    dvmHashTableFree(pTab);
+    ALOGV("TestHash END");
+
+    return true;
+}
+
+#endif /*NDEBUG*/
diff --git a/vm/test/TestIndirectRefTable.cpp b/vm/test/TestIndirectRefTable.cpp
new file mode 100644
index 0000000..f144254
--- /dev/null
+++ b/vm/test/TestIndirectRefTable.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Test the indirect reference table implementation.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <sys/time.h>
+
+#ifndef NDEBUG
+
+#define DBUG_MSG    ALOGI
+
+class Stopwatch {
+public:
+    Stopwatch() {
+        reset();
+    }
+
+    void reset() {
+        start_ = now();
+    }
+
+    float elapsedSeconds() {
+        return (now() - start_) * 0.000001f;
+    }
+
+private:
+    u8 start_;
+
+    static u8 now() {
+#ifdef HAVE_POSIX_CLOCKS
+        struct timespec tm;
+        clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+        return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
+#else
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return tv.tv_sec * 1000000LL + tv.tv_usec;
+#endif
+    }
+};
+
+/*
+ * Basic add/get/delete tests in an unsegmented table.
+ */
+static bool basicTest()
+{
+    static const int kTableMax = 20;
+    IndirectRefTable irt;
+    IndirectRef iref0, iref1, iref2, iref3;
+    IndirectRef manyRefs[kTableMax];
+    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
+    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    const u4 cookie = IRT_FIRST_SEGMENT;
+    bool result = false;
+
+    if (!irt.init(kTableMax/2, kTableMax, kIndirectKindGlobal)) {
+        return false;
+    }
+
+    iref0 = (IndirectRef) 0x11110;
+    if (irt.remove(cookie, iref0)) {
+        ALOGE("unexpectedly successful removal");
+        goto bail;
+    }
+
+    /*
+     * Add three, check, remove in the order in which they were added.
+     */
+    DBUG_MSG("+++ START fifo\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        ALOGE("trivial add1 failed");
+        goto bail;
+    }
+
+    if (irt.get(iref0) != obj0 ||
+            irt.get(iref1) != obj1 ||
+            irt.get(iref2) != obj2) {
+        ALOGE("objects don't match expected values %p %p %p vs. %p %p %p",
+                irt.get(iref0), irt.get(iref1), irt.get(iref2),
+                obj0, obj1, obj2);
+        goto bail;
+    } else {
+        DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1);
+    }
+
+    if (!irt.remove(cookie, iref0) ||
+            !irt.remove(cookie, iref1) ||
+            !irt.remove(cookie, iref2))
+    {
+        ALOGE("fifo deletion failed");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (irt.capacity() != 0) {
+        ALOGE("fifo del not empty");
+        goto bail;
+    }
+
+    /* get invalid entry (off the end of the list) */
+    if (irt.get(iref0) != kInvalidIndirectRefObject) {
+        ALOGE("stale entry get succeeded unexpectedly");
+        goto bail;
+    }
+
+    /*
+     * Add three, remove in the opposite order.
+     */
+    DBUG_MSG("+++ START lifo\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        ALOGE("trivial add2 failed");
+        goto bail;
+    }
+
+    if (!irt.remove(cookie, iref2) ||
+            !irt.remove(cookie, iref1) ||
+            !irt.remove(cookie, iref0))
+    {
+        ALOGE("lifo deletion failed");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (irt.capacity() != 0) {
+        ALOGE("lifo del not empty");
+        goto bail;
+    }
+
+    /*
+     * Add three, remove middle / middle / bottom / top.  (Second attempt
+     * to remove middle should fail.)
+     */
+    DBUG_MSG("+++ START unorder\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        ALOGE("trivial add3 failed");
+        goto bail;
+    }
+
+    if (irt.capacity() != 3) {
+        ALOGE("expected 3 entries, found %d", irt.capacity());
+        goto bail;
+    }
+
+    if (!irt.remove(cookie, iref1) || irt.remove(cookie, iref1)) {
+        ALOGE("unorder deletion1 failed");
+        goto bail;
+    }
+
+    /* get invalid entry (from hole) */
+    if (irt.get(iref1) != kInvalidIndirectRefObject) {
+        ALOGE("hole get succeeded unexpectedly");
+        goto bail;
+    }
+
+    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
+        ALOGE("unorder deletion2 failed");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (irt.capacity() != 0) {
+        ALOGE("unorder del not empty");
+        goto bail;
+    }
+
+    /*
+     * Add four entries.  Remove #1, add new entry, verify that table size
+     * is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
+     * that we delete one and don't hole-compact the other.
+     */
+    DBUG_MSG("+++ START hole fill\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    iref3 = irt.add(cookie, obj3);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) {
+        ALOGE("trivial add4 failed");
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref1)) {
+        ALOGE("remove 1 of 4 failed");
+        goto bail;
+    }
+    iref1 = irt.add(cookie, obj1);
+    if (irt.capacity() != 4) {
+        ALOGE("hole not filled");
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref1) || !irt.remove(cookie, iref3)) {
+        ALOGE("remove 1/3 failed");
+        goto bail;
+    }
+    if (irt.capacity() != 3) {
+        ALOGE("should be 3 after two deletions");
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
+        ALOGE("remove 2/0 failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("not empty after split remove");
+        goto bail;
+    }
+
+    /*
+     * Add an entry, remove it, add a new entry, and try to use the original
+     * iref.  They have the same slot number but are for different objects.
+     * With the extended checks in place, this should fail.
+     */
+    DBUG_MSG("+++ START switched\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    iref1 = irt.add(cookie, obj1);
+    if (irt.remove(cookie, iref0)) {
+        ALOGE("mismatched del succeeded (%p vs %p)", iref0, iref1);
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref1)) {
+        ALOGE("switched del failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("switching del not empty");
+        goto bail;
+    }
+
+    /*
+     * Same as above, but with the same object.  A more rigorous checker
+     * (e.g. with slot serialization) will catch this.
+     */
+    DBUG_MSG("+++ START switched same object\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    iref1 = irt.add(cookie, obj0);
+    if (iref0 != iref1) {
+        /* try 0, should not work */
+        if (irt.remove(cookie, iref0)) {
+            ALOGE("temporal del succeeded (%p vs %p)", iref0, iref1);
+            goto bail;
+        }
+    }
+    if (!irt.remove(cookie, iref1)) {
+        ALOGE("temporal cleanup failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("temporal del not empty");
+        goto bail;
+    }
+
+    DBUG_MSG("+++ START null lookup\n");
+    if (irt.get(NULL) != kInvalidIndirectRefObject) {
+        ALOGE("null lookup succeeded");
+        goto bail;
+    }
+
+    DBUG_MSG("+++ START stale lookup\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    if (irt.get(iref0) != kInvalidIndirectRefObject) {
+        ALOGE("stale lookup succeeded");
+        goto bail;
+    }
+
+    /*
+     * Test table overflow.
+     */
+    DBUG_MSG("+++ START overflow\n");
+    int i;
+    for (i = 0; i < kTableMax; i++) {
+        manyRefs[i] = irt.add(cookie, obj0);
+        if (manyRefs[i] == NULL) {
+            ALOGE("Failed adding %d of %d", i, kTableMax);
+            goto bail;
+        }
+    }
+    if (irt.add(cookie, obj0) != NULL) {
+        ALOGE("Table overflow succeeded");
+        goto bail;
+    }
+    if (irt.capacity() != (size_t)kTableMax) {
+        ALOGE("Expected %d entries, found %d", kTableMax, irt.capacity());
+        goto bail;
+    }
+    irt.dump("table with 20 entries, all filled");
+    for (i = 0; i < kTableMax-1; i++) {
+        if (!irt.remove(cookie, manyRefs[i])) {
+            ALOGE("multi-remove failed at %d", i);
+            goto bail;
+        }
+    }
+    irt.dump("table with 20 entries, 19 of them holes");
+    /* because of removal order, should have 20 entries, 19 of them holes */
+    if (irt.capacity() != (size_t)kTableMax) {
+        ALOGE("Expected %d entries (with holes), found %d",
+                kTableMax, irt.capacity());
+        goto bail;
+    }
+    if (!irt.remove(cookie, manyRefs[kTableMax-1])) {
+        ALOGE("multi-remove final failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("multi-del not empty");
+        goto bail;
+    }
+
+    /* Done */
+    DBUG_MSG("+++ basic test complete\n");
+    result = true;
+
+bail:
+    irt.destroy();
+    return result;
+}
+
+static bool performanceTest()
+{
+    static const int kTableMax = 100;
+    IndirectRefTable irt;
+    IndirectRef manyRefs[kTableMax];
+    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
+    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    const u4 cookie = IRT_FIRST_SEGMENT;
+    const int kLoops = 100000;
+    Stopwatch stopwatch;
+
+    DBUG_MSG("+++ START performance\n");
+
+    if (!irt.init(kTableMax, kTableMax, kIndirectKindGlobal)) {
+        return false;
+    }
+
+    stopwatch.reset();
+    for (int loop = 0; loop < kLoops; loop++) {
+        for (int i = 0; i < kTableMax; i++) {
+            manyRefs[i] = irt.add(cookie, obj0);
+        }
+        for (int i = 0; i < kTableMax; i++) {
+            irt.remove(cookie, manyRefs[i]);
+        }
+    }
+    DBUG_MSG("Add/remove %d objects FIFO order, %d iterations, %0.3fms / iteration",
+            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000 / kLoops);
+
+    stopwatch.reset();
+    for (int loop = 0; loop < kLoops; loop++) {
+        for (int i = 0; i < kTableMax; i++) {
+            manyRefs[i] = irt.add(cookie, obj0);
+        }
+        for (int i = kTableMax; i-- > 0; ) {
+            irt.remove(cookie, manyRefs[i]);
+        }
+    }
+    DBUG_MSG("Add/remove %d objects LIFO order, %d iterations, %0.3fms / iteration",
+            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);
+
+    for (int i = 0; i < kTableMax; i++) {
+        manyRefs[i] = irt.add(cookie, obj0);
+    }
+    stopwatch.reset();
+    for (int loop = 0; loop < kLoops; loop++) {
+        for (int i = 0; i < kTableMax; i++) {
+            irt.get(manyRefs[i]);
+        }
+    }
+    DBUG_MSG("Get %d objects, %d iterations, %0.3fms / iteration",
+            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);
+    for (int i = kTableMax; i-- > 0; ) {
+        irt.remove(cookie, manyRefs[i]);
+    }
+
+    irt.destroy();
+    return true;
+}
+
+/*
+ * Some quick tests.
+ */
+bool dvmTestIndirectRefTable()
+{
+    if (!basicTest()) {
+        ALOGE("IRT basic test failed");
+        return false;
+    }
+
+    if (!performanceTest()) {
+        ALOGE("IRT performance test failed");
+        return false;
+    }
+
+    return true;
+}
+
+#endif /*NDEBUG*/